From 3583421ea5e13deabe4208347c56c60eabb0cd26 Mon Sep 17 00:00:00 2001 From: Steve Bosman Date: Sun, 30 Jan 2022 00:03:52 +0000 Subject: [PATCH 01/15] 95 Workday - remove warnings using const and let --- 95_Weekday/javascript/weekday.js | 92 +++++++++++++++----------------- 1 file changed, 44 insertions(+), 48 deletions(-) diff --git a/95_Weekday/javascript/weekday.js b/95_Weekday/javascript/weekday.js index 276e1817..caf45990 100644 --- a/95_Weekday/javascript/weekday.js +++ b/95_Weekday/javascript/weekday.js @@ -10,33 +10,29 @@ function print(str) function input() { - var input_element; - var input_str; - return new Promise(function (resolve) { - input_element = document.createElement("INPUT"); + const input_element = document.createElement("INPUT"); print("? "); input_element.setAttribute("type", "text"); input_element.setAttribute("length", "50"); document.getElementById("output").appendChild(input_element); input_element.focus(); - input_str = undefined; input_element.addEventListener("keydown", function (event) { - if (event.keyCode == 13) { - input_str = input_element.value; - document.getElementById("output").removeChild(input_element); - print(input_str); - print("\n"); - resolve(input_str); - } + if (event.keyCode === 13) { + const input_str = input_element.value; + document.getElementById("output").removeChild(input_element); + print(input_str); + print("\n"); + resolve(input_str); + } }); }); } function tab(space) { - var str = ""; + let str = ""; while (space-- > 0) str += " "; return str; @@ -50,19 +46,19 @@ function fnb(arg) { return Math.floor(arg / 7); } -var t = [, 0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5]; +const t = [, 0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5]; -var k5; -var k6; -var k7; +let k5; +let k6; +let k7; function time_spent(f, a8) { - k1 = Math.floor(f * a8); - i5 = Math.floor(k1 / 365); + let k1 = Math.floor(f * a8); + const i5 = Math.floor(k1 / 365); k1 -= i5 * 365; - i6 = Math.floor(k1 / 30); - i7 = k1 - (i6 * 30); + const i6 = Math.floor(k1 / 30); + const i7 = k1 - (i6 * 30); k5 -= i5; k6 -= i6; k7 -= i7; @@ -89,47 +85,47 @@ async function main() print("GIVES FACTS ABOUT A DATE OF INTEREST TO YOU.\n"); print("\n"); print("ENTER TODAY'S DATE IN THE FORM: 3,24,1979 "); - str = await input(); - m1 = parseInt(str); - d1 = parseInt(str.substr(str.indexOf(",") + 1)); - y1 = parseInt(str.substr(str.lastIndexOf(",") + 1)); + let str = await input(); + const m1 = parseInt(str); + const d1 = parseInt(str.substr(str.indexOf(",") + 1)); + const y1 = parseInt(str.substr(str.lastIndexOf(",") + 1)); // This program determines the day of the week // for a date after 1582 print("ENTER DAY OF BIRTH (OR OTHER DAY OF INTEREST)"); str = await input(); - m = parseInt(str); - d = parseInt(str.substr(str.indexOf(",") + 1)); - y = parseInt(str.substr(str.lastIndexOf(",") + 1)); + const m = parseInt(str); + const d = parseInt(str.substr(str.indexOf(",") + 1)); + const y = parseInt(str.substr(str.lastIndexOf(",") + 1)); print("\n"); - i1 = Math.floor((y - 1500) / 100); + const i1 = Math.floor((y - 1500) / 100); // Test for date before current calendar. if (y - 1582 < 0) { print("NOT PREPARED TO GIVE DAY OF WEEK PRIOR TO MDLXXXII.\n"); } else { - a = i1 * 5 + (i1 + 3) / 4; - i2 = Math.floor(a - fnb(a) * 7); - y2 = Math.floor(y / 100); - y3 = Math.floor(y - y2 * 100); + let a = i1 * 5 + (i1 + 3) / 4; + const i2 = Math.floor(a - fnb(a) * 7); + const y2 = Math.floor(y / 100); + const y3 = Math.floor(y - y2 * 100); a = y3 / 4 + y3 + d + t[m] + i2; - b = Math.floor(a - fnb(a) * 7) + 1; + let b = Math.floor(a - fnb(a) * 7) + 1; if (m <= 2) { - if (y3 != 0) { + if (y3 !== 0) { t1 = Math.floor(y - fna(y) * 4); } else { a = i1 - 1; t1 = Math.floor(a - fna(a) * 4); } - if (t1 == 0) { - if (b == 0) + if (t1 === 0) { + if (b === 0) b = 6; b--; } } - if (b == 0) + if (b === 0) b = 7; if ((y1 * 12 + m1) * 31 + d1 < (y * 12 + m) * 31 + d) { print(m + "/" + d + "/" + y + " WILL BE A "); - } else if ((y1 * 12 + m1) * 31 + d1 == (y * 12 + m) * 31 + d) { + } else if ((y1 * 12 + m1) * 31 + d1 === (y * 12 + m) * 31 + d) { print(m + "/" + d + "/" + y + " IS A "); } else { print(m + "/" + d + "/" + y + " WAS A "); @@ -141,7 +137,7 @@ async function main() case 4: print("WEDNESDAY.\n"); break; case 5: print("THURSDAY.\n"); break; case 6: - if (d == 13) { + if (d === 13) { print("FRIDAY THE THIRTEENTH---BEWARE!\n"); } else { print("FRIDAY.\n"); @@ -149,11 +145,11 @@ async function main() break; case 7: print("SATURDAY.\n"); break; } - if ((y1 * 12 + m1) * 31 + d1 != (y * 12 + m) * 31 + d) { - i5 = y1 - y; + if ((y1 * 12 + m1) * 31 + d1 !== (y * 12 + m) * 31 + d) { + let i5 = y1 - y; print("\n"); - i6 = m1 - m; - i7 = d1 - d; + let i6 = m1 - m; + let i7 = d1 - d; if (i7 < 0) { i6--; i7 += 30; @@ -163,17 +159,17 @@ async function main() i6 += 12; } if (i5 >= 0) { - if (i7 == 0 && i6 == 0) + if (i7 === 0 && i6 === 0) print("***HAPPY BIRTHDAY***\n"); print(" \tYEARS\tMONTHS\tDAYS\n"); print(" \t-----\t------\t----\n"); print("YOUR AGE (IF BIRTHDATE) \t" + i5 + "\t" + i6 + "\t" + i7 + "\n"); - a8 = (i5 * 365) + (i6 * 30) + i7 + Math.floor(i6 / 2); + const a8 = (i5 * 365) + (i6 * 30) + i7 + Math.floor(i6 / 2); k5 = i5; k6 = i6; k7 = i7; // Calculate retirement date. - e = y + 65; + const e = y + 65; // Calculate time spent in the following functions. print("YOU HAVE SLEPT \t\t\t"); time_spent(0.35, a8); @@ -187,7 +183,7 @@ async function main() print("YOU HAVE WORKED/PLAYED \t\t"); } time_spent(0.23, a8); - if (k6 == 12) { + if (k6 === 12) { k5++; k6 = 0; } From 63f78550cdf0d0c73e7b97059be8def4b6d2486f Mon Sep 17 00:00:00 2001 From: Steve Bosman Date: Sun, 30 Jan 2022 09:59:11 +0000 Subject: [PATCH 02/15] 95 Workday - introduce some comments, and methods; improve some variable names; spotted a bug --- 95_Weekday/javascript/weekday.js | 160 ++++++++++++++++++++----------- 1 file changed, 103 insertions(+), 57 deletions(-) diff --git a/95_Weekday/javascript/weekday.js b/95_Weekday/javascript/weekday.js index caf45990..dd1d82d5 100644 --- a/95_Weekday/javascript/weekday.js +++ b/95_Weekday/javascript/weekday.js @@ -46,7 +46,33 @@ function fnb(arg) { return Math.floor(arg / 7); } -const t = [, 0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5]; +// in a non-leap year the day of the week for the first of each month moves by the following amounts. +const MONTHLY_DAY_OF_WEEK_OFFSETS = [0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5]; + +/** + * Reads a date, and extracts the date information. + * This expects date parts to be comma separated, using US date ordering, + * i.e. Month,Day,Year. + * @returns {Promise<[number,number,number]>} [year, month, dayOfMonth] + */ +async function readDateElements() { + let dateString = await input(); + const month = parseInt(dateString); + const dayOfMonth = parseInt(dateString.substr(dateString.indexOf(",") + 1)); + const year = parseInt(dateString.substr(dateString.lastIndexOf(",") + 1)); + return [year, month, dayOfMonth]; +} + +/** + * Returns a US formatted date, i.e. Month/Day/Year. + * @param year + * @param month + * @param dayOfMonth + * @returns {string} + */ +function getFormattedDate(year, month, dayOfMonth) { + return month + "/" + dayOfMonth + "/" + year; +} let k5; let k6; @@ -73,6 +99,46 @@ function time_spent(f, a8) print(i5 + "\t" + i6 + "\t" + i7 + "\n"); } +function getDayOfWeek(dobYear, dobMonth, dobDayOfMonth) { + const i1 = Math.floor((dobYear - 1500) / 100); + let a = i1 * 5 + (i1 + 3) / 4; + const i2 = Math.floor(a - fnb(a) * 7); + const y2 = Math.floor(dobYear / 100); + const y3 = Math.floor(dobYear - y2 * 100); + a = y3 / 4 + y3 + dobDayOfMonth + MONTHLY_DAY_OF_WEEK_OFFSETS[dobMonth-1] + i2; + let dayOfWeek = Math.floor(a - fnb(a) * 7) + 1; + if (dobMonth <= 2) { + if (y3 !== 0) { + t1 = Math.floor(dobYear - fna(dobYear) * 4); + } else { + a = i1 - 1; + t1 = Math.floor(a - fna(a) * 4); + } + if (t1 === 0) { + if (dayOfWeek === 0) { + dayOfWeek = 6; + } + dayOfWeek--; + } + } + if (dayOfWeek === 0) { + dayOfWeek = 7; + } + return dayOfWeek; +} + +/** + * The following performs a special hash on the day parts which guarantees + * that different days will return different numbers, and the numbers returned are in ordered. + * @param todayYear + * @param todayMonth + * @param todayDayOfMonth + * @returns {*} + */ +function getNormalisedDay(todayYear, todayMonth, todayDayOfMonth) { + return (todayYear * 12 + todayMonth) * 31 + todayDayOfMonth; +} + // Main control section async function main() { @@ -85,59 +151,38 @@ async function main() print("GIVES FACTS ABOUT A DATE OF INTEREST TO YOU.\n"); print("\n"); print("ENTER TODAY'S DATE IN THE FORM: 3,24,1979 "); - let str = await input(); - const m1 = parseInt(str); - const d1 = parseInt(str.substr(str.indexOf(",") + 1)); - const y1 = parseInt(str.substr(str.lastIndexOf(",") + 1)); + const [todayYear, todayMonth, todayDayOfMonth] = await readDateElements(); // This program determines the day of the week // for a date after 1582 print("ENTER DAY OF BIRTH (OR OTHER DAY OF INTEREST)"); - str = await input(); - const m = parseInt(str); - const d = parseInt(str.substr(str.indexOf(",") + 1)); - const y = parseInt(str.substr(str.lastIndexOf(",") + 1)); + const [dobYear, dobMonth, dobDayOfMonth] = await readDateElements(); print("\n"); - const i1 = Math.floor((y - 1500) / 100); // Test for date before current calendar. - if (y - 1582 < 0) { + // Note: this test is unreliable - the Gregorian calendar was introduced on Friday 15 October 1582 + // and the weekday algorithm fails for dates prior to that + if (dobYear - 1582 < 0) { print("NOT PREPARED TO GIVE DAY OF WEEK PRIOR TO MDLXXXII.\n"); } else { - let a = i1 * 5 + (i1 + 3) / 4; - const i2 = Math.floor(a - fnb(a) * 7); - const y2 = Math.floor(y / 100); - const y3 = Math.floor(y - y2 * 100); - a = y3 / 4 + y3 + d + t[m] + i2; - let b = Math.floor(a - fnb(a) * 7) + 1; - if (m <= 2) { - if (y3 !== 0) { - t1 = Math.floor(y - fna(y) * 4); - } else { - a = i1 - 1; - t1 = Math.floor(a - fna(a) * 4); - } - if (t1 === 0) { - if (b === 0) - b = 6; - b--; - } - } - if (b === 0) - b = 7; - if ((y1 * 12 + m1) * 31 + d1 < (y * 12 + m) * 31 + d) { - print(m + "/" + d + "/" + y + " WILL BE A "); - } else if ((y1 * 12 + m1) * 31 + d1 === (y * 12 + m) * 31 + d) { - print(m + "/" + d + "/" + y + " IS A "); + const dayOfWeek = getDayOfWeek(dobYear, dobMonth, dobDayOfMonth); + + const normalisedToday = getNormalisedDay(todayYear, todayMonth, todayDayOfMonth); + const normalisedDob = getNormalisedDay(dobYear, dobMonth, dobDayOfMonth); + + if (normalisedToday < normalisedDob) { + print(getFormattedDate(dobYear, dobMonth, dobDayOfMonth) + " WILL BE A "); + } else if (normalisedToday === normalisedDob) { + print(getFormattedDate(dobYear, dobMonth, dobDayOfMonth) + " IS A "); } else { - print(m + "/" + d + "/" + y + " WAS A "); + print(getFormattedDate(dobYear, dobMonth, dobDayOfMonth) + " WAS A "); } - switch (b) { + switch (dayOfWeek) { case 1: print("SUNDAY.\n"); break; case 2: print("MONDAY.\n"); break; case 3: print("TUESDAY.\n"); break; case 4: print("WEDNESDAY.\n"); break; case 5: print("THURSDAY.\n"); break; case 6: - if (d === 13) { + if (dobDayOfMonth === 13) { print("FRIDAY THE THIRTEENTH---BEWARE!\n"); } else { print("FRIDAY.\n"); @@ -145,31 +190,32 @@ async function main() break; case 7: print("SATURDAY.\n"); break; } - if ((y1 * 12 + m1) * 31 + d1 !== (y * 12 + m) * 31 + d) { - let i5 = y1 - y; + if (normalisedToday !== normalisedDob) { + let yearsBetweenDates = todayYear - dobYear; print("\n"); - let i6 = m1 - m; - let i7 = d1 - d; - if (i7 < 0) { - i6--; - i7 += 30; + let monthsBetweenDates = todayMonth - dobMonth; + let daysBetweenDates = todayDayOfMonth - dobDayOfMonth; + if (daysBetweenDates < 0) { + monthsBetweenDates--; + daysBetweenDates += 30; } - if (i6 < 0) { - i5--; - i6 += 12; + if (monthsBetweenDates < 0) { + yearsBetweenDates--; + monthsBetweenDates += 12; } - if (i5 >= 0) { - if (i7 === 0 && i6 === 0) + if (yearsBetweenDates >= 0) { + if (daysBetweenDates === 0 && monthsBetweenDates === 0) { print("***HAPPY BIRTHDAY***\n"); + } print(" \tYEARS\tMONTHS\tDAYS\n"); print(" \t-----\t------\t----\n"); - print("YOUR AGE (IF BIRTHDATE) \t" + i5 + "\t" + i6 + "\t" + i7 + "\n"); - const a8 = (i5 * 365) + (i6 * 30) + i7 + Math.floor(i6 / 2); - k5 = i5; - k6 = i6; - k7 = i7; + print("YOUR AGE (IF BIRTHDATE) \t" + yearsBetweenDates + "\t" + monthsBetweenDates + "\t" + daysBetweenDates + "\n"); + const a8 = (yearsBetweenDates * 365) + (monthsBetweenDates * 30) + daysBetweenDates + Math.floor(monthsBetweenDates / 2); + k5 = yearsBetweenDates; + k6 = monthsBetweenDates; + k7 = daysBetweenDates; // Calculate retirement date. - const e = y + 65; + const e = dobYear + 65; // Calculate time spent in the following functions. print("YOU HAVE SLEPT \t\t\t"); time_spent(0.35, a8); From a28a0adafa97cf9a21186f1702c70bd22a33d92a Mon Sep 17 00:00:00 2001 From: Steve Bosman Date: Sun, 30 Jan 2022 12:13:00 +0000 Subject: [PATCH 03/15] 95 Workday - break time spent calculation and printining into multiple parts --- 95_Weekday/javascript/weekday.js | 103 ++++++++++++++++++++----------- 1 file changed, 67 insertions(+), 36 deletions(-) diff --git a/95_Weekday/javascript/weekday.js b/95_Weekday/javascript/weekday.js index dd1d82d5..2451f19b 100644 --- a/95_Weekday/javascript/weekday.js +++ b/95_Weekday/javascript/weekday.js @@ -74,29 +74,50 @@ function getFormattedDate(year, month, dayOfMonth) { return month + "/" + dayOfMonth + "/" + year; } -let k5; -let k6; -let k7; - -function time_spent(f, a8) +/** + * Calculate years, months and days as factor of days. + * This is a naive calculation which assumes all months are 30 days. + * @param factor + * @param dayCount + * @returns {{years: number, months: number, days: number}} + */ +function time_spent(factor, dayCount) { - let k1 = Math.floor(f * a8); - const i5 = Math.floor(k1 / 365); - k1 -= i5 * 365; - const i6 = Math.floor(k1 / 30); - const i7 = k1 - (i6 * 30); - k5 -= i5; - k6 -= i6; - k7 -= i7; - if (k7 < 0) { - k7 += 30; - k6--; + let totalDays = Math.floor(factor * dayCount); + const years = Math.floor(totalDays / 365); + totalDays -= years * 365; + const months = Math.floor(totalDays / 30); + const days = totalDays - (months * 30); + return {years, months, days} +} + +/** + * Print the supplied time + * @param years + * @param months + * @param days + */ +function printTimeSpent({years, months, days}) { + print(years + "\t" + months + "\t" + days + "\n"); +} + +/** + * Adjust unaccounted time by remove years, months and days supplied. + * @param {{years:number, months:number, days:number}} unaccountedTime + * @param {{years:number, months:number, days:number}} timeToRemove + */ +function adjustUnaccountedTime(unaccountedTime, timeToRemove) { + unaccountedTime.years -= timeToRemove.years; + unaccountedTime.months -= timeToRemove.months; + unaccountedTime.days -= timeToRemove.days; + if (unaccountedTime.days < 0) { + unaccountedTime.days += 30; + unaccountedTime.months--; } - if (k6 <= 0) { - k6 += 12; - k5--; + if (unaccountedTime.months <= 0) { + unaccountedTime.months += 12; + unaccountedTime.years--; } - print(i5 + "\t" + i6 + "\t" + i7 + "\n"); } function getDayOfWeek(dobYear, dobMonth, dobDayOfMonth) { @@ -210,32 +231,42 @@ async function main() print(" \tYEARS\tMONTHS\tDAYS\n"); print(" \t-----\t------\t----\n"); print("YOUR AGE (IF BIRTHDATE) \t" + yearsBetweenDates + "\t" + monthsBetweenDates + "\t" + daysBetweenDates + "\n"); - const a8 = (yearsBetweenDates * 365) + (monthsBetweenDates * 30) + daysBetweenDates + Math.floor(monthsBetweenDates / 2); - k5 = yearsBetweenDates; - k6 = monthsBetweenDates; - k7 = daysBetweenDates; - // Calculate retirement date. - const e = dobYear + 65; + const approximateDaysBetween = (yearsBetweenDates * 365) + (monthsBetweenDates * 30) + daysBetweenDates + Math.floor(monthsBetweenDates / 2); + // Create an object containing time unaccounted for + const unaccountedTime = {years: yearsBetweenDates, months: monthsBetweenDates, days: daysBetweenDates}; + // Calculate time spent in the following functions. print("YOU HAVE SLEPT \t\t\t"); - time_spent(0.35, a8); + const sleepTimeSpent = time_spent(0.35, approximateDaysBetween); + printTimeSpent(sleepTimeSpent); + + adjustUnaccountedTime(unaccountedTime, sleepTimeSpent); print("YOU HAVE EATEN \t\t\t"); - time_spent(0.17, a8); - if (k5 <= 3) { + const eatenTimeSpent = time_spent(0.17, approximateDaysBetween); + printTimeSpent(eatenTimeSpent); + + adjustUnaccountedTime(unaccountedTime, eatenTimeSpent); + if (unaccountedTime.years <= 3) { print("YOU HAVE PLAYED \t\t\t"); - } else if (k5 <= 9) { + } else if (unaccountedTime.years <= 9) { print("YOU HAVE PLAYED/STUDIED \t\t"); } else { print("YOU HAVE WORKED/PLAYED \t\t"); } - time_spent(0.23, a8); - if (k6 === 12) { - k5++; - k6 = 0; + const workPlayTimeSpent = time_spent(0.23, approximateDaysBetween); + printTimeSpent(workPlayTimeSpent); + + adjustUnaccountedTime(unaccountedTime, workPlayTimeSpent); + if (unaccountedTime.months === 12) { + unaccountedTime.years++; + unaccountedTime.months = 0; } - print("YOU HAVE RELAXED \t\t" + k5 + "\t" + k6 + "\t" + k7 + "\n"); + print("YOU HAVE RELAXED \t\t"); + printTimeSpent(unaccountedTime) + + const retirementYear = dobYear + 65; print("\n"); - print(tab(16) + "*** YOU MAY RETIRE IN " + e + " ***\n"); + print(tab(16) + "*** YOU MAY RETIRE IN " + retirementYear + " ***\n"); print("\n"); } } From cd3545199222ea77478ad53f31e6dd26ced418df Mon Sep 17 00:00:00 2001 From: Steve Bosman Date: Mon, 31 Jan 2022 00:14:03 +0000 Subject: [PATCH 04/15] 95 Workday - tidy up time calculation to make it more readable --- 95_Weekday/javascript/weekday.js | 145 +++++++++++++++---------------- 1 file changed, 72 insertions(+), 73 deletions(-) diff --git a/95_Weekday/javascript/weekday.js b/95_Weekday/javascript/weekday.js index 2451f19b..f7e28758 100644 --- a/95_Weekday/javascript/weekday.js +++ b/95_Weekday/javascript/weekday.js @@ -3,51 +3,40 @@ // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess) // -function print(str) -{ +function print(str) { document.getElementById("output").appendChild(document.createTextNode(str)); } -function input() -{ +function input() { return new Promise(function (resolve) { - const input_element = document.createElement("INPUT"); - - print("? "); - input_element.setAttribute("type", "text"); - input_element.setAttribute("length", "50"); - document.getElementById("output").appendChild(input_element); - input_element.focus(); - input_element.addEventListener("keydown", function (event) { - if (event.keyCode === 13) { - const input_str = input_element.value; - document.getElementById("output").removeChild(input_element); - print(input_str); - print("\n"); - resolve(input_str); - } - }); - }); + const input_element = document.createElement("INPUT"); + + print("? "); + input_element.setAttribute("type", "text"); + input_element.setAttribute("length", "50"); + document.getElementById("output").appendChild(input_element); + input_element.focus(); + input_element.addEventListener("keydown", function (event) { + if (event.keyCode === 13) { + const input_str = input_element.value; + document.getElementById("output").removeChild(input_element); + print(input_str); + print("\n"); + resolve(input_str); + } + }); + }); } -function tab(space) -{ +function tab(space) { let str = ""; while (space-- > 0) str += " "; return str; } -function fna(arg) { - return Math.floor(arg / 4); -} - -function fnb(arg) { - return Math.floor(arg / 7); -} - // in a non-leap year the day of the week for the first of each month moves by the following amounts. -const MONTHLY_DAY_OF_WEEK_OFFSETS = [0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5]; +const COMMON_YEAR_MONTH_OFFSET = [0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5]; /** * Reads a date, and extracts the date information. @@ -67,11 +56,11 @@ async function readDateElements() { * Returns a US formatted date, i.e. Month/Day/Year. * @param year * @param month - * @param dayOfMonth + * @param day * @returns {string} */ -function getFormattedDate(year, month, dayOfMonth) { - return month + "/" + dayOfMonth + "/" + year; +function getFormattedDate(year, month, day) { + return month + "/" + day + "/" + year; } /** @@ -81,8 +70,7 @@ function getFormattedDate(year, month, dayOfMonth) { * @param dayCount * @returns {{years: number, months: number, days: number}} */ -function time_spent(factor, dayCount) -{ +function time_spent(factor, dayCount) { let totalDays = Math.floor(factor * dayCount); const years = Math.floor(totalDays / 365); totalDays -= years * 365; @@ -120,30 +108,30 @@ function adjustUnaccountedTime(unaccountedTime, timeToRemove) { } } -function getDayOfWeek(dobYear, dobMonth, dobDayOfMonth) { - const i1 = Math.floor((dobYear - 1500) / 100); - let a = i1 * 5 + (i1 + 3) / 4; - const i2 = Math.floor(a - fnb(a) * 7); - const y2 = Math.floor(dobYear / 100); - const y3 = Math.floor(dobYear - y2 * 100); - a = y3 / 4 + y3 + dobDayOfMonth + MONTHLY_DAY_OF_WEEK_OFFSETS[dobMonth-1] + i2; - let dayOfWeek = Math.floor(a - fnb(a) * 7) + 1; - if (dobMonth <= 2) { - if (y3 !== 0) { - t1 = Math.floor(dobYear - fna(dobYear) * 4); - } else { - a = i1 - 1; - t1 = Math.floor(a - fna(a) * 4); - } - if (t1 === 0) { - if (dayOfWeek === 0) { - dayOfWeek = 6; - } - dayOfWeek--; - } +function isLeapYear(year) { + if ((year % 4) !== 0) { + return false; + } else if ((year % 100) !== 0) { + return true; + } else if ((year % 400) !== 0) { + return false; } - if (dayOfWeek === 0) { - dayOfWeek = 7; + return true; +} + +function getDayOfWeek(year, month, day) { + const centuriesSince1500 = Math.floor((year - 1500) / 100); + let centuryOffset = centuriesSince1500 * 5 + (centuriesSince1500 + 3) / 4; + centuryOffset = Math.floor(centuryOffset % 7); + // January 1st moves forward by approximately 1.25 days per year + const yearInCentury = year % 100; + const yearInCenturyOffsets = yearInCentury / 4 + yearInCentury; + + const a = yearInCenturyOffsets + day + COMMON_YEAR_MONTH_OFFSET[month-1] + centuryOffset; + + let dayOfWeek = Math.floor(a % 7) + 1; + if (month <= 2 && isLeapYear(year)) { + dayOfWeek--; } return dayOfWeek; } @@ -151,18 +139,17 @@ function getDayOfWeek(dobYear, dobMonth, dobDayOfMonth) { /** * The following performs a special hash on the day parts which guarantees * that different days will return different numbers, and the numbers returned are in ordered. - * @param todayYear - * @param todayMonth - * @param todayDayOfMonth + * @param year + * @param month + * @param day * @returns {*} */ -function getNormalisedDay(todayYear, todayMonth, todayDayOfMonth) { - return (todayYear * 12 + todayMonth) * 31 + todayDayOfMonth; +function getNormalisedDay(year, month, day) { + return (year * 12 + month) * 31 + day; } // Main control section -async function main() -{ +async function main() { print(tab(32) + "WEEKDAY\n"); print(tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n"); print("\n"); @@ -197,11 +184,21 @@ async function main() print(getFormattedDate(dobYear, dobMonth, dobDayOfMonth) + " WAS A "); } switch (dayOfWeek) { - case 1: print("SUNDAY.\n"); break; - case 2: print("MONDAY.\n"); break; - case 3: print("TUESDAY.\n"); break; - case 4: print("WEDNESDAY.\n"); break; - case 5: print("THURSDAY.\n"); break; + case 1: + print("SUNDAY.\n"); + break; + case 2: + print("MONDAY.\n"); + break; + case 3: + print("TUESDAY.\n"); + break; + case 4: + print("WEDNESDAY.\n"); + break; + case 5: + print("THURSDAY.\n"); + break; case 6: if (dobDayOfMonth === 13) { print("FRIDAY THE THIRTEENTH---BEWARE!\n"); @@ -209,7 +206,9 @@ async function main() print("FRIDAY.\n"); } break; - case 7: print("SATURDAY.\n"); break; + case 7: + print("SATURDAY.\n"); + break; } if (normalisedToday !== normalisedDob) { let yearsBetweenDates = todayYear - dobYear; From 84615c1a753af6cbd75b73a51b3d9d860978d49d Mon Sep 17 00:00:00 2001 From: Steve Bosman Date: Mon, 31 Jan 2022 16:52:55 +0000 Subject: [PATCH 05/15] 95 Workday - tidy-up time calculation to make it more readable --- 95_Weekday/javascript/weekday.js | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/95_Weekday/javascript/weekday.js b/95_Weekday/javascript/weekday.js index f7e28758..5adb74f4 100644 --- a/95_Weekday/javascript/weekday.js +++ b/95_Weekday/javascript/weekday.js @@ -119,6 +119,18 @@ function isLeapYear(year) { return true; } +/** + * Determine the day of the week. + * This calculation returns a number between 1 and 7 where Sunday=1, Monday=2, ..., Saturday=7. + * First it calculates a known date near the start of the century (defined as a year ending "00"). + * January 1st in "00" years is always one of: Saturday (years divisible by 400), Friday, Wednesday, or Monday. + * This is a combination of years being 52 weeks and either 1 (non-leap years) or 2 (leap years) days, + * and years ending "00" only being leap years if they are also divisible by 400. + * @param year + * @param month + * @param day + * @returns {number} Value between 1 and 7 representing Sunday to Saturday. + */ function getDayOfWeek(year, month, day) { const centuriesSince1500 = Math.floor((year - 1500) / 100); let centuryOffset = centuriesSince1500 * 5 + (centuriesSince1500 + 3) / 4; @@ -127,12 +139,14 @@ function getDayOfWeek(year, month, day) { const yearInCentury = year % 100; const yearInCenturyOffsets = yearInCentury / 4 + yearInCentury; - const a = yearInCenturyOffsets + day + COMMON_YEAR_MONTH_OFFSET[month-1] + centuryOffset; - - let dayOfWeek = Math.floor(a % 7) + 1; + let dayOfWeek = centuryOffset + yearInCenturyOffsets + day + COMMON_YEAR_MONTH_OFFSET[month-1]; + dayOfWeek = Math.floor(dayOfWeek % 7) + 1; if (month <= 2 && isLeapYear(year)) { dayOfWeek--; } + if (dayOfWeek === 0) { + dayOfWeek = 7; + } return dayOfWeek; } From 8a694ee30f76ac264e2527f1153f35521e9cc5e3 Mon Sep 17 00:00:00 2001 From: Steve Bosman Date: Tue, 1 Feb 2022 00:08:34 +0000 Subject: [PATCH 06/15] 95 Weekday - introduce classes for durations and dates --- 95_Weekday/javascript/weekday.js | 271 ++++++++++++++++++++----------- 1 file changed, 175 insertions(+), 96 deletions(-) diff --git a/95_Weekday/javascript/weekday.js b/95_Weekday/javascript/weekday.js index 5adb74f4..6feeda2e 100644 --- a/95_Weekday/javascript/weekday.js +++ b/95_Weekday/javascript/weekday.js @@ -35,32 +35,66 @@ function tab(space) { return str; } -// in a non-leap year the day of the week for the first of each month moves by the following amounts. +class DateStruct { + year; + month; + day; + + /** + * Build a DateStruct + * @param {number} year + * @param {number} month + * @param {number} day + */ + constructor(year, month, day) { + this.year = year; + this.month = month; + this.day = day; + } +} + +class Duration { + years; + months; + days; + + /** + * Build a Duration + * @param {number} years + * @param {number} months + * @param {number} days + */ + constructor(years, months, days) { + this.years = years; + this.months = months; + this.days = days; + } +} + +// In a common (non-leap) year the day of the week for the first of each month moves by the following amounts. const COMMON_YEAR_MONTH_OFFSET = [0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5]; /** * Reads a date, and extracts the date information. * This expects date parts to be comma separated, using US date ordering, * i.e. Month,Day,Year. - * @returns {Promise<[number,number,number]>} [year, month, dayOfMonth] + * @returns {Promise<{year: number, month: number, day: number}>} */ async function readDateElements() { let dateString = await input(); const month = parseInt(dateString); - const dayOfMonth = parseInt(dateString.substr(dateString.indexOf(",") + 1)); + const day = parseInt(dateString.substr(dateString.indexOf(",") + 1)); const year = parseInt(dateString.substr(dateString.lastIndexOf(",") + 1)); - return [year, month, dayOfMonth]; + return {year, month, day}; } /** * Returns a US formatted date, i.e. Month/Day/Year. - * @param year - * @param month - * @param day + * @param {DateStruct} date * @returns {string} */ -function getFormattedDate(year, month, day) { - return month + "/" + day + "/" + year; +function getFormattedDate(date) { + return date.month + "/" + date.day + "/" + date.year; } /** @@ -68,7 +102,7 @@ function getFormattedDate(year, month, day) { * This is a naive calculation which assumes all months are 30 days. * @param factor * @param dayCount - * @returns {{years: number, months: number, days: number}} + * @returns {Duration} */ function time_spent(factor, dayCount) { let totalDays = Math.floor(factor * dayCount); @@ -76,23 +110,21 @@ function time_spent(factor, dayCount) { totalDays -= years * 365; const months = Math.floor(totalDays / 30); const days = totalDays - (months * 30); - return {years, months, days} + return new Duration(years, months, days); } /** - * Print the supplied time - * @param years - * @param months - * @param days + * Print the supplied duration. + * @param {Duration} duration */ -function printTimeSpent({years, months, days}) { - print(years + "\t" + months + "\t" + days + "\n"); +function printTimeSpent(duration) { + print(duration.years + "\t" + duration.months + "\t" + duration.days + "\n"); } /** * Adjust unaccounted time by remove years, months and days supplied. - * @param {{years:number, months:number, days:number}} unaccountedTime - * @param {{years:number, months:number, days:number}} timeToRemove + * @param {Duration} unaccountedTime + * @param {Duration} timeToRemove */ function adjustUnaccountedTime(unaccountedTime, timeToRemove) { unaccountedTime.years -= timeToRemove.years; @@ -108,6 +140,11 @@ function adjustUnaccountedTime(unaccountedTime, timeToRemove) { } } +/** + * Determine if the given year is a leap year. + * @param year + * @returns {boolean} + */ function isLeapYear(year) { if ((year % 4) !== 0) { return false; @@ -122,26 +159,25 @@ function isLeapYear(year) { /** * Determine the day of the week. * This calculation returns a number between 1 and 7 where Sunday=1, Monday=2, ..., Saturday=7. - * First it calculates a known date near the start of the century (defined as a year ending "00"). - * January 1st in "00" years is always one of: Saturday (years divisible by 400), Friday, Wednesday, or Monday. - * This is a combination of years being 52 weeks and either 1 (non-leap years) or 2 (leap years) days, - * and years ending "00" only being leap years if they are also divisible by 400. - * @param year - * @param month - * @param day + * @param {DateStruct} date * @returns {number} Value between 1 and 7 representing Sunday to Saturday. */ -function getDayOfWeek(year, month, day) { - const centuriesSince1500 = Math.floor((year - 1500) / 100); +function getDayOfWeek(date) { + // Calculate an offset based on the century part of the year. + const centuriesSince1500 = Math.floor((date.year - 1500) / 100); let centuryOffset = centuriesSince1500 * 5 + (centuriesSince1500 + 3) / 4; centuryOffset = Math.floor(centuryOffset % 7); + + // Calculate an offset based on the shortened two digit year. // January 1st moves forward by approximately 1.25 days per year - const yearInCentury = year % 100; + const yearInCentury = date.year % 100; const yearInCenturyOffsets = yearInCentury / 4 + yearInCentury; - let dayOfWeek = centuryOffset + yearInCenturyOffsets + day + COMMON_YEAR_MONTH_OFFSET[month-1]; + // combine offsets with day and month + let dayOfWeek = centuryOffset + yearInCenturyOffsets + date.day + COMMON_YEAR_MONTH_OFFSET[date.month-1]; + dayOfWeek = Math.floor(dayOfWeek % 7) + 1; - if (month <= 2 && isLeapYear(year)) { + if (date.month <= 2 && isLeapYear(date.year)) { dayOfWeek--; } if (dayOfWeek === 0) { @@ -151,15 +187,95 @@ function getDayOfWeek(year, month, day) { } /** - * The following performs a special hash on the day parts which guarantees - * that different days will return different numbers, and the numbers returned are in ordered. - * @param year - * @param month - * @param day - * @returns {*} + * Obtain text for the day of the week. + * @param {DateStruct} date + * @returns {string} */ -function getNormalisedDay(year, month, day) { - return (year * 12 + month) * 31 + day; +function getDayOfWeekText(date) { + const dayOfWeek = getDayOfWeek(date); + let dayOfWeekText = ""; + switch (dayOfWeek) { + case 1: + dayOfWeekText = "SUNDAY."; + break; + case 2: + dayOfWeekText = "MONDAY."; + break; + case 3: + dayOfWeekText = "TUESDAY."; + break; + case 4: + dayOfWeekText = "WEDNESDAY."; + break; + case 5: + dayOfWeekText = "THURSDAY."; + break; + case 6: + if (date.day === 13) { + dayOfWeekText = "FRIDAY THE THIRTEENTH---BEWARE!"; + } else { + dayOfWeekText = "FRIDAY."; + } + break; + case 7: + dayOfWeekText = "SATURDAY."; + break; + } + return dayOfWeekText; +} + +/** + * The following performs a hash on the day parts which guarantees that + * 1. different days will return different numbers + * 2. the numbers returned are ordered. + * @param {DateStruct} date + * @returns {number} + */ +function getNormalisedDay(date) { + return (date.year * 12 + date.month) * 31 + date.day; +} + +/** + * Determine approximate difference between two dates. + * This is a naive calculation which assumes all months are 30 days. + * @param {DateStruct} date1 + * @param {DateStruct} date2 + * @returns {Duration} + */ +function difference(date1, date2) { + let years = date1.year - date2.year; + let months = date1.month - date2.month; + let days = date1.day - date2.day; + if (days < 0) { + months--; + days += 30; + } + if (months < 0) { + years--; + months += 12; + } + return new Duration(years, months, days); +} + +/** + * Determine if the supplied date could be a Gregorian date. + * Be aware the Gregorian calendar was not introduced in all places at once, + * see https://en.wikipedia.org/wiki/Gregorian_calendar + * @param {DateStruct} date + * @returns {boolean} true if date could be Gregorian; otherwise false. + */ +function isGregorianDate(date) { + let result = false; + if (date.year > 1582) { + result = true; + } else if (date.year === 1582) { + if (date.month > 10) { + result = true; + } else if (date.month === 10 && date.day >= 15) { + result = true; + } + } + return result; } // Main control section @@ -173,80 +289,43 @@ async function main() { print("GIVES FACTS ABOUT A DATE OF INTEREST TO YOU.\n"); print("\n"); print("ENTER TODAY'S DATE IN THE FORM: 3,24,1979 "); - const [todayYear, todayMonth, todayDayOfMonth] = await readDateElements(); + const today = await readDateElements(); // This program determines the day of the week // for a date after 1582 print("ENTER DAY OF BIRTH (OR OTHER DAY OF INTEREST)"); - const [dobYear, dobMonth, dobDayOfMonth] = await readDateElements(); + const dateOfBirth = await readDateElements(); print("\n"); // Test for date before current calendar. - // Note: this test is unreliable - the Gregorian calendar was introduced on Friday 15 October 1582 - // and the weekday algorithm fails for dates prior to that - if (dobYear - 1582 < 0) { - print("NOT PREPARED TO GIVE DAY OF WEEK PRIOR TO MDLXXXII.\n"); + if (!isGregorianDate(dateOfBirth)) { + print("NOT PREPARED TO GIVE DAY OF WEEK PRIOR TO X.XV.MDLXXXII.\n"); } else { - const dayOfWeek = getDayOfWeek(dobYear, dobMonth, dobDayOfMonth); - - const normalisedToday = getNormalisedDay(todayYear, todayMonth, todayDayOfMonth); - const normalisedDob = getNormalisedDay(dobYear, dobMonth, dobDayOfMonth); + const normalisedToday = getNormalisedDay(today); + const normalisedDob = getNormalisedDay(dateOfBirth); + const dateOfBirthText = getFormattedDate(dateOfBirth); + let dayOfWeekText = getDayOfWeekText(dateOfBirth); if (normalisedToday < normalisedDob) { - print(getFormattedDate(dobYear, dobMonth, dobDayOfMonth) + " WILL BE A "); + print(dateOfBirthText + " WILL BE A " + dayOfWeekText + "\n"); } else if (normalisedToday === normalisedDob) { - print(getFormattedDate(dobYear, dobMonth, dobDayOfMonth) + " IS A "); + print(dateOfBirthText + " IS A " + dayOfWeekText + "\n"); } else { - print(getFormattedDate(dobYear, dobMonth, dobDayOfMonth) + " WAS A "); - } - switch (dayOfWeek) { - case 1: - print("SUNDAY.\n"); - break; - case 2: - print("MONDAY.\n"); - break; - case 3: - print("TUESDAY.\n"); - break; - case 4: - print("WEDNESDAY.\n"); - break; - case 5: - print("THURSDAY.\n"); - break; - case 6: - if (dobDayOfMonth === 13) { - print("FRIDAY THE THIRTEENTH---BEWARE!\n"); - } else { - print("FRIDAY.\n"); - } - break; - case 7: - print("SATURDAY.\n"); - break; + print(dateOfBirthText + " WAS A " + dayOfWeekText + "\n"); } + if (normalisedToday !== normalisedDob) { - let yearsBetweenDates = todayYear - dobYear; print("\n"); - let monthsBetweenDates = todayMonth - dobMonth; - let daysBetweenDates = todayDayOfMonth - dobDayOfMonth; - if (daysBetweenDates < 0) { - monthsBetweenDates--; - daysBetweenDates += 30; - } - if (monthsBetweenDates < 0) { - yearsBetweenDates--; - monthsBetweenDates += 12; - } - if (yearsBetweenDates >= 0) { - if (daysBetweenDates === 0 && monthsBetweenDates === 0) { + let differenceBetweenDates = difference(today, dateOfBirth); + if (differenceBetweenDates.years >= 0) { + if (differenceBetweenDates.days === 0 && differenceBetweenDates.months === 0) { print("***HAPPY BIRTHDAY***\n"); } print(" \tYEARS\tMONTHS\tDAYS\n"); print(" \t-----\t------\t----\n"); - print("YOUR AGE (IF BIRTHDATE) \t" + yearsBetweenDates + "\t" + monthsBetweenDates + "\t" + daysBetweenDates + "\n"); - const approximateDaysBetween = (yearsBetweenDates * 365) + (monthsBetweenDates * 30) + daysBetweenDates + Math.floor(monthsBetweenDates / 2); + print("YOUR AGE (IF BIRTHDATE) \t"); + printTimeSpent(differenceBetweenDates); + const approximateDaysBetween = (differenceBetweenDates.years * 365) + (differenceBetweenDates.months * 30) + differenceBetweenDates.days + Math.floor(differenceBetweenDates.months / 2); // Create an object containing time unaccounted for - const unaccountedTime = {years: yearsBetweenDates, months: monthsBetweenDates, days: daysBetweenDates}; + const unaccountedTime = {...differenceBetweenDates}; // Calculate time spent in the following functions. print("YOU HAVE SLEPT \t\t\t"); @@ -277,7 +356,7 @@ async function main() { print("YOU HAVE RELAXED \t\t"); printTimeSpent(unaccountedTime) - const retirementYear = dobYear + 65; + const retirementYear = dateOfBirth.year + 65; print("\n"); print(tab(16) + "*** YOU MAY RETIRE IN " + retirementYear + " ***\n"); print("\n"); From 8e9ed09ce3b00426712f4a6e7c9f804d40dc4a7f Mon Sep 17 00:00:00 2001 From: Steve Bosman Date: Tue, 1 Feb 2022 00:10:47 +0000 Subject: [PATCH 07/15] 95 Weekday - introduce classes for durations and dates --- 95_Weekday/javascript/weekday.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/95_Weekday/javascript/weekday.js b/95_Weekday/javascript/weekday.js index 6feeda2e..ec2386d0 100644 --- a/95_Weekday/javascript/weekday.js +++ b/95_Weekday/javascript/weekday.js @@ -78,14 +78,14 @@ const COMMON_YEAR_MONTH_OFFSET = [0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5]; * Reads a date, and extracts the date information. * This expects date parts to be comma separated, using US date ordering, * i.e. Month,Day,Year. - * @returns {Promise<{year: number, month: number, day: number}>} + * @returns {Promise} */ async function readDateElements() { let dateString = await input(); const month = parseInt(dateString); const day = parseInt(dateString.substr(dateString.indexOf(",") + 1)); const year = parseInt(dateString.substr(dateString.lastIndexOf(",") + 1)); - return {year, month, day}; + return new DateStruct(year, month, day); } /** From d976d8297412d55f999c2e65c97f61f243052f0e Mon Sep 17 00:00:00 2001 From: Steve Bosman Date: Wed, 2 Feb 2022 14:30:04 +0000 Subject: [PATCH 08/15] Create build.yml --- .github/workflows/build.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..d371424b --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,17 @@ +name: Java CI + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 11 + uses: actions/setup-java@v2 + with: + java-version: '11' + distribution: 'adopt' + - name: Build with Maven + run: mvn --batch-mode --update-snapshots verify From 6311b6a7854854f7033bf741d6ab3dcf64a4ffb3 Mon Sep 17 00:00:00 2001 From: Steve Bosman Date: Wed, 2 Feb 2022 14:33:25 +0000 Subject: [PATCH 09/15] Revert "Create build.yml" This reverts commit d976d8297412d55f999c2e65c97f61f243052f0e. --- .github/workflows/build.yml | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index d371424b..00000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Java CI - -on: [push] - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Set up JDK 11 - uses: actions/setup-java@v2 - with: - java-version: '11' - distribution: 'adopt' - - name: Build with Maven - run: mvn --batch-mode --update-snapshots verify From 0434ac8fd404eea517ed314e7e43907202fde91d Mon Sep 17 00:00:00 2001 From: Steve Bosman Date: Wed, 2 Feb 2022 23:11:27 +0000 Subject: [PATCH 10/15] 95 Weekday - move date functions into DateStruct --- 95_Weekday/javascript/weekday.js | 62 ++++++++++++++++---------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/95_Weekday/javascript/weekday.js b/95_Weekday/javascript/weekday.js index ec2386d0..2e1129a0 100644 --- a/95_Weekday/javascript/weekday.js +++ b/95_Weekday/javascript/weekday.js @@ -51,6 +51,34 @@ class DateStruct { this.month = month; this.day = day; } + + /** + * Determine if the date could be a Gregorian date. + * Be aware the Gregorian calendar was not introduced in all places at once, + * see https://en.wikipedia.org/wiki/Gregorian_calendar + * @returns {boolean} true if date could be Gregorian; otherwise false. + */ + isGregorianDate() { + let result = false; + if (this.year > 1582) { + result = true; + } else if (this.year === 1582) { + if (this.month > 10) { + result = true; + } else if (this.month === 10 && this.day >= 15) { + result = true; + } + } + return result; + } + + /** + * Returns a US formatted date, i.e. Month/Day/Year. + * @returns {string} + */ + getFormattedDate() { + return this.month + "/" + this.day + "/" + this.year; + } } class Duration { @@ -88,15 +116,6 @@ async function readDateElements() { return new DateStruct(year, month, day); } -/** - * Returns a US formatted date, i.e. Month/Day/Year. - * @param {DateStruct} date - * @returns {string} - */ -function getFormattedDate(date) { - return date.month + "/" + date.day + "/" + date.year; -} - /** * Calculate years, months and days as factor of days. * This is a naive calculation which assumes all months are 30 days. @@ -257,27 +276,6 @@ function difference(date1, date2) { return new Duration(years, months, days); } -/** - * Determine if the supplied date could be a Gregorian date. - * Be aware the Gregorian calendar was not introduced in all places at once, - * see https://en.wikipedia.org/wiki/Gregorian_calendar - * @param {DateStruct} date - * @returns {boolean} true if date could be Gregorian; otherwise false. - */ -function isGregorianDate(date) { - let result = false; - if (date.year > 1582) { - result = true; - } else if (date.year === 1582) { - if (date.month > 10) { - result = true; - } else if (date.month === 10 && date.day >= 15) { - result = true; - } - } - return result; -} - // Main control section async function main() { print(tab(32) + "WEEKDAY\n"); @@ -296,13 +294,13 @@ async function main() { const dateOfBirth = await readDateElements(); print("\n"); // Test for date before current calendar. - if (!isGregorianDate(dateOfBirth)) { + if (!dateOfBirth.isGregorianDate()) { print("NOT PREPARED TO GIVE DAY OF WEEK PRIOR TO X.XV.MDLXXXII.\n"); } else { const normalisedToday = getNormalisedDay(today); const normalisedDob = getNormalisedDay(dateOfBirth); - const dateOfBirthText = getFormattedDate(dateOfBirth); + const dateOfBirthText = dateOfBirth.getFormattedDate(); let dayOfWeekText = getDayOfWeekText(dateOfBirth); if (normalisedToday < normalisedDob) { print(dateOfBirthText + " WILL BE A " + dayOfWeekText + "\n"); From 277ab470192122617b4f49f371e9ff7ca5b6a96a Mon Sep 17 00:00:00 2001 From: Steve Bosman Date: Thu, 3 Feb 2022 23:44:23 +0000 Subject: [PATCH 11/15] 95 Weekday - move functions into classes --- 95_Weekday/javascript/weekday.js | 175 ++++++++++++++++++------------- 1 file changed, 105 insertions(+), 70 deletions(-) diff --git a/95_Weekday/javascript/weekday.js b/95_Weekday/javascript/weekday.js index 2e1129a0..653b3de3 100644 --- a/95_Weekday/javascript/weekday.js +++ b/95_Weekday/javascript/weekday.js @@ -36,9 +36,9 @@ function tab(space) { } class DateStruct { - year; - month; - day; + #year; + #month; + #day; /** * Build a DateStruct @@ -47,9 +47,25 @@ class DateStruct { * @param {number} day */ constructor(year, month, day) { - this.year = year; - this.month = month; - this.day = day; + this.#year = year; + this.#month = month; + this.#day = day; + } + + get year() { + return this.#year; + } + + get month() { + return this.#month; + } + + get day() { + return this.#day; + } + + clone = () => { + return new DateStruct(this.#year, this.#month, this.#day) } /** @@ -58,14 +74,14 @@ class DateStruct { * see https://en.wikipedia.org/wiki/Gregorian_calendar * @returns {boolean} true if date could be Gregorian; otherwise false. */ - isGregorianDate() { + isGregorianDate = function () { let result = false; - if (this.year > 1582) { + if (this.#year > 1582) { result = true; - } else if (this.year === 1582) { - if (this.month > 10) { + } else if (this.#year === 1582) { + if (this.#month > 10) { result = true; - } else if (this.month === 10 && this.day >= 15) { + } else if (this.#month === 10 && this.#day >= 15) { result = true; } } @@ -76,15 +92,15 @@ class DateStruct { * Returns a US formatted date, i.e. Month/Day/Year. * @returns {string} */ - getFormattedDate() { - return this.month + "/" + this.day + "/" + this.year; + toString = function () { + return this.#month + "/" + this.#day + "/" + this.#year; } } class Duration { - years; - months; - days; + #years; + #months; + #days; /** * Build a Duration @@ -93,9 +109,70 @@ class Duration { * @param {number} days */ constructor(years, months, days) { - this.years = years; - this.months = months; - this.days = days; + this.#years = years; + this.#months = months; + this.#days = days; + } + + get years() { + return this.#years; + } + + get months() { + return this.#months; + } + + get days() { + return this.#days; + } + + clone = () => { + return new Duration(this.#years, this.#months, this.#days) + } + + /** + * Adjust Duration by removing years, months and days from supplied Duration. + * This is a naive calculation which assumes all months are 30 days. + * @param {Duration} timeToRemove + */ + remove = (timeToRemove) => { + this.#years -= timeToRemove.years; + this.#months -= timeToRemove.months; + this.#days -= timeToRemove.days; + if (this.#days < 0) { + this.#days += 30; + this.#months--; + } + if (this.#months < 0) { + this.#months += 12; + this.#years--; + } + } + + toString = () => { + return this.#years + "/" + this.#months + "/" + this.#days; + } + + /** + * Determine approximate Duration between two dates. + * This is a naive calculation which assumes all months are 30 days. + * @param {DateStruct} date1 + * @param {DateStruct} date2 + * @returns {Duration} + */ + static between(date1, date2) { + let years = date1.year - date2.year; + let months = date1.month - date2.month; + let days = date1.day - date2.day; + if (days < 0) { + months--; + days += 30; + } + if (months < 0) { + years--; + months += 12; + } + return new Duration(years, months, days); } } @@ -140,25 +217,6 @@ function printTimeSpent(duration) { print(duration.years + "\t" + duration.months + "\t" + duration.days + "\n"); } -/** - * Adjust unaccounted time by remove years, months and days supplied. - * @param {Duration} unaccountedTime - * @param {Duration} timeToRemove - */ -function adjustUnaccountedTime(unaccountedTime, timeToRemove) { - unaccountedTime.years -= timeToRemove.years; - unaccountedTime.months -= timeToRemove.months; - unaccountedTime.days -= timeToRemove.days; - if (unaccountedTime.days < 0) { - unaccountedTime.days += 30; - unaccountedTime.months--; - } - if (unaccountedTime.months <= 0) { - unaccountedTime.months += 12; - unaccountedTime.years--; - } -} - /** * Determine if the given year is a leap year. * @param year @@ -193,7 +251,7 @@ function getDayOfWeek(date) { const yearInCenturyOffsets = yearInCentury / 4 + yearInCentury; // combine offsets with day and month - let dayOfWeek = centuryOffset + yearInCenturyOffsets + date.day + COMMON_YEAR_MONTH_OFFSET[date.month-1]; + let dayOfWeek = centuryOffset + yearInCenturyOffsets + date.day + COMMON_YEAR_MONTH_OFFSET[date.month - 1]; dayOfWeek = Math.floor(dayOfWeek % 7) + 1; if (date.month <= 2 && isLeapYear(date.year)) { @@ -254,28 +312,6 @@ function getNormalisedDay(date) { return (date.year * 12 + date.month) * 31 + date.day; } -/** - * Determine approximate difference between two dates. - * This is a naive calculation which assumes all months are 30 days. - * @param {DateStruct} date1 - * @param {DateStruct} date2 - * @returns {Duration} - */ -function difference(date1, date2) { - let years = date1.year - date2.year; - let months = date1.month - date2.month; - let days = date1.day - date2.day; - if (days < 0) { - months--; - days += 30; - } - if (months < 0) { - years--; - months += 12; - } - return new Duration(years, months, days); -} - // Main control section async function main() { print(tab(32) + "WEEKDAY\n"); @@ -300,19 +336,18 @@ async function main() { const normalisedToday = getNormalisedDay(today); const normalisedDob = getNormalisedDay(dateOfBirth); - const dateOfBirthText = dateOfBirth.getFormattedDate(); let dayOfWeekText = getDayOfWeekText(dateOfBirth); if (normalisedToday < normalisedDob) { - print(dateOfBirthText + " WILL BE A " + dayOfWeekText + "\n"); + print(dateOfBirth + " WILL BE A " + dayOfWeekText + "\n"); } else if (normalisedToday === normalisedDob) { - print(dateOfBirthText + " IS A " + dayOfWeekText + "\n"); + print(dateOfBirth + " IS A " + dayOfWeekText + "\n"); } else { - print(dateOfBirthText + " WAS A " + dayOfWeekText + "\n"); + print(dateOfBirth + " WAS A " + dayOfWeekText + "\n"); } if (normalisedToday !== normalisedDob) { print("\n"); - let differenceBetweenDates = difference(today, dateOfBirth); + let differenceBetweenDates = Duration.between(today, dateOfBirth); if (differenceBetweenDates.years >= 0) { if (differenceBetweenDates.days === 0 && differenceBetweenDates.months === 0) { print("***HAPPY BIRTHDAY***\n"); @@ -323,19 +358,19 @@ async function main() { printTimeSpent(differenceBetweenDates); const approximateDaysBetween = (differenceBetweenDates.years * 365) + (differenceBetweenDates.months * 30) + differenceBetweenDates.days + Math.floor(differenceBetweenDates.months / 2); // Create an object containing time unaccounted for - const unaccountedTime = {...differenceBetweenDates}; + const unaccountedTime = differenceBetweenDates.clone(); // Calculate time spent in the following functions. print("YOU HAVE SLEPT \t\t\t"); const sleepTimeSpent = time_spent(0.35, approximateDaysBetween); printTimeSpent(sleepTimeSpent); - adjustUnaccountedTime(unaccountedTime, sleepTimeSpent); + unaccountedTime.remove(sleepTimeSpent); print("YOU HAVE EATEN \t\t\t"); const eatenTimeSpent = time_spent(0.17, approximateDaysBetween); printTimeSpent(eatenTimeSpent); - adjustUnaccountedTime(unaccountedTime, eatenTimeSpent); + unaccountedTime.remove(eatenTimeSpent); if (unaccountedTime.years <= 3) { print("YOU HAVE PLAYED \t\t\t"); } else if (unaccountedTime.years <= 9) { @@ -346,7 +381,7 @@ async function main() { const workPlayTimeSpent = time_spent(0.23, approximateDaysBetween); printTimeSpent(workPlayTimeSpent); - adjustUnaccountedTime(unaccountedTime, workPlayTimeSpent); + unaccountedTime.remove(workPlayTimeSpent); if (unaccountedTime.months === 12) { unaccountedTime.years++; unaccountedTime.months = 0; From a599e47e3d16be1f8e4c4d8da0a6c00e42b7e468 Mon Sep 17 00:00:00 2001 From: Steve Bosman Date: Sun, 6 Feb 2022 00:10:37 +0000 Subject: [PATCH 12/15] 95 Weekday - reduce use of lambdas; add a single method for tidying up values --- 95_Weekday/javascript/weekday.js | 85 +++++++++++++++++++------------- 1 file changed, 51 insertions(+), 34 deletions(-) diff --git a/95_Weekday/javascript/weekday.js b/95_Weekday/javascript/weekday.js index 653b3de3..71e29617 100644 --- a/95_Weekday/javascript/weekday.js +++ b/95_Weekday/javascript/weekday.js @@ -3,10 +3,18 @@ // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess) // +/** + * Print given string to the end of the "output" element. + * @param str + */ function print(str) { document.getElementById("output").appendChild(document.createTextNode(str)); } +/** + * Obtain user input + * @returns {Promise} + */ function input() { return new Promise(function (resolve) { const input_element = document.createElement("INPUT"); @@ -28,13 +36,26 @@ function input() { }); } -function tab(space) { +/** + * Create a string consisting of the given number of spaces + * @param spaceCount + * @returns {string} + */ +function tab(spaceCount) { let str = ""; - while (space-- > 0) + while (spaceCount-- > 0) str += " "; return str; } +const MONTHS_PER_YEAR = 12; +const DAYS_PER_COMMON_YEAR = 365; +const DAYS_PER_IDEAL_MONTH = 30; +const MAXIMUM_DAYS_PER_MONTH = 31; + +/** + * Date representation. + */ class DateStruct { #year; #month; @@ -64,17 +85,13 @@ class DateStruct { return this.#day; } - clone = () => { - return new DateStruct(this.#year, this.#month, this.#day) - } - /** * Determine if the date could be a Gregorian date. * Be aware the Gregorian calendar was not introduced in all places at once, * see https://en.wikipedia.org/wiki/Gregorian_calendar * @returns {boolean} true if date could be Gregorian; otherwise false. */ - isGregorianDate = function () { + isGregorianDate() { let result = false; if (this.#year > 1582) { result = true; @@ -92,11 +109,15 @@ class DateStruct { * Returns a US formatted date, i.e. Month/Day/Year. * @returns {string} */ - toString = function () { + toString() { return this.#month + "/" + this.#day + "/" + this.#year; } } +/** + * Duration representation. + * Note: this class only handles positive durations well + */ class Duration { #years; #months; @@ -112,6 +133,7 @@ class Duration { this.#years = years; this.#months = months; this.#days = days; + this.#fixRanges(); } get years() { @@ -126,8 +148,8 @@ class Duration { return this.#days; } - clone = () => { - return new Duration(this.#years, this.#months, this.#days) + clone() { + return new Duration(this.#years, this.#months, this.#days); } /** @@ -135,21 +157,28 @@ class Duration { * This is a naive calculation which assumes all months are 30 days. * @param {Duration} timeToRemove */ - remove = (timeToRemove) => { + remove(timeToRemove) { this.#years -= timeToRemove.years; this.#months -= timeToRemove.months; this.#days -= timeToRemove.days; + this.#fixRanges(); + } + + /** + * Move days and months into expected range. + */ + #fixRanges() { if (this.#days < 0) { - this.#days += 30; + this.#days += DAYS_PER_IDEAL_MONTH; this.#months--; } if (this.#months < 0) { - this.#months += 12; + this.#months += MONTHS_PER_YEAR; this.#years--; } } - toString = () => { + toString() { return this.#years + "/" + this.#months + "/" + this.#days; } @@ -164,14 +193,6 @@ class Duration { let years = date1.year - date2.year; let months = date1.month - date2.month; let days = date1.day - date2.day; - if (days < 0) { - months--; - days += 30; - } - if (months < 0) { - years--; - months += 12; - } return new Duration(years, months, days); } } @@ -202,10 +223,10 @@ async function readDateElements() { */ function time_spent(factor, dayCount) { let totalDays = Math.floor(factor * dayCount); - const years = Math.floor(totalDays / 365); - totalDays -= years * 365; - const months = Math.floor(totalDays / 30); - const days = totalDays - (months * 30); + const years = Math.floor(totalDays / DAYS_PER_COMMON_YEAR); + totalDays -= years * DAYS_PER_COMMON_YEAR; + const months = Math.floor(totalDays / DAYS_PER_IDEAL_MONTH); + const days = totalDays - (months * DAYS_PER_IDEAL_MONTH); return new Duration(years, months, days); } @@ -309,7 +330,7 @@ function getDayOfWeekText(date) { * @returns {number} */ function getNormalisedDay(date) { - return (date.year * 12 + date.month) * 31 + date.day; + return (date.year * MONTHS_PER_YEAR + date.month) * MAXIMUM_DAYS_PER_MONTH + date.day; } // Main control section @@ -356,7 +377,7 @@ async function main() { print(" \t-----\t------\t----\n"); print("YOUR AGE (IF BIRTHDATE) \t"); printTimeSpent(differenceBetweenDates); - const approximateDaysBetween = (differenceBetweenDates.years * 365) + (differenceBetweenDates.months * 30) + differenceBetweenDates.days + Math.floor(differenceBetweenDates.months / 2); + const approximateDaysBetween = (differenceBetweenDates.years * DAYS_PER_COMMON_YEAR) + (differenceBetweenDates.months * DAYS_PER_IDEAL_MONTH) + differenceBetweenDates.days + Math.floor(differenceBetweenDates.months / 2); // Create an object containing time unaccounted for const unaccountedTime = differenceBetweenDates.clone(); @@ -372,7 +393,7 @@ async function main() { unaccountedTime.remove(eatenTimeSpent); if (unaccountedTime.years <= 3) { - print("YOU HAVE PLAYED \t\t\t"); + print("YOU HAVE PLAYED \t\t"); } else if (unaccountedTime.years <= 9) { print("YOU HAVE PLAYED/STUDIED \t\t"); } else { @@ -382,12 +403,8 @@ async function main() { printTimeSpent(workPlayTimeSpent); unaccountedTime.remove(workPlayTimeSpent); - if (unaccountedTime.months === 12) { - unaccountedTime.years++; - unaccountedTime.months = 0; - } print("YOU HAVE RELAXED \t\t"); - printTimeSpent(unaccountedTime) + printTimeSpent(unaccountedTime); const retirementYear = dateOfBirth.year + 65; print("\n"); From 463351287e69aa6507a26f867617f4771acb029d Mon Sep 17 00:00:00 2001 From: Steve Bosman Date: Sun, 6 Feb 2022 21:53:50 +0000 Subject: [PATCH 13/15] 95 Weekday - move methods into appropriate? scopes or classes --- 95_Weekday/javascript/weekday.js | 290 +++++++++++++++---------------- 1 file changed, 143 insertions(+), 147 deletions(-) diff --git a/95_Weekday/javascript/weekday.js b/95_Weekday/javascript/weekday.js index 71e29617..69b385d8 100644 --- a/95_Weekday/javascript/weekday.js +++ b/95_Weekday/javascript/weekday.js @@ -50,8 +50,10 @@ function tab(spaceCount) { const MONTHS_PER_YEAR = 12; const DAYS_PER_COMMON_YEAR = 365; -const DAYS_PER_IDEAL_MONTH = 30; +const DAYS_PER_IDEALISED_MONTH = 30; const MAXIMUM_DAYS_PER_MONTH = 31; +// In a common (non-leap) year the day of the week for the first of each month moves by the following amounts. +const COMMON_YEAR_MONTH_OFFSET = [0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5]; /** * Date representation. @@ -105,6 +107,60 @@ class DateStruct { return result; } + /** + * The following performs a hash on the day parts which guarantees that + * 1. different days will return different numbers + * 2. the numbers returned are ordered. + * @returns {number} + */ + getNormalisedDay() { + return (this.year * MONTHS_PER_YEAR + this.month) * MAXIMUM_DAYS_PER_MONTH + this.day; + } + + /** + * Determine the day of the week. + * This calculation returns a number between 1 and 7 where Sunday=1, Monday=2, ..., Saturday=7. + * @returns {number} Value between 1 and 7 representing Sunday to Saturday. + */ + getDayOfWeek() { + // Calculate an offset based on the century part of the year. + const centuriesSince1500 = Math.floor((this.year - 1500) / 100); + let centuryOffset = centuriesSince1500 * 5 + (centuriesSince1500 + 3) / 4; + centuryOffset = Math.floor(centuryOffset % 7); + + // Calculate an offset based on the shortened two digit year. + // January 1st moves forward by approximately 1.25 days per year + const yearInCentury = this.year % 100; + const yearInCenturyOffsets = yearInCentury / 4 + yearInCentury; + + // combine offsets with day and month + let dayOfWeek = centuryOffset + yearInCenturyOffsets + this.day + COMMON_YEAR_MONTH_OFFSET[this.month - 1]; + + dayOfWeek = Math.floor(dayOfWeek % 7) + 1; + if (this.month <= 2 && this.isLeapYear()) { + dayOfWeek--; + } + if (dayOfWeek === 0) { + dayOfWeek = 7; + } + return dayOfWeek; + } + + /** + * Determine if the given year is a leap year. + * @returns {boolean} + */ + isLeapYear() { + if ((this.year % 4) !== 0) { + return false; + } else if ((this.year % 100) !== 0) { + return true; + } else if ((this.year % 400) !== 0) { + return false; + } + return true; + } + /** * Returns a US formatted date, i.e. Month/Day/Year. * @returns {string} @@ -169,7 +225,7 @@ class Duration { */ #fixRanges() { if (this.#days < 0) { - this.#days += DAYS_PER_IDEAL_MONTH; + this.#days += DAYS_PER_IDEALISED_MONTH; this.#months--; } if (this.#months < 0) { @@ -178,10 +234,6 @@ class Duration { } } - toString() { - return this.#years + "/" + this.#months + "/" + this.#days; - } - /** * Determine approximate Duration between two dates. * This is a naive calculation which assumes all months are 30 days. @@ -195,146 +247,90 @@ class Duration { let days = date1.day - date2.day; return new Duration(years, months, days); } -} -// In a common (non-leap) year the day of the week for the first of each month moves by the following amounts. -const COMMON_YEAR_MONTH_OFFSET = [0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5]; - -/** - * Reads a date, and extracts the date information. - * This expects date parts to be comma separated, using US date ordering, - * i.e. Month,Day,Year. - * @returns {Promise} - */ -async function readDateElements() { - let dateString = await input(); - const month = parseInt(dateString); - const day = parseInt(dateString.substr(dateString.indexOf(",") + 1)); - const year = parseInt(dateString.substr(dateString.lastIndexOf(",") + 1)); - return new DateStruct(year, month, day); -} - -/** - * Calculate years, months and days as factor of days. - * This is a naive calculation which assumes all months are 30 days. - * @param factor - * @param dayCount - * @returns {Duration} - */ -function time_spent(factor, dayCount) { - let totalDays = Math.floor(factor * dayCount); - const years = Math.floor(totalDays / DAYS_PER_COMMON_YEAR); - totalDays -= years * DAYS_PER_COMMON_YEAR; - const months = Math.floor(totalDays / DAYS_PER_IDEAL_MONTH); - const days = totalDays - (months * DAYS_PER_IDEAL_MONTH); - return new Duration(years, months, days); -} - -/** - * Print the supplied duration. - * @param {Duration} duration - */ -function printTimeSpent(duration) { - print(duration.years + "\t" + duration.months + "\t" + duration.days + "\n"); -} - -/** - * Determine if the given year is a leap year. - * @param year - * @returns {boolean} - */ -function isLeapYear(year) { - if ((year % 4) !== 0) { - return false; - } else if ((year % 100) !== 0) { - return true; - } else if ((year % 400) !== 0) { - return false; + /** + * Calculate years, months and days as factor of days. + * This is a naive calculation which assumes all months are 30 days. + * @param dayCount Total day to convert to a duration + * @param factor Factor to apply when calculating the duration + * @returns {Duration} + */ + static fromDays(dayCount, factor) { + let totalDays = Math.floor(factor * dayCount); + const years = Math.floor(totalDays / DAYS_PER_COMMON_YEAR); + totalDays -= years * DAYS_PER_COMMON_YEAR; + const months = Math.floor(totalDays / DAYS_PER_IDEALISED_MONTH); + const days = totalDays - (months * DAYS_PER_IDEALISED_MONTH); + return new Duration(years, months, days); } - return true; -} -/** - * Determine the day of the week. - * This calculation returns a number between 1 and 7 where Sunday=1, Monday=2, ..., Saturday=7. - * @param {DateStruct} date - * @returns {number} Value between 1 and 7 representing Sunday to Saturday. - */ -function getDayOfWeek(date) { - // Calculate an offset based on the century part of the year. - const centuriesSince1500 = Math.floor((date.year - 1500) / 100); - let centuryOffset = centuriesSince1500 * 5 + (centuriesSince1500 + 3) / 4; - centuryOffset = Math.floor(centuryOffset % 7); - - // Calculate an offset based on the shortened two digit year. - // January 1st moves forward by approximately 1.25 days per year - const yearInCentury = date.year % 100; - const yearInCenturyOffsets = yearInCentury / 4 + yearInCentury; - - // combine offsets with day and month - let dayOfWeek = centuryOffset + yearInCenturyOffsets + date.day + COMMON_YEAR_MONTH_OFFSET[date.month - 1]; - - dayOfWeek = Math.floor(dayOfWeek % 7) + 1; - if (date.month <= 2 && isLeapYear(date.year)) { - dayOfWeek--; + toString() { + return this.#years + "/" + this.#months + "/" + this.#days; } - if (dayOfWeek === 0) { - dayOfWeek = 7; - } - return dayOfWeek; -} - -/** - * Obtain text for the day of the week. - * @param {DateStruct} date - * @returns {string} - */ -function getDayOfWeekText(date) { - const dayOfWeek = getDayOfWeek(date); - let dayOfWeekText = ""; - switch (dayOfWeek) { - case 1: - dayOfWeekText = "SUNDAY."; - break; - case 2: - dayOfWeekText = "MONDAY."; - break; - case 3: - dayOfWeekText = "TUESDAY."; - break; - case 4: - dayOfWeekText = "WEDNESDAY."; - break; - case 5: - dayOfWeekText = "THURSDAY."; - break; - case 6: - if (date.day === 13) { - dayOfWeekText = "FRIDAY THE THIRTEENTH---BEWARE!"; - } else { - dayOfWeekText = "FRIDAY."; - } - break; - case 7: - dayOfWeekText = "SATURDAY."; - break; - } - return dayOfWeekText; -} - -/** - * The following performs a hash on the day parts which guarantees that - * 1. different days will return different numbers - * 2. the numbers returned are ordered. - * @param {DateStruct} date - * @returns {number} - */ -function getNormalisedDay(date) { - return (date.year * MONTHS_PER_YEAR + date.month) * MAXIMUM_DAYS_PER_MONTH + date.day; } // Main control section async function main() { + /** + * Reads a date, and extracts the date information. + * This expects date parts to be comma separated, using US date ordering, + * i.e. Month,Day,Year. + * @returns {Promise} + */ + async function inputDate() { + let dateString = await input(); + const month = parseInt(dateString); + const day = parseInt(dateString.substr(dateString.indexOf(",") + 1)); + const year = parseInt(dateString.substr(dateString.lastIndexOf(",") + 1)); + return new DateStruct(year, month, day); + } + + /** + * Print the supplied duration. + * @param {Duration} duration + */ + function printTimeSpent(duration) { + print(duration.years + "\t" + duration.months + "\t" + duration.days + "\n"); + } + + /** + * Obtain text for the day of the week. + * @param {DateStruct} date + * @returns {string} + */ + function getDayOfWeekText(date) { + const dayOfWeek = date.getDayOfWeek(); + let dayOfWeekText = ""; + switch (dayOfWeek) { + case 1: + dayOfWeekText = "SUNDAY."; + break; + case 2: + dayOfWeekText = "MONDAY."; + break; + case 3: + dayOfWeekText = "TUESDAY."; + break; + case 4: + dayOfWeekText = "WEDNESDAY."; + break; + case 5: + dayOfWeekText = "THURSDAY."; + break; + case 6: + if (date.day === 13) { + dayOfWeekText = "FRIDAY THE THIRTEENTH---BEWARE!"; + } else { + dayOfWeekText = "FRIDAY."; + } + break; + case 7: + dayOfWeekText = "SATURDAY."; + break; + } + return dayOfWeekText; + } + print(tab(32) + "WEEKDAY\n"); print(tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n"); print("\n"); @@ -344,18 +340,18 @@ async function main() { print("GIVES FACTS ABOUT A DATE OF INTEREST TO YOU.\n"); print("\n"); print("ENTER TODAY'S DATE IN THE FORM: 3,24,1979 "); - const today = await readDateElements(); + const today = await inputDate(); // This program determines the day of the week // for a date after 1582 print("ENTER DAY OF BIRTH (OR OTHER DAY OF INTEREST)"); - const dateOfBirth = await readDateElements(); + const dateOfBirth = await inputDate(); print("\n"); // Test for date before current calendar. if (!dateOfBirth.isGregorianDate()) { print("NOT PREPARED TO GIVE DAY OF WEEK PRIOR TO X.XV.MDLXXXII.\n"); } else { - const normalisedToday = getNormalisedDay(today); - const normalisedDob = getNormalisedDay(dateOfBirth); + const normalisedToday = today.getNormalisedDay(); + const normalisedDob = dateOfBirth.getNormalisedDay(); let dayOfWeekText = getDayOfWeekText(dateOfBirth); if (normalisedToday < normalisedDob) { @@ -377,29 +373,29 @@ async function main() { print(" \t-----\t------\t----\n"); print("YOUR AGE (IF BIRTHDATE) \t"); printTimeSpent(differenceBetweenDates); - const approximateDaysBetween = (differenceBetweenDates.years * DAYS_PER_COMMON_YEAR) + (differenceBetweenDates.months * DAYS_PER_IDEAL_MONTH) + differenceBetweenDates.days + Math.floor(differenceBetweenDates.months / 2); + const approximateDaysBetween = (differenceBetweenDates.years * DAYS_PER_COMMON_YEAR) + (differenceBetweenDates.months * DAYS_PER_IDEALISED_MONTH) + differenceBetweenDates.days + Math.floor(differenceBetweenDates.months / 2); // Create an object containing time unaccounted for const unaccountedTime = differenceBetweenDates.clone(); // Calculate time spent in the following functions. print("YOU HAVE SLEPT \t\t\t"); - const sleepTimeSpent = time_spent(0.35, approximateDaysBetween); + const sleepTimeSpent = Duration.fromDays(approximateDaysBetween, 0.35); printTimeSpent(sleepTimeSpent); unaccountedTime.remove(sleepTimeSpent); print("YOU HAVE EATEN \t\t\t"); - const eatenTimeSpent = time_spent(0.17, approximateDaysBetween); + const eatenTimeSpent = Duration.fromDays(approximateDaysBetween, 0.17); printTimeSpent(eatenTimeSpent); unaccountedTime.remove(eatenTimeSpent); if (unaccountedTime.years <= 3) { print("YOU HAVE PLAYED \t\t"); } else if (unaccountedTime.years <= 9) { - print("YOU HAVE PLAYED/STUDIED \t\t"); + print("YOU HAVE PLAYED/STUDIED \t"); } else { print("YOU HAVE WORKED/PLAYED \t\t"); } - const workPlayTimeSpent = time_spent(0.23, approximateDaysBetween); + const workPlayTimeSpent = Duration.fromDays(approximateDaysBetween, 0.23); printTimeSpent(workPlayTimeSpent); unaccountedTime.remove(workPlayTimeSpent); From b981f2b9de9f65127b22144d156a61aa5502c6cc Mon Sep 17 00:00:00 2001 From: Steve Bosman Date: Sun, 6 Feb 2022 22:38:56 +0000 Subject: [PATCH 14/15] 95 Weekday - move methods into appropriate? scopes or classes --- 95_Weekday/javascript/weekday.js | 82 ++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 36 deletions(-) diff --git a/95_Weekday/javascript/weekday.js b/95_Weekday/javascript/weekday.js index 69b385d8..223e17fe 100644 --- a/95_Weekday/javascript/weekday.js +++ b/95_Weekday/javascript/weekday.js @@ -52,8 +52,6 @@ const MONTHS_PER_YEAR = 12; const DAYS_PER_COMMON_YEAR = 365; const DAYS_PER_IDEALISED_MONTH = 30; const MAXIMUM_DAYS_PER_MONTH = 31; -// In a common (non-leap) year the day of the week for the first of each month moves by the following amounts. -const COMMON_YEAR_MONTH_OFFSET = [0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5]; /** * Date representation. @@ -123,6 +121,9 @@ class DateStruct { * @returns {number} Value between 1 and 7 representing Sunday to Saturday. */ getDayOfWeek() { + // In a common (non-leap) year the day of the week for the first of each month moves by the following amounts. + const COMMON_YEAR_MONTH_OFFSET = [0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5]; + // Calculate an offset based on the century part of the year. const centuriesSince1500 = Math.floor((this.year - 1500) / 100); let centuryOffset = centuriesSince1500 * 5 + (centuriesSince1500 + 3) / 4; @@ -234,6 +235,29 @@ class Duration { } } + /** + * Computes an approximation of the days covered by the duration. + * The calculation assumes all years are 365 days, months are 30 days each, + * and adds on an extra bit the more months that have passed. + * @returns {number} + */ + getApproximateDays() { + return ( + (this.#years * DAYS_PER_COMMON_YEAR) + + (this.#months * DAYS_PER_IDEALISED_MONTH) + + this.#days + + Math.floor(this.#months / 2) + ); + } + + /** + * Returns a formatted duration with tab separated values, i.e. Years\tMonths\tDays. + * @returns {string} + */ + toString() { + return this.#years + "\t" + this.#months + "\t" + this.#days; + } + /** * Determine approximate Duration between two dates. * This is a naive calculation which assumes all months are 30 days. @@ -263,10 +287,6 @@ class Duration { const days = totalDays - (months * DAYS_PER_IDEALISED_MONTH); return new Duration(years, months, days); } - - toString() { - return this.#years + "/" + this.#months + "/" + this.#days; - } } // Main control section @@ -285,14 +305,6 @@ async function main() { return new DateStruct(year, month, day); } - /** - * Print the supplied duration. - * @param {Duration} duration - */ - function printTimeSpent(duration) { - print(duration.years + "\t" + duration.months + "\t" + duration.days + "\n"); - } - /** * Obtain text for the day of the week. * @param {DateStruct} date @@ -371,36 +383,34 @@ async function main() { } print(" \tYEARS\tMONTHS\tDAYS\n"); print(" \t-----\t------\t----\n"); - print("YOUR AGE (IF BIRTHDATE) \t"); - printTimeSpent(differenceBetweenDates); - const approximateDaysBetween = (differenceBetweenDates.years * DAYS_PER_COMMON_YEAR) + (differenceBetweenDates.months * DAYS_PER_IDEALISED_MONTH) + differenceBetweenDates.days + Math.floor(differenceBetweenDates.months / 2); - // Create an object containing time unaccounted for + print("YOUR AGE (IF BIRTHDATE) \t" + differenceBetweenDates + "\n"); + + const approximateDaysBetween = differenceBetweenDates.getApproximateDays(); const unaccountedTime = differenceBetweenDates.clone(); - // Calculate time spent in the following functions. - print("YOU HAVE SLEPT \t\t\t"); + // 35% sleeping const sleepTimeSpent = Duration.fromDays(approximateDaysBetween, 0.35); - printTimeSpent(sleepTimeSpent); - + print("YOU HAVE SLEPT \t\t\t" + sleepTimeSpent + "\n"); unaccountedTime.remove(sleepTimeSpent); - print("YOU HAVE EATEN \t\t\t"); + + // 17% eating const eatenTimeSpent = Duration.fromDays(approximateDaysBetween, 0.17); - printTimeSpent(eatenTimeSpent); - + print("YOU HAVE EATEN \t\t\t" + eatenTimeSpent + "\n"); unaccountedTime.remove(eatenTimeSpent); - if (unaccountedTime.years <= 3) { - print("YOU HAVE PLAYED \t\t"); - } else if (unaccountedTime.years <= 9) { - print("YOU HAVE PLAYED/STUDIED \t"); - } else { - print("YOU HAVE WORKED/PLAYED \t\t"); - } - const workPlayTimeSpent = Duration.fromDays(approximateDaysBetween, 0.23); - printTimeSpent(workPlayTimeSpent); + // 23% working, studying or playing + const workPlayTimeSpent = Duration.fromDays(approximateDaysBetween, 0.23); + if (unaccountedTime.years <= 3) { + print("YOU HAVE PLAYED \t\t" + workPlayTimeSpent + "\n"); + } else if (unaccountedTime.years <= 9) { + print("YOU HAVE PLAYED/STUDIED \t" + workPlayTimeSpent + "\n"); + } else { + print("YOU HAVE WORKED/PLAYED \t\t" + workPlayTimeSpent + "\n"); + } unaccountedTime.remove(workPlayTimeSpent); - print("YOU HAVE RELAXED \t\t"); - printTimeSpent(unaccountedTime); + + // Remaining time spent relaxing + print("YOU HAVE RELAXED \t\t" + unaccountedTime + "\n"); const retirementYear = dateOfBirth.year + 65; print("\n"); From 9a76fa76fbf8ac9af76cbf291acf417e043a6b91 Mon Sep 17 00:00:00 2001 From: Steve Bosman Date: Sun, 6 Feb 2022 22:40:07 +0000 Subject: [PATCH 15/15] 95 Weekday - move methods into appropriate? scopes or classes --- 95_Weekday/javascript/weekday.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/95_Weekday/javascript/weekday.js b/95_Weekday/javascript/weekday.js index 223e17fe..6070c71c 100644 --- a/95_Weekday/javascript/weekday.js +++ b/95_Weekday/javascript/weekday.js @@ -52,6 +52,8 @@ const MONTHS_PER_YEAR = 12; const DAYS_PER_COMMON_YEAR = 365; const DAYS_PER_IDEALISED_MONTH = 30; const MAXIMUM_DAYS_PER_MONTH = 31; +// In a common (non-leap) year the day of the week for the first of each month moves by the following amounts. +const COMMON_YEAR_MONTH_OFFSET = [0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5]; /** * Date representation. @@ -121,9 +123,6 @@ class DateStruct { * @returns {number} Value between 1 and 7 representing Sunday to Saturday. */ getDayOfWeek() { - // In a common (non-leap) year the day of the week for the first of each month moves by the following amounts. - const COMMON_YEAR_MONTH_OFFSET = [0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5]; - // Calculate an offset based on the century part of the year. const centuriesSince1500 = Math.floor((this.year - 1500) / 100); let centuryOffset = centuriesSince1500 * 5 + (centuriesSince1500 + 3) / 4;