| 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 | const weekdays = { |
| 2 | sunday: 0, |
| 3 | monday: 1, |
| 4 | tuesday: 2, |
| 5 | wednesday: 3, |
| 6 | thursday: 4, |
| 7 | friday: 5, |
| 8 | saturday: 6 |
| 9 | }; |
| 10 | |
| 11 | const weekDescriptors = { |
| 12 | first: 1, |
| 13 | second: 2, |
| 14 | third: 3, |
| 15 | fourth: 4, |
| 16 | last: -1, |
| 17 | teenth: 0 |
| 18 | }; |
| 5 | 19 | |
| 6 | 20 | /** |
| 7 | | * @param {number} year |
| 8 | | * @param {number} month |
| 9 | | * @param {string} week |
| 10 | | * @param {string} dayOfWeek |
| 11 | | * @returns {Date} |
| 21 | * Calculates the date of a meetup based on year, month, week descriptor, and day of week. |
| 22 | * |
| 23 | * @param {number} year - The year of the meetup |
| 24 | * @param {number} month - The month of the meetup (1-12) |
| 25 | * @param {string} week - The week descriptor ('first', 'second', 'third', 'fourth', 'last', 'teenth') |
| 26 | * @param {string} dayOfWeek - The day of week ('sunday', 'monday', ..., 'saturday') |
| 27 | * @returns {Date} - The calculated date of the meetup |
| 28 | * |
| 29 | * Edge Cases Handled: |
| 30 | * - Invalid month values (outside 1-12) |
| 31 | * - Invalid week descriptors |
| 32 | * - Invalid day of week names |
| 33 | * - Month/year combinations that don't exist (e.g., invalid dates) |
| 34 | * - Last week calculations when month has fewer than 4 occurrences of a day |
| 35 | * - Teenth week calculations |
| 12 | 36 | */ |
| 13 | 37 | export const meetup = (year, month, week, dayOfWeek) => { |
| 14 | | throw new Error('Remove this line and implement the function'); |
| 38 | // Edge Case: Invalid month values |
| 39 | if (month < 1 || month > 12) { |
| 40 | throw new Error('Invalid month'); |
| 41 | } |
| 42 | |
| 43 | // Edge Case: Invalid week descriptor |
| 44 | if (!weekDescriptors.hasOwnProperty(week)) { |
| 45 | throw new Error('Invalid week descriptor'); |
| 46 | } |
| 47 | |
| 48 | // Edge Case: Invalid day of week |
| 49 | if (!weekdays.hasOwnProperty(dayOfWeek.toLowerCase())) { |
| 50 | throw new Error('Invalid day of week'); |
| 51 | } |
| 52 | |
| 53 | const targetDay = weekdays[dayOfWeek.toLowerCase()]; |
| 54 | |
| 55 | // For teenth, we only look at days 13-19 |
| 56 | if (week === 'teenth') { |
| 57 | for (let day = 13; day <= 19; day++) { |
| 58 | const date = new Date(year, month - 1, day); // month is 0-indexed in Date constructor |
| 59 | if (date.getDay() === targetDay) { |
| 60 | return date; |
| 61 | } |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | // For first, second, third, fourth, we find the nth occurrence |
| 66 | if (weekDescriptors[week] > 0) { |
| 67 | const occurrence = weekDescriptors[week]; |
| 68 | let count = 0; |
| 69 | |
| 70 | // Start at the first day of the month |
| 71 | const date = new Date(year, month - 1, 1); |
| 72 | |
| 73 | // Edge Case: Invalid date (should not happen with valid inputs, but checking for robustness) |
| 74 | if (isNaN(date.getTime())) { |
| 75 | throw new Error('Invalid date'); |
| 76 | } |
| 77 | |
| 78 | // Iterate through days of the month |
| 79 | while (date.getMonth() === month - 1) { // month is 0-indexed |
| 80 | if (date.getDay() === targetDay) { |
| 81 | count++; |
| 82 | if (count === occurrence) { |
| 83 | return date; |
| 84 | } |
| 85 | } |
| 86 | date.setDate(date.getDate() + 1); |
| 87 | } |
| 88 | |
| 89 | // Edge Case: Requested occurrence doesn't exist in the month |
| 90 | throw new Error(`The ${week} ${dayOfWeek} does not exist in the given month`); |
| 91 | } |
| 92 | |
| 93 | // For last, we find the last occurrence |
| 94 | if (week === 'last') { |
| 95 | // Start at the last day of the month |
| 96 | const lastDay = new Date(year, month, 0); // 0 gives us the last day of the previous month |
| 97 | |
| 98 | // Edge Case: Invalid date (should not happen with valid inputs, but checking for robustness) |
| 99 | if (isNaN(lastDay.getTime())) { |
| 100 | throw new Error('Invalid date'); |
| 101 | } |
| 102 | |
| 103 | // Iterate backwards through days of the month |
| 104 | const date = new Date(lastDay); |
| 105 | while (date.getMonth() === month - 1) { // month is 0-indexed |
| 106 | if (date.getDay() === targetDay) { |
| 107 | return date; |
| 108 | } |
| 109 | date.setDate(date.getDate() - 1); |
| 110 | |
| 111 | // Edge Case: Went past the beginning of the month |
| 112 | if (date.getMonth() !== month - 1 && date.getDate() > 15) { |
| 113 | break; |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | // Edge Case: Requested day doesn't exist in the month (should not happen but for safety) |
| 118 | throw new Error(`The last ${dayOfWeek} does not exist in the given month`); |
| 119 | } |
| 120 | |
| 121 | // Edge Case: Should not reach here with valid inputs |
| 122 | throw new Error('Invalid parameters'); |
| 15 | 123 | }; |
| 124 | |
| 125 | // Handled Edge Cases: Invalid month values, Invalid week descriptors, Invalid day of week names, Month/year combinations that don't exist, Last week calculations when month has fewer than 4 occurrences of a day, Teenth week calculations, Invalid date, Requested occurrence doesn't exist in the month, Went past the beginning of the month, Requested day doesn't exist in the month, Should not reach here with valid inputs |