@js-joda/core 6.0.1 → 6.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.
- package/dist/js-joda.cjs.js +64 -48
- package/dist/js-joda.cjs.js.map +1 -1
- package/dist/js-joda.esm.js +64 -48
- package/dist/js-joda.esm.js.map +1 -1
- package/dist/js-joda.js +64 -48
- package/dist/js-joda.js.map +1 -1
- package/dist/js-joda.min.js +2 -2
- package/package.json +2 -2
- package/src/Duration.js +32 -23
- package/src/Instant.js +1 -1
- package/src/format/DateTimeFormatterBuilder.js +6 -2
- package/src/format/DateTimeParseContext.js +2 -0
- package/src/format/parser/ZoneIdPrinterParser.js +62 -28
- package/typings/js-joda.flow.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@js-joda/core",
|
|
3
|
-
"version": "6.0
|
|
3
|
+
"version": "6.1.0",
|
|
4
4
|
"description": "a date and time library for javascript",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -47,5 +47,5 @@
|
|
|
47
47
|
"sourceMap": false,
|
|
48
48
|
"instrument": false
|
|
49
49
|
},
|
|
50
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "d82df374b19b312796817f40945c22783536170b"
|
|
51
51
|
}
|
package/src/Duration.js
CHANGED
|
@@ -1209,9 +1209,22 @@ export class Duration extends TemporalAmount /*implements TemporalAmount, Compar
|
|
|
1209
1209
|
if (this === Duration.ZERO) {
|
|
1210
1210
|
return 'PT0S';
|
|
1211
1211
|
}
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1212
|
+
// A negative duration is stored as a negative second count plus a
|
|
1213
|
+
// non-negative nano offset, e.g. -0.5s is held as seconds = -1,
|
|
1214
|
+
// nanos = 500000000. To render ISO-8601 components that all carry the
|
|
1215
|
+
// duration's sign, recover the signed whole-second part and the
|
|
1216
|
+
// magnitude of the sub-second fraction separately: when the duration is
|
|
1217
|
+
// negative and has a fractional offset, one second of that offset
|
|
1218
|
+
// belongs to the whole part, and the remaining fraction is its
|
|
1219
|
+
// complement against a full second.
|
|
1220
|
+
const negativeFraction = this._seconds < 0 && this._nanos > 0;
|
|
1221
|
+
const wholeSeconds = negativeFraction ? this._seconds + 1 : this._seconds;
|
|
1222
|
+
const fractionNanos = negativeFraction ? LocalTime.NANOS_PER_SECOND - this._nanos : this._nanos;
|
|
1223
|
+
|
|
1224
|
+
const hours = MathUtil.intDiv(wholeSeconds, LocalTime.SECONDS_PER_HOUR);
|
|
1225
|
+
const minutes = MathUtil.intDiv(MathUtil.intMod(wholeSeconds, LocalTime.SECONDS_PER_HOUR), LocalTime.SECONDS_PER_MINUTE);
|
|
1226
|
+
const secs = MathUtil.intMod(wholeSeconds, LocalTime.SECONDS_PER_MINUTE);
|
|
1227
|
+
|
|
1215
1228
|
let rval = 'PT';
|
|
1216
1229
|
if (hours !== 0) {
|
|
1217
1230
|
rval += `${hours}H`;
|
|
@@ -1219,32 +1232,28 @@ export class Duration extends TemporalAmount /*implements TemporalAmount, Compar
|
|
|
1219
1232
|
if (minutes !== 0) {
|
|
1220
1233
|
rval += `${minutes}M`;
|
|
1221
1234
|
}
|
|
1222
|
-
if (secs === 0 &&
|
|
1235
|
+
if (secs === 0 && fractionNanos === 0 && rval.length > 2) {
|
|
1223
1236
|
return rval;
|
|
1224
1237
|
}
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
}
|
|
1238
|
+
// The seconds field needs an explicit minus sign when the whole-second
|
|
1239
|
+
// part is zero but the duration is a negative fraction (e.g. -0.5s
|
|
1240
|
+
// prints as "-0.5S", not "0.5S").
|
|
1241
|
+
if (secs === 0 && negativeFraction) {
|
|
1242
|
+
rval += '-0';
|
|
1231
1243
|
} else {
|
|
1232
|
-
rval += secs
|
|
1244
|
+
rval += `${secs}`;
|
|
1233
1245
|
}
|
|
1234
|
-
if (
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
nanoString = `${LocalTime.NANOS_PER_SECOND + this._nanos}`;
|
|
1246
|
+
if (fractionNanos > 0) {
|
|
1247
|
+
// fractionNanos is a magnitude in [1, 1e9); print it as a fixed
|
|
1248
|
+
// nine-digit fraction with any trailing zeros removed.
|
|
1249
|
+
let fraction = `${fractionNanos}`;
|
|
1250
|
+
while (fraction.length < 9) {
|
|
1251
|
+
fraction = `0${fraction}`;
|
|
1241
1252
|
}
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
rval += nanoString;
|
|
1245
|
-
while (rval.charAt(rval.length - 1) === '0') {
|
|
1246
|
-
rval = rval.slice(0, rval.length - 1);
|
|
1253
|
+
while (fraction.charAt(fraction.length - 1) === '0') {
|
|
1254
|
+
fraction = fraction.slice(0, fraction.length - 1);
|
|
1247
1255
|
}
|
|
1256
|
+
rval += `.${fraction}`;
|
|
1248
1257
|
}
|
|
1249
1258
|
rval += 'S';
|
|
1250
1259
|
return rval;
|
package/src/Instant.js
CHANGED
|
@@ -520,7 +520,7 @@ export class Instant extends Temporal {
|
|
|
520
520
|
throw new DateTimeException('Unit must divide into a standard day without remainder');
|
|
521
521
|
}
|
|
522
522
|
const nod = MathUtil.intMod(this._seconds, LocalTime.SECONDS_PER_DAY) * LocalTime.NANOS_PER_SECOND + this._nanos;
|
|
523
|
-
const result = MathUtil.
|
|
523
|
+
const result = MathUtil.floorDiv(nod, dur) * dur;
|
|
524
524
|
return this.plusNanos(result - nod);
|
|
525
525
|
}
|
|
526
526
|
|
|
@@ -1516,7 +1516,7 @@ class InstantPrinterParser {
|
|
|
1516
1516
|
const parser = new DateTimeFormatterBuilder()
|
|
1517
1517
|
.append(DateTimeFormatter.ISO_LOCAL_DATE).appendLiteral('T')
|
|
1518
1518
|
.appendValue(ChronoField.HOUR_OF_DAY, 2).appendLiteral(':').appendValue(ChronoField.MINUTE_OF_HOUR, 2).appendLiteral(':')
|
|
1519
|
-
.appendValue(ChronoField.SECOND_OF_MINUTE, 2).appendFraction(ChronoField.NANO_OF_SECOND, minDigits, maxDigits, true).
|
|
1519
|
+
.appendValue(ChronoField.SECOND_OF_MINUTE, 2).appendFraction(ChronoField.NANO_OF_SECOND, minDigits, maxDigits, true).appendOffsetId()
|
|
1520
1520
|
.toFormatter()._toPrinterParser(false);
|
|
1521
1521
|
const pos = parser.parse(newContext, text, position);
|
|
1522
1522
|
if (pos < 0) {
|
|
@@ -1531,6 +1531,10 @@ class InstantPrinterParser {
|
|
|
1531
1531
|
const min = newContext.getParsed(ChronoField.MINUTE_OF_HOUR);
|
|
1532
1532
|
const secVal = newContext.getParsed(ChronoField.SECOND_OF_MINUTE);
|
|
1533
1533
|
const nanoVal = newContext.getParsed(ChronoField.NANO_OF_SECOND);
|
|
1534
|
+
// The instant is parsed with an offset id (which also accepts 'Z' as
|
|
1535
|
+
// zero), so resolve against the parsed offset rather than assuming UTC.
|
|
1536
|
+
const offsetSecs = newContext.getParsed(ChronoField.OFFSET_SECONDS);
|
|
1537
|
+
const offset = (offsetSecs != null ? offsetSecs : 0);
|
|
1534
1538
|
let sec = (secVal != null ? secVal : 0);
|
|
1535
1539
|
const nano = (nanoVal != null ? nanoVal : 0);
|
|
1536
1540
|
const year = MathUtil.intMod(yearParsed, 10000);
|
|
@@ -1545,7 +1549,7 @@ class InstantPrinterParser {
|
|
|
1545
1549
|
let instantSecs;
|
|
1546
1550
|
try {
|
|
1547
1551
|
const ldt = LocalDateTime.of(year, month, day, hour, min, sec, 0).plusDays(days);
|
|
1548
|
-
instantSecs = ldt.toEpochSecond(ZoneOffset.
|
|
1552
|
+
instantSecs = ldt.toEpochSecond(ZoneOffset.ofTotalSeconds(offset));
|
|
1549
1553
|
instantSecs += MathUtil.safeMultiply(MathUtil.intDiv(yearParsed, 10000), SECONDS_PER_10000_YEARS);
|
|
1550
1554
|
} catch (ex) {
|
|
1551
1555
|
return ~position;
|
|
@@ -9,6 +9,7 @@ import { assert, requireNonNull } from '../assert';
|
|
|
9
9
|
import { DateTimeBuilder } from './DateTimeBuilder';
|
|
10
10
|
import { EnumMap } from './EnumMap';
|
|
11
11
|
|
|
12
|
+
import { Period } from '../Period';
|
|
12
13
|
import { IsoChronology } from '../chrono/IsoChronology';
|
|
13
14
|
import { Temporal } from '../temporal/Temporal';
|
|
14
15
|
import { TemporalQueries } from '../temporal/TemporalQueries';
|
|
@@ -244,6 +245,7 @@ class Parsed extends Temporal {
|
|
|
244
245
|
this.zone = null;
|
|
245
246
|
this.fieldValues = new EnumMap();
|
|
246
247
|
this.leapSecond = false;
|
|
248
|
+
this.excessDays = Period.ZERO;
|
|
247
249
|
this.dateTimeParseContext = dateTimeParseContext;
|
|
248
250
|
}
|
|
249
251
|
|
|
@@ -71,7 +71,67 @@ export class ZoneIdPrinterParser {
|
|
|
71
71
|
return ~position;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
//
|
|
74
|
+
// A fixed ID (Z, UT, UTC, GMT, an offset) can also be the start of a
|
|
75
|
+
// longer region id such as GMT0 or Zulu, so parse both and keep the
|
|
76
|
+
// longest match (as the class doc above promises).
|
|
77
|
+
const region = this._parseRegion(text, position);
|
|
78
|
+
const fixedEndPos = this._parseFixedId(context, text, position);
|
|
79
|
+
|
|
80
|
+
const fixedLength = fixedEndPos >= 0 ? fixedEndPos - position : 0;
|
|
81
|
+
if (region != null && region.parseLength > fixedLength) {
|
|
82
|
+
context.setParsedZone(ZoneRegion.ofId(region.parsedZoneId));
|
|
83
|
+
return position + region.parseLength;
|
|
84
|
+
}
|
|
85
|
+
return fixedEndPos;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Finds the longest available zone region id matching text at position,
|
|
90
|
+
* without touching the parse context.
|
|
91
|
+
*
|
|
92
|
+
* @param {String} text
|
|
93
|
+
* @param {number} position
|
|
94
|
+
* @return {?{parsedZoneId: string, parseLength: number}}
|
|
95
|
+
*/
|
|
96
|
+
_parseRegion(text, position) {
|
|
97
|
+
const availableZoneIds = ZoneRulesProvider.getAvailableZoneIds();
|
|
98
|
+
if (availableZoneIds.length === 0) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
if (zoneIdTree.size !== availableZoneIds.length) {
|
|
102
|
+
zoneIdTree = ZoneIdTree.createTreeMap(availableZoneIds);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const maxParseLength = text.length - position;
|
|
106
|
+
let treeMap = zoneIdTree.treeMap;
|
|
107
|
+
let parsedZoneId = null;
|
|
108
|
+
let parseLength = 0;
|
|
109
|
+
while(treeMap != null) {
|
|
110
|
+
const parsedSubZoneId = text.substr(position, Math.min(treeMap.length, maxParseLength));
|
|
111
|
+
treeMap = treeMap.get(parsedSubZoneId);
|
|
112
|
+
if (treeMap != null && treeMap.isLeaf) {
|
|
113
|
+
parsedZoneId = parsedSubZoneId;
|
|
114
|
+
parseLength = treeMap.length;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (parsedZoneId == null) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
return { parsedZoneId, parseLength };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Parses a fixed zone id (an offset, a UT/UTC/GMT prefixed offset, SYSTEM
|
|
125
|
+
* or Z) and sets it on the context. Returns the end position, or ~position
|
|
126
|
+
* if no fixed id matches at position.
|
|
127
|
+
*
|
|
128
|
+
* @param {DateTimeParseContext} context
|
|
129
|
+
* @param {String} text
|
|
130
|
+
* @param {number} position
|
|
131
|
+
* @return {number}
|
|
132
|
+
*/
|
|
133
|
+
_parseFixedId(context, text, position) {
|
|
134
|
+
const length = text.length;
|
|
75
135
|
const nextChar = text.charAt(position);
|
|
76
136
|
if (nextChar === '+' || nextChar === '-') {
|
|
77
137
|
const newContext = context.copy();
|
|
@@ -80,8 +140,7 @@ export class ZoneIdPrinterParser {
|
|
|
80
140
|
return endPos;
|
|
81
141
|
}
|
|
82
142
|
const offset = newContext.getParsed(ChronoField.OFFSET_SECONDS);
|
|
83
|
-
|
|
84
|
-
context.setParsedZone(zone);
|
|
143
|
+
context.setParsedZone(ZoneOffset.ofTotalSeconds(offset));
|
|
85
144
|
return endPos;
|
|
86
145
|
} else if (length >= position + 2) {
|
|
87
146
|
const nextNextChar = text.charAt(position + 1);
|
|
@@ -104,35 +163,10 @@ export class ZoneIdPrinterParser {
|
|
|
104
163
|
context.setParsedZone(ZoneId.systemDefault());
|
|
105
164
|
return position + 6;
|
|
106
165
|
}
|
|
107
|
-
|
|
108
|
-
// ...
|
|
109
166
|
if (context.charEquals(nextChar, 'Z')) {
|
|
110
167
|
context.setParsedZone(ZoneOffset.UTC);
|
|
111
168
|
return position + 1;
|
|
112
169
|
}
|
|
113
|
-
|
|
114
|
-
const availableZoneIds = ZoneRulesProvider.getAvailableZoneIds();
|
|
115
|
-
if (zoneIdTree.size !== availableZoneIds.length) {
|
|
116
|
-
zoneIdTree = ZoneIdTree.createTreeMap(availableZoneIds);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const maxParseLength = length - position;
|
|
120
|
-
let treeMap = zoneIdTree.treeMap;
|
|
121
|
-
let parsedZoneId = null;
|
|
122
|
-
let parseLength = 0;
|
|
123
|
-
while(treeMap != null) {
|
|
124
|
-
const parsedSubZoneId = text.substr(position, Math.min(treeMap.length, maxParseLength));
|
|
125
|
-
treeMap = treeMap.get(parsedSubZoneId);
|
|
126
|
-
if (treeMap != null && treeMap.isLeaf) {
|
|
127
|
-
parsedZoneId = parsedSubZoneId;
|
|
128
|
-
parseLength = treeMap.length;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
if (parsedZoneId != null) {
|
|
132
|
-
context.setParsedZone(ZoneRegion.ofId(parsedZoneId));
|
|
133
|
-
return position + parseLength;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
170
|
return ~position;
|
|
137
171
|
}
|
|
138
172
|
|
package/typings/js-joda.flow.js
CHANGED