| 1 | | // |
| 2 | | // This is only a SKELETON file for the 'Meetup' exercise. It's been provided as a |
| 3 | | // convenience to get you started writing code faster. |
| 4 | | // |
| 1 | /* |
| 2 | * Meetup exercise implementation |
| 3 | * Finds the exact date of a meetup given year, month, week, and day of week |
| 4 | */ |
| 5 | 5 | |
| 6 | 6 | /** |
| 7 | | * @param {number} year |
| 8 | | * @param {number} month |
| 9 | | * @param {string} week |
| 10 | | * @param {string} dayOfWeek |
| 11 | | * @returns {Date} |
| 7 | * Calculates the date of a meetup based on the given criteria |
| 8 | * @param {number} year - The year of the meetup |
| 9 | * @param {number} month - The month of the meetup (1-12) |
| 10 | * @param {string} week - The week descriptor ('first', 'second', 'third', 'fourth', 'last', 'teenth') |
| 11 | * @param {string} dayOfWeek - The day of the week ('Sunday', 'Monday', etc.) |
| 12 | * @returns {Date} - The calculated date of the meetup |
| 13 | * @throws {Error} - If the inputs are invalid or no matching date is found |
| 12 | 14 | */ |
| 13 | 15 | export const meetup = (year, month, week, dayOfWeek) => { |
| 14 | | throw new Error('Remove this line and implement the function'); |
| 16 | // Validate inputs |
| 17 | if (!Number.isInteger(year) || year < 1) { |
| 18 | throw new Error('Invalid year'); |
| 19 | } |
| 20 | |
| 21 | if (!Number.isInteger(month) || month < 1 || month > 12) { |
| 22 | throw new Error('Invalid month'); |
| 23 | } |
| 24 | |
| 25 | const validWeeks = ['first', 'second', 'third', 'fourth', 'last', 'teenth']; |
| 26 | if (!validWeeks.includes(week)) { |
| 27 | throw new Error('Invalid week descriptor'); |
| 28 | } |
| 29 | |
| 30 | const validDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; |
| 31 | if (!validDays.includes(dayOfWeek)) { |
| 32 | throw new Error('Invalid day of week'); |
| 33 | } |
| 34 | |
| 35 | // Get the target day of week as a number (0-6) |
| 36 | const targetDay = validDays.indexOf(dayOfWeek); |
| 37 | |
| 38 | // Create a date object for the first day of the month |
| 39 | const firstDay = new Date(year, month - 1, 1); |
| 40 | |
| 41 | // Edge Case: Handle 'teenth' week (days 13-19) |
| 42 | if (week === 'teenth') { |
| 43 | // Start from the 13th of the month |
| 44 | const startDate = new Date(year, month - 1, 13); |
| 45 | |
| 46 | // Find the first occurrence of the target day on or after the 13th |
| 47 | let currentDay = startDate; |
| 48 | while (currentDay.getDay() !== targetDay) { |
| 49 | currentDay.setDate(currentDay.getDate() + 1); |
| 50 | |
| 51 | // Edge Case: If we've moved past the 19th, there's an error in our logic |
| 52 | if (currentDay.getDate() > 19) { |
| 53 | throw new Error('Could not find teenth day'); |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | return currentDay; |
| 58 | } |
| 59 | |
| 60 | // Edge Case: Handle 'last' week |
| 61 | if (week === 'last') { |
| 62 | // Get the last day of the month |
| 63 | const lastDay = new Date(year, month, 0); |
| 64 | |
| 65 | // Start from the last day and work backwards to find the target day |
| 66 | let currentDay = new Date(lastDay); |
| 67 | while (currentDay.getDay() !== targetDay) { |
| 68 | currentDay.setDate(currentDay.getDate() - 1); |
| 69 | |
| 70 | // Edge Case: If we've moved to the previous month, there's an error |
| 71 | if (currentDay.getMonth() !== lastDay.getMonth()) { |
| 72 | throw new Error('Could not find last day'); |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | return currentDay; |
| 77 | } |
| 78 | |
| 79 | // Handle 'first', 'second', 'third', 'fourth' weeks |
| 80 | const weekNumbers = { |
| 81 | 'first': 1, |
| 82 | 'second': 2, |
| 83 | 'third': 3, |
| 84 | 'fourth': 4 |
| 85 | }; |
| 86 | |
| 87 | // Edge Case: Validate that we have a valid week number |
| 88 | if (!weekNumbers.hasOwnProperty(week)) { |
| 89 | throw new Error('Invalid week descriptor'); |
| 90 | } |
| 91 | |
| 92 | const weekNumber = weekNumbers[week]; |
| 93 | |
| 94 | // Find the first occurrence of the target day in the month |
| 95 | let firstOccurrence = new Date(firstDay); |
| 96 | while (firstOccurrence.getDay() !== targetDay) { |
| 97 | firstOccurrence.setDate(firstOccurrence.getDate() + 1); |
| 98 | |
| 99 | // Edge Case: If we've moved to the next month, the day doesn't exist in this month |
| 100 | if (firstOccurrence.getMonth() !== firstDay.getMonth()) { |
| 101 | throw new Error(`No ${week} ${dayOfWeek} in the given month`); |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | // Calculate the target date by adding the appropriate number of weeks |
| 106 | const targetDate = new Date(firstOccurrence); |
| 107 | targetDate.setDate(firstOccurrence.getDate() + (weekNumber - 1) * 7); |
| 108 | |
| 109 | // Edge Case: Check if the target date is still in the same month |
| 110 | if (targetDate.getMonth() !== firstDay.getMonth()) { |
| 111 | throw new Error(`No ${week} ${dayOfWeek} in the given month`); |
| 112 | } |
| 113 | |
| 114 | return targetDate; |
| 15 | 115 | }; |
| 116 | |
| 117 | // Handled Edge Cases: Invalid year, invalid month, invalid week descriptor, invalid day of week, teenth week past 19th, last week moving to previous month, target day not existing in month for numbered weeks, target date moving to next month for numbered weeks |