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.
- package/README.md +186 -6
- package/dist/calculate.d.ts +25 -0
- package/dist/calculate.d.ts.map +1 -1
- package/dist/calculate.js +125 -0
- package/dist/calendar.d.ts +45 -0
- package/dist/calendar.d.ts.map +1 -1
- package/dist/calendar.js +68 -0
- package/dist/calendars.d.ts +156 -0
- package/dist/calendars.d.ts.map +1 -0
- package/dist/calendars.js +348 -0
- package/dist/compare.d.ts +27 -0
- package/dist/compare.d.ts.map +1 -1
- package/dist/compare.js +46 -0
- package/dist/esm/calculate.d.ts +25 -0
- package/dist/esm/calculate.d.ts.map +1 -1
- package/dist/esm/calculate.js +125 -0
- package/dist/esm/calendar.d.ts +45 -0
- package/dist/esm/calendar.d.ts.map +1 -1
- package/dist/esm/calendar.js +68 -0
- package/dist/esm/calendars.d.ts +156 -0
- package/dist/esm/calendars.d.ts.map +1 -0
- package/dist/esm/calendars.js +348 -0
- package/dist/esm/compare.d.ts +27 -0
- package/dist/esm/compare.d.ts.map +1 -1
- package/dist/esm/compare.js +46 -0
- package/dist/esm/finance.d.ts +236 -0
- package/dist/esm/finance.d.ts.map +1 -0
- package/dist/esm/finance.js +495 -0
- package/dist/esm/healthcare.d.ts +260 -0
- package/dist/esm/healthcare.d.ts.map +1 -0
- package/dist/esm/healthcare.js +447 -0
- package/dist/esm/holidays.d.ts +11 -1
- package/dist/esm/holidays.d.ts.map +1 -1
- package/dist/esm/holidays.js +220 -1
- package/dist/esm/index.d.ts +19 -7
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +23 -9
- package/dist/esm/iterate.d.ts +55 -0
- package/dist/esm/iterate.d.ts.map +1 -1
- package/dist/esm/iterate.js +86 -0
- package/dist/esm/locale.d.ts +53 -0
- package/dist/esm/locale.d.ts.map +1 -1
- package/dist/esm/locale.js +141 -0
- package/dist/esm/precision.d.ts +225 -0
- package/dist/esm/precision.d.ts.map +1 -0
- package/dist/esm/precision.js +491 -0
- package/dist/esm/scheduling.d.ts +206 -0
- package/dist/esm/scheduling.d.ts.map +1 -0
- package/dist/esm/scheduling.js +329 -0
- package/dist/esm/temporal.d.ts +237 -0
- package/dist/esm/temporal.d.ts.map +1 -0
- package/dist/esm/temporal.js +660 -0
- package/dist/esm/validate.d.ts +30 -0
- package/dist/esm/validate.d.ts.map +1 -1
- package/dist/esm/validate.js +48 -0
- package/dist/finance.d.ts +236 -0
- package/dist/finance.d.ts.map +1 -0
- package/dist/finance.js +495 -0
- package/dist/healthcare.d.ts +260 -0
- package/dist/healthcare.d.ts.map +1 -0
- package/dist/healthcare.js +447 -0
- package/dist/holidays.d.ts +11 -1
- package/dist/holidays.d.ts.map +1 -1
- package/dist/holidays.js +220 -1
- package/dist/index.d.ts +19 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -9
- package/dist/iterate.d.ts +55 -0
- package/dist/iterate.d.ts.map +1 -1
- package/dist/iterate.js +86 -0
- package/dist/locale.d.ts +53 -0
- package/dist/locale.d.ts.map +1 -1
- package/dist/locale.js +141 -0
- package/dist/precision.d.ts +225 -0
- package/dist/precision.d.ts.map +1 -0
- package/dist/precision.js +491 -0
- package/dist/scheduling.d.ts +206 -0
- package/dist/scheduling.d.ts.map +1 -0
- package/dist/scheduling.js +329 -0
- package/dist/temporal.d.ts +237 -0
- package/dist/temporal.d.ts.map +1 -0
- package/dist/temporal.js +660 -0
- package/dist/validate.d.ts +30 -0
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +48 -0
- package/package.json +31 -1
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Healthcare utilities for medical scheduling and compliance timing
|
|
3
|
+
* Provides medication schedules, shift patterns, on-call rotations, and compliance windows
|
|
4
|
+
*/
|
|
5
|
+
import { Duration } from './duration.js';
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Constants
|
|
8
|
+
// ============================================================================
|
|
9
|
+
/**
|
|
10
|
+
* Number of doses per day for each frequency
|
|
11
|
+
*/
|
|
12
|
+
export const MEDICATION_FREQUENCIES = {
|
|
13
|
+
'QD': 1,
|
|
14
|
+
'BID': 2,
|
|
15
|
+
'TID': 3,
|
|
16
|
+
'QID': 4,
|
|
17
|
+
'q4h': 6,
|
|
18
|
+
'q6h': 4,
|
|
19
|
+
'q8h': 3,
|
|
20
|
+
'q12h': 2,
|
|
21
|
+
'PRN': null
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Hours per shift pattern
|
|
25
|
+
*/
|
|
26
|
+
export const SHIFT_DURATIONS = {
|
|
27
|
+
'8hr': 8,
|
|
28
|
+
'12hr': 12,
|
|
29
|
+
'24hr': 24
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Default medication timing config
|
|
33
|
+
*/
|
|
34
|
+
export const DEFAULT_MEDICATION_CONFIG = {
|
|
35
|
+
wakeTime: '07:00',
|
|
36
|
+
sleepTime: '22:00',
|
|
37
|
+
withMeals: false
|
|
38
|
+
};
|
|
39
|
+
// ============================================================================
|
|
40
|
+
// Helper Functions
|
|
41
|
+
// ============================================================================
|
|
42
|
+
function toDate(input) {
|
|
43
|
+
if (input instanceof Date)
|
|
44
|
+
return new Date(input.getTime());
|
|
45
|
+
return new Date(input);
|
|
46
|
+
}
|
|
47
|
+
function parseTimeString(time) {
|
|
48
|
+
const [hour, minute] = time.split(':').map(Number);
|
|
49
|
+
return { hour, minute };
|
|
50
|
+
}
|
|
51
|
+
function setTimeOnDate(date, hour, minute) {
|
|
52
|
+
const result = new Date(date.getTime());
|
|
53
|
+
result.setHours(hour, minute, 0, 0);
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
// ============================================================================
|
|
57
|
+
// Medication Scheduling Functions
|
|
58
|
+
// ============================================================================
|
|
59
|
+
/**
|
|
60
|
+
* Get medication administration times for a given date and frequency
|
|
61
|
+
*
|
|
62
|
+
* @param date - The date to get medication times for
|
|
63
|
+
* @param frequency - Medical frequency abbreviation (QD, BID, TID, etc.)
|
|
64
|
+
* @param config - Optional configuration for wake/sleep times
|
|
65
|
+
* @returns Array of Date objects for each dose, empty array for PRN
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```ts
|
|
69
|
+
* const times = getMedicationTimes(new Date('2024-01-15'), 'BID');
|
|
70
|
+
* // Returns [7:00 AM, 7:00 PM] (twice daily)
|
|
71
|
+
*
|
|
72
|
+
* const customTimes = getMedicationTimes(new Date('2024-01-15'), 'TID', {
|
|
73
|
+
* wakeTime: '06:00',
|
|
74
|
+
* sleepTime: '21:00'
|
|
75
|
+
* });
|
|
76
|
+
* // Returns [6:00 AM, 1:30 PM, 9:00 PM] (three times daily)
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export function getMedicationTimes(date, frequency, config) {
|
|
80
|
+
const d = toDate(date);
|
|
81
|
+
const cfg = { ...DEFAULT_MEDICATION_CONFIG, ...config };
|
|
82
|
+
const doses = MEDICATION_FREQUENCIES[frequency];
|
|
83
|
+
if (doses === null) {
|
|
84
|
+
return []; // PRN - as needed
|
|
85
|
+
}
|
|
86
|
+
const wake = parseTimeString(cfg.wakeTime);
|
|
87
|
+
const sleep = parseTimeString(cfg.sleepTime);
|
|
88
|
+
// Calculate awake hours
|
|
89
|
+
let awakeMinutes = (sleep.hour * 60 + sleep.minute) - (wake.hour * 60 + wake.minute);
|
|
90
|
+
if (awakeMinutes <= 0)
|
|
91
|
+
awakeMinutes += 24 * 60; // Handle overnight
|
|
92
|
+
const times = [];
|
|
93
|
+
if (doses === 1) {
|
|
94
|
+
// QD: morning dose
|
|
95
|
+
times.push(setTimeOnDate(d, wake.hour, wake.minute));
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
// Distribute evenly across waking hours
|
|
99
|
+
const interval = awakeMinutes / doses;
|
|
100
|
+
for (let i = 0; i < doses; i++) {
|
|
101
|
+
const minutesFromWake = Math.round(i * interval);
|
|
102
|
+
let totalMinutes = wake.hour * 60 + wake.minute + minutesFromWake;
|
|
103
|
+
// Handle day rollover
|
|
104
|
+
if (totalMinutes >= 24 * 60) {
|
|
105
|
+
totalMinutes -= 24 * 60;
|
|
106
|
+
}
|
|
107
|
+
const hour = Math.floor(totalMinutes / 60);
|
|
108
|
+
const minute = totalMinutes % 60;
|
|
109
|
+
times.push(setTimeOnDate(d, hour, minute));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return times;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get the next medication time after a given date/time
|
|
116
|
+
*
|
|
117
|
+
* @param after - The date/time to find the next medication time after
|
|
118
|
+
* @param frequency - Medical frequency abbreviation
|
|
119
|
+
* @param config - Optional configuration
|
|
120
|
+
* @returns Next medication Date, or null for PRN
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```ts
|
|
124
|
+
* const next = getNextMedicationTime(new Date('2024-01-15T10:00:00'), 'BID');
|
|
125
|
+
* // Returns 7:00 PM on same day (next BID dose after 10 AM)
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
export function getNextMedicationTime(after, frequency, config) {
|
|
129
|
+
if (frequency === 'PRN') {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
const afterDate = toDate(after);
|
|
133
|
+
// Check today's doses first
|
|
134
|
+
const todayTimes = getMedicationTimes(afterDate, frequency, config);
|
|
135
|
+
for (const time of todayTimes) {
|
|
136
|
+
if (time.getTime() > afterDate.getTime()) {
|
|
137
|
+
return time;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// Get first dose tomorrow
|
|
141
|
+
const tomorrow = new Date(afterDate.getTime());
|
|
142
|
+
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
143
|
+
const tomorrowTimes = getMedicationTimes(tomorrow, frequency, config);
|
|
144
|
+
return tomorrowTimes[0] || null;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Parse a medication frequency string to MedicationFrequency type
|
|
148
|
+
*
|
|
149
|
+
* @param freq - String to parse (case-insensitive)
|
|
150
|
+
* @returns MedicationFrequency or null if invalid
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```ts
|
|
154
|
+
* parseMedicationFrequency('bid'); // 'BID'
|
|
155
|
+
* parseMedicationFrequency('q8h'); // 'q8h'
|
|
156
|
+
* parseMedicationFrequency('invalid'); // null
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
export function parseMedicationFrequency(freq) {
|
|
160
|
+
const normalized = freq.toLowerCase();
|
|
161
|
+
const mapping = {
|
|
162
|
+
'qd': 'QD',
|
|
163
|
+
'od': 'QD',
|
|
164
|
+
'once daily': 'QD',
|
|
165
|
+
'bid': 'BID',
|
|
166
|
+
'twice daily': 'BID',
|
|
167
|
+
'tid': 'TID',
|
|
168
|
+
'three times daily': 'TID',
|
|
169
|
+
'qid': 'QID',
|
|
170
|
+
'four times daily': 'QID',
|
|
171
|
+
'q4h': 'q4h',
|
|
172
|
+
'every 4 hours': 'q4h',
|
|
173
|
+
'q6h': 'q6h',
|
|
174
|
+
'every 6 hours': 'q6h',
|
|
175
|
+
'q8h': 'q8h',
|
|
176
|
+
'every 8 hours': 'q8h',
|
|
177
|
+
'q12h': 'q12h',
|
|
178
|
+
'every 12 hours': 'q12h',
|
|
179
|
+
'prn': 'PRN',
|
|
180
|
+
'as needed': 'PRN'
|
|
181
|
+
};
|
|
182
|
+
return mapping[normalized] || null;
|
|
183
|
+
}
|
|
184
|
+
// ============================================================================
|
|
185
|
+
// Shift Scheduling Functions
|
|
186
|
+
// ============================================================================
|
|
187
|
+
/**
|
|
188
|
+
* Generate shift schedule for a date range
|
|
189
|
+
*
|
|
190
|
+
* @param start - Start of range
|
|
191
|
+
* @param end - End of range
|
|
192
|
+
* @param config - Shift configuration
|
|
193
|
+
* @returns Array of DateRange objects representing shifts
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```ts
|
|
197
|
+
* const shifts = generateShiftSchedule(
|
|
198
|
+
* new Date('2024-01-15'),
|
|
199
|
+
* new Date('2024-01-17'),
|
|
200
|
+
* { pattern: '12hr', startTime: { hour: 7, minute: 0 } }
|
|
201
|
+
* );
|
|
202
|
+
* // Returns 4 shifts: day/night on 15th, day/night on 16th
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
export function generateShiftSchedule(start, end, config) {
|
|
206
|
+
const startDate = toDate(start);
|
|
207
|
+
const endDate = toDate(end);
|
|
208
|
+
const shiftHours = SHIFT_DURATIONS[config.pattern];
|
|
209
|
+
const shiftsPerDay = 24 / shiftHours;
|
|
210
|
+
const shifts = [];
|
|
211
|
+
const current = new Date(startDate.getTime());
|
|
212
|
+
current.setHours(config.startTime.hour, config.startTime.minute, 0, 0);
|
|
213
|
+
// If start time is after the input start, go back one shift cycle
|
|
214
|
+
if (current.getTime() > startDate.getTime()) {
|
|
215
|
+
current.setTime(current.getTime() - shiftHours * 60 * 60 * 1000);
|
|
216
|
+
}
|
|
217
|
+
while (current.getTime() < endDate.getTime()) {
|
|
218
|
+
const shiftStart = new Date(current.getTime());
|
|
219
|
+
const shiftEnd = new Date(current.getTime() + shiftHours * 60 * 60 * 1000);
|
|
220
|
+
// Only include shifts that overlap with the requested range
|
|
221
|
+
if (shiftEnd.getTime() > startDate.getTime() && shiftStart.getTime() < endDate.getTime()) {
|
|
222
|
+
shifts.push({ start: shiftStart, end: shiftEnd });
|
|
223
|
+
}
|
|
224
|
+
current.setTime(current.getTime() + shiftHours * 60 * 60 * 1000);
|
|
225
|
+
}
|
|
226
|
+
return shifts;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Get the shift containing a specific time
|
|
230
|
+
*
|
|
231
|
+
* @param date - The date/time to check
|
|
232
|
+
* @param config - Shift configuration
|
|
233
|
+
* @returns DateRange of the shift containing the time
|
|
234
|
+
*
|
|
235
|
+
* @example
|
|
236
|
+
* ```ts
|
|
237
|
+
* const shift = getShiftForTime(
|
|
238
|
+
* new Date('2024-01-15T14:00:00'),
|
|
239
|
+
* { pattern: '8hr', startTime: { hour: 7, minute: 0 } }
|
|
240
|
+
* );
|
|
241
|
+
* // Returns { start: 7:00 AM, end: 3:00 PM } (day shift)
|
|
242
|
+
* ```
|
|
243
|
+
*/
|
|
244
|
+
export function getShiftForTime(date, config) {
|
|
245
|
+
const d = toDate(date);
|
|
246
|
+
const shiftHours = SHIFT_DURATIONS[config.pattern];
|
|
247
|
+
const shiftMs = shiftHours * 60 * 60 * 1000;
|
|
248
|
+
// Find the shift start that contains this time
|
|
249
|
+
const dayStart = new Date(d.getTime());
|
|
250
|
+
dayStart.setHours(config.startTime.hour, config.startTime.minute, 0, 0);
|
|
251
|
+
// Calculate how many shift cycles from dayStart
|
|
252
|
+
let diff = d.getTime() - dayStart.getTime();
|
|
253
|
+
if (diff < 0) {
|
|
254
|
+
// Time is before today's first shift start
|
|
255
|
+
diff += 24 * 60 * 60 * 1000;
|
|
256
|
+
dayStart.setTime(dayStart.getTime() - 24 * 60 * 60 * 1000);
|
|
257
|
+
}
|
|
258
|
+
const shiftIndex = Math.floor(diff / shiftMs);
|
|
259
|
+
const shiftStart = new Date(dayStart.getTime() + shiftIndex * shiftMs);
|
|
260
|
+
const shiftEnd = new Date(shiftStart.getTime() + shiftMs);
|
|
261
|
+
return { start: shiftStart, end: shiftEnd };
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Check if a date/time is during a specific shift
|
|
265
|
+
*
|
|
266
|
+
* @param date - The date/time to check
|
|
267
|
+
* @param shiftStart - When the shift started
|
|
268
|
+
* @param config - Shift configuration
|
|
269
|
+
* @returns true if the time is during the shift
|
|
270
|
+
*
|
|
271
|
+
* @example
|
|
272
|
+
* ```ts
|
|
273
|
+
* isOnShift(
|
|
274
|
+
* new Date('2024-01-15T10:00:00'),
|
|
275
|
+
* new Date('2024-01-15T07:00:00'),
|
|
276
|
+
* { pattern: '8hr', startTime: { hour: 7, minute: 0 } }
|
|
277
|
+
* ); // true (10 AM is during 7 AM - 3 PM shift)
|
|
278
|
+
* ```
|
|
279
|
+
*/
|
|
280
|
+
export function isOnShift(date, shiftStart, config) {
|
|
281
|
+
const d = toDate(date);
|
|
282
|
+
const start = toDate(shiftStart);
|
|
283
|
+
const shiftHours = SHIFT_DURATIONS[config.pattern];
|
|
284
|
+
const shiftEnd = new Date(start.getTime() + shiftHours * 60 * 60 * 1000);
|
|
285
|
+
return d.getTime() >= start.getTime() && d.getTime() < shiftEnd.getTime();
|
|
286
|
+
}
|
|
287
|
+
// ============================================================================
|
|
288
|
+
// On-Call Rotation Functions
|
|
289
|
+
// ============================================================================
|
|
290
|
+
/**
|
|
291
|
+
* Create an on-call rotation schedule
|
|
292
|
+
*
|
|
293
|
+
* @param start - Start of rotation period
|
|
294
|
+
* @param end - End of rotation period
|
|
295
|
+
* @param staff - Array of staff names to rotate through
|
|
296
|
+
* @param hoursPerShift - Hours per on-call shift (default 24)
|
|
297
|
+
* @returns Array of OnCallSlot assignments
|
|
298
|
+
*
|
|
299
|
+
* @example
|
|
300
|
+
* ```ts
|
|
301
|
+
* const rotation = createOnCallRotation(
|
|
302
|
+
* new Date('2024-01-15'),
|
|
303
|
+
* new Date('2024-01-18'),
|
|
304
|
+
* ['Dr. Smith', 'Dr. Jones', 'Dr. Brown'],
|
|
305
|
+
* 24
|
|
306
|
+
* );
|
|
307
|
+
* // Returns 3 slots, one per doctor per day
|
|
308
|
+
* ```
|
|
309
|
+
*/
|
|
310
|
+
export function createOnCallRotation(start, end, staff, hoursPerShift = 24) {
|
|
311
|
+
if (staff.length === 0) {
|
|
312
|
+
return [];
|
|
313
|
+
}
|
|
314
|
+
const startDate = toDate(start);
|
|
315
|
+
const endDate = toDate(end);
|
|
316
|
+
const shiftMs = hoursPerShift * 60 * 60 * 1000;
|
|
317
|
+
const slots = [];
|
|
318
|
+
const current = new Date(startDate.getTime());
|
|
319
|
+
current.setHours(0, 0, 0, 0); // Start at midnight
|
|
320
|
+
let staffIndex = 0;
|
|
321
|
+
while (current.getTime() < endDate.getTime()) {
|
|
322
|
+
const slotEnd = new Date(Math.min(current.getTime() + shiftMs, endDate.getTime()));
|
|
323
|
+
slots.push({
|
|
324
|
+
staff: staff[staffIndex % staff.length],
|
|
325
|
+
start: new Date(current.getTime()),
|
|
326
|
+
end: slotEnd
|
|
327
|
+
});
|
|
328
|
+
staffIndex++;
|
|
329
|
+
current.setTime(current.getTime() + shiftMs);
|
|
330
|
+
}
|
|
331
|
+
return slots;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Get the staff member on call at a specific time
|
|
335
|
+
*
|
|
336
|
+
* @param date - The date/time to check
|
|
337
|
+
* @param rotation - The on-call rotation schedule
|
|
338
|
+
* @returns Staff name or null if no one is on call
|
|
339
|
+
*
|
|
340
|
+
* @example
|
|
341
|
+
* ```ts
|
|
342
|
+
* const onCall = getOnCallStaff(new Date('2024-01-16T03:00:00'), rotation);
|
|
343
|
+
* // Returns 'Dr. Jones' (whoever is on call at 3 AM on the 16th)
|
|
344
|
+
* ```
|
|
345
|
+
*/
|
|
346
|
+
export function getOnCallStaff(date, rotation) {
|
|
347
|
+
const d = toDate(date);
|
|
348
|
+
for (const slot of rotation) {
|
|
349
|
+
if (d.getTime() >= slot.start.getTime() && d.getTime() < slot.end.getTime()) {
|
|
350
|
+
return slot.staff;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
// ============================================================================
|
|
356
|
+
// Compliance Window Functions
|
|
357
|
+
// ============================================================================
|
|
358
|
+
/**
|
|
359
|
+
* Check if an event occurred within its compliance window
|
|
360
|
+
*
|
|
361
|
+
* @param event - When the event occurred
|
|
362
|
+
* @param deadline - The compliance deadline
|
|
363
|
+
* @returns true if event is before or at deadline
|
|
364
|
+
*
|
|
365
|
+
* @example
|
|
366
|
+
* ```ts
|
|
367
|
+
* isWithinComplianceWindow(
|
|
368
|
+
* new Date('2024-01-15T10:00:00'),
|
|
369
|
+
* new Date('2024-01-15T12:00:00')
|
|
370
|
+
* ); // true (event occurred before deadline)
|
|
371
|
+
* ```
|
|
372
|
+
*/
|
|
373
|
+
export function isWithinComplianceWindow(event, deadline) {
|
|
374
|
+
const eventDate = toDate(event);
|
|
375
|
+
const deadlineDate = toDate(deadline);
|
|
376
|
+
return eventDate.getTime() <= deadlineDate.getTime();
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Calculate the compliance deadline from an event
|
|
380
|
+
*
|
|
381
|
+
* @param event - The triggering event
|
|
382
|
+
* @param windowHours - Hours until deadline
|
|
383
|
+
* @returns Deadline Date
|
|
384
|
+
*
|
|
385
|
+
* @example
|
|
386
|
+
* ```ts
|
|
387
|
+
* const deadline = getComplianceDeadline(
|
|
388
|
+
* new Date('2024-01-15T08:00:00'),
|
|
389
|
+
* 72
|
|
390
|
+
* );
|
|
391
|
+
* // Returns 2024-01-18T08:00:00 (72 hours later)
|
|
392
|
+
* ```
|
|
393
|
+
*/
|
|
394
|
+
export function getComplianceDeadline(event, windowHours) {
|
|
395
|
+
const eventDate = toDate(event);
|
|
396
|
+
return new Date(eventDate.getTime() + windowHours * 60 * 60 * 1000);
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Calculate time remaining until a compliance deadline
|
|
400
|
+
*
|
|
401
|
+
* @param event - Current time or event time
|
|
402
|
+
* @param deadline - The compliance deadline
|
|
403
|
+
* @returns Duration until deadline, or null if already past
|
|
404
|
+
*
|
|
405
|
+
* @example
|
|
406
|
+
* ```ts
|
|
407
|
+
* const remaining = timeUntilDeadline(
|
|
408
|
+
* new Date('2024-01-15T10:00:00'),
|
|
409
|
+
* new Date('2024-01-16T10:00:00')
|
|
410
|
+
* );
|
|
411
|
+
* // Returns Duration of 24 hours
|
|
412
|
+
* ```
|
|
413
|
+
*/
|
|
414
|
+
export function timeUntilDeadline(event, deadline) {
|
|
415
|
+
const eventDate = toDate(event);
|
|
416
|
+
const deadlineDate = toDate(deadline);
|
|
417
|
+
const diff = deadlineDate.getTime() - eventDate.getTime();
|
|
418
|
+
if (diff < 0) {
|
|
419
|
+
return null; // Already past deadline
|
|
420
|
+
}
|
|
421
|
+
return Duration.fromMilliseconds(diff);
|
|
422
|
+
}
|
|
423
|
+
// ============================================================================
|
|
424
|
+
// Utility Functions
|
|
425
|
+
// ============================================================================
|
|
426
|
+
/**
|
|
427
|
+
* Calculate rest hours between two shifts
|
|
428
|
+
*
|
|
429
|
+
* @param shift1End - End of first shift
|
|
430
|
+
* @param shift2Start - Start of second shift
|
|
431
|
+
* @returns Hours of rest between shifts
|
|
432
|
+
*
|
|
433
|
+
* @example
|
|
434
|
+
* ```ts
|
|
435
|
+
* const rest = calculateRestBetweenShifts(
|
|
436
|
+
* new Date('2024-01-15T19:00:00'),
|
|
437
|
+
* new Date('2024-01-16T07:00:00')
|
|
438
|
+
* );
|
|
439
|
+
* // Returns 12 (hours of rest)
|
|
440
|
+
* ```
|
|
441
|
+
*/
|
|
442
|
+
export function calculateRestBetweenShifts(shift1End, shift2Start) {
|
|
443
|
+
const end = toDate(shift1End);
|
|
444
|
+
const start = toDate(shift2Start);
|
|
445
|
+
const diffMs = start.getTime() - end.getTime();
|
|
446
|
+
return diffMs / (60 * 60 * 1000);
|
|
447
|
+
}
|
package/dist/esm/holidays.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @fileoverview International holiday utilities
|
|
3
3
|
* Calculate holidays for multiple countries including fixed, movable, and lunar-based holidays
|
|
4
4
|
*/
|
|
5
|
-
export type CountryCode = 'UK' | 'NL' | 'DE' | 'CA' | 'AU' | 'IT' | 'ES' | 'CN' | 'IN' | 'US';
|
|
5
|
+
export type CountryCode = 'UK' | 'NL' | 'DE' | 'CA' | 'AU' | 'IT' | 'ES' | 'CN' | 'IN' | 'US' | 'JP' | 'FR' | 'BR' | 'MX' | 'KR' | 'SG' | 'PL' | 'SE' | 'BE' | 'CH';
|
|
6
6
|
export interface Holiday {
|
|
7
7
|
name: string;
|
|
8
8
|
date: Date;
|
|
@@ -18,6 +18,16 @@ export declare function getItalyHolidays(year: number): Holiday[];
|
|
|
18
18
|
export declare function getSpainHolidays(year: number): Holiday[];
|
|
19
19
|
export declare function getChinaHolidays(year: number): Holiday[];
|
|
20
20
|
export declare function getIndiaHolidays(year: number): Holiday[];
|
|
21
|
+
export declare function getJapanHolidays(year: number): Holiday[];
|
|
22
|
+
export declare function getFranceHolidays(year: number): Holiday[];
|
|
23
|
+
export declare function getBrazilHolidays(year: number): Holiday[];
|
|
24
|
+
export declare function getMexicoHolidays(year: number): Holiday[];
|
|
25
|
+
export declare function getSouthKoreaHolidays(year: number): Holiday[];
|
|
26
|
+
export declare function getSingaporeHolidays(year: number): Holiday[];
|
|
27
|
+
export declare function getPolandHolidays(year: number): Holiday[];
|
|
28
|
+
export declare function getSwedenHolidays(year: number): Holiday[];
|
|
29
|
+
export declare function getBelgiumHolidays(year: number): Holiday[];
|
|
30
|
+
export declare function getSwitzerlandHolidays(year: number): Holiday[];
|
|
21
31
|
/**
|
|
22
32
|
* Get holidays for a specific country and year
|
|
23
33
|
* @param year - The year
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"holidays.d.ts","sourceRoot":"","sources":["../../src/holidays.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"holidays.d.ts","sourceRoot":"","sources":["../../src/holidays.ts"],"names":[],"mappings":"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"}
|