@smcv/opening-hours 1.0.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.
@@ -0,0 +1,420 @@
1
+ type OpenState = "closed" | "open";
2
+ declare const Days: {
3
+ readonly sunday: 0;
4
+ readonly monday: 1;
5
+ readonly tuesday: 2;
6
+ readonly wednesday: 3;
7
+ readonly thursday: 4;
8
+ readonly friday: 5;
9
+ readonly saturday: 6;
10
+ };
11
+ type Day = keyof typeof Days;
12
+ type DateValue = Date | number | string;
13
+ type TimeRangeString = string;
14
+ type TimeRangeWithData<T extends Record<string, unknown> = Record<string, unknown>> = {
15
+ hours?: TimeRangeString;
16
+ time?: TimeRangeString;
17
+ } & T;
18
+ type TimeRangeInput<T extends Record<string, unknown> = Record<string, unknown>> = TimeRangeString | TimeRangeWithData<T>;
19
+ type DayRange = `${Day}...${Day}`;
20
+ type DateRange = `${string}...${string}`;
21
+ type OpeningHoursConfig<T extends Record<string, unknown> = Record<string, unknown>> = Record<Day | DayRange, TimeRangeInput<T>[] | TimeRangeWithData<T>>;
22
+ /**
23
+ * Schema.org OpeningHoursSpecification interface
24
+ * @see https://schema.org/OpeningHoursSpecification
25
+ */
26
+ interface OpeningHoursSpecification {
27
+ "@type": "OpeningHoursSpecification";
28
+ dayOfWeek?: string | string[];
29
+ opens?: string;
30
+ closes?: string;
31
+ validFrom?: string;
32
+ validThrough?: string;
33
+ }
34
+ declare class Time {
35
+ private readonly hour;
36
+ private readonly minute;
37
+ constructor(hour: number, minute?: number);
38
+ /**
39
+ * Create a Time instance from a string.
40
+ * Supports formats like '09:00', '09:00:00', or '09:00:00-05:00'.
41
+ */
42
+ static fromString(timeString: string): Time;
43
+ /**
44
+ * Create a Time instance from a Date object
45
+ */
46
+ static fromDate(date: Date): Time;
47
+ /**
48
+ * Get the hour component
49
+ */
50
+ getHour(): number;
51
+ /**
52
+ * Get the minute component
53
+ */
54
+ getMinute(): number;
55
+ /**
56
+ * Format the time as a string
57
+ */
58
+ format(format?: string): string;
59
+ /**
60
+ * Convert to string in H:i format
61
+ */
62
+ toString(): string;
63
+ /**
64
+ * Check if this time is before another time
65
+ */
66
+ isBefore(time: Time): boolean;
67
+ /**
68
+ * Check if this time is after another time
69
+ */
70
+ isAfter(time: Time): boolean;
71
+ /**
72
+ * Check if this time is equal to another time
73
+ */
74
+ isEqual(time: Time): boolean;
75
+ /**
76
+ * Add minutes to the time
77
+ */
78
+ addMinutes(minutes: number): Time;
79
+ /**
80
+ * Get the time as minutes since midnight
81
+ */
82
+ toMinutesSinceMidnight(): number;
83
+ /**
84
+ * Calculate difference in minutes between two times
85
+ */
86
+ diffInMinutes(time: Time): number;
87
+ /**
88
+ * Create a date with this time
89
+ */
90
+ toDate(baseDate?: Date): Date;
91
+ }
92
+ type TimeRangeData<T extends Record<string, unknown> = Record<string, unknown>> = T;
93
+ declare class TimeRange<T extends Record<string, unknown> = Record<string, unknown>> {
94
+ private readonly start;
95
+ private readonly end;
96
+ private readonly data?;
97
+ constructor(start: Time, end: Time, data?: T);
98
+ /**
99
+ * Create a TimeRange from a string (e.g., '09:00-17:00')
100
+ */
101
+ static fromString<T extends Record<string, unknown> = Record<string, unknown>>(rangeString: string, data?: T): TimeRange<T>;
102
+ /**
103
+ * Get the start time
104
+ */
105
+ getStart(): Time;
106
+ /**
107
+ * Get the end time
108
+ */
109
+ getEnd(): Time;
110
+ /**
111
+ * Get the associated data
112
+ */
113
+ getData(): T | undefined;
114
+ /**
115
+ * Check if a specific time is contained in this range
116
+ */
117
+ containsTime(time: Time, overflow?: boolean): boolean;
118
+ /**
119
+ * Check if the range spans overnight (end time is before start time)
120
+ */
121
+ spansOvernight(): boolean;
122
+ /**
123
+ * Check if this range overlaps with another range
124
+ */
125
+ overlaps(timeRange: TimeRange<T>, overflow?: boolean): boolean;
126
+ /**
127
+ * Get the duration of this range in minutes
128
+ */
129
+ durationInMinutes(overflow?: boolean): number;
130
+ /**
131
+ * Convert to string representation
132
+ */
133
+ toString(): string;
134
+ }
135
+ declare class OpeningHoursForDay<T extends Record<string, unknown> = Record<string, unknown>> {
136
+ private readonly timeRanges;
137
+ private readonly overflow;
138
+ private readonly displayOptions?;
139
+ private readonly data?;
140
+ constructor(timeRanges?: TimeRange<T>[], overflow?: boolean, displayOptions?: DisplayOptions, data?: T);
141
+ /**
142
+ * Check if open at the given time
143
+ */
144
+ isOpenAt(time: Time): boolean;
145
+ /**
146
+ * Check if open at the given DateTime
147
+ */
148
+ isOpenAtDateTime(dateTime: Date): boolean;
149
+ /**
150
+ * Check if the business is open during the day (has any time ranges)
151
+ */
152
+ isOpenDuringDay(): boolean;
153
+ /**
154
+ * Get all time ranges
155
+ */
156
+ getTimeRanges(): TimeRange<T>[];
157
+ /**
158
+ * Get the data associated with this day (e.g. a note on a closed exception)
159
+ */
160
+ getData(): T | undefined;
161
+ /**
162
+ * Find the current open range at the given time
163
+ */
164
+ getCurrentOpenRange(time: Time): TimeRange<T> | null;
165
+ /**
166
+ * Find the current open range at the given DateTime
167
+ */
168
+ getCurrentOpenRangeAtDateTime(dateTime: Date): TimeRange<T> | null;
169
+ /**
170
+ * Check if this day has the same opening hours as another day
171
+ */
172
+ equals(openingHoursForDay: OpeningHoursForDay<T>): boolean;
173
+ /**
174
+ * Format a time range for display
175
+ */
176
+ protected formatTimeRange(timeRange: TimeRange<T>, options?: DisplayOptions): string;
177
+ /**
178
+ * Convert to string representation
179
+ */
180
+ toString(options?: DisplayOptions): string;
181
+ }
182
+ type DayOfWeek = keyof typeof Days;
183
+ interface OpeningHoursOptions<T extends Record<string, unknown> = Record<string, unknown>> extends Partial<OpeningHoursConfig<T>> {
184
+ overflow?: boolean;
185
+ exceptions?: Record<string, TimeRangeInput<T>[] | ((date: Date) => TimeRangeInput<T>[])>;
186
+ filters?: Array<(date: Date) => TimeRangeInput<T>[]>;
187
+ /**
188
+ * IANA timezone name representing the organization's local time
189
+ */
190
+ timezone?: string;
191
+ }
192
+ interface DisplayOptions {
193
+ locale?: string;
194
+ weekday?: "narrow" | "short" | "long";
195
+ firstDayOfWeek?: "monday" | "sunday";
196
+ dayRangeSeparator?: string;
197
+ timeRangeSeparator?: string;
198
+ timeFormat?: string;
199
+ timeRangesSeparator?: string;
200
+ showTimezone?: boolean;
201
+ timeZoneName?: "short" | "long" | "shortOffset" | "longOffset" | "shortGeneric" | "longGeneric";
202
+ closedText?: string;
203
+ }
204
+ declare class OpeningHours<T extends Record<string, unknown> = Record<string, unknown>> {
205
+ private readonly config;
206
+ private readonly schedule;
207
+ private readonly exceptions;
208
+ private readonly filters;
209
+ private readonly timezone?;
210
+ private readonly overflow;
211
+ private readonly displayOptions?;
212
+ constructor(config: OpeningHoursOptions<T> & DisplayOptions);
213
+ private initializeSchedule;
214
+ private normalizeTimeRanges;
215
+ /**
216
+ * Check if a TimeRangeInput has no hours/time (metadata-only item)
217
+ */
218
+ private isMetadataOnly;
219
+ /**
220
+ * Filter out metadata-only items, keeping only actual time ranges
221
+ */
222
+ private filterActualRanges;
223
+ /**
224
+ * Extract data from a metadata-only item in a schedule (no hours/time)
225
+ */
226
+ private extractDataFromSchedule;
227
+ /**
228
+ * Get the data for a specific date (from exceptions only)
229
+ */
230
+ private getDataForDate;
231
+ /**
232
+ * Convert a TimeRangeInput to a TimeRange object
233
+ */
234
+ private convertToTimeRange;
235
+ /**
236
+ * Get day name from a Date object
237
+ */
238
+ private getDayName;
239
+ /**
240
+ * Format a date as YYYY-MM-DD
241
+ */
242
+ private formatDate;
243
+ /**
244
+ * Get time ranges for a specific date
245
+ */
246
+ private getTimeRangesForDate;
247
+ /**
248
+ * Create a static instance of OpeningHours
249
+ */
250
+ static create<T extends Record<string, unknown> = Record<string, unknown>>(config: OpeningHoursOptions<T> & DisplayOptions): OpeningHours<T>;
251
+ /**
252
+ * Check if the business is open on a specific day
253
+ */
254
+ isOpenOn(day: Day): boolean;
255
+ /**
256
+ * Check if the business is open at a specific date and time
257
+ */
258
+ isOpenAt(dateTime: Date): boolean;
259
+ /**
260
+ * Check if the business is closed at a specific date and time
261
+ */
262
+ isClosedAt(dateTime: Date): boolean;
263
+ /**
264
+ * Get opening hours for a specific day of the week
265
+ */
266
+ forDay(day: Day): OpeningHoursForDay<T>;
267
+ /**
268
+ * Get opening hours for all days of the week
269
+ */
270
+ forWeek(): Record<Day, OpeningHoursForDay<T>>;
271
+ /**
272
+ * Get opening hours for a specific date
273
+ */
274
+ forDate(date: Date): OpeningHoursForDay<T>;
275
+ /**
276
+ * Get all exceptions
277
+ */
278
+ getExceptions(): Record<string, OpeningHoursForDay<T>>;
279
+ /**
280
+ * Calculate the difference in open time between two dates (in hours)
281
+ */
282
+ diffInOpenHours(startDate: Date, endDate: Date): number;
283
+ /**
284
+ * Calculate the difference in open time between two dates (in minutes)
285
+ */
286
+ diffInOpenMinutes(startDate: Date, endDate: Date): number;
287
+ /**
288
+ * Calculate the open minutes within a timespan (all within the same day)
289
+ */
290
+ private calculateOpenMinutesForTimespan;
291
+ /**
292
+ * Check if there are exceptions between two dates
293
+ */
294
+ private hasExceptionBetween;
295
+ /**
296
+ * Calculate the difference in open time between two dates (in seconds)
297
+ */
298
+ diffInOpenSeconds(startDate: Date, endDate: Date): number;
299
+ /**
300
+ * Calculate the difference in closed time between two dates (in hours)
301
+ */
302
+ diffInClosedHours(startDate: Date, endDate: Date): number;
303
+ /**
304
+ * Calculate the difference in closed time between two dates (in minutes)
305
+ */
306
+ diffInClosedMinutes(startDate: Date, endDate: Date): number;
307
+ /**
308
+ * Calculate the difference in closed time between two dates (in seconds)
309
+ */
310
+ diffInClosedSeconds(startDate: Date, endDate: Date): number;
311
+ /**
312
+ * Get the current open range at a specific date and time
313
+ */
314
+ currentOpenRange(dateTime: Date): TimeRange<T> | null;
315
+ /**
316
+ * Get the start of the current open range at a specific date and time
317
+ */
318
+ currentOpenRangeStart(dateTime: Date): Date | null;
319
+ /**
320
+ * Get the end of the current open range at a specific date and time
321
+ */
322
+ currentOpenRangeEnd(dateTime: Date): Date | null;
323
+ /**
324
+ * Find the next open range after a specific date and time
325
+ */
326
+ private findNextOpenRange;
327
+ /**
328
+ * Find the previous open range before a specific date and time
329
+ */
330
+ private findPreviousOpenRange;
331
+ /**
332
+ * Get the next open range after a specific date and time
333
+ */
334
+ nextOpenRange(dateTime: Date): TimeRange<T> | null;
335
+ /**
336
+ * Get the start of the next open range after a specific date and time
337
+ */
338
+ nextOpenRangeStart(dateTime: Date): Date | null;
339
+ /**
340
+ * Get the end of the next open range after a specific date and time
341
+ */
342
+ nextOpenRangeEnd(dateTime: Date): Date | null;
343
+ /**
344
+ * Convenience alias for nextOpenRangeStart
345
+ */
346
+ nextOpen(dateTime: Date): Date | null;
347
+ /**
348
+ * Get the next closing time after a specific date and time
349
+ */
350
+ nextClose(dateTime: Date): Date | null;
351
+ /**
352
+ * Get the previous open range before a specific date and time
353
+ */
354
+ previousOpenRange(dateTime: Date): TimeRange<T> | null;
355
+ /**
356
+ * Get the start of the previous open range before a specific date and time
357
+ */
358
+ previousOpenRangeStart(dateTime: Date): Date | null;
359
+ /**
360
+ * Get the end of the previous open range before a specific date and time
361
+ */
362
+ previousOpenRangeEnd(dateTime: Date): Date | null;
363
+ static from<T extends Record<string, unknown> = Record<string, unknown>>(config: OpeningHoursOptions<T> & DisplayOptions): OpeningHours<T>;
364
+ /**
365
+ * Create OpeningHours from structured data (schema.org format)
366
+ */
367
+ static createFromStructuredData<T extends Record<string, unknown> = Record<string, unknown>>(inputData: unknown, displayOptions?: DisplayOptions): OpeningHours<T>;
368
+ /**
369
+ * Format a Time object as ISO-8601 time with timezone offset
370
+ */
371
+ private formatISOTime;
372
+ /**
373
+ * Convert to structured data (schema.org format)
374
+ */
375
+ asStructuredData(timezone?: string | null): OpeningHoursSpecification[];
376
+ /**
377
+ * Convert a date to the configured timezone.
378
+ * Returns a date object whose clock time reflects the target timezone
379
+ * but represents the same absolute instant.
380
+ */
381
+ private toTimezone;
382
+ /**
383
+ * Convert a date interpreted in the configured timezone back to UTC.
384
+ */
385
+ private fromTimezone;
386
+ /**
387
+ * Get a string representation of a day's schedule for comparison
388
+ */
389
+ private getScheduleStringForDay;
390
+ /**
391
+ * Get time ranges for a specific day of the week
392
+ */
393
+ private getTimeRangesForDayOfWeek;
394
+ /**
395
+ * Format a day range, e.g. "Mon...Fri" or "Sat"
396
+ */
397
+ protected formatDayRange(days: DayOfWeek[], options?: DisplayOptions): string;
398
+ /**
399
+ * Format a time range for display (e.g. 9:00-17:00)
400
+ */
401
+ protected formatTimeRange(timeRange: TimeRange<T>, options?: DisplayOptions): string;
402
+ /**
403
+ * Render opening hours as array where each array item corresponds to a line of output
404
+ * and contains day range followed by time ranges
405
+ */
406
+ toArray(options?: DisplayOptions): string[][];
407
+ /**
408
+ * Format a day name using Intl.DateTimeFormat
409
+ */
410
+ private formatDayName;
411
+ /**
412
+ * Get timezone abbreviation for display
413
+ */
414
+ private getTimezoneName;
415
+ /**
416
+ * Render opening hours as multi-line string
417
+ */
418
+ toString(options?: DisplayOptions): string;
419
+ }
420
+ export { TimeRangeWithData, TimeRangeString, TimeRangeInput, TimeRangeData, TimeRange, Time, OpeningHoursSpecification, OpeningHoursOptions, OpeningHoursForDay, OpeningHoursConfig, OpeningHours, OpenState, Days, DayRange, Day, DateValue, DateRange };