ts-time-utils 3.0.4 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +186 -6
  2. package/dist/calculate.d.ts +25 -0
  3. package/dist/calculate.d.ts.map +1 -1
  4. package/dist/calculate.js +125 -0
  5. package/dist/calendar.d.ts +45 -0
  6. package/dist/calendar.d.ts.map +1 -1
  7. package/dist/calendar.js +68 -0
  8. package/dist/calendars.d.ts +156 -0
  9. package/dist/calendars.d.ts.map +1 -0
  10. package/dist/calendars.js +348 -0
  11. package/dist/compare.d.ts +27 -0
  12. package/dist/compare.d.ts.map +1 -1
  13. package/dist/compare.js +46 -0
  14. package/dist/esm/calculate.d.ts +25 -0
  15. package/dist/esm/calculate.d.ts.map +1 -1
  16. package/dist/esm/calculate.js +125 -0
  17. package/dist/esm/calendar.d.ts +45 -0
  18. package/dist/esm/calendar.d.ts.map +1 -1
  19. package/dist/esm/calendar.js +68 -0
  20. package/dist/esm/calendars.d.ts +156 -0
  21. package/dist/esm/calendars.d.ts.map +1 -0
  22. package/dist/esm/calendars.js +348 -0
  23. package/dist/esm/compare.d.ts +27 -0
  24. package/dist/esm/compare.d.ts.map +1 -1
  25. package/dist/esm/compare.js +46 -0
  26. package/dist/esm/finance.d.ts +236 -0
  27. package/dist/esm/finance.d.ts.map +1 -0
  28. package/dist/esm/finance.js +495 -0
  29. package/dist/esm/healthcare.d.ts +260 -0
  30. package/dist/esm/healthcare.d.ts.map +1 -0
  31. package/dist/esm/healthcare.js +447 -0
  32. package/dist/esm/holidays.d.ts +11 -1
  33. package/dist/esm/holidays.d.ts.map +1 -1
  34. package/dist/esm/holidays.js +220 -1
  35. package/dist/esm/index.d.ts +19 -7
  36. package/dist/esm/index.d.ts.map +1 -1
  37. package/dist/esm/index.js +23 -9
  38. package/dist/esm/iterate.d.ts +55 -0
  39. package/dist/esm/iterate.d.ts.map +1 -1
  40. package/dist/esm/iterate.js +86 -0
  41. package/dist/esm/locale.d.ts +53 -0
  42. package/dist/esm/locale.d.ts.map +1 -1
  43. package/dist/esm/locale.js +141 -0
  44. package/dist/esm/precision.d.ts +225 -0
  45. package/dist/esm/precision.d.ts.map +1 -0
  46. package/dist/esm/precision.js +491 -0
  47. package/dist/esm/scheduling.d.ts +206 -0
  48. package/dist/esm/scheduling.d.ts.map +1 -0
  49. package/dist/esm/scheduling.js +329 -0
  50. package/dist/esm/temporal.d.ts +237 -0
  51. package/dist/esm/temporal.d.ts.map +1 -0
  52. package/dist/esm/temporal.js +660 -0
  53. package/dist/esm/validate.d.ts +30 -0
  54. package/dist/esm/validate.d.ts.map +1 -1
  55. package/dist/esm/validate.js +48 -0
  56. package/dist/finance.d.ts +236 -0
  57. package/dist/finance.d.ts.map +1 -0
  58. package/dist/finance.js +495 -0
  59. package/dist/healthcare.d.ts +260 -0
  60. package/dist/healthcare.d.ts.map +1 -0
  61. package/dist/healthcare.js +447 -0
  62. package/dist/holidays.d.ts +11 -1
  63. package/dist/holidays.d.ts.map +1 -1
  64. package/dist/holidays.js +220 -1
  65. package/dist/index.d.ts +19 -7
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +23 -9
  68. package/dist/iterate.d.ts +55 -0
  69. package/dist/iterate.d.ts.map +1 -1
  70. package/dist/iterate.js +86 -0
  71. package/dist/locale.d.ts +53 -0
  72. package/dist/locale.d.ts.map +1 -1
  73. package/dist/locale.js +141 -0
  74. package/dist/precision.d.ts +225 -0
  75. package/dist/precision.d.ts.map +1 -0
  76. package/dist/precision.js +491 -0
  77. package/dist/scheduling.d.ts +206 -0
  78. package/dist/scheduling.d.ts.map +1 -0
  79. package/dist/scheduling.js +329 -0
  80. package/dist/temporal.d.ts +237 -0
  81. package/dist/temporal.d.ts.map +1 -0
  82. package/dist/temporal.js +660 -0
  83. package/dist/validate.d.ts +30 -0
  84. package/dist/validate.d.ts.map +1 -1
  85. package/dist/validate.js +48 -0
  86. package/package.json +31 -1
@@ -0,0 +1,495 @@
1
+ /**
2
+ * @fileoverview Finance utilities for market-aware date calculations
3
+ * Provides US market hours, trading days, settlement dates, and options expiration
4
+ */
5
+ /** Market hours for US exchanges */
6
+ export const MARKET_HOURS = {
7
+ NYSE: {
8
+ open: { hour: 9, minute: 30 },
9
+ close: { hour: 16, minute: 0 },
10
+ timezone: 'America/New_York',
11
+ preMarket: { hour: 4, minute: 0 },
12
+ afterHours: { hour: 20, minute: 0 }
13
+ },
14
+ NASDAQ: {
15
+ open: { hour: 9, minute: 30 },
16
+ close: { hour: 16, minute: 0 },
17
+ timezone: 'America/New_York',
18
+ preMarket: { hour: 4, minute: 0 },
19
+ afterHours: { hour: 20, minute: 0 }
20
+ }
21
+ };
22
+ /** US market holidays (NYSE/NASDAQ follow same schedule) */
23
+ export const US_MARKET_HOLIDAYS = [
24
+ "New Year's Day",
25
+ 'Martin Luther King Jr. Day',
26
+ "Presidents' Day",
27
+ 'Good Friday',
28
+ 'Memorial Day',
29
+ 'Juneteenth',
30
+ 'Independence Day',
31
+ 'Labor Day',
32
+ 'Thanksgiving Day',
33
+ 'Christmas Day'
34
+ ];
35
+ /**
36
+ * Helper to convert DateInput to Date
37
+ */
38
+ function toDate(input) {
39
+ if (input instanceof Date)
40
+ return new Date(input);
41
+ return new Date(input);
42
+ }
43
+ /**
44
+ * Calculate Easter Sunday using the Anonymous Gregorian algorithm
45
+ */
46
+ function getEasterSunday(year) {
47
+ const a = year % 19;
48
+ const b = Math.floor(year / 100);
49
+ const c = year % 100;
50
+ const d = Math.floor(b / 4);
51
+ const e = b % 4;
52
+ const f = Math.floor((b + 8) / 25);
53
+ const g = Math.floor((b - f + 1) / 3);
54
+ const h = (19 * a + b - d - g + 15) % 30;
55
+ const i = Math.floor(c / 4);
56
+ const k = c % 4;
57
+ const l = (32 + 2 * e + 2 * i - h - k) % 7;
58
+ const m = Math.floor((a + 11 * h + 22 * l) / 451);
59
+ const month = Math.floor((h + l - 7 * m + 114) / 31);
60
+ const day = ((h + l - 7 * m + 114) % 31) + 1;
61
+ return new Date(year, month - 1, day);
62
+ }
63
+ /**
64
+ * Get nth occurrence of a weekday in a month
65
+ */
66
+ function getNthWeekdayOfMonth(year, month, dayOfWeek, n) {
67
+ const firstDay = new Date(year, month, 1);
68
+ const firstWeekday = firstDay.getDay();
69
+ let dayOffset = dayOfWeek - firstWeekday;
70
+ if (dayOffset < 0)
71
+ dayOffset += 7;
72
+ const date = 1 + dayOffset + (n - 1) * 7;
73
+ return new Date(year, month, date);
74
+ }
75
+ /**
76
+ * Get last occurrence of a weekday in a month
77
+ */
78
+ function getLastWeekdayOfMonth(year, month, dayOfWeek) {
79
+ const lastDay = new Date(year, month + 1, 0);
80
+ const lastDate = lastDay.getDate();
81
+ const lastWeekday = lastDay.getDay();
82
+ let diff = lastWeekday - dayOfWeek;
83
+ if (diff < 0)
84
+ diff += 7;
85
+ return new Date(year, month, lastDate - diff);
86
+ }
87
+ /**
88
+ * Adjust date if it falls on a weekend (observed holiday rules)
89
+ * - If Saturday, observe on Friday
90
+ * - If Sunday, observe on Monday
91
+ */
92
+ function adjustForWeekend(date) {
93
+ const day = date.getDay();
94
+ if (day === 0) { // Sunday -> Monday
95
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate() + 1);
96
+ }
97
+ else if (day === 6) { // Saturday -> Friday
98
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate() - 1);
99
+ }
100
+ return date;
101
+ }
102
+ /**
103
+ * Get all US market holidays for a year
104
+ */
105
+ function getUSMarketHolidays(year) {
106
+ const holidays = [];
107
+ // New Year's Day (January 1, or observed)
108
+ holidays.push(adjustForWeekend(new Date(year, 0, 1)));
109
+ // Martin Luther King Jr. Day (3rd Monday of January)
110
+ holidays.push(getNthWeekdayOfMonth(year, 0, 1, 3));
111
+ // Presidents' Day (3rd Monday of February)
112
+ holidays.push(getNthWeekdayOfMonth(year, 1, 1, 3));
113
+ // Good Friday (2 days before Easter)
114
+ const easter = getEasterSunday(year);
115
+ holidays.push(new Date(easter.getFullYear(), easter.getMonth(), easter.getDate() - 2));
116
+ // Memorial Day (last Monday of May)
117
+ holidays.push(getLastWeekdayOfMonth(year, 4, 1));
118
+ // Juneteenth (June 19, or observed)
119
+ holidays.push(adjustForWeekend(new Date(year, 5, 19)));
120
+ // Independence Day (July 4, or observed)
121
+ holidays.push(adjustForWeekend(new Date(year, 6, 4)));
122
+ // Labor Day (1st Monday of September)
123
+ holidays.push(getNthWeekdayOfMonth(year, 8, 1, 1));
124
+ // Thanksgiving Day (4th Thursday of November)
125
+ holidays.push(getNthWeekdayOfMonth(year, 10, 4, 4));
126
+ // Christmas Day (December 25, or observed)
127
+ holidays.push(adjustForWeekend(new Date(year, 11, 25)));
128
+ return holidays;
129
+ }
130
+ /**
131
+ * Compare two dates by year, month, day only (ignoring time)
132
+ */
133
+ function isSameDay(date1, date2) {
134
+ return date1.getFullYear() === date2.getFullYear() &&
135
+ date1.getMonth() === date2.getMonth() &&
136
+ date1.getDate() === date2.getDate();
137
+ }
138
+ /**
139
+ * Check if a date is a US market holiday
140
+ * @param date - Date to check
141
+ * @param market - Market (default: NYSE)
142
+ * @returns True if the date is a market holiday
143
+ *
144
+ * @example
145
+ * ```ts
146
+ * isMarketHoliday(new Date('2024-12-25')); // true (Christmas)
147
+ * isMarketHoliday(new Date('2024-01-02')); // false
148
+ * ```
149
+ */
150
+ export function isMarketHoliday(date, market = 'NYSE') {
151
+ const d = toDate(date);
152
+ const year = d.getFullYear();
153
+ const holidays = getUSMarketHolidays(year);
154
+ return holidays.some(h => isSameDay(d, h));
155
+ }
156
+ /**
157
+ * Check if a date is a trading day (weekday and not a market holiday)
158
+ * @param date - Date to check
159
+ * @param market - Market (default: NYSE)
160
+ * @returns True if the date is a trading day
161
+ *
162
+ * @example
163
+ * ```ts
164
+ * isTradingDay(new Date('2024-01-15')); // true (Monday)
165
+ * isTradingDay(new Date('2024-01-13')); // false (Saturday)
166
+ * ```
167
+ */
168
+ export function isTradingDay(date, market = 'NYSE') {
169
+ const d = toDate(date);
170
+ const day = d.getDay();
171
+ // Weekend check
172
+ if (day === 0 || day === 6)
173
+ return false;
174
+ // Holiday check
175
+ return !isMarketHoliday(d, market);
176
+ }
177
+ /**
178
+ * Check if the market is currently open
179
+ * @param date - Date/time to check
180
+ * @param market - Market (default: NYSE)
181
+ * @returns True if market is open at the specified time
182
+ *
183
+ * @example
184
+ * ```ts
185
+ * // Check if NYSE is open now
186
+ * isMarketOpen(new Date(), 'NYSE');
187
+ *
188
+ * // Check specific time
189
+ * isMarketOpen(new Date('2024-01-15T10:30:00-05:00')); // true
190
+ * ```
191
+ */
192
+ export function isMarketOpen(date, market = 'NYSE') {
193
+ const d = toDate(date);
194
+ // First check if it's a trading day
195
+ if (!isTradingDay(d, market))
196
+ return false;
197
+ const hours = MARKET_HOURS[market];
198
+ // Convert to market timezone for comparison
199
+ // For simplicity, we assume the input is already in market timezone
200
+ // or we compare hours directly
201
+ const hour = d.getHours();
202
+ const minute = d.getMinutes();
203
+ const timeInMinutes = hour * 60 + minute;
204
+ const openInMinutes = hours.open.hour * 60 + hours.open.minute;
205
+ const closeInMinutes = hours.close.hour * 60 + hours.close.minute;
206
+ return timeInMinutes >= openInMinutes && timeInMinutes < closeInMinutes;
207
+ }
208
+ /**
209
+ * Get market hours configuration
210
+ * @param market - Market (default: NYSE)
211
+ * @returns Market hours configuration (deep copy)
212
+ *
213
+ * @example
214
+ * ```ts
215
+ * const hours = getMarketHours('NASDAQ');
216
+ * console.log(hours.open); // { hour: 9, minute: 30 }
217
+ * ```
218
+ */
219
+ export function getMarketHours(market = 'NYSE') {
220
+ const source = MARKET_HOURS[market];
221
+ return {
222
+ open: { ...source.open },
223
+ close: { ...source.close },
224
+ timezone: source.timezone,
225
+ preMarket: source.preMarket ? { ...source.preMarket } : undefined,
226
+ afterHours: source.afterHours ? { ...source.afterHours } : undefined
227
+ };
228
+ }
229
+ /**
230
+ * Get market open time for a specific date
231
+ * @param date - Date to get market open for
232
+ * @param market - Market (default: NYSE)
233
+ * @returns Date set to market open time
234
+ *
235
+ * @example
236
+ * ```ts
237
+ * const open = getMarketOpen(new Date('2024-01-15'));
238
+ * console.log(open); // 2024-01-15T09:30:00
239
+ * ```
240
+ */
241
+ export function getMarketOpen(date, market = 'NYSE') {
242
+ const d = toDate(date);
243
+ const hours = MARKET_HOURS[market];
244
+ const result = new Date(d);
245
+ result.setHours(hours.open.hour, hours.open.minute, 0, 0);
246
+ return result;
247
+ }
248
+ /**
249
+ * Get market close time for a specific date
250
+ * @param date - Date to get market close for
251
+ * @param market - Market (default: NYSE)
252
+ * @returns Date set to market close time
253
+ *
254
+ * @example
255
+ * ```ts
256
+ * const close = getMarketClose(new Date('2024-01-15'));
257
+ * console.log(close); // 2024-01-15T16:00:00
258
+ * ```
259
+ */
260
+ export function getMarketClose(date, market = 'NYSE') {
261
+ const d = toDate(date);
262
+ const hours = MARKET_HOURS[market];
263
+ const result = new Date(d);
264
+ result.setHours(hours.close.hour, hours.close.minute, 0, 0);
265
+ return result;
266
+ }
267
+ /**
268
+ * Get next market open time after a given date
269
+ * @param after - Start searching after this date
270
+ * @param market - Market (default: NYSE)
271
+ * @returns Next market open date/time
272
+ *
273
+ * @example
274
+ * ```ts
275
+ * // If it's Friday evening, returns Monday 9:30 AM
276
+ * const nextOpen = getNextMarketOpen(new Date('2024-01-12T17:00:00'));
277
+ * ```
278
+ */
279
+ export function getNextMarketOpen(after, market = 'NYSE') {
280
+ const d = toDate(after);
281
+ const hours = MARKET_HOURS[market];
282
+ // Start with current day's open
283
+ let candidate = getMarketOpen(d, market);
284
+ // If we're past today's open, start from tomorrow
285
+ if (d >= candidate) {
286
+ candidate.setDate(candidate.getDate() + 1);
287
+ }
288
+ // Find next trading day
289
+ while (!isTradingDay(candidate, market)) {
290
+ candidate.setDate(candidate.getDate() + 1);
291
+ }
292
+ // Set to market open time
293
+ candidate.setHours(hours.open.hour, hours.open.minute, 0, 0);
294
+ return candidate;
295
+ }
296
+ /**
297
+ * Get next market close time after a given date
298
+ * @param after - Start searching after this date
299
+ * @param market - Market (default: NYSE)
300
+ * @returns Next market close date/time
301
+ *
302
+ * @example
303
+ * ```ts
304
+ * const nextClose = getNextMarketClose(new Date('2024-01-15T10:00:00'));
305
+ * // Returns 2024-01-15T16:00:00 (same day close)
306
+ * ```
307
+ */
308
+ export function getNextMarketClose(after, market = 'NYSE') {
309
+ const d = toDate(after);
310
+ const hours = MARKET_HOURS[market];
311
+ // Start with current day's close
312
+ let candidate = getMarketClose(d, market);
313
+ // If we're past today's close or not a trading day, go to next trading day
314
+ if (d >= candidate || !isTradingDay(d, market)) {
315
+ candidate.setDate(candidate.getDate() + 1);
316
+ while (!isTradingDay(candidate, market)) {
317
+ candidate.setDate(candidate.getDate() + 1);
318
+ }
319
+ }
320
+ // Set to market close time
321
+ candidate.setHours(hours.close.hour, hours.close.minute, 0, 0);
322
+ return candidate;
323
+ }
324
+ /**
325
+ * Calculate settlement date (T+N) from trade date
326
+ * @param tradeDate - Trade date
327
+ * @param days - Number of business days for settlement (e.g., 1 for T+1, 2 for T+2)
328
+ * @param market - Market (default: NYSE)
329
+ * @returns Settlement date
330
+ *
331
+ * @example
332
+ * ```ts
333
+ * // T+2 settlement
334
+ * const settlement = getSettlementDate(new Date('2024-01-15'), 2);
335
+ * // Returns 2024-01-17 (skipping weekends/holidays)
336
+ * ```
337
+ */
338
+ export function getSettlementDate(tradeDate, days, market = 'NYSE') {
339
+ const d = toDate(tradeDate);
340
+ const result = new Date(d);
341
+ result.setHours(0, 0, 0, 0);
342
+ let remaining = days;
343
+ while (remaining > 0) {
344
+ result.setDate(result.getDate() + 1);
345
+ if (isTradingDay(result, market)) {
346
+ remaining--;
347
+ }
348
+ }
349
+ return result;
350
+ }
351
+ /**
352
+ * Calculate trade date from settlement date (reverse T+N)
353
+ * @param settlementDate - Settlement date
354
+ * @param days - Number of business days for settlement
355
+ * @param market - Market (default: NYSE)
356
+ * @returns Trade date
357
+ *
358
+ * @example
359
+ * ```ts
360
+ * const tradeDate = getTradeDateFromSettlement(new Date('2024-01-17'), 2);
361
+ * // Returns 2024-01-15
362
+ * ```
363
+ */
364
+ export function getTradeDateFromSettlement(settlementDate, days, market = 'NYSE') {
365
+ const d = toDate(settlementDate);
366
+ const result = new Date(d);
367
+ result.setHours(0, 0, 0, 0);
368
+ let remaining = days;
369
+ while (remaining > 0) {
370
+ result.setDate(result.getDate() - 1);
371
+ if (isTradingDay(result, market)) {
372
+ remaining--;
373
+ }
374
+ }
375
+ return result;
376
+ }
377
+ /**
378
+ * Iterate through each trading day in a range
379
+ * @param start - Start date
380
+ * @param end - End date
381
+ * @param market - Market (default: NYSE)
382
+ * @returns Array of trading days
383
+ *
384
+ * @example
385
+ * ```ts
386
+ * const days = eachTradingDay(new Date('2024-01-15'), new Date('2024-01-19'));
387
+ * // Returns Mon, Tue, Wed, Thu, Fri (if no holidays)
388
+ * ```
389
+ */
390
+ export function eachTradingDay(start, end, market = 'NYSE') {
391
+ const startDate = toDate(start);
392
+ const endDate = toDate(end);
393
+ startDate.setHours(0, 0, 0, 0);
394
+ endDate.setHours(0, 0, 0, 0);
395
+ const days = [];
396
+ const current = new Date(startDate);
397
+ while (current <= endDate) {
398
+ if (isTradingDay(current, market)) {
399
+ days.push(new Date(current));
400
+ }
401
+ current.setDate(current.getDate() + 1);
402
+ }
403
+ return days;
404
+ }
405
+ /**
406
+ * Count trading days between two dates
407
+ * @param start - Start date
408
+ * @param end - End date
409
+ * @param market - Market (default: NYSE)
410
+ * @returns Number of trading days
411
+ *
412
+ * @example
413
+ * ```ts
414
+ * const count = countTradingDays(new Date('2024-01-15'), new Date('2024-01-19'));
415
+ * // Returns 5 (Mon-Fri if no holidays)
416
+ * ```
417
+ */
418
+ export function countTradingDays(start, end, market = 'NYSE') {
419
+ return eachTradingDay(start, end, market).length;
420
+ }
421
+ /**
422
+ * Add trading days to a date
423
+ * @param date - Start date
424
+ * @param days - Number of trading days to add (can be negative)
425
+ * @param market - Market (default: NYSE)
426
+ * @returns Resulting date
427
+ *
428
+ * @example
429
+ * ```ts
430
+ * const result = addTradingDays(new Date('2024-01-15'), 5);
431
+ * // Returns 5 trading days later
432
+ * ```
433
+ */
434
+ export function addTradingDays(date, days, market = 'NYSE') {
435
+ const d = toDate(date);
436
+ const result = new Date(d);
437
+ result.setHours(0, 0, 0, 0);
438
+ if (days === 0)
439
+ return result;
440
+ const direction = days > 0 ? 1 : -1;
441
+ let remaining = Math.abs(days);
442
+ while (remaining > 0) {
443
+ result.setDate(result.getDate() + direction);
444
+ if (isTradingDay(result, market)) {
445
+ remaining--;
446
+ }
447
+ }
448
+ return result;
449
+ }
450
+ /**
451
+ * Get options expiration date
452
+ * @param year - Year
453
+ * @param month - Month (1-12)
454
+ * @param type - Expiration type (default: 'monthly')
455
+ * @returns Options expiration date
456
+ *
457
+ * @example
458
+ * ```ts
459
+ * // Monthly options expire on 3rd Friday
460
+ * const exp = getOptionsExpiration(2024, 1, 'monthly');
461
+ *
462
+ * // Weekly options expire every Friday
463
+ * const weekly = getOptionsExpiration(2024, 1, 'weekly');
464
+ *
465
+ * // Quarterly options expire on 3rd Friday of Mar, Jun, Sep, Dec
466
+ * const quarterly = getOptionsExpiration(2024, 3, 'quarterly');
467
+ * ```
468
+ */
469
+ export function getOptionsExpiration(year, month, type = 'monthly') {
470
+ // Adjust month to 0-indexed
471
+ const monthIndex = month - 1;
472
+ switch (type) {
473
+ case 'monthly':
474
+ case 'quarterly': {
475
+ // 3rd Friday of the month
476
+ const thirdFriday = getNthWeekdayOfMonth(year, monthIndex, 5, 3);
477
+ // If it's a holiday, move to Thursday
478
+ if (isMarketHoliday(thirdFriday)) {
479
+ thirdFriday.setDate(thirdFriday.getDate() - 1);
480
+ }
481
+ return thirdFriday;
482
+ }
483
+ case 'weekly': {
484
+ // First Friday of the month for weekly
485
+ const firstFriday = getNthWeekdayOfMonth(year, monthIndex, 5, 1);
486
+ // If it's a holiday, move to Thursday
487
+ if (isMarketHoliday(firstFriday)) {
488
+ firstFriday.setDate(firstFriday.getDate() - 1);
489
+ }
490
+ return firstFriday;
491
+ }
492
+ default:
493
+ return getNthWeekdayOfMonth(year, monthIndex, 5, 3);
494
+ }
495
+ }