@lbd-sh/date-tz 1.0.1 → 1.0.2
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 +137 -88
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,147 +1,196 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Date TZ
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Powerful timezone-aware date utilities for JavaScript and TypeScript. `DateTz` keeps timestamps in sync with IANA timezones, handles daylight-saving changes transparently, and exposes a tiny, dependency-free API that stays close to the platform `Date` object while remaining predictable.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Features
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- Full TypeScript support with `DateTz`, `IDateTz`, and the bundled `timezones` catalog.
|
|
8
|
+
- Minute-precision timestamps normalised to UTC while exposing friendly getters (`year`, `month`, `day`, ...).
|
|
9
|
+
- Formatting and parsing with familiar tokens (`YYYY`, `MM`, `DD`, `HH`, `hh`, `aa`, `tz`, and more).
|
|
10
|
+
- Arithmetic helpers (`add`, `set`) that respect leap years, month lengths, and DST.
|
|
11
|
+
- Instant timezone conversion with `convertToTimezone` and `cloneToTimezone`, backed by automatic DST detection (`Intl.DateTimeFormat`).
|
|
12
|
+
- Works in Node.js and modern browsers without polyfills.
|
|
8
13
|
|
|
9
|
-
##
|
|
14
|
+
## Installation
|
|
10
15
|
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
|
|
16
|
+
```bash
|
|
17
|
+
npm install @lbd-sh/date-tz
|
|
18
|
+
# or
|
|
19
|
+
yarn add @lbd-sh/date-tz
|
|
14
20
|
```
|
|
15
21
|
|
|
16
|
-
|
|
17
|
-
- Throws an error if the provided timezone is invalid.
|
|
22
|
+
## Quick Start
|
|
18
23
|
|
|
19
|
-
|
|
24
|
+
```ts
|
|
25
|
+
import { DateTz } from '@lbd-sh/date-tz';
|
|
20
26
|
|
|
21
|
-
|
|
27
|
+
// Create a Rome-based date for 2025-03-01 09:15
|
|
28
|
+
const meeting = new DateTz(Date.UTC(2025, 2, 1, 8, 15), 'Europe/Rome');
|
|
22
29
|
|
|
23
|
-
|
|
30
|
+
meeting.toString(); // "2025-03-01 09:15:00"
|
|
31
|
+
meeting.toString('DD MMM YYYY HH:mm'); // "01 Mar 2025 09:15"
|
|
24
32
|
|
|
25
|
-
|
|
26
|
-
|
|
33
|
+
// Move the meeting forward and switch to New York time
|
|
34
|
+
meeting.add(1, 'day').add(2, 'hour');
|
|
35
|
+
const nyc = meeting.cloneToTimezone('America/New_York');
|
|
27
36
|
|
|
28
|
-
|
|
37
|
+
nyc.toString('YYYY-MM-DD HH:mm tz'); // "2025-03-02 05:15 America/New_York"
|
|
38
|
+
nyc.isDst; // true or false depending on the date
|
|
39
|
+
```
|
|
29
40
|
|
|
30
|
-
|
|
41
|
+
## Usage
|
|
31
42
|
|
|
32
|
-
|
|
43
|
+
### Creating Dates
|
|
33
44
|
|
|
34
|
-
|
|
45
|
+
```ts
|
|
46
|
+
import { DateTz, IDateTz } from '@lbd-sh/date-tz';
|
|
35
47
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
- `day: number` — Returns the day of the month (1–31).
|
|
40
|
-
- `hour: number` — Returns the hour (0–23).
|
|
41
|
-
- `minute: number` — Returns the minute (0–59).
|
|
42
|
-
- `dayOfWeek: number` — Returns the day of the week (0–6).
|
|
48
|
+
new DateTz(Date.now(), 'UTC');
|
|
49
|
+
new DateTz(1719753300000, 'Europe/Rome');
|
|
50
|
+
new DateTz({ timestamp: Date.now(), timezone: 'Asia/Tokyo' } satisfies IDateTz);
|
|
43
51
|
|
|
44
|
-
|
|
52
|
+
DateTz.now('America/Los_Angeles');
|
|
53
|
+
```
|
|
45
54
|
|
|
46
|
-
|
|
55
|
+
Notes:
|
|
47
56
|
|
|
48
|
-
|
|
57
|
+
- Timestamps are stored in milliseconds since the Unix epoch and are truncated to the nearest minute (`seconds` and `milliseconds` are dropped) for deterministic arithmetic.
|
|
58
|
+
- Timezone identifiers must exist in the bundled `timezones` map; an error is thrown otherwise.
|
|
49
59
|
|
|
50
|
-
|
|
60
|
+
### Formatting
|
|
51
61
|
|
|
52
|
-
|
|
62
|
+
`DateTz.toString` accepts an optional pattern and locale. Unspecified tokens fall back to the default `DateTz.defaultFormat` (`YYYY-MM-DD HH:mm:ss`).
|
|
53
63
|
|
|
54
|
-
|
|
64
|
+
```ts
|
|
65
|
+
const invoice = new DateTz(Date.UTC(2025, 5, 12, 12, 0), 'Europe/Paris');
|
|
55
66
|
|
|
56
|
-
|
|
67
|
+
invoice.toString(); // "2025-06-12 14:00:00"
|
|
68
|
+
invoice.toString('DD/MM/YYYY HH:mm tz'); // "12/06/2025 14:00 Europe/Paris"
|
|
69
|
+
invoice.toString('LM DD, YYYY hh:mm aa', 'it'); // "Giugno 12, 2025 02:00 pm"
|
|
70
|
+
```
|
|
57
71
|
|
|
58
|
-
|
|
72
|
+
Available tokens:
|
|
59
73
|
|
|
60
|
-
|
|
74
|
+
| Token | Meaning | Example |
|
|
75
|
+
| ----- | ------- | ------- |
|
|
76
|
+
| `YYYY`, `yyyy` | Full year | `2025` |
|
|
77
|
+
| `YY`, `yy` | Year, two digits | `25` |
|
|
78
|
+
| `MM` | Month (01–12) | `06` |
|
|
79
|
+
| `LM` | Locale month name (capitalised) | `Giugno` |
|
|
80
|
+
| `DD` | Day of month (01–31) | `12` |
|
|
81
|
+
| `HH` | Hours (00–23) | `14` |
|
|
82
|
+
| `hh` | Hours (01–12) | `02` |
|
|
83
|
+
| `mm` | Minutes (00–59) | `00` |
|
|
84
|
+
| `ss` | Seconds (00–59) | `00` |
|
|
85
|
+
| `aa` | `am`/`pm` marker | `pm` |
|
|
86
|
+
| `AA` | `AM`/`PM` marker | `PM` |
|
|
87
|
+
| `tz` | Timezone identifier | `Europe/Paris` |
|
|
61
88
|
|
|
62
|
-
|
|
63
|
-
| ---------- | --------------- |
|
|
64
|
-
| YYYY, yyyy | Full year |
|
|
65
|
-
| YY, yy | Last 2 digits |
|
|
66
|
-
| MM | Month (01–12) |
|
|
67
|
-
| DD | Day (01–31) |
|
|
68
|
-
| HH | Hour (00–23) |
|
|
69
|
-
| hh | Hour (01–12) |
|
|
70
|
-
| mm | Minute (00–59) |
|
|
71
|
-
| ss | Second (00–59) |
|
|
72
|
-
| aa, AA | AM/PM marker |
|
|
73
|
-
| tz | Timezone string |
|
|
89
|
+
### Parsing strings
|
|
74
90
|
|
|
75
|
-
|
|
91
|
+
`DateTz.parse` mirrors `toString`: pass the source string, its pattern, and an optional timezone (default `UTC`).
|
|
76
92
|
|
|
77
|
-
|
|
93
|
+
```ts
|
|
94
|
+
import { DateTz } from '@lbd-sh/date-tz';
|
|
78
95
|
|
|
79
|
-
|
|
96
|
+
const parsed = DateTz.parse(
|
|
97
|
+
'2025-06-12 02:00 PM',
|
|
98
|
+
'YYYY-MM-DD hh:mm AA',
|
|
99
|
+
'America/New_York'
|
|
100
|
+
);
|
|
80
101
|
|
|
81
|
-
|
|
102
|
+
parsed.timestamp; // Milliseconds in UTC (minute precision)
|
|
103
|
+
parsed.timezone; // "America/New_York"
|
|
104
|
+
parsed.isDst; // true/false depending on the moment
|
|
105
|
+
```
|
|
82
106
|
|
|
83
|
-
|
|
107
|
+
> 12-hour patterns require both `hh` and the `AA`/`aa` markers when parsing. If you only need 24-hour formats, prefer `HH`.
|
|
84
108
|
|
|
85
|
-
|
|
109
|
+
### Arithmetic
|
|
86
110
|
|
|
87
|
-
|
|
111
|
+
Mutating helpers operate in-place and normalise overflows:
|
|
88
112
|
|
|
89
|
-
|
|
113
|
+
```ts
|
|
114
|
+
const endOfQuarter = new DateTz(Date.UTC(2025, 2, 31, 23, 0), 'UTC');
|
|
90
115
|
|
|
91
|
-
|
|
116
|
+
endOfQuarter.add(1, 'day'); // 2025-04-01 23:00
|
|
117
|
+
endOfQuarter.set(6, 'month'); // 2025-06-01 23:00
|
|
118
|
+
endOfQuarter.set(2026, 'year'); // 2026-06-01 23:00
|
|
119
|
+
```
|
|
92
120
|
|
|
93
|
-
|
|
121
|
+
All arithmetic respects leap years, month lengths, and daylight-saving changes via the offset cache.
|
|
94
122
|
|
|
95
|
-
###
|
|
123
|
+
### Comparing
|
|
96
124
|
|
|
97
|
-
|
|
125
|
+
```ts
|
|
126
|
+
const rome = DateTz.now('Europe/Rome');
|
|
127
|
+
const madrid = DateTz.now('Europe/Madrid');
|
|
98
128
|
|
|
99
|
-
|
|
129
|
+
rome.isComparable(madrid); // false (different timezones)
|
|
100
130
|
|
|
101
|
-
|
|
131
|
+
const laterInRome = new DateTz(rome.timestamp + 60_000, 'Europe/Rome');
|
|
132
|
+
rome.compare(laterInRome); // negative number
|
|
133
|
+
```
|
|
102
134
|
|
|
103
|
-
|
|
135
|
+
`compare` throws when timezones differ to avoid accidental cross-timezone comparisons. Use `isComparable` first, or convert one date.
|
|
104
136
|
|
|
105
|
-
|
|
137
|
+
### Timezone conversion
|
|
106
138
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
- `daysInYear(year: number): number` — Returns days in a year.
|
|
139
|
+
```ts
|
|
140
|
+
const departure = new DateTz(Date.UTC(2025, 4, 15, 6, 45), 'Europe/Rome');
|
|
110
141
|
|
|
111
|
-
|
|
142
|
+
departure.isDst; // true/false depending on the date
|
|
112
143
|
|
|
113
|
-
|
|
144
|
+
// Modify in place
|
|
145
|
+
departure.convertToTimezone('America/New_York');
|
|
114
146
|
|
|
115
|
-
|
|
116
|
-
const
|
|
117
|
-
|
|
147
|
+
// Clone to keep the original instance
|
|
148
|
+
const arrival = departure.cloneToTimezone('Asia/Tokyo');
|
|
149
|
+
```
|
|
118
150
|
|
|
119
|
-
|
|
120
|
-
console.log(dt.day);
|
|
151
|
+
Timezone changes refresh the internal offset cache and leverage `Intl.DateTimeFormat` when available to detect real-world DST shifts.
|
|
121
152
|
|
|
122
|
-
|
|
123
|
-
|
|
153
|
+
### Accessing components
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
const dt = DateTz.now('UTC');
|
|
157
|
+
|
|
158
|
+
dt.year; // e.g. 2025
|
|
159
|
+
dt.month; // 0-based (0 = January)
|
|
160
|
+
dt.day; // 1-31
|
|
161
|
+
dt.hour; // 0-23
|
|
162
|
+
dt.minute; // 0-59
|
|
163
|
+
dt.dayOfWeek; // 0 (Sunday) to 6 (Saturday)
|
|
164
|
+
dt.timezone; // Timezone id provided at creation
|
|
165
|
+
dt.timezoneOffset; // { sdt, dst } seconds from UTC
|
|
124
166
|
```
|
|
125
167
|
|
|
126
|
-
|
|
168
|
+
## Type Definitions
|
|
169
|
+
|
|
170
|
+
The package ships with comprehensive typings:
|
|
127
171
|
|
|
128
|
-
|
|
172
|
+
- `DateTz` implements `IDateTz`.
|
|
173
|
+
- `IDateTz` describes objects that can seed the constructor.
|
|
174
|
+
- `timezones` is a `Record<string, { sdt: number; dst: number }>` that exposes offsets in seconds.
|
|
129
175
|
|
|
130
|
-
|
|
131
|
-
- Throws if trying to compare across timezones.
|
|
132
|
-
- Throws if parsing a 12-hour format without AM/PM marker.
|
|
176
|
+
Import what you need:
|
|
133
177
|
|
|
134
|
-
|
|
178
|
+
```ts
|
|
179
|
+
import { DateTz, IDateTz, timezones } from '@lbd-sh/date-tz';
|
|
180
|
+
```
|
|
135
181
|
|
|
136
|
-
##
|
|
182
|
+
## Timezone catalogue
|
|
137
183
|
|
|
138
|
-
|
|
139
|
-
- `
|
|
140
|
-
-
|
|
184
|
+
- Contains 500+ IANA identifiers with both standard (`sdt`) and daylight (`dst`) offsets in seconds.
|
|
185
|
+
- When `dst === sdt`, the zone does not observe daylight saving time.
|
|
186
|
+
- You can inspect or extend the map in `timezones.ts` before bundling into your application.
|
|
141
187
|
|
|
142
|
-
|
|
188
|
+
## Publishing & Packaging
|
|
143
189
|
|
|
144
|
-
|
|
190
|
+
- Build with `npm run build` (TypeScript emits to `dist/` with declarations and source maps).
|
|
191
|
+
- `package.json` maps `main` and `types` to the compiled output, so consumers do not need TypeScript.
|
|
192
|
+
- The GitHub Action (`.github/workflows/production.yaml`) compiles, versions, and publishes to npm as `@lbd-sh/date-tz`.
|
|
145
193
|
|
|
146
|
-
|
|
194
|
+
## License
|
|
147
195
|
|
|
196
|
+
ISC © lbd-sh
|