mmntjs-timezone 0.0.1
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 +138 -0
- package/dist/1970-2030.cjs +761 -0
- package/dist/1970-2030.cjs.map +1 -0
- package/dist/1970-2030.d.cts +7 -0
- package/dist/1970-2030.d.ts +7 -0
- package/dist/1970-2030.js +726 -0
- package/dist/1970-2030.js.map +1 -0
- package/dist/index.cjs +790 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +42 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +755 -0
- package/dist/index.js.map +1 -0
- package/dist/install-core-C8jxZ6mt.d.cts +98 -0
- package/dist/install-core-C8jxZ6mt.d.ts +98 -0
- package/dist/logic.cjs +958 -0
- package/dist/logic.cjs.map +1 -0
- package/dist/logic.d.cts +102 -0
- package/dist/logic.d.ts +102 -0
- package/dist/logic.js +923 -0
- package/dist/logic.js.map +1 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# mmntjs-timezone
|
|
2
|
+
|
|
3
|
+
Drop-in replacement for `moment-timezone`.
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
mmntjs-timezone is moving toward a compatibility-first architecture:
|
|
8
|
+
|
|
9
|
+
- Moment Timezone compatible packed-data APIs remain the public boundary
|
|
10
|
+
- runtime-loaded zones/links/countries are stored in an internal registry
|
|
11
|
+
- built-in IANA zone fallback currently still uses `Intl` until bundled authoritative data lands
|
|
12
|
+
- the long-term target is full tzdata-backed compatibility with lazy decode and compact storage
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
moment-timezone mmntjs-timezone
|
|
16
|
+
│ │
|
|
17
|
+
├─ packed tzdb ── Intl.DateTimeFormat
|
|
18
|
+
├─ add/link data ── Intl.supportedValuesOf
|
|
19
|
+
├─ zone().abbr() ── timeZoneName: "short"
|
|
20
|
+
└─ zone().offset() ── formatToParts → UTC comparison
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Behavioral Compatibility
|
|
24
|
+
|
|
25
|
+
| Feature | Status | Notes |
|
|
26
|
+
|---------|--------|-------|
|
|
27
|
+
| `moment.tz(input, zone)` | ✅ | Parse wall-clock in zone |
|
|
28
|
+
| `moment.tz(input, format, zone)` | ✅ | Parse with format in zone |
|
|
29
|
+
| `moment.tz(input, format, strict, zone)` | ✅ | Strict format dispatch |
|
|
30
|
+
| `moment(ts).tz(zone)` | ✅ | Convert instant to zone |
|
|
31
|
+
| `moment.utc(ts).tz(zone)` | ✅ | Convert UTC to zone |
|
|
32
|
+
| `moment.parseZone(s).tz(zone)` | ✅ | Convert parsed offset to zone |
|
|
33
|
+
| `moment.tz().format("z")` | ✅ | Timezone abbreviation |
|
|
34
|
+
| `moment.tz().format("Z")` | ✅ | Offset display |
|
|
35
|
+
| `moment.tz().utcOffset()` | ✅ | Numeric offset |
|
|
36
|
+
| `moment.tz().zoneAbbr()` | ✅ | Abbreviation API |
|
|
37
|
+
| `moment.tz().zoneName()` | ✅ | Long zone name API |
|
|
38
|
+
| `moment.tz.zone(name)` | ✅ | Zone object API |
|
|
39
|
+
| `moment.tz.names()` | ✅ | List all zone names |
|
|
40
|
+
| `moment.tz.guess()` | ✅ | Runtime timezone detection |
|
|
41
|
+
| `moment.tz.setDefault(z)` | ⚠️ Partial | Stores zone name; apply requires core changes |
|
|
42
|
+
| DST spring-forward | ✅ | Adjusted forward by 1h |
|
|
43
|
+
| DST fall-back | ✅ | First-occurrence (DST side) |
|
|
44
|
+
| `moment.tz(input, zone).valueOf()` | ✅ | Matches moment-timezone |
|
|
45
|
+
| `zone.abbr(ts)` | ✅ | Matches moment-timezone |
|
|
46
|
+
| `zone.offset(ts)` | ✅ | Matches moment-timezone |
|
|
47
|
+
| `zone.utcOffset(ts)` | ✅ | Matches moment-timezone |
|
|
48
|
+
|
|
49
|
+
### Oracle verification
|
|
50
|
+
|
|
51
|
+
All behavioral tests compare mmntjs-timezone output against moment-timezone.
|
|
52
|
+
Hand-written expected strings are NOT used for timezone-specific values.
|
|
53
|
+
|
|
54
|
+
### Deterministic
|
|
55
|
+
|
|
56
|
+
- Fixed random seed for property tests
|
|
57
|
+
- Cached `Intl.DateTimeFormat` per timezone
|
|
58
|
+
- Offset cache uses `Math.floor(timestamp / 1000)` — deterministic per-second
|
|
59
|
+
- All tests pass across 6 timezone environments (UTC, America/New_York, Europe/Berlin, Asia/Tokyo, Australia/Sydney, America/Los_Angeles)
|
|
60
|
+
|
|
61
|
+
## Current Status
|
|
62
|
+
|
|
63
|
+
The package now exposes the core packed-data compatibility APIs and preloads bundled authoritative tzdata generated from `moment-timezone` at build time:
|
|
64
|
+
|
|
65
|
+
- `moment.tz.add(data)`
|
|
66
|
+
- `moment.tz.link(links)`
|
|
67
|
+
- `moment.tz.load(bundle)`
|
|
68
|
+
- `moment.tz.unpack(data)`
|
|
69
|
+
- `moment.tz.unpackBase60(input)`
|
|
70
|
+
- `moment.tz.countries()`
|
|
71
|
+
- `moment.tz.zonesForCountry(code)`
|
|
72
|
+
|
|
73
|
+
Current limitation:
|
|
74
|
+
|
|
75
|
+
- internal storage still uses unpacked JS arrays/objects rather than the planned compact typed-array / lazy-decode representation
|
|
76
|
+
|
|
77
|
+
### Intl-backed abbreviation limitations
|
|
78
|
+
|
|
79
|
+
`zone.abbr()` uses `Intl.DateTimeFormat` with `timeZoneName: "short"`. This differs from moment-timezone's packed tzdb:
|
|
80
|
+
|
|
81
|
+
- **Historical abbreviations**: Intl may not know pre-1970 abbreviations (e.g., "BST" for pre-WW2 London). The oracle tests in `zone-object.test.ts` verify this for epoch=0.
|
|
82
|
+
- **Generic vs standard/daylight**: Some zones return generic "CT" or "MT" instead of "CST"/"CDT" or "EST"/"EDT". `tryLocaleAbbr()` filters out non-standard results via heuristic (2-5 uppercase letters, no "GMT" prefix).
|
|
83
|
+
- **Known overrides**: Zones like `Asia/Taipei`, `Africa/Cairo` use `KNOWN_ABBR` table (line 217) where Intl doesn't provide a short name.
|
|
84
|
+
- **Fallback**: When Intl fails to produce a short name, the abbreviation is synthesized as `GMT{±HHMM}` (e.g., "GMT+0530" for Asia/Kolkata).
|
|
85
|
+
- **Locale probing**: Multiple locales (`en-US`, `ja-JP`, `zh-CN`, etc.) are tried since different locales sometimes yield a short name where others produce a long name. The winning locale is cached per zone.
|
|
86
|
+
|
|
87
|
+
Abbreviation behavior is oracle-tested against moment-timezone in `regression.test.ts` (including DST transitions and cache ordering).
|
|
88
|
+
|
|
89
|
+
- **Default timezone**: `moment.tz.setDefault()` stores the zone name. Full integration (making `moment()` respect the default) requires changes to the mmntjs core. The current behavior is:
|
|
90
|
+
- `moment.defaultZone` is set
|
|
91
|
+
- `moment.tz(explicit, zone)` still uses the explicit zone
|
|
92
|
+
- `moment.utc()` is unaffected
|
|
93
|
+
- `moment()` does NOT automatically create in the default zone
|
|
94
|
+
|
|
95
|
+
## Testing
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# Run all tests
|
|
99
|
+
bun test
|
|
100
|
+
|
|
101
|
+
# Run across 6 timezones
|
|
102
|
+
bash ../../scripts/run-timezone-tests.sh
|
|
103
|
+
|
|
104
|
+
# Run property tests
|
|
105
|
+
bun test test/property.test.ts
|
|
106
|
+
bun test test/properties-intensive.test.ts
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## License
|
|
110
|
+
|
|
111
|
+
MIT
|
|
112
|
+
|
|
113
|
+
## Bundle Size
|
|
114
|
+
|
|
115
|
+
Comparison with original `moment-timezone` (all minified, `bunx tsup --minify`).
|
|
116
|
+
|
|
117
|
+
### Full data (all historical zones)
|
|
118
|
+
|
|
119
|
+
| | raw | gzip |
|
|
120
|
+
|---|---|---|
|
|
121
|
+
| `moment-timezone-with-data.min.js` | 710KB | 38KB |
|
|
122
|
+
| `mmntjs-timezone/index.js` (ESM) | **296KB** (−58%) | **35KB** (−8%) |
|
|
123
|
+
|
|
124
|
+
### 1970–2030 subset
|
|
125
|
+
|
|
126
|
+
| | raw | gzip |
|
|
127
|
+
|---|---|---|
|
|
128
|
+
| `moment-timezone-with-data-1970-2030.min.js` | 131KB | 20KB |
|
|
129
|
+
| `mmntjs-timezone/1970-2030.js` (ESM) | **78KB** (−40%) | **22KB** (+10%) |
|
|
130
|
+
|
|
131
|
+
### Logic only (no tzdata, load-your-own)
|
|
132
|
+
|
|
133
|
+
| | raw | gzip |
|
|
134
|
+
|---|---|---|
|
|
135
|
+
| `moment-timezone.min.js` | 7KB | 3KB |
|
|
136
|
+
| `mmntjs-timezone/logic.js` (ESM) | **12KB** | **4.7KB** |
|
|
137
|
+
|
|
138
|
+
Both full and 1970–2030 use the same `!D|` delta-encoded binary blob format generated from the upstream `moment-timezone` npm package at build time. The 1970–2030 gzip is 2KB larger because the filter is mechanical (timestamp window) rather than hand-picked like the original.
|