@lbd-sh/date-tz 1.0.11 → 1.0.13

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 CHANGED
@@ -83,8 +83,9 @@ DateTz ships as CommonJS with TypeScript declarations. No runtime dependencies,
83
83
  | `DateTz.parse(str, pattern?, tz?)` | Parse formatted strings into `DateTz` instances. |
84
84
  | `DateTz.defaultFormat` | Default pattern used by `toString()` when no arguments are provided. |
85
85
  | Getters | `year`, `month`, `day`, `hour`, `minute`, `dayOfWeek`, `isDst`, `timezoneOffset`. |
86
- | Mutators | `add(value, unit)`, `set(value, unit)`, `convertToTimezone(tz)` (mutating), `cloneToTimezone(tz)` (immutable). |
87
- | Comparison | `compare(other)`, `isComparable(other)` guard against cross-zone mistakes. |
86
+ | Mutators | `add(value, unit)`, `subtract(value, unit)`, `plus(duration)`, `minus(duration)`, `startOf(unit)`, `endOf(unit)`, `set(value, unit)`, `convertToTimezone(tz)` (mutating), `cloneToTimezone(tz)` (immutable). |
87
+ | Comparison | `compare(other)`, `diff(other, unit?, asFloat?)`, `isBefore(other, unit?)`, `isAfter(other, unit?)`, `isSame(other, unit?)`, `isSameOrBefore(other, unit?)`, `isSameOrAfter(other, unit?)`, `isBetween(start, end, unit?, inclusivity?)`. |
88
+ | Convenience | `clone()`, `toJSDate()`, `toISOString()`, `toISO()`, `toUnix()`, `valueOf()`. |
88
89
 
89
90
  ---
90
91
 
@@ -152,6 +153,8 @@ Parsing throws on invalid zones or incompatible patterns (e.g. `hh` without `aa`
152
153
 
153
154
  ### Arithmetic Cookbook
154
155
 
156
+ The `add`, `subtract`, `plus`, and `minus` helpers stay DST-safe and respect calendar rollovers.
157
+
155
158
  ```ts
156
159
  const sprint = new DateTz(Date.UTC(2025, 1, 1, 9, 0), 'Europe/Amsterdam');
157
160
 
@@ -159,13 +162,21 @@ sprint.add(14, 'day'); // Compose to simulate weeks
159
162
  sprint.set(sprint.month + 1, 'month').set(1, 'day'); // First day of next month
160
163
  while ([0, 6].includes(sprint.dayOfWeek)) sprint.add(1, 'day'); // Skip weekend
161
164
  sprint.set(10, 'hour').set(0, 'minute'); // Move to 10:00
165
+ const reminder = sprint.clone().subtract(1, 'day').set(17, 'hour'); // 24h reminder
166
+
167
+ const launch = DateTz.parse('2025-07-01 08:30', 'YYYY-MM-DD HH:mm', 'UTC')
168
+ .plus({ weeks: 2, hours: 4 })
169
+ .minus({ days: 1 });
170
+
171
+ launch.startOf('day'); // snap to midnight
172
+ launch.endOf('day'); // advance to the end of the same day (23:59)
162
173
  ```
163
174
 
164
175
  ### Immutability Pattern
165
176
 
166
177
  ```ts
167
178
  const base = DateTz.now('UTC');
168
- const nextRun = new DateTz(base).add(1, 'day');
179
+ const nextRun = base.clone().add(1, 'day');
169
180
  ```
170
181
 
171
182
  Mutators change the instance; use cloning when you need persistence.
@@ -191,6 +202,45 @@ const ny = DateTz.now('America/New_York');
191
202
  if (!rome.isComparable(ny)) ny.convertToTimezone(rome.timezone);
192
203
  ```
193
204
 
205
+ ### Differences & Ranges
206
+
207
+ Moment/Luxon-style comparison helpers make it simple to reason about relative ordering with optional rounding units and inclusive/exclusive bounds.
208
+
209
+ ```ts
210
+ const start = DateTz.parse('2025-06-10 08:00', 'YYYY-MM-DD HH:mm', 'Europe/Rome');
211
+ const end = DateTz.parse('2025-06-12 11:30', 'YYYY-MM-DD HH:mm', 'Europe/Rome');
212
+
213
+ start.diff(end); // -183000000 (milliseconds)
214
+ start.diff(end, 'hour'); // -51
215
+ start.diff(end, 'day', true); // -2.145833...
216
+
217
+ start.isBefore(end); // true
218
+ end.isAfter(start, 'hour'); // true
219
+ start.isSame(end, 'day'); // false
220
+ start.isSameOrBefore(end); // true
221
+ start.isSameOrAfter(end); // false
222
+ start.isBetween(
223
+ DateTz.parse('2025-06-09 00:00', 'YYYY-MM-DD HH:mm', 'Europe/Rome'),
224
+ DateTz.parse('2025-06-15 23:59', 'YYYY-MM-DD HH:mm', 'Europe/Rome'),
225
+ 'day',
226
+ '[]'
227
+ ); // true, inclusive
228
+ ```
229
+
230
+ ### Interop Helpers
231
+
232
+ Bridge to native JavaScript types or serialisation formats without losing your timezone context.
233
+
234
+ ```ts
235
+ const report = DateTz.parse('2025-11-05 17:45', 'YYYY-MM-DD HH:mm', 'Europe/Paris');
236
+
237
+ report.toJSDate(); // Native Date
238
+ report.toISO(); // "2025-11-05T16:45:00.000Z"
239
+ report.toISOString();// Same value, alias for toISO()
240
+ report.toUnix(); // 1751733900
241
+ report.valueOf(); // 1751733900000
242
+ ```
243
+
194
244
  ---
195
245
 
196
246
  ## Daylight Saving Time Deep Dive
@@ -0,0 +1,71 @@
1
+ import { DateTzDiffUnit, DateTzDurationLike, DateTzGranularity, DateTzInclusivity, IDateTz } from "./idate-tz";
2
+ export declare class DateTz implements IDateTz {
3
+ timestamp: number;
4
+ timezone: string;
5
+ private offsetCache?;
6
+ static defaultFormat: string;
7
+ constructor(value: IDateTz);
8
+ constructor(value: number, tz?: string);
9
+ get timezoneOffset(): import("./timezones").TimezoneOffset;
10
+ compare(other: IDateTz): number;
11
+ isComparable(other: IDateTz): boolean;
12
+ toString(): string;
13
+ toString(pattern: string): string;
14
+ add(value: number, unit: 'minute' | 'hour' | 'day' | 'month' | 'year'): this;
15
+ subtract(value: number, unit: 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year'): this;
16
+ plus(duration?: DateTzDurationLike): this;
17
+ minus(duration?: DateTzDurationLike): this;
18
+ diff(other: IDateTz, unit?: DateTzDiffUnit, asFloat?: boolean): number;
19
+ startOf(unit: DateTzGranularity): this;
20
+ endOf(unit: DateTzGranularity): this;
21
+ clone(): DateTz;
22
+ toJSDate(): Date;
23
+ toISOString(): string;
24
+ toISO(): string;
25
+ toUnix(): number;
26
+ valueOf(): number;
27
+ isBefore(other: IDateTz, unit?: DateTzDiffUnit): boolean;
28
+ isAfter(other: IDateTz, unit?: DateTzDiffUnit): boolean;
29
+ isSame(other: IDateTz, unit?: DateTzDiffUnit): boolean;
30
+ isSameOrBefore(other: IDateTz, unit?: DateTzDiffUnit): boolean;
31
+ isSameOrAfter(other: IDateTz, unit?: DateTzDiffUnit): boolean;
32
+ isBetween(start: IDateTz, end: IDateTz, unit?: DateTzDiffUnit, inclusivity?: DateTzInclusivity): boolean;
33
+ private _year;
34
+ private _month;
35
+ private _day;
36
+ private _hour;
37
+ private _minute;
38
+ private _dayOfWeek;
39
+ convertToTimezone(tz: string): this;
40
+ cloneToTimezone(tz: string): DateTz;
41
+ private stripSMs;
42
+ private invalidateOffsetCache;
43
+ private getOffsetSeconds;
44
+ private getOffsetInfo;
45
+ private computeOffsetInfo;
46
+ private getIntlOffsetSeconds;
47
+ private getLocalParts;
48
+ private setLocalComponents;
49
+ private toDateInstance;
50
+ private ensureComparable;
51
+ private normalizeDiffUnit;
52
+ private normalizeGranularity;
53
+ private diffInMonths;
54
+ private roundDiff;
55
+ private compareWithUnitDate;
56
+ private compareWithUnit;
57
+ private shift;
58
+ private shiftCalendar;
59
+ set(value: number, unit: 'year' | 'month' | 'day' | 'hour' | 'minute'): this;
60
+ private isLeapYear;
61
+ private daysInYear;
62
+ static parse(dateString: string, pattern?: string, tz?: string): DateTz;
63
+ static now(tz?: string): DateTz;
64
+ get isDst(): boolean;
65
+ get year(): number;
66
+ get month(): number;
67
+ get day(): number;
68
+ get hour(): number;
69
+ get minute(): number;
70
+ get dayOfWeek(): number;
71
+ }