timezone-soft 1.5.2 → 1.6.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 spencer kelly
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,160 +1,193 @@
1
1
  <div align="center">
2
-
3
- <div>parse abbreviated, sloppy, and informal timezone names</div>
4
- <div><img src="https://cloud.githubusercontent.com/assets/399657/23590290/ede73772-01aa-11e7-8915-181ef21027bc.png" /></div>
5
-
6
- <div align="center">
7
- <a href="https://npmjs.org/package/timezone-soft">
8
- <img src="https://img.shields.io/npm/v/timezone-soft.svg?style=flat-square" />
9
- </a>
10
- <!-- <a href="https://codecov.io/gh/spencermountain/timezone-soft">
11
- <img src="https://codecov.io/gh/spencermountain/timezone-soft/branch/master/graph/badge.svg" />
12
- </a> -->
13
- <a href="https://unpkg.com/timezone-soft/builds/timezone-soft.min.js">
14
- <img src="https://badge-size.herokuapp.com/spencermountain/timezone-soft/master/builds/timezone-soft.min.js" />
15
- </a>
16
- </div>
17
- <div align="center">
18
- <code>npm install timezone-soft</code>
19
- </div>
20
- <sub>
21
- by
22
- <a href="https://spencermountain.github.io/">Spencer Kelly</a>
23
- </sub>
24
- <div align="center">
25
- <sup><i>(formerly called 'spacetime-informal')</i></sup>
26
- </div>
2
+ <img src="https://cloud.githubusercontent.com/assets/399657/23590290/ede73772-01aa-11e7-8915-181ef21027bc.png" />
3
+ <div>informal timezone lookup</div>
4
+ <a href="https://npmjs.org/package/timezone-soft">
5
+ <img src="https://img.shields.io/npm/v/timezone-soft.svg?style=flat-square" />
6
+ </a>
7
+ <a href="https://bundlephobia.com/result?p=timezone-soft@latest">
8
+ <img src="https://badgen.net/bundlejs/min/timezone-soft" />
9
+ </a>
10
+ <div><code>npm install timezone-soft</code></div>
27
11
  </div>
28
- <p></p>
29
12
 
30
13
  <!-- spacer -->
31
- <img height="25px" src="https://user-images.githubusercontent.com/399657/68221862-17ceb980-ffb8-11e9-87d4-7b30b6488f16.png"/>
14
+ <img height="50px" src="https://user-images.githubusercontent.com/399657/68221862-17ceb980-ffb8-11e9-87d4-7b30b6488f16.png"/>
32
15
 
33
16
  ```js
34
- import soft from 'timezone-soft'
35
-
36
- // get an IANA tz from user input
37
- let timezones = soft('milwaukee')[0]
38
- /*[{
39
- iana: 'America/Chicago',
40
- standard: { name: 'Central Standard Time', abbrev: 'CST' },
41
- daylight: { name: 'Central Daylight Time', abbrev: 'CDT' }
42
- }
43
- ]*/
17
+ import tzSoft from 'timezone-soft'
18
+
19
+ const matches = tzSoft('milwaukee')
20
+ matches[0].iana // 'America/Chicago'
44
21
  ```
45
22
 
23
+ People are not often aware of timezone [IANA IDs](https://www.iana.org/time-zones), and tend to use informal schemes to refer to timezones - things like `'PST'`, `'eastern time'`, `'vancouver bc'`, and `'china'`.
24
+
25
+ These names have cultural overlap, and their meaning can depend on the date.
26
+
27
+ This library applies opinionated heuristics to help turn this user-input into ranked matching IANA candidates.
28
+
29
+ Originally built for [spacetime](https://github.com/spencermountain/spacetime),
30
+ and formerly called `timezone-soft-informal`. This is a compressed dictionary of lookup terms for timezone ids, and some basic ranking heuristics when >1 results.
31
+
46
32
  <!-- spacer -->
47
33
  <img height="25px" src="https://user-images.githubusercontent.com/399657/68221862-17ceb980-ffb8-11e9-87d4-7b30b6488f16.png"/>
48
34
 
49
- **[IANA timezone codes](https://www.iana.org/time-zones)** are the official reference for timezone information, and is what you should use, whenever possible.
50
-
51
- Humans though, _are goofballs_, and use a whole different informal scheme:
35
+ <div align="center">
36
+ <img src="https://cloud.githubusercontent.com/assets/399657/23590290/ede73772-01aa-11e7-8915-181ef21027bc.png" />
37
+ </div>
52
38
 
53
- ---
39
+ ### Usage
40
+ ```js
41
+ const tzSoft = require('timezone-soft') //commonjs supported
54
42
 
55
- - In (North) America: **PST, MST, EST**...
56
- - in Europe (lately): **WEST, CEST, EEST**...
57
- - in Africa: **EAT, CAT, WAST**...
58
- - in Australia: **AWST, AEDT, ACST**...
43
+ tzSoft('EST')[0].iana // 'America/New_York'
44
+ tzSoft('central')[0].iana // 'America/Chicago'
45
+ tzSoft('venezuela')[0].iana // 'America/Caracas'
46
+ tzSoft('south east asia')[0].iana // 'Asia/Bangkok'
47
+ ```
59
48
 
60
- ---
49
+ `tzSoft(input: string)`
61
50
 
62
- #### these line-up with the IANA codes sometimes.
51
+ This returns an array of matching timezone objects, ordered by preference. An empty or
52
+ unrecognized string returns `[]`
63
53
 
64
- #### ...other times they don't.
54
+ A match looks like this:
55
+ ```js
56
+ {
57
+ name: 'Central Time',
58
+ iana: 'America/Chicago',
59
+ standard: {
60
+ name: 'Central Standard Time',
61
+ abbr: 'CST',
62
+ offset: -6
63
+ },
64
+ daylight: {
65
+ name: 'Central Daylight Time',
66
+ abbr: 'CDT',
67
+ offset: -5,
68
+ start: '2nd-sun-mar-2h',
69
+ end: '1st-sun-nov-2h'
70
+ },
71
+ long: '(UTC-06:00) Central Time (US & Canada)'
72
+ }
73
+ ```
65
74
 
66
- <!-- spacer -->
67
- <img height="15px" src="https://user-images.githubusercontent.com/399657/68221862-17ceb980-ffb8-11e9-87d4-7b30b6488f16.png"/>
75
+ Offsets are hours east of UTC; negative values are west of UTC. `daylight` can be
76
+ `null`.
68
77
 
69
- These names also collide -
78
+ `start` and `end` values are descriptive rule strings.
70
79
 
71
- '**_IST_**' is used to mean:
72
80
 
73
- - '_Indian Stardard Time_'
74
- - '_Irish Stardard Time_'
75
- - '_Israeli Stardard Time_'
81
+ ## Ambiguous inputs
76
82
 
77
- These names also produce all-sorts of ambiguities, regarding DST-changes-
83
+ Abbreviations can describe several places. For example:
78
84
 
79
- Both Winnipeg and Mexico City are **CST**, but have a much different DST schedule:
80
- ![image](https://user-images.githubusercontent.com/399657/52489224-b34d0e00-2b8f-11e9-9de8-0688bec52464.png)
85
+ ```js
86
+ soft('IST').map(zone => zone.iana)
87
+ // ['Asia/Kolkata', 'Europe/Dublin', 'Asia/Jerusalem', 'Asia/Colombo']
88
+ ```
81
89
 
82
- _(thanks [timeanddate.com](https://www.timeanddate.com)!)_
90
+ Explicit IANA IDs containing `/` are resolved case-insensitively through the pinned
91
+ IANA **2026d** Zone/Link table before informal matching. Unknown IDs are not guessed
92
+ from their city component. A recognized ID without bundled display metadata returns
93
+ `[]`. Curated non-IANA phrases containing `/` can still match registered aliases.
83
94
 
84
- -of course, there's a bunch of political/historical/disputed stuff going on, too. Apologies if this library steps into that unknowingly.
95
+ All returned IDs use that table's canonical targets. For example, `Europe/Kiev`
96
+ returns `Europe/Kyiv`, `Asia/Kashgar` returns `Asia/Urumqi` (UTC+6, distinct from
97
+ Shanghai's UTC+8), and `America/Yellowknife` returns `America/Edmonton`.
98
+ This policy uses the main IANA files plus `backward`, not the optional `backzone`
99
+ historical split. Ordinary abbreviations such as `EST` remain informal queries.
85
100
 
86
- <img height="15px" src="https://user-images.githubusercontent.com/399657/68221862-17ceb980-ffb8-11e9-87d4-7b30b6488f16.png"/>
101
+ Alias matches are sorted by
102
+ the number of packed aliases associated with each zone, descending. Ties preserve
103
+ insertion order in the source data. Canonicalization then merges duplicate targets
104
+ while preserving their first occurrence. This is a heuristic, not a population ranking
105
+ or a confidence score; adding aliases can change the preferred result.
87
106
 
88
- ...so that's what we're trying to fix - to _'soften'_ this exchange, between human and IANA timezone nomenclature, using some _opinionated-but-common-sense_ rules and decision-making.
107
+ Show all candidates when ambiguity matters, or ask for a city or IANA ID. The
108
+ library does not use the user's location to choose a result. Regression fixtures
109
+ cover the ordering of `CST`, `IST`, and `BST`.
89
110
 
90
- It was originally built for use in the _[spacetime timezone library](https://github.com/spencermountain/spacetime)_.
111
+ ## UTC and GMT offsets
91
112
 
92
- <!-- spacer -->
93
- <img height="25px" src="https://user-images.githubusercontent.com/399657/68221862-17ceb980-ffb8-11e9-87d4-7b30b6488f16.png"/>
113
+ `UTC` (including lowercase or surrounding whitespace) resolves only to `Etc/UTC`,
114
+ with abbreviation `UTC` and name `Coordinated Universal Time`. The aliases `UCT`,
115
+ `universal`, `zulu`, and `coordinated universal time` resolve to the same record.
116
+ `GMT` resolves to `Etc/GMT`. Geographic aliases cannot outrank these inputs.
94
117
 
95
- ### Usage
118
+ Whole-hour offsets from UTC-12 through UTC+14 are supported:
96
119
 
97
120
  ```js
98
- const soft = require('timezone-soft')
121
+ soft('UTC+0')[0].iana // 'Etc/GMT'
122
+ soft('UTC+14')[0].iana // 'Etc/GMT-14'
123
+ soft('-5h')[0].iana // 'Etc/GMT+5'
124
+ ```
99
125
 
100
- soft('EST')
101
- // 'America/New_York'
126
+ Surrounding whitespace is accepted for offset inputs. `UTC-5` means five hours
127
+ behind UTC. For compatibility, `GMT+5` follows the reversed IANA `Etc/GMT+5`
128
+ convention; its numeric offset and `long` description use the normal UTC sign.
129
+ `Etc/GMT+13` and `Etc/GMT+14` return `[]` because they are not IANA IDs;
130
+ `Etc/GMT-13` and `Etc/GMT-14` remain valid.
102
131
 
103
- soft('central')
104
- // 'America/Chicago'
132
+ Fractional offset strings such as `UTC+5:30` return `[]`: the IANA fixed-offset
133
+ `Etc/GMT` IDs have whole-hour precision. Use a named zone such as `Asia/Kolkata` or
134
+ `india` instead. See the [IANA definitions](https://data.iana.org/time-zones/tzdb/etcetera).
105
135
 
106
- soft('venezuela')
107
- // 'America/Caracas'
136
+ ## Dates and daylight saving time
108
137
 
109
- soft('south east asia')
110
- // 'Asia/Bangkok'
111
- ```
138
+ This package finds timezone names and supplies curated display metadata. Its
139
+ bundled DST rules are approximate, are not versioned by year, and are not suitable
140
+ for calculating historical or future transitions. See the
141
+ [data notes](data/README.md) for the rule syntax and provenance limitations.
112
142
 
113
- Typescript/Deno/Webpack:
143
+ Use a date-aware timezone library to determine the applicable abbreviation at a
144
+ specific instant. For example, with [spacetime](https://github.com/spencermountain/timezone-soft):
114
145
 
115
146
  ```js
116
- import soft from 'timezone-soft'
147
+ import spacetime from 'spacetime'
148
+ import tzSoft from 'timezone-soft'
149
+
150
+ const display = tzSoft('montreal')[0]
151
+ if (display) {
152
+ const now = spacetime.now(display.iana)
153
+ const info = now.isDST() && display.daylight ? display.daylight : display.standard
154
+ console.log(now.time() + ' ' + info.abbr)
155
+ }
117
156
  ```
118
157
 
119
- it was built to be as forgiving as possible, and return the most common-sense IANA timezone id from user-input.
120
-
121
- <div align="center">
122
- <img height="50px" src="https://user-images.githubusercontent.com/399657/68221814-05ed1680-ffb8-11e9-8b6b-c7528d163871.png"/>
123
- </div>
124
-
125
- ---
158
+ The `standard` and `daylight` fields are conventional display categories, not
159
+ IANA's `tm_isdst` flags. In Dublin, `standard` means winter GMT (UTC+0, Greenwich
160
+ Mean Time), and `daylight` means summer IST (UTC+1, Irish Standard Time). IANA's
161
+ native model treats Irish summer as standard and winter as negative DST; this API
162
+ retains its existing winter/summer arrangement for compatibility. Do not select a
163
+ field using a raw IANA DST flag without reconciling those conventions.
126
164
 
127
- <!-- spacer -->
128
- <img height="25px" src="https://user-images.githubusercontent.com/399657/68221862-17ceb980-ffb8-11e9-87d4-7b30b6488f16.png"/>
165
+ The identifier table is versioned independently of display metadata. The returned
166
+ metadata is only as current as this package's curated data. Coverage includes
167
+ `America/Ciudad_Juarez` (Mountain time with US DST rules) and
168
+ `America/Coyhaique` (permanent UTC−3). Runtime coverage checks flag new missing
169
+ records.
129
170
 
130
- ### DST
131
171
 
132
- Often, the proper timezone name will depend on which date you are referencing.
133
- You can reckon this pretty-easily with [spacetime](https://github.com/spencermountain/spacetime), like this:
172
+ ## TypeScript
134
173
 
135
- ```js
136
- const spacetime = require('spacetime')
137
- const soft = require('timezone-soft')
174
+ ```ts
175
+ import tzSoft, { type DisplayFormat } from 'timezone-soft'
138
176
 
139
- let display = soft('montreal')[0]
140
- let show = display.standard.abbrev
177
+ const matches: DisplayFormat[] = tzSoft('montreal')
178
+ const zone = matches[0]
141
179
 
142
- // are we in standard time, or daylight time?
143
- let s = spacetime.now(display.iana)
144
- if (display.daylight && s.isDST()) {
145
- show = display.daylight.abbrev
180
+ if (zone) {
181
+ console.log(zone.iana) // 'America/Toronto'
182
+ console.log(zone.standard.abbr) // 'EST'
183
+ console.log(zone.daylight?.abbr) // 'EDT'; undefined for zones without DST
184
+ } else {
185
+ console.log('No matching timezone')
146
186
  }
147
- console.log(s.time() + ' ' + show)
148
- // '4:20pm EDT'
149
187
  ```
150
188
 
151
- <!-- spacer -->
152
- <img height="25px" src="https://user-images.githubusercontent.com/399657/68221862-17ceb980-ffb8-11e9-87d4-7b30b6488f16.png"/>
153
-
154
- work-in-progress!
155
-
156
189
  ### See also
157
190
 
158
- - [TimeZoneNames](https://github.com/mattjohnsonpint/TimeZoneNames) .NET Standard Library by Matt Johnson-Pint
191
+ - [TimeZoneNames](https://github.com/mattjohnsonpint/TimeZoneNames) for .NET.
159
192
 
160
- MIT
193
+ MIT, PRs welcome