@js-joda/timezone 3.0.0 → 3.1.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 (42) hide show
  1. package/README.md +26 -8
  2. package/dist/js-joda-timezone-10-year-range.esm.js +203 -143
  3. package/dist/js-joda-timezone-10-year-range.esm.js.map +1 -1
  4. package/dist/js-joda-timezone-10-year-range.js +202 -142
  5. package/dist/js-joda-timezone-10-year-range.js.map +1 -1
  6. package/dist/js-joda-timezone-10-year-range.min.js +2 -2
  7. package/dist/js-joda-timezone-300-year-range.esm.js +434 -374
  8. package/dist/js-joda-timezone-300-year-range.esm.js.map +1 -1
  9. package/dist/js-joda-timezone-300-year-range.js +433 -373
  10. package/dist/js-joda-timezone-300-year-range.js.map +1 -1
  11. package/dist/js-joda-timezone-300-year-range.min.js +2 -2
  12. package/dist/js-joda-timezone-4-year-range.esm.js +190 -130
  13. package/dist/js-joda-timezone-4-year-range.esm.js.map +1 -1
  14. package/dist/js-joda-timezone-4-year-range.js +189 -129
  15. package/dist/js-joda-timezone-4-year-range.js.map +1 -1
  16. package/dist/js-joda-timezone-4-year-range.min.js +2 -2
  17. package/dist/js-joda-timezone-60-year-range.esm.js +319 -259
  18. package/dist/js-joda-timezone-60-year-range.esm.js.map +1 -1
  19. package/dist/js-joda-timezone-60-year-range.js +318 -258
  20. package/dist/js-joda-timezone-60-year-range.js.map +1 -1
  21. package/dist/js-joda-timezone-60-year-range.min.js +2 -2
  22. package/dist/js-joda-timezone-empty.esm.js +89 -29
  23. package/dist/js-joda-timezone-empty.esm.js.map +1 -1
  24. package/dist/js-joda-timezone-empty.js +88 -28
  25. package/dist/js-joda-timezone-empty.js.map +1 -1
  26. package/dist/js-joda-timezone-empty.min.js +2 -2
  27. package/dist/js-joda-timezone-lifetime-range.esm.js +430 -370
  28. package/dist/js-joda-timezone-lifetime-range.esm.js.map +1 -1
  29. package/dist/js-joda-timezone-lifetime-range.js +429 -369
  30. package/dist/js-joda-timezone-lifetime-range.js.map +1 -1
  31. package/dist/js-joda-timezone-lifetime-range.min.js +2 -2
  32. package/dist/js-joda-timezone.esm.js +434 -374
  33. package/dist/js-joda-timezone.esm.js.map +1 -1
  34. package/dist/js-joda-timezone.js +433 -373
  35. package/dist/js-joda-timezone.js.map +1 -1
  36. package/dist/js-joda-timezone.min.js +2 -2
  37. package/package.json +3 -3
  38. package/src/{MomentZoneRules.js → TzdbZoneRules.js} +99 -39
  39. package/src/{MomentZoneRulesProvider.js → TzdbZoneRulesProvider.js} +3 -3
  40. package/src/js-joda-timezone.js +2 -2
  41. package/src/plug.js +5 -5
  42. package/src/unpack.js +15 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@js-joda/timezone",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "iana tzdb timezone bindings for js-joda",
5
5
  "typings": "typings/js-joda-timezone.d.ts",
6
6
  "main": "dist/js-joda-timezone.js",
@@ -45,12 +45,12 @@
45
45
  "@js-joda/core": ">=5.7.0"
46
46
  },
47
47
  "devDependencies": {
48
- "@js-joda/core": "^6.2.0"
48
+ "@js-joda/core": "^6.3.0"
49
49
  },
50
50
  "license": "BSD-3-Clause",
51
51
  "bugs": {
52
52
  "url": "https://github.com/js-joda/js-joda/issues"
53
53
  },
54
54
  "homepage": "https://js-joda.github.io/js-joda",
55
- "gitHead": "3b54bb6036347604e8d1ab2eebf5ec1b20033c06"
55
+ "gitHead": "8c0a3b1b555bc09fced243dac13fdf5ef9746dc7"
56
56
  }
@@ -5,10 +5,10 @@
5
5
  */
6
6
 
7
7
  import {
8
- LocalDateTime, Instant, ZoneOffset, ZoneOffsetTransition, ZoneRules
8
+ Duration, LocalDateTime, Instant, ZoneOffset, ZoneOffsetTransition, ZoneRules
9
9
  } from '@js-joda/core';
10
10
 
11
- export class MomentZoneRules extends ZoneRules{
11
+ export class TzdbZoneRules extends ZoneRules{
12
12
  constructor(tzdbInfo){
13
13
  super();
14
14
  this._tzdbInfo = tzdbInfo;
@@ -220,14 +220,25 @@ export class MomentZoneRules extends ZoneRules{
220
220
  * has changed over time.
221
221
  * The standard offset is the offset before any daylight saving time is applied.
222
222
  * This is typically the offset applicable during winter.
223
+ * <p>
224
+ * Needs tz data with standard offsets (field 7 of the packed format); throws for older data.
223
225
  *
224
226
  * @param {Instant} instant - the instant to find the offset information for, not null, but null
225
227
  * may be ignored if the rules have a single offset for all instants
226
228
  * @return {ZoneOffset} the standard offset, not null
229
+ * @throws {Error} if the tz data of the zone has no standard offsets
227
230
  */
228
- // eslint-disable-next-line no-unused-vars
229
231
  standardOffset(instant){
230
- notSupported('ZoneRules.standardOffset');
232
+ return ZoneOffset.ofTotalSeconds(this._standardOffsetInSeconds(instant.toEpochMilli()));
233
+ }
234
+
235
+ _standardOffsetInSeconds(epochMilli){
236
+ const stdOffsets = this._tzdbInfo.stdOffsets;
237
+ if (stdOffsets == null) {
238
+ throw new Error(`The tz data of zone ${this._tzdbInfo.name} has no standard offsets, ` +
239
+ 'load tz data with standard offsets (packed field 7) for standardOffset, daylightSavings and isDaylightSavings');
240
+ }
241
+ return -offsetInSeconds(stdOffsets[binarySearch(this._tzdbInfo.untils, epochMilli)]);
231
242
  }
232
243
 
233
244
  /**
@@ -242,10 +253,12 @@ export class MomentZoneRules extends ZoneRules{
242
253
  * @param {Instant} instant - the instant to find the daylight savings for, not null, but null
243
254
  * may be ignored if the rules have a single offset for all instants
244
255
  * @return {Duration} the difference between the standard and actual offset, not null
256
+ * @throws {Error} if the tz data of the zone has no standard offsets
245
257
  */
246
- // eslint-disable-next-line no-unused-vars
247
258
  daylightSavings(instant){
248
- notSupported('ZoneRules.daylightSavings');
259
+ const epochMilli = instant.toEpochMilli();
260
+ const index = binarySearch(this._tzdbInfo.untils, epochMilli);
261
+ return Duration.ofSeconds(this._offsetByIndexInSeconds(index) - this._standardOffsetInSeconds(epochMilli));
249
262
  }
250
263
 
251
264
  /**
@@ -255,11 +268,11 @@ export class MomentZoneRules extends ZoneRules{
255
268
  *
256
269
  * @param {Instant} instant - the instant to find the offset information for, not null, but null
257
270
  * may be ignored if the rules have a single offset for all instants
258
- * @return {boolean} the standard offset, not null
271
+ * @return {boolean} true if the standard offset differs from the actual offset
272
+ * @throws {Error} if the tz data of the zone has no standard offsets
259
273
  */
260
- // eslint-disable-next-line no-unused-vars
261
274
  isDaylightSavings(instant) {
262
- notSupported('ZoneRules.isDaylightSavings');
275
+ return !this.daylightSavings(instant).isZero();
263
276
  }
264
277
 
265
278
  /**
@@ -289,9 +302,13 @@ export class MomentZoneRules extends ZoneRules{
289
302
  * may be ignored if the rules have a single offset for all instants
290
303
  * @return {ZoneOffsetTransition} the next transition after the specified instant, null if this is after the last transition
291
304
  */
292
- // eslint-disable-next-line no-unused-vars
293
305
  nextTransition(instant){
294
- notSupported('ZoneRules.nextTransition');
306
+ const indices = this._transitionIndices();
307
+ const untils = this._tzdbInfo.untils;
308
+ // transitions are at whole milliseconds, so "after the instant" equals "after its truncated milli"
309
+ const epochMilli = instant.toEpochMilli();
310
+ const k = firstIndexWhere(indices, (i) => untils[i] > epochMilli);
311
+ return k < indices.length ? this._createTransition(indices[k]) : null;
295
312
  }
296
313
 
297
314
  /**
@@ -305,47 +322,75 @@ export class MomentZoneRules extends ZoneRules{
305
322
  * may be ignored if the rules have a single offset for all instants
306
323
  * @return {ZoneOffsetTransition} the previous transition after the specified instant, null if this is before the first transition
307
324
  */
308
- // eslint-disable-next-line no-unused-vars
309
325
  previousTransition(instant){
310
- notSupported('ZoneRules.previousTransition');
326
+ const indices = this._transitionIndices();
327
+ const untils = this._tzdbInfo.untils;
328
+ // round up to the next milli if the instant has a fraction of a milli,
329
+ // so that a transition at the truncated milli counts as before the instant
330
+ const epochMilli = instant.toEpochMilli() + (instant.nano() % 1000000 > 0 ? 1 : 0);
331
+ const k = firstIndexWhere(indices, (i) => untils[i] >= epochMilli);
332
+ return k > 0 ? this._createTransition(indices[k - 1]) : null;
311
333
  }
312
334
 
313
335
  /**
314
- * Gets the complete list of fully defined transitions.
315
- * <p>
316
- * The complete set of transitions for this rules instance is defined by this method
317
- * and {@link #getTransitionRules()}. This method returns those transitions that have
318
- * been fully defined. These are typically historical, but may be in the future.
336
+ * The indices i of the periods whose end, untils[i], is an offset transition, in ascending
337
+ * order. Period boundaries where the offset doesn't change (only the abbreviation or the
338
+ * isdst flag) and the last open-ended period are not transitions. Computed on first use and
339
+ * cached on the zone's tz data, which all rules instances of the zone share.
340
+ *
341
+ * @return {number[]}
342
+ * @private
343
+ */
344
+ _transitionIndices(){
345
+ const tzdbInfo = this._tzdbInfo;
346
+ if (tzdbInfo._transitionIndices == null) {
347
+ const indices = [];
348
+ for (let i = 0; i < tzdbInfo.offsets.length - 1; i++) {
349
+ if (this._offsetByIndexInSeconds(i) !== this._offsetByIndexInSeconds(i + 1)) {
350
+ indices.push(i);
351
+ }
352
+ }
353
+ tzdbInfo._transitionIndices = indices;
354
+ }
355
+ return tzdbInfo._transitionIndices;
356
+ }
357
+
358
+ /**
359
+ * @param {number} index - the index of the period that ends with the transition
360
+ * @return {ZoneOffsetTransition}
361
+ * @private
362
+ */
363
+ _createTransition(index){
364
+ const offsetBefore = ZoneOffset.ofTotalSeconds(this._offsetByIndexInSeconds(index));
365
+ const offsetAfter = ZoneOffset.ofTotalSeconds(this._offsetByIndexInSeconds(index + 1));
366
+ const instant = Instant.ofEpochMilli(this._tzdbInfo.untils[index]);
367
+ return ZoneOffsetTransition.of(LocalDateTime.ofInstant(instant, offsetBefore), offsetBefore, offsetAfter);
368
+ }
369
+
370
+ /**
371
+ * Not supported, always throws.
319
372
  * <p>
320
- * The list will be empty for fixed offset rules and for any time-zone where there has
321
- * only ever been a single offset. The list will also be empty if the transition rules are unknown.
373
+ * In java.time, the complete set of transitions is this list of the historic transitions
374
+ * together with {@link #transitionRules()} for the later years. The tzdb data of this
375
+ * package contains only explicit transitions through the year 2499 and doesn't say where
376
+ * the historic part ends, so it can't return what java.time returns.
377
+ * Use {@link #nextTransition} or {@link #previousTransition} to iterate over the transitions.
322
378
  *
323
- * @return {ZoneOffsetTransition[]} an immutable list of fully defined transitions, not null
379
+ * @throws {Error} always
324
380
  */
325
381
  transitions(){
326
382
  notSupported('ZoneRules.transitions');
327
383
  }
328
384
 
329
385
  /**
330
- * Gets the list of transition rules for years beyond those defined in the transition list.
331
- * <p>
332
- * The complete set of transitions for this rules instance is defined by this method
333
- * and {@link #getTransitions()}. This method returns instances of {@link ZoneOffsetTransitionRule}
334
- * that define an algorithm for when transitions will occur.
386
+ * Not supported, always throws.
335
387
  * <p>
336
- * For any given {@code ZoneRules}, this list contains the transition rules for years
337
- * beyond those years that have been fully defined. These rules typically refer to future
338
- * daylight saving time rule changes.
339
- * <p>
340
- * If the zone defines daylight savings into the future, then the list will normally
341
- * be of size two and hold information about entering and exiting daylight savings.
342
- * If the zone does not have daylight savings, or information about future changes
343
- * is uncertain, then the list will be empty.
344
- * <p>
345
- * The list will be empty for fixed offset rules and for any time-zone where there is no
346
- * daylight saving time. The list will also be empty if the transition rules are unknown.
388
+ * In java.time, this returns the recurring rules (such as "last Sunday in March") for the
389
+ * years after the list of {@link #transitions()}. The tzdb data of this package is expanded
390
+ * into explicit transitions through the year 2499 and has no such rules.
391
+ * Use {@link #nextTransition} or {@link #previousTransition} to iterate over the transitions.
347
392
  *
348
- * @return {ZoneOffsetTransitionRule[]} an immutable list of transition rules, not null
393
+ * @throws {Error} always
349
394
  */
350
395
  transitionRules(){
351
396
  notSupported('ZoneRules.transitionRules');
@@ -360,7 +405,7 @@ export class MomentZoneRules extends ZoneRules{
360
405
  if (this === other) {
361
406
  return true;
362
407
  }
363
- if (other instanceof MomentZoneRules) {
408
+ if (other instanceof TzdbZoneRules) {
364
409
  return this._tzdbInfo === other._tzdbInfo;
365
410
  }
366
411
  return false;
@@ -460,6 +505,21 @@ function binarySearch(array, value) {
460
505
  return hi;
461
506
  }
462
507
 
508
+ // the first index k of a sorted array for which predicate(array[k]) is true, assuming the
509
+ // predicate is false for a prefix and true for the rest; array.length if it is never true
510
+ function firstIndexWhere(array, predicate) {
511
+ let lo = 0, hi = array.length;
512
+ while (lo < hi) {
513
+ const mid = (lo + hi) >> 1;
514
+ if (predicate(array[mid])) {
515
+ hi = mid;
516
+ } else {
517
+ lo = mid + 1;
518
+ }
519
+ }
520
+ return lo;
521
+ }
522
+
463
523
  function notSupported(msg){
464
524
  throw new Error(`not supported: ${msg}`);
465
525
  }
@@ -9,7 +9,7 @@ import {
9
9
  ZoneRulesProvider,
10
10
  } from '@js-joda/core';
11
11
 
12
- import { MomentZoneRules } from './MomentZoneRules';
12
+ import { TzdbZoneRules } from './TzdbZoneRules';
13
13
 
14
14
  import { unpack } from './unpack';
15
15
 
@@ -20,7 +20,7 @@ const AVAILABLE_ZONE_IDS = [];
20
20
  const zones = {};
21
21
  const links = {};
22
22
 
23
- export class MomentZoneRulesProvider extends ZoneRulesProvider {
23
+ export class TzdbZoneRulesProvider extends ZoneRulesProvider {
24
24
  /**
25
25
  * Gets the rules for the zone ID.
26
26
  * <p>
@@ -36,7 +36,7 @@ export class MomentZoneRulesProvider extends ZoneRulesProvider {
36
36
  if(tzdbZoneInfo == null){
37
37
  throw new DateTimeException(`Unknown time-zone ID: ${zoneId}`);
38
38
  }
39
- return new MomentZoneRules(tzdbZoneInfo);
39
+ return new TzdbZoneRules(tzdbZoneInfo);
40
40
  }
41
41
 
42
42
 
@@ -5,9 +5,9 @@
5
5
 
6
6
  import latest from './tzdbData';
7
7
 
8
- import { MomentZoneRulesProvider } from './MomentZoneRulesProvider';
8
+ import { TzdbZoneRulesProvider } from './TzdbZoneRulesProvider';
9
9
  import autoPlug from './auto-plug';
10
10
 
11
- MomentZoneRulesProvider.loadTzdbData(latest);
11
+ TzdbZoneRulesProvider.loadTzdbData(latest);
12
12
 
13
13
  autoPlug();
package/src/plug.js CHANGED
@@ -3,17 +3,17 @@
3
3
  * @license BSD-3-Clause (see LICENSE in the root directory of this source tree)
4
4
  */
5
5
 
6
- import { MomentZoneRulesProvider } from './MomentZoneRulesProvider';
6
+ import { TzdbZoneRulesProvider } from './TzdbZoneRulesProvider';
7
7
  import extendSystemDefaultZoneId from './system-default-zone';
8
8
 
9
9
  /**
10
10
  * @private
11
11
  */
12
12
  export default function (jsJoda) {
13
- jsJoda.ZoneRulesProvider.getRules = MomentZoneRulesProvider.getRules;
14
- jsJoda.ZoneRulesProvider.getAvailableZoneIds = MomentZoneRulesProvider.getAvailableZoneIds;
15
- jsJoda.ZoneRulesProvider.getTzdbData = MomentZoneRulesProvider.getTzdbData;
16
- jsJoda.ZoneRulesProvider.loadTzdbData = MomentZoneRulesProvider.loadTzdbData;
13
+ jsJoda.ZoneRulesProvider.getRules = TzdbZoneRulesProvider.getRules;
14
+ jsJoda.ZoneRulesProvider.getAvailableZoneIds = TzdbZoneRulesProvider.getAvailableZoneIds;
15
+ jsJoda.ZoneRulesProvider.getTzdbData = TzdbZoneRulesProvider.getTzdbData;
16
+ jsJoda.ZoneRulesProvider.loadTzdbData = TzdbZoneRulesProvider.loadTzdbData;
17
17
 
18
18
  extendSystemDefaultZoneId(jsJoda.ZoneId);
19
19
  return jsJoda;
package/src/unpack.js CHANGED
@@ -84,14 +84,19 @@ function mapIndices (source, indices) {
84
84
  /**
85
85
  * @private
86
86
  *
87
+ * Changes: field 7, the standard offset per period type (encoded like the offsets), is unpacked
88
+ * into `stdOffsets` when present.
89
+ *
87
90
  * @param string
88
- * @returns {{offsets: *[], abbrs: *[], name, untils: *, population: number}}
91
+ * @returns {{offsets: *[], abbrs: *[], name, untils: *, population: number, stdOffsets: (number[]|undefined)}}
89
92
  */
90
93
  export function unpack (string) {
91
94
  var data = string.split('|'),
92
95
  offsets = data[2].split(' '),
93
96
  indices = data[3].split(''),
94
- untils = data[4].split(' ');
97
+ untils = data[4].split(' '),
98
+ stdOffsets = data[7] ? data[7].split(' ') : null,
99
+ result;
95
100
 
96
101
  arrayToInt(offsets);
97
102
  arrayToInt(indices);
@@ -99,12 +104,19 @@ export function unpack (string) {
99
104
 
100
105
  intToUntil(untils, indices.length);
101
106
 
102
- return {
107
+ result = {
103
108
  name : data[0],
104
109
  abbrs : mapIndices(data[1].split(' '), indices),
105
110
  offsets : mapIndices(offsets, indices),
106
111
  untils : untils,
107
112
  population : data[5] | 0
108
113
  };
114
+
115
+ if (stdOffsets) {
116
+ arrayToInt(stdOffsets);
117
+ result.stdOffsets = mapIndices(stdOffsets, indices);
118
+ }
119
+
120
+ return result;
109
121
  }
110
122