ts-time-utils 1.1.0 → 3.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.
Files changed (92) hide show
  1. package/README.md +567 -12
  2. package/dist/calculate.d.ts +7 -2
  3. package/dist/calculate.d.ts.map +1 -1
  4. package/dist/calculate.js +13 -3
  5. package/dist/calendar.d.ts +103 -0
  6. package/dist/calendar.d.ts.map +1 -1
  7. package/dist/calendar.js +224 -0
  8. package/dist/chain.d.ts +269 -0
  9. package/dist/chain.d.ts.map +1 -0
  10. package/dist/chain.js +422 -0
  11. package/dist/compare.d.ts +217 -0
  12. package/dist/compare.d.ts.map +1 -0
  13. package/dist/compare.js +417 -0
  14. package/dist/cron.d.ts +82 -0
  15. package/dist/cron.d.ts.map +1 -0
  16. package/dist/cron.js +294 -0
  17. package/dist/esm/calculate.d.ts +7 -2
  18. package/dist/esm/calculate.d.ts.map +1 -1
  19. package/dist/esm/calculate.js +13 -3
  20. package/dist/esm/calendar.d.ts +103 -0
  21. package/dist/esm/calendar.d.ts.map +1 -1
  22. package/dist/esm/calendar.js +224 -0
  23. package/dist/esm/chain.d.ts +269 -0
  24. package/dist/esm/chain.d.ts.map +1 -0
  25. package/dist/esm/chain.js +422 -0
  26. package/dist/esm/compare.d.ts +217 -0
  27. package/dist/esm/compare.d.ts.map +1 -0
  28. package/dist/esm/compare.js +417 -0
  29. package/dist/esm/cron.d.ts +82 -0
  30. package/dist/esm/cron.d.ts.map +1 -0
  31. package/dist/esm/cron.js +294 -0
  32. package/dist/esm/fiscal.d.ts +195 -0
  33. package/dist/esm/fiscal.d.ts.map +1 -0
  34. package/dist/esm/fiscal.js +295 -0
  35. package/dist/esm/format.d.ts +65 -0
  36. package/dist/esm/format.d.ts.map +1 -1
  37. package/dist/esm/format.js +202 -0
  38. package/dist/esm/holidays.d.ts +62 -0
  39. package/dist/esm/holidays.d.ts.map +1 -0
  40. package/dist/esm/holidays.js +793 -0
  41. package/dist/esm/index.d.ts +18 -6
  42. package/dist/esm/index.d.ts.map +1 -1
  43. package/dist/esm/index.js +20 -6
  44. package/dist/esm/iterate.d.ts +212 -0
  45. package/dist/esm/iterate.d.ts.map +1 -0
  46. package/dist/esm/iterate.js +409 -0
  47. package/dist/esm/parse.d.ts +45 -0
  48. package/dist/esm/parse.d.ts.map +1 -1
  49. package/dist/esm/parse.js +207 -0
  50. package/dist/esm/plugins.d.ts +129 -0
  51. package/dist/esm/plugins.d.ts.map +1 -0
  52. package/dist/esm/plugins.js +173 -0
  53. package/dist/esm/timezone.d.ts +52 -0
  54. package/dist/esm/timezone.d.ts.map +1 -1
  55. package/dist/esm/timezone.js +171 -0
  56. package/dist/esm/validate.d.ts +51 -0
  57. package/dist/esm/validate.d.ts.map +1 -1
  58. package/dist/esm/validate.js +92 -0
  59. package/dist/esm/workingHours.d.ts +70 -0
  60. package/dist/esm/workingHours.d.ts.map +1 -1
  61. package/dist/esm/workingHours.js +161 -0
  62. package/dist/fiscal.d.ts +195 -0
  63. package/dist/fiscal.d.ts.map +1 -0
  64. package/dist/fiscal.js +295 -0
  65. package/dist/format.d.ts +65 -0
  66. package/dist/format.d.ts.map +1 -1
  67. package/dist/format.js +202 -0
  68. package/dist/holidays.d.ts +62 -0
  69. package/dist/holidays.d.ts.map +1 -0
  70. package/dist/holidays.js +793 -0
  71. package/dist/index.d.ts +18 -6
  72. package/dist/index.d.ts.map +1 -1
  73. package/dist/index.js +20 -6
  74. package/dist/iterate.d.ts +212 -0
  75. package/dist/iterate.d.ts.map +1 -0
  76. package/dist/iterate.js +409 -0
  77. package/dist/parse.d.ts +45 -0
  78. package/dist/parse.d.ts.map +1 -1
  79. package/dist/parse.js +207 -0
  80. package/dist/plugins.d.ts +129 -0
  81. package/dist/plugins.d.ts.map +1 -0
  82. package/dist/plugins.js +173 -0
  83. package/dist/timezone.d.ts +52 -0
  84. package/dist/timezone.d.ts.map +1 -1
  85. package/dist/timezone.js +171 -0
  86. package/dist/validate.d.ts +51 -0
  87. package/dist/validate.d.ts.map +1 -1
  88. package/dist/validate.js +92 -0
  89. package/dist/workingHours.d.ts +70 -0
  90. package/dist/workingHours.d.ts.map +1 -1
  91. package/dist/workingHours.js +161 -0
  92. package/package.json +40 -1
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Plugin system for extending ChainedDate with custom methods
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * import { extend } from 'ts-time-utils/plugins';
7
+ * import { ChainedDate } from 'ts-time-utils/chain';
8
+ *
9
+ * // Define plugin methods
10
+ * extend('myPlugin', {
11
+ * nextMonday(this: ChainedDate): ChainedDate {
12
+ * const day = this.weekday();
13
+ * const daysUntilMonday = day === 0 ? 1 : 8 - day;
14
+ * return this.add(daysUntilMonday, 'days');
15
+ * }
16
+ * });
17
+ *
18
+ * // Use plugin methods
19
+ * chain(new Date()).nextMonday().format('YYYY-MM-DD');
20
+ * ```
21
+ */
22
+ /**
23
+ * Initialize the plugin system with ChainedDate class
24
+ * This is called automatically when chain.js is imported
25
+ * @internal
26
+ */
27
+ export declare function __initPluginSystem(ChainedDateClass: any): void;
28
+ /**
29
+ * Plugin function type - methods receive ChainedDate instance as `this`
30
+ */
31
+ export type PluginFunction = (this: any, ...args: any[]) => any;
32
+ /**
33
+ * Plugin definition - map of method names to functions
34
+ */
35
+ export interface Plugin {
36
+ [methodName: string]: PluginFunction;
37
+ }
38
+ /**
39
+ * Extend ChainedDate with custom methods
40
+ *
41
+ * @param pluginName - Unique name for the plugin
42
+ * @param methods - Object mapping method names to functions
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * extend('businessDays', {
47
+ * addBusinessDays(this: ChainedDate, days: number): ChainedDate {
48
+ * let current = this.clone();
49
+ * let remaining = days;
50
+ *
51
+ * while (remaining > 0) {
52
+ * current = current.add(1, 'days');
53
+ * if (current.isWeekday()) remaining--;
54
+ * }
55
+ *
56
+ * return current;
57
+ * }
58
+ * });
59
+ *
60
+ * chain(new Date()).addBusinessDays(5);
61
+ * ```
62
+ */
63
+ export declare function extend(pluginName: string, methods: Plugin): void;
64
+ /**
65
+ * Remove a plugin and its methods from ChainedDate
66
+ *
67
+ * @param pluginName - Name of the plugin to uninstall
68
+ *
69
+ * @example
70
+ * ```ts
71
+ * uninstall('businessDays');
72
+ * ```
73
+ */
74
+ export declare function uninstall(pluginName: string): void;
75
+ /**
76
+ * Get list of all registered plugin names
77
+ *
78
+ * @returns Array of plugin names
79
+ *
80
+ * @example
81
+ * ```ts
82
+ * getRegisteredPlugins(); // ['businessDays', 'myPlugin']
83
+ * ```
84
+ */
85
+ export declare function getRegisteredPlugins(): string[];
86
+ /**
87
+ * Get list of methods provided by a plugin
88
+ *
89
+ * @param pluginName - Name of the plugin
90
+ * @returns Array of method names, or empty array if plugin not found
91
+ *
92
+ * @example
93
+ * ```ts
94
+ * getPluginMethods('businessDays'); // ['addBusinessDays', 'subtractBusinessDays']
95
+ * ```
96
+ */
97
+ export declare function getPluginMethods(pluginName: string): string[];
98
+ /**
99
+ * Check if a plugin is registered
100
+ *
101
+ * @param pluginName - Name of the plugin
102
+ * @returns True if plugin is registered
103
+ *
104
+ * @example
105
+ * ```ts
106
+ * isPluginRegistered('businessDays'); // true
107
+ * ```
108
+ */
109
+ export declare function isPluginRegistered(pluginName: string): boolean;
110
+ /**
111
+ * Declare plugin methods for TypeScript support
112
+ *
113
+ * Use module augmentation to add type definitions for your plugin methods:
114
+ *
115
+ * @example
116
+ * ```ts
117
+ * import { ChainedDate } from 'ts-time-utils/chain';
118
+ *
119
+ * declare module 'ts-time-utils/chain' {
120
+ * interface ChainedDate {
121
+ * addBusinessDays(days: number): ChainedDate;
122
+ * subtractBusinessDays(days: number): ChainedDate;
123
+ * }
124
+ * }
125
+ * ```
126
+ */
127
+ export interface PluginDeclaration {
128
+ }
129
+ //# sourceMappingURL=plugins.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugins.d.ts","sourceRoot":"","sources":["../src/plugins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AA0BH;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,gBAAgB,EAAE,GAAG,QAEvD;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,CAAC;CACtC;AAcD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAwBhE;AAED;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAqBlD;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,EAAE,CAE/C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAG7D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,iBAAiB;CAAG"}
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Plugin system for extending ChainedDate with custom methods
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * import { extend } from 'ts-time-utils/plugins';
7
+ * import { ChainedDate } from 'ts-time-utils/chain';
8
+ *
9
+ * // Define plugin methods
10
+ * extend('myPlugin', {
11
+ * nextMonday(this: ChainedDate): ChainedDate {
12
+ * const day = this.weekday();
13
+ * const daysUntilMonday = day === 0 ? 1 : 8 - day;
14
+ * return this.add(daysUntilMonday, 'days');
15
+ * }
16
+ * });
17
+ *
18
+ * // Use plugin methods
19
+ * chain(new Date()).nextMonday().format('YYYY-MM-DD');
20
+ * ```
21
+ */
22
+ // Import ChainedDate class directly - safe because chain.js doesn't import plugins.js
23
+ // We need the actual class (not just the type) to modify its prototype
24
+ let ChainedDateConstructor = null;
25
+ /**
26
+ * Get ChainedDate class lazily to ensure it's fully initialized
27
+ */
28
+ function getChainedDate() {
29
+ if (!ChainedDateConstructor) {
30
+ // Use dynamic import that works in both CJS and ESM
31
+ try {
32
+ // Try ESM import path first
33
+ ChainedDateConstructor = globalThis.__chainedDateClass;
34
+ if (!ChainedDateConstructor) {
35
+ // Fallback: The class will be set by chain.js when it loads
36
+ throw new Error('ChainedDate not yet loaded. Import chain.js before using plugins.');
37
+ }
38
+ }
39
+ catch (e) {
40
+ throw new Error('ChainedDate class not available. Ensure chain.js is imported before registering plugins.');
41
+ }
42
+ }
43
+ return ChainedDateConstructor;
44
+ }
45
+ /**
46
+ * Initialize the plugin system with ChainedDate class
47
+ * This is called automatically when chain.js is imported
48
+ * @internal
49
+ */
50
+ export function __initPluginSystem(ChainedDateClass) {
51
+ ChainedDateConstructor = ChainedDateClass;
52
+ }
53
+ const registry = {};
54
+ /**
55
+ * Extend ChainedDate with custom methods
56
+ *
57
+ * @param pluginName - Unique name for the plugin
58
+ * @param methods - Object mapping method names to functions
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * extend('businessDays', {
63
+ * addBusinessDays(this: ChainedDate, days: number): ChainedDate {
64
+ * let current = this.clone();
65
+ * let remaining = days;
66
+ *
67
+ * while (remaining > 0) {
68
+ * current = current.add(1, 'days');
69
+ * if (current.isWeekday()) remaining--;
70
+ * }
71
+ *
72
+ * return current;
73
+ * }
74
+ * });
75
+ *
76
+ * chain(new Date()).addBusinessDays(5);
77
+ * ```
78
+ */
79
+ export function extend(pluginName, methods) {
80
+ if (registry[pluginName]) {
81
+ throw new Error(`Plugin "${pluginName}" is already registered. Use a different name or uninstall first.`);
82
+ }
83
+ // Get ChainedDate class and save original methods before overwriting
84
+ const ChainedDateClass = getChainedDate();
85
+ const originalMethods = new Map();
86
+ Object.entries(methods).forEach(([methodName, fn]) => {
87
+ // Save original method if it exists
88
+ if (methodName in ChainedDateClass.prototype) {
89
+ const original = ChainedDateClass.prototype[methodName];
90
+ if (typeof original === 'function') {
91
+ originalMethods.set(methodName, original);
92
+ }
93
+ console.warn(`Method "${methodName}" already exists on ChainedDate and will be overwritten`);
94
+ }
95
+ // Add the plugin method
96
+ ChainedDateClass.prototype[methodName] = fn;
97
+ });
98
+ // Register the plugin with its original methods
99
+ registry[pluginName] = { plugin: methods, originalMethods };
100
+ }
101
+ /**
102
+ * Remove a plugin and its methods from ChainedDate
103
+ *
104
+ * @param pluginName - Name of the plugin to uninstall
105
+ *
106
+ * @example
107
+ * ```ts
108
+ * uninstall('businessDays');
109
+ * ```
110
+ */
111
+ export function uninstall(pluginName) {
112
+ const entry = registry[pluginName];
113
+ if (!entry) {
114
+ throw new Error(`Plugin "${pluginName}" is not registered`);
115
+ }
116
+ // Get ChainedDate class and restore/remove methods
117
+ const ChainedDateClass = getChainedDate();
118
+ Object.keys(entry.plugin).forEach((methodName) => {
119
+ // If there was an original method, restore it
120
+ const original = entry.originalMethods.get(methodName);
121
+ if (original) {
122
+ ChainedDateClass.prototype[methodName] = original;
123
+ }
124
+ else {
125
+ // Otherwise, delete the method entirely
126
+ delete ChainedDateClass.prototype[methodName];
127
+ }
128
+ });
129
+ // Remove from registry
130
+ delete registry[pluginName];
131
+ }
132
+ /**
133
+ * Get list of all registered plugin names
134
+ *
135
+ * @returns Array of plugin names
136
+ *
137
+ * @example
138
+ * ```ts
139
+ * getRegisteredPlugins(); // ['businessDays', 'myPlugin']
140
+ * ```
141
+ */
142
+ export function getRegisteredPlugins() {
143
+ return Object.keys(registry);
144
+ }
145
+ /**
146
+ * Get list of methods provided by a plugin
147
+ *
148
+ * @param pluginName - Name of the plugin
149
+ * @returns Array of method names, or empty array if plugin not found
150
+ *
151
+ * @example
152
+ * ```ts
153
+ * getPluginMethods('businessDays'); // ['addBusinessDays', 'subtractBusinessDays']
154
+ * ```
155
+ */
156
+ export function getPluginMethods(pluginName) {
157
+ const entry = registry[pluginName];
158
+ return entry ? Object.keys(entry.plugin) : [];
159
+ }
160
+ /**
161
+ * Check if a plugin is registered
162
+ *
163
+ * @param pluginName - Name of the plugin
164
+ * @returns True if plugin is registered
165
+ *
166
+ * @example
167
+ * ```ts
168
+ * isPluginRegistered('businessDays'); // true
169
+ * ```
170
+ */
171
+ export function isPluginRegistered(pluginName) {
172
+ return pluginName in registry;
173
+ }
@@ -31,4 +31,56 @@ export declare function compareZoneOffsets(zoneA: string, zoneB: string, date?:
31
31
  * For example useful for naive scheduling.
32
32
  */
33
33
  export declare function reinterpretAsZone(date: Date, targetZone: string): Date | null;
34
+ /**
35
+ * Check if a date is in Daylight Saving Time for a given timezone
36
+ * @param date - date to check
37
+ * @param zone - IANA timezone string
38
+ */
39
+ export declare function isDST(date: Date, zone: string): boolean | null;
40
+ /**
41
+ * Get the next DST transition (if any) for a timezone
42
+ * @param date - starting date
43
+ * @param zone - IANA timezone string
44
+ * @returns next DST transition date or null if no DST in that zone
45
+ */
46
+ export declare function getNextDSTTransition(date: Date, zone: string): Date | null;
47
+ /**
48
+ * Find overlapping working hours between multiple timezones
49
+ * @param zones - array of IANA timezone strings
50
+ * @param workHoursStart - work hours start (0-24)
51
+ * @param workHoursEnd - work hours end (0-24)
52
+ * @param date - reference date (default: today)
53
+ * @returns array of overlapping hour ranges in UTC, or null if no overlap
54
+ */
55
+ export declare function findCommonWorkingHours(zones: string[], workHoursStart?: number, workHoursEnd?: number, date?: Date): {
56
+ startUTC: number;
57
+ endUTC: number;
58
+ } | null;
59
+ /**
60
+ * Get all timezone abbreviations for a zone on a given date
61
+ * @param zone - IANA timezone string
62
+ * @param date - reference date
63
+ */
64
+ export declare function getTimezoneAbbreviation(zone: string, date?: Date): string | null;
65
+ /**
66
+ * Convert a wall clock time from one timezone to another
67
+ * @param date - date with time in source timezone
68
+ * @param fromZone - source timezone
69
+ * @param toZone - target timezone
70
+ */
71
+ export declare function convertBetweenZones(date: Date, fromZone: string, toZone: string): Date | null;
72
+ /**
73
+ * Get the time difference between two timezones in hours
74
+ * @param zoneA - first timezone
75
+ * @param zoneB - second timezone
76
+ * @param date - reference date
77
+ */
78
+ export declare function getTimezoneDifferenceHours(zoneA: string, zoneB: string, date?: Date): number | null;
79
+ /**
80
+ * Check if two timezones have the same offset at a given date
81
+ * @param zoneA - first timezone
82
+ * @param zoneB - second timezone
83
+ * @param date - reference date
84
+ */
85
+ export declare function isSameTimezone(zoneA: string, zoneB: string, date?: Date): boolean | null;
34
86
  //# sourceMappingURL=timezone.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"timezone.d.ts","sourceRoot":"","sources":["../src/timezone.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,sDAAsD;AACtD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,IAAiB,GAAG,MAAM,GAAG,IAAI,CAgBtF;AAED,iCAAiC;AACjC,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,IAAI,CAAC,qBAA0B,GAAG,MAAM,CAG3G;AAED,yCAAyC;AACzC,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAIvE;AAED,qFAAqF;AACrF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;CAAE,GAAG,IAAI,CAoB9J;AAED,yDAAyD;AACzD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOrD;AAED,uEAAuE;AACvE,eAAO,MAAM,gBAAgB,UAK5B,CAAC;AAEF,0CAA0C;AAC1C,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,iDAAiD;AACjD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,IAAiB,GAAG,MAAM,GAAG,IAAI,CAKvG;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAI7E"}
1
+ {"version":3,"file":"timezone.d.ts","sourceRoot":"","sources":["../src/timezone.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,sDAAsD;AACtD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,IAAiB,GAAG,MAAM,GAAG,IAAI,CAgBtF;AAED,iCAAiC;AACjC,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,IAAI,CAAC,qBAA0B,GAAG,MAAM,CAG3G;AAED,yCAAyC;AACzC,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAIvE;AAED,qFAAqF;AACrF,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;CAAE,GAAG,IAAI,CAoB9J;AAED,yDAAyD;AACzD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOrD;AAED,uEAAuE;AACvE,eAAO,MAAM,gBAAgB,UAK5B,CAAC;AAEF,0CAA0C;AAC1C,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,iDAAiD;AACjD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,IAAiB,GAAG,MAAM,GAAG,IAAI,CAKvG;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAI7E;AAED;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CA0B9D;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAiD1E;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EAAE,EACf,cAAc,GAAE,MAAU,EAC1B,YAAY,GAAE,MAAW,EACzB,IAAI,GAAE,IAAiB,GACtB;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA6B7C;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,IAAiB,GAAG,MAAM,GAAG,IAAI,CAY5F;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAa7F;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,IAAiB,GAAG,MAAM,GAAG,IAAI,CAI/G;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,IAAiB,GAAG,OAAO,GAAG,IAAI,CAIpG"}
package/dist/timezone.js CHANGED
@@ -97,3 +97,174 @@ export function reinterpretAsZone(date, targetZone) {
97
97
  return null;
98
98
  return new Date(Date.UTC(target.year, target.month - 1, target.day, target.hour, target.minute, target.second));
99
99
  }
100
+ /**
101
+ * Check if a date is in Daylight Saving Time for a given timezone
102
+ * @param date - date to check
103
+ * @param zone - IANA timezone string
104
+ */
105
+ export function isDST(date, zone) {
106
+ if (!isValidTimeZone(zone))
107
+ return null;
108
+ // Compare offset in January vs July - the one with larger offset is DST
109
+ const january = new Date(date.getFullYear(), 0, 1);
110
+ const july = new Date(date.getFullYear(), 6, 1);
111
+ const janOffset = getTimezoneOffset(zone, january);
112
+ const julOffset = getTimezoneOffset(zone, july);
113
+ const currentOffset = getTimezoneOffset(zone, date);
114
+ if (janOffset === null || julOffset === null || currentOffset === null) {
115
+ return null;
116
+ }
117
+ // If offsets are the same, no DST in this zone
118
+ if (janOffset === julOffset) {
119
+ return false;
120
+ }
121
+ // In northern hemisphere, summer (July) has larger offset
122
+ // In southern hemisphere, summer (January) has larger offset
123
+ // DST is whichever is the "larger" offset
124
+ const maxOffset = Math.max(janOffset, julOffset);
125
+ return currentOffset === maxOffset;
126
+ }
127
+ /**
128
+ * Get the next DST transition (if any) for a timezone
129
+ * @param date - starting date
130
+ * @param zone - IANA timezone string
131
+ * @returns next DST transition date or null if no DST in that zone
132
+ */
133
+ export function getNextDSTTransition(date, zone) {
134
+ if (!isValidTimeZone(zone))
135
+ return null;
136
+ const january = new Date(date.getFullYear(), 0, 1);
137
+ const july = new Date(date.getFullYear(), 6, 1);
138
+ const janOffset = getTimezoneOffset(zone, january);
139
+ const julOffset = getTimezoneOffset(zone, july);
140
+ if (janOffset === null || julOffset === null)
141
+ return null;
142
+ // No DST if offsets are the same
143
+ if (janOffset === julOffset)
144
+ return null;
145
+ // Binary search for the transition within the next year
146
+ const currentOffset = getTimezoneOffset(zone, date);
147
+ if (currentOffset === null)
148
+ return null;
149
+ // Check day by day for up to 366 days
150
+ const searchDate = new Date(date);
151
+ for (let i = 1; i <= 366; i++) {
152
+ searchDate.setDate(searchDate.getDate() + 1);
153
+ const newOffset = getTimezoneOffset(zone, searchDate);
154
+ if (newOffset !== null && newOffset !== currentOffset) {
155
+ // Found a transition, now narrow it down
156
+ const prevDay = new Date(searchDate);
157
+ prevDay.setDate(prevDay.getDate() - 1);
158
+ // Binary search within the day
159
+ let low = prevDay.getTime();
160
+ let high = searchDate.getTime();
161
+ while (high - low > 60000) { // 1 minute precision
162
+ const mid = Math.floor((low + high) / 2);
163
+ const midDate = new Date(mid);
164
+ const midOffset = getTimezoneOffset(zone, midDate);
165
+ if (midOffset === currentOffset) {
166
+ low = mid;
167
+ }
168
+ else {
169
+ high = mid;
170
+ }
171
+ }
172
+ return new Date(high);
173
+ }
174
+ }
175
+ return null;
176
+ }
177
+ /**
178
+ * Find overlapping working hours between multiple timezones
179
+ * @param zones - array of IANA timezone strings
180
+ * @param workHoursStart - work hours start (0-24)
181
+ * @param workHoursEnd - work hours end (0-24)
182
+ * @param date - reference date (default: today)
183
+ * @returns array of overlapping hour ranges in UTC, or null if no overlap
184
+ */
185
+ export function findCommonWorkingHours(zones, workHoursStart = 9, workHoursEnd = 17, date = new Date()) {
186
+ if (zones.length === 0)
187
+ return null;
188
+ // Convert each zone's work hours to UTC
189
+ const utcRanges = zones.map(zone => {
190
+ const offset = getTimezoneOffset(zone, date);
191
+ if (offset === null)
192
+ return null;
193
+ // Offset is in minutes, positive means ahead of UTC
194
+ // So to convert local time to UTC, we subtract the offset
195
+ const startUTC = workHoursStart - (offset / 60);
196
+ const endUTC = workHoursEnd - (offset / 60);
197
+ return { startUTC, endUTC };
198
+ });
199
+ if (utcRanges.some(r => r === null))
200
+ return null;
201
+ const validRanges = utcRanges;
202
+ // Find intersection of all ranges
203
+ let overlapStart = Math.max(...validRanges.map(r => r.startUTC));
204
+ let overlapEnd = Math.min(...validRanges.map(r => r.endUTC));
205
+ if (overlapStart >= overlapEnd) {
206
+ return null; // No overlap
207
+ }
208
+ return { startUTC: overlapStart, endUTC: overlapEnd };
209
+ }
210
+ /**
211
+ * Get all timezone abbreviations for a zone on a given date
212
+ * @param zone - IANA timezone string
213
+ * @param date - reference date
214
+ */
215
+ export function getTimezoneAbbreviation(zone, date = new Date()) {
216
+ try {
217
+ const fmt = new Intl.DateTimeFormat('en-US', {
218
+ timeZone: zone,
219
+ timeZoneName: 'short'
220
+ });
221
+ const parts = fmt.formatToParts(date);
222
+ const tzPart = parts.find(p => p.type === 'timeZoneName');
223
+ return tzPart?.value || null;
224
+ }
225
+ catch {
226
+ return null;
227
+ }
228
+ }
229
+ /**
230
+ * Convert a wall clock time from one timezone to another
231
+ * @param date - date with time in source timezone
232
+ * @param fromZone - source timezone
233
+ * @param toZone - target timezone
234
+ */
235
+ export function convertBetweenZones(date, fromZone, toZone) {
236
+ // First, interpret the date as being in fromZone
237
+ const fromOffset = getTimezoneOffset(fromZone, date);
238
+ const toOffset = getTimezoneOffset(toZone, date);
239
+ if (fromOffset === null || toOffset === null)
240
+ return null;
241
+ // Calculate the difference in minutes
242
+ const diffMinutes = toOffset - fromOffset;
243
+ // Apply the difference
244
+ const result = new Date(date.getTime() + diffMinutes * 60 * 1000);
245
+ return result;
246
+ }
247
+ /**
248
+ * Get the time difference between two timezones in hours
249
+ * @param zoneA - first timezone
250
+ * @param zoneB - second timezone
251
+ * @param date - reference date
252
+ */
253
+ export function getTimezoneDifferenceHours(zoneA, zoneB, date = new Date()) {
254
+ const diff = compareZoneOffsets(zoneA, zoneB, date);
255
+ if (diff === null)
256
+ return null;
257
+ return diff / 60;
258
+ }
259
+ /**
260
+ * Check if two timezones have the same offset at a given date
261
+ * @param zoneA - first timezone
262
+ * @param zoneB - second timezone
263
+ * @param date - reference date
264
+ */
265
+ export function isSameTimezone(zoneA, zoneB, date = new Date()) {
266
+ const diff = compareZoneOffsets(zoneA, zoneB, date);
267
+ if (diff === null)
268
+ return null;
269
+ return diff === 0;
270
+ }
@@ -59,4 +59,55 @@ export declare function isValidTimeString(time: string): boolean;
59
59
  * @param dateString - date string to validate
60
60
  */
61
61
  export declare function isValidISOString(dateString: string): boolean;
62
+ /**
63
+ * Check if two dates are in the same week (ISO week, Monday-Sunday)
64
+ * @param date1 - first date
65
+ * @param date2 - second date
66
+ */
67
+ export declare function isSameWeek(date1: Date, date2: Date): boolean;
68
+ /**
69
+ * Check if two dates are in the same month
70
+ * @param date1 - first date
71
+ * @param date2 - second date
72
+ */
73
+ export declare function isSameMonth(date1: Date, date2: Date): boolean;
74
+ /**
75
+ * Check if two dates are in the same year
76
+ * @param date1 - first date
77
+ * @param date2 - second date
78
+ */
79
+ export declare function isSameYear(date1: Date, date2: Date): boolean;
80
+ /**
81
+ * Check if a date is in the current week
82
+ * @param date - date to check
83
+ */
84
+ export declare function isThisWeek(date: Date): boolean;
85
+ /**
86
+ * Check if a date is in the current month
87
+ * @param date - date to check
88
+ */
89
+ export declare function isThisMonth(date: Date): boolean;
90
+ /**
91
+ * Check if a date is in the current year
92
+ * @param date - date to check
93
+ */
94
+ export declare function isThisYear(date: Date): boolean;
95
+ /**
96
+ * Check if a date is a business day (weekday, optionally excluding holidays)
97
+ * @param date - date to check
98
+ * @param holidays - optional array of holiday dates to exclude
99
+ */
100
+ export declare function isBusinessDay(date: Date, holidays?: Date[]): boolean;
101
+ /**
102
+ * Check if a date is in the last N days (including today)
103
+ * @param date - date to check
104
+ * @param n - number of days
105
+ */
106
+ export declare function isInLastNDays(date: Date, n: number): boolean;
107
+ /**
108
+ * Check if a date is in the next N days (including today)
109
+ * @param date - date to check
110
+ * @param n - number of days
111
+ */
112
+ export declare function isInNextNDays(date: Date, n: number): boolean;
62
113
  //# sourceMappingURL=validate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAWjE;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEhD;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAE1C;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAE5C;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAO3C;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAQ/C;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAQ9C;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,GAAG,OAAO,CAM3D;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAG7C;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAE7C;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGvD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAG5D"}
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAWjE;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEhD;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAE1C;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAE5C;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAO3C;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAQ/C;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAQ9C;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,GAAG,OAAO,CAM3D;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAG7C;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAE7C;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGvD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAG5D;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,GAAG,OAAO,CAW5D;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,GAAG,OAAO,CAK7D;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,GAAG,OAAO,CAE5D;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAE9C;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAE/C;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAE9C;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,GAAE,IAAI,EAAO,GAAG,OAAO,CAIxE;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAU5D;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAU5D"}