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);