| 4 | 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 year, month, week descriptor, and day of week. |
| 8 | * |
| 9 | * @param {number} year - The year of the meetup |
| 10 | * @param {number} month - The month of the meetup (1-12) |
| 11 | * @param {string} week - The week descriptor ('first', 'second', 'third', 'fourth', 'last', 'teenth') |
| 12 | * @param {string} dayOfWeek - The day of the week ('Sunday', 'Monday', etc.) |
| 13 | * @returns {Date} - The calculated date of the meetup |
| 14 | * |
| 15 | * @example |
| 16 | * meetup(2018, 1, 'first', 'Monday') // Returns Date for January 1, 2018 |
| 17 | * meetup(1953, 8, 'teenth', 'Saturday') // Returns Date for August 15, 1953 |
| 12 | 18 | */ |
| 13 | 19 | export const meetup = (year, month, week, dayOfWeek) => { |
| 14 | | throw new Error('Remove this line and implement the function'); |
| 20 | // Map day names to their index (0 = Sunday, 1 = Monday, ..., 6 = Saturday) |
| 21 | const days = { |
| 22 | 'Sunday': 0, |
| 23 | 'Monday': 1, |
| 24 | 'Tuesday': 2, |
| 25 | 'Wednesday': 3, |
| 26 | 'Thursday': 4, |
| 27 | 'Friday': 5, |
| 28 | 'Saturday': 6 |
| 29 | }; |
| 30 | |
| 31 | // Edge Case: Invalid day of week |
| 32 | if (!(dayOfWeek in days)) { |
| 33 | throw new Error('Invalid day of week'); |
| 34 | } |
| 35 | |
| 36 | const dayIndex = days[dayOfWeek]; |
| 37 | |
| 38 | // Create a date object for the first day of the given month |
| 39 | const firstDay = new Date(year, month - 1, 1); |
| 40 | |
| 41 | // Edge Case: Invalid month (JavaScript Date will wrap months, so we need to check) |
| 42 | if (firstDay.getMonth() !== month - 1) { |
| 43 | throw new Error('Invalid month'); |
| 44 | } |
| 45 | |
| 46 | // Edge Case: Invalid year (JavaScript Date handles most years, but we check for negative values) |
| 47 | if (year < 1) { |
| 48 | throw new Error('Invalid year'); |
| 49 | } |
| 50 | |
| 51 | // Find the first occurrence of the specified day in the month |
| 52 | let firstOccurrence = firstDay.getDate() + (dayIndex - firstDay.getDay() + 7) % 7; |
| 53 | if (firstOccurrence > 7) firstOccurrence -= 7; |
| 54 | |
| 55 | // Create a date for the first occurrence of the specified day |
| 56 | const firstDate = new Date(year, month - 1, firstOccurrence); |
| 57 | |
| 58 | // Edge Case: If the first occurrence is in the previous month, move to the next week |
| 59 | if (firstDate.getMonth() !== month - 1) { |
| 60 | firstDate.setDate(firstDate.getDate() + 7); |
| 61 | } |
| 62 | |
| 63 | switch (week) { |
| 64 | case 'first': |
| 65 | return firstDate; |
| 66 | |
| 67 | case 'second': |
| 68 | // Edge Case: If adding 7 days goes to next month, this is still valid behavior |
| 69 | return new Date(firstDate.getFullYear(), firstDate.getMonth(), firstDate.getDate() + 7); |
| 70 | |
| 71 | case 'third': |
| 72 | // Edge Case: If adding 14 days goes to next month, this is still valid behavior |
| 73 | return new Date(firstDate.getFullYear(), firstDate.getMonth(), firstDate.getDate() + 14); |
| 74 | |
| 75 | case 'fourth': |
| 76 | // Edge Case: If adding 21 days goes to next month, this is still valid behavior |
| 77 | return new Date(firstDate.getFullYear(), firstDate.getMonth(), firstDate.getDate() + 21); |
| 78 | |
| 79 | case 'last': |
| 80 | // Start with the first occurrence and keep adding weeks until the next one would be in the next month |
| 81 | let lastDate = new Date(firstDate); |
| 82 | let nextDate = new Date(firstDate); |
| 83 | nextDate.setDate(nextDate.getDate() + 7); |
| 84 | |
| 85 | // Edge Case: Handle months where the day doesn't exist (e.g., February 30th) |
| 86 | while (nextDate.getMonth() === month - 1 && nextDate.getDate() > 0) { |
| 87 | lastDate = new Date(nextDate); |
| 88 | nextDate.setDate(nextDate.getDate() + 7); |
| 89 | } |
| 90 | |
| 91 | return lastDate; |
| 92 | |
| 93 | case 'teenth': |
| 94 | // Find the occurrence of the day in the teenth range (13-19) |
| 95 | let teenthDate = new Date(firstDate); |
| 96 | |
| 97 | // Keep adding weeks until we're in the teenth range |
| 98 | while (teenthDate.getDate() < 13) { |
| 99 | teenthDate.setDate(teenthDate.getDate() + 7); |
| 100 | } |
| 101 | |
| 102 | // Edge Case: If we've gone past the teenth range, this indicates an error in logic |
| 103 | if (teenthDate.getDate() > 19) { |
| 104 | throw new Error('Could not find teenth day'); |
| 105 | } |
| 106 | |
| 107 | return teenthDate; |
| 108 | |
| 109 | default: |
| 110 | // Edge Case: Invalid week descriptor |
| 111 | throw new Error('Invalid week descriptor'); |
| 112 | } |
| 113 | |
| 114 | // Handled Edge Cases: Invalid day of week, Invalid month, Invalid year, First occurrence in previous month, Adding days that go to next month, Months where day doesn't exist, Going past teenth range, Invalid week descriptor |
| 15 | 115 | }; |