From 4c3f6c06bc1dd39dc9788dfa20c6fd14e5dd52f3 Mon Sep 17 00:00:00 2001 From: Chetan Patil Date: Wed, 5 Oct 2022 16:20:11 +0200 Subject: [PATCH 1/9] fixes: #1121 fix date-to-day conversion issue - updated functionality to use Zeller's congruence to find day - updated test cases --- Conversions/DateToDay.js | 55 +++++++++++++++++------------- Conversions/test/DateToDay.test.js | 4 +-- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/Conversions/DateToDay.js b/Conversions/DateToDay.js index 8683bdec4b..809a9af43c 100644 --- a/Conversions/DateToDay.js +++ b/Conversions/DateToDay.js @@ -9,34 +9,34 @@ algorithm shown below gives us the number of the day and finally converts it to the name of the day. - Algorithm & Explanation : https://cs.uwaterloo.ca/~alopez-o/math-faq/node73.html + Algorithm & Explanation : https://en.wikipedia.org/wiki/Zeller%27s_congruence */ // March is taken as the first month of the year. const calcMonthList = { - 1: 11, - 2: 12, - 3: 1, - 4: 2, - 5: 3, - 6: 4, - 7: 5, - 8: 6, - 9: 7, - 10: 8, - 11: 9, - 12: 10 + 1: 13, + 2: 14, + 3: 3, + 4: 4, + 5: 5, + 6: 6, + 7: 7, + 8: 8, + 9: 9, + 10: 10, + 11: 11, + 12: 12 } // show the week day in a number : Sunday - Saturday => 0 - 6 const daysNameList = { // weeks-day - 0: 'Sunday', - 1: 'Monday', - 2: 'Tuesday', - 3: 'Wednesday', - 4: 'Thursday', - 5: 'Friday', - 6: 'Saturday' + 1: 'Sunday', + 2: 'Monday', + 3: 'Tuesday', + 4: 'Wednesday', + 5: 'Thursday', + 6: 'Friday', + 0: 'Saturday' } const DateToDay = (date) => { @@ -45,18 +45,25 @@ const DateToDay = (date) => { return new TypeError('Argument is not a string.') } // extract the date - const [day, month, year] = date.split('/').map((x) => Number(x)) + let [day, month, year] = date.split('/').map((x) => Number(x)) // check the data are valid or not. - if (day < 0 || day > 31 || month > 12 || month < 0) { + if (day < 1 || day > 31 || month > 12 || month < 1) { return new TypeError('Date is not valid.') } + + // In case of Jan and Feb, we consider it as previous year + // e.g., 1/1/1987 here year is 1986 (-1) + if (month < 3) { + year -= 1 + } + // divide year to century and yearDigit value. const yearDigit = (year % 100) const century = Math.floor(year / 100) // Apply the algorithm shown above - const weekDay = Math.abs((day + Math.floor((2.6 * calcMonthList[month]) - 0.2) - (2 * century) + yearDigit + Math.floor(yearDigit / 4) + Math.floor(century / 4)) % 7) + const weekDay = Math.abs((day + Math.floor((calcMonthList[month] + 1) * 2.6) + yearDigit + (yearDigit / 4) + (century / 4) - (2 * century)) % 7) // return the weekDay name. - return daysNameList[weekDay] + return daysNameList[Math.floor(weekDay)] } // Example : DateToDay("18/12/2020") => Friday diff --git a/Conversions/test/DateToDay.test.js b/Conversions/test/DateToDay.test.js index a932dff08c..d5cec6c4fa 100644 --- a/Conversions/test/DateToDay.test.js +++ b/Conversions/test/DateToDay.test.js @@ -2,7 +2,7 @@ import { DateToDay } from '../DateToDay' test('The date 18/02/2001 is Monday', () => { const res = DateToDay('18/02/2001') - expect(res).toBe('Monday') + expect(res).toBe('Sunday') }) test('The date 18/12/2020 is Friday', () => { @@ -16,5 +16,5 @@ test('The date 12/12/2012 is Wednesday', () => { }) test('The date 01/01/2001 is Friday', () => { const res = DateToDay('01/01/2001') - expect(res).toBe('Friday') + expect(res).toBe('Monday') }) From c18e7c9220c6c5084b90e77fb92f43c058084fb2 Mon Sep 17 00:00:00 2001 From: Chetan Patil Date: Wed, 5 Oct 2022 16:38:18 +0200 Subject: [PATCH 2/9] test: added test case with date converting wrong day --- Conversions/test/DateToDay.test.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Conversions/test/DateToDay.test.js b/Conversions/test/DateToDay.test.js index d5cec6c4fa..ca6b47658e 100644 --- a/Conversions/test/DateToDay.test.js +++ b/Conversions/test/DateToDay.test.js @@ -18,3 +18,8 @@ test('The date 01/01/2001 is Friday', () => { const res = DateToDay('01/01/2001') expect(res).toBe('Monday') }) + +test('The date 1/1/2020 is Wednesday', () => { + const res = DateToDay('1/1/2020') + expect(res).toBe('Wednesday') +}) From eea609af6c641b066c7a8e63c93a5d9b583845d4 Mon Sep 17 00:00:00 2001 From: Chetan Patil Date: Fri, 7 Oct 2022 11:41:53 +0200 Subject: [PATCH 3/9] Feedback implementation - updated comments - removed monthList completely - converted daysName List to an array --- Conversions/DateToDay.js | 42 ++++++++++++---------------------------- 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/Conversions/DateToDay.js b/Conversions/DateToDay.js index 809a9af43c..840742406f 100644 --- a/Conversions/DateToDay.js +++ b/Conversions/DateToDay.js @@ -12,32 +12,8 @@ Algorithm & Explanation : https://en.wikipedia.org/wiki/Zeller%27s_congruence */ -// March is taken as the first month of the year. -const calcMonthList = { - 1: 13, - 2: 14, - 3: 3, - 4: 4, - 5: 5, - 6: 6, - 7: 7, - 8: 8, - 9: 9, - 10: 10, - 11: 11, - 12: 12 -} - -// show the week day in a number : Sunday - Saturday => 0 - 6 -const daysNameList = { // weeks-day - 1: 'Sunday', - 2: 'Monday', - 3: 'Tuesday', - 4: 'Wednesday', - 5: 'Thursday', - 6: 'Friday', - 0: 'Saturday' -} +// Array holding name of the day: Saturday - Sunday - Friday => 0 - 1 - 6 +const daysNameArr = ['Saturday', 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'] const DateToDay = (date) => { // firstly, check that input is a string or not. @@ -51,19 +27,25 @@ const DateToDay = (date) => { return new TypeError('Date is not valid.') } - // In case of Jan and Feb, we consider it as previous year + // In case of Jan and Feb: + // Year: we consider it as previous year // e.g., 1/1/1987 here year is 1986 (-1) + // Month: we consider value as 13 & 14 respectively if (month < 3) { - year -= 1 + year-- + month += 12 } // divide year to century and yearDigit value. const yearDigit = (year % 100) const century = Math.floor(year / 100) + // Apply the algorithm shown above - const weekDay = Math.abs((day + Math.floor((calcMonthList[month] + 1) * 2.6) + yearDigit + (yearDigit / 4) + (century / 4) - (2 * century)) % 7) + const weekDay = Math.abs((day + Math.floor((month + 1) * 2.6) + yearDigit + (yearDigit / 4) + (century / 4) - (2 * century)) % 7) + + // There is possibility that weekDay is in decimal, so Math.floor() needed // return the weekDay name. - return daysNameList[Math.floor(weekDay)] + return daysNameArr[Math.floor(weekDay)] } // Example : DateToDay("18/12/2020") => Friday From 032d8d7208c5217fdc982782c14aca190769878a Mon Sep 17 00:00:00 2001 From: Chetan Patil Date: Fri, 7 Oct 2022 15:25:53 +0200 Subject: [PATCH 4/9] :recycle: corrected use of Math.abs and Math.floor --- Conversions/DateToDay.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Conversions/DateToDay.js b/Conversions/DateToDay.js index 840742406f..76dcfac20c 100644 --- a/Conversions/DateToDay.js +++ b/Conversions/DateToDay.js @@ -41,11 +41,11 @@ const DateToDay = (date) => { const century = Math.floor(year / 100) // Apply the algorithm shown above - const weekDay = Math.abs((day + Math.floor((month + 1) * 2.6) + yearDigit + (yearDigit / 4) + (century / 4) - (2 * century)) % 7) + const weekDay = Math.floor((day + Math.floor((month + 1) * 2.6) + yearDigit + (yearDigit / 4) + (century / 4) - (2 * century)) % 7) // There is possibility that weekDay is in decimal, so Math.floor() needed // return the weekDay name. - return daysNameArr[Math.floor(weekDay)] + return daysNameArr[weekDay] } // Example : DateToDay("18/12/2020") => Friday From 23747384726af854c9e0d546d5c35e7c49d8d140 Mon Sep 17 00:00:00 2001 From: Chetan Patil <31822501+Chetan07j@users.noreply.github.com> Date: Sat, 8 Oct 2022 00:09:34 +0200 Subject: [PATCH 5/9] :bug: Math.floor updation --- Conversions/DateToDay.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Conversions/DateToDay.js b/Conversions/DateToDay.js index 76dcfac20c..00e01b6d6d 100644 --- a/Conversions/DateToDay.js +++ b/Conversions/DateToDay.js @@ -41,7 +41,7 @@ const DateToDay = (date) => { const century = Math.floor(year / 100) // Apply the algorithm shown above - const weekDay = Math.floor((day + Math.floor((month + 1) * 2.6) + yearDigit + (yearDigit / 4) + (century / 4) - (2 * century)) % 7) + const weekDay = (day + Math.floor((month + 1) * 2.6) + yearDigit + Math.floor(yearDigit / 4) + Math.floor(century / 4) - (2 * century)) % 7 // There is possibility that weekDay is in decimal, so Math.floor() needed // return the weekDay name. From 4a534f42d5940de0aa35ca83c00271eccacbf29a Mon Sep 17 00:00:00 2001 From: Chetan Patil <31822501+Chetan07j@users.noreply.github.com> Date: Sat, 8 Oct 2022 18:50:53 +0200 Subject: [PATCH 6/9] added more test cases --- Conversions/test/DateToDay.test.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Conversions/test/DateToDay.test.js b/Conversions/test/DateToDay.test.js index ca6b47658e..f0809821db 100644 --- a/Conversions/test/DateToDay.test.js +++ b/Conversions/test/DateToDay.test.js @@ -1,6 +1,6 @@ import { DateToDay } from '../DateToDay' -test('The date 18/02/2001 is Monday', () => { +test('The date 18/02/2001 is Sunday', () => { const res = DateToDay('18/02/2001') expect(res).toBe('Sunday') }) @@ -14,7 +14,7 @@ test('The date 12/12/2012 is Wednesday', () => { const res = DateToDay('12/12/2012') expect(res).toBe('Wednesday') }) -test('The date 01/01/2001 is Friday', () => { +test('The date 01/01/2001 is Monday', () => { const res = DateToDay('01/01/2001') expect(res).toBe('Monday') }) @@ -23,3 +23,13 @@ test('The date 1/1/2020 is Wednesday', () => { const res = DateToDay('1/1/2020') expect(res).toBe('Wednesday') }) + +test('The date 2/3/2014 is Sunday', () => { + const res = DateToDay('2/3/2014') + expect(res).toBe('Sunday') +}) + +test('The date 28/2/2017 is Tuesday', () => { + const res = DateToDay('28/2/2017') + expect(res).toBe('Tuesday') +}) From a48e7a0fd7a59c5d3ea4efcea068fe1896990d6d Mon Sep 17 00:00:00 2001 From: Chetan Patil <31822501+Chetan07j@users.noreply.github.com> Date: Sat, 8 Oct 2022 18:52:07 +0200 Subject: [PATCH 7/9] :recycle: modulo operator implementation for computer --- Conversions/DateToDay.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Conversions/DateToDay.js b/Conversions/DateToDay.js index 00e01b6d6d..542432cabf 100644 --- a/Conversions/DateToDay.js +++ b/Conversions/DateToDay.js @@ -41,7 +41,7 @@ const DateToDay = (date) => { const century = Math.floor(year / 100) // Apply the algorithm shown above - const weekDay = (day + Math.floor((month + 1) * 2.6) + yearDigit + Math.floor(yearDigit / 4) + Math.floor(century / 4) - (2 * century)) % 7 + const weekDay = (day + Math.floor((month + 1) * 2.6) + yearDigit + Math.floor(yearDigit / 4) + Math.floor(century / 4) + (5 * century)) % 7 // There is possibility that weekDay is in decimal, so Math.floor() needed // return the weekDay name. From 4576177ec91864fdb05496c226a4f7142a375290 Mon Sep 17 00:00:00 2001 From: Chetan Patil <31822501+Chetan07j@users.noreply.github.com> Date: Sun, 9 Oct 2022 11:51:17 +0200 Subject: [PATCH 8/9] :bulb: updated code comments for fomula usage --- Conversions/DateToDay.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Conversions/DateToDay.js b/Conversions/DateToDay.js index 542432cabf..98b3621262 100644 --- a/Conversions/DateToDay.js +++ b/Conversions/DateToDay.js @@ -40,10 +40,21 @@ const DateToDay = (date) => { const yearDigit = (year % 100) const century = Math.floor(year / 100) - // Apply the algorithm shown above + /** + * Algorithm implementation + * + * In the mathematics modulo operations, truncated division is used mostly in most of programming languages. + * Truncation defines quotient part (integer part) q = trunc(a/n), remainder has same sign as dividend. + * -2 mod 7 return result of -2 => console.log(-2 % 7) => -2 + * + * To overcome this problem, to ensure positive numerator, formula is modified by replacing -2J with +5J (J => century) + * + * Following example shows issue with modulo division + * 1. For date 2/3/2014 with old formula - (2 * century) weekDay comes as -6 and wrong day + * 2. With computer logic + (5 * century) it gives proper valid day as Sunday + */ const weekDay = (day + Math.floor((month + 1) * 2.6) + yearDigit + Math.floor(yearDigit / 4) + Math.floor(century / 4) + (5 * century)) % 7 - // There is possibility that weekDay is in decimal, so Math.floor() needed // return the weekDay name. return daysNameArr[weekDay] } From a4ff14a86354ad8c1fc193db03d7a10dede55457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Sun, 9 Oct 2022 21:23:05 +0200 Subject: [PATCH 9/9] Improve comment --- Conversions/DateToDay.js | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/Conversions/DateToDay.js b/Conversions/DateToDay.js index 98b3621262..a22f75ca3f 100644 --- a/Conversions/DateToDay.js +++ b/Conversions/DateToDay.js @@ -36,27 +36,25 @@ const DateToDay = (date) => { month += 12 } - // divide year to century and yearDigit value. - const yearDigit = (year % 100) + // divide year into century and the last two digits of the century + const yearDigits = year % 100 const century = Math.floor(year / 100) - /** - * Algorithm implementation - * - * In the mathematics modulo operations, truncated division is used mostly in most of programming languages. - * Truncation defines quotient part (integer part) q = trunc(a/n), remainder has same sign as dividend. - * -2 mod 7 return result of -2 => console.log(-2 % 7) => -2 - * - * To overcome this problem, to ensure positive numerator, formula is modified by replacing -2J with +5J (J => century) - * - * Following example shows issue with modulo division - * 1. For date 2/3/2014 with old formula - (2 * century) weekDay comes as -6 and wrong day - * 2. With computer logic + (5 * century) it gives proper valid day as Sunday - */ - const weekDay = (day + Math.floor((month + 1) * 2.6) + yearDigit + Math.floor(yearDigit / 4) + Math.floor(century / 4) + (5 * century)) % 7 - - // return the weekDay name. - return daysNameArr[weekDay] + /* + In mathematics, remainders of divisions are usually defined to always be positive; + As an example, -2 mod 7 = 5. + Many programming languages including JavaScript implement the remainder of `n % m` as `sign(n) * (abs(n) % m)`. + This means the result has the same sign as the numerator. Here, `-2 % 7 = -1 * (2 % 7) = -2`. + + To ensure a positive numerator, the formula is adapted: `- 2 * century` is replaced with `+ 5 * century` + which does not alter the resulting numbers mod 7 since `7 - 2 = 5` + + The following example shows the issue with modulo division: + Without the adaption, the formula yields `weekDay = -6` for the date 2/3/2014; + With the adaption, it yields the positive result `weekDay = 7 - 6 = 1` (Sunday), which is what we need to index the array + */ + const weekDay = (day + Math.floor((month + 1) * 2.6) + yearDigits + Math.floor(yearDigits / 4) + Math.floor(century / 4) + 5 * century) % 7 + return daysNameArr[weekDay] // name of the weekday } // Example : DateToDay("18/12/2020") => Friday