-
Notifications
You must be signed in to change notification settings - Fork 147
/
temporal-types.js
500 lines (456 loc) · 16.4 KB
/
temporal-types.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
/**
* Copyright (c) 2002-2019 "Neo4j,"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as util from './internal/temporal-util'
import {
assertNumberOrInteger,
assertString,
assertValidDate
} from './internal/util'
import { newError } from './error'
const IDENTIFIER_PROPERTY_ATTRIBUTES = {
value: true,
enumerable: false,
configurable: false
}
const DURATION_IDENTIFIER_PROPERTY = '__isDuration__'
const LOCAL_TIME_IDENTIFIER_PROPERTY = '__isLocalTime__'
const TIME_IDENTIFIER_PROPERTY = '__isTime__'
const DATE_IDENTIFIER_PROPERTY = '__isDate__'
const LOCAL_DATE_TIME_IDENTIFIER_PROPERTY = '__isLocalDateTime__'
const DATE_TIME_IDENTIFIER_PROPERTY = '__isDateTime__'
/**
* Represents an ISO 8601 duration. Contains both date-based values (years, months, days) and time-based values (seconds, nanoseconds).
* Created `Duration` objects are frozen with `Object.freeze()` in constructor and thus immutable.
*/
export class Duration {
/**
* @constructor
* @param {Integer|number} months the number of months for the new duration.
* @param {Integer|number} days the number of days for the new duration.
* @param {Integer|number} seconds the number of seconds for the new duration.
* @param {Integer|number} nanoseconds the number of nanoseconds for the new duration.
*/
constructor (months, days, seconds, nanoseconds) {
this.months = assertNumberOrInteger(months, 'Months')
this.days = assertNumberOrInteger(days, 'Days')
assertNumberOrInteger(seconds, 'Seconds')
assertNumberOrInteger(nanoseconds, 'Nanoseconds')
this.seconds = util.normalizeSecondsForDuration(seconds, nanoseconds)
this.nanoseconds = util.normalizeNanosecondsForDuration(nanoseconds)
Object.freeze(this)
}
toString () {
return util.durationToIsoString(
this.months,
this.days,
this.seconds,
this.nanoseconds
)
}
}
Object.defineProperty(
Duration.prototype,
DURATION_IDENTIFIER_PROPERTY,
IDENTIFIER_PROPERTY_ATTRIBUTES
)
/**
* Test if given object is an instance of {@link Duration} class.
* @param {object} obj the object to test.
* @return {boolean} `true` if given object is a {@link Duration}, `false` otherwise.
*/
export function isDuration (obj) {
return hasIdentifierProperty(obj, DURATION_IDENTIFIER_PROPERTY)
}
/**
* Represents an instant capturing the time of day, but not the date, nor the timezone.
* Created `LocalTime` objects are frozen with `Object.freeze()` in constructor and thus immutable.
*/
export class LocalTime {
/**
* @constructor
* @param {Integer|number} hour the hour for the new local time.
* @param {Integer|number} minute the minute for the new local time.
* @param {Integer|number} second the second for the new local time.
* @param {Integer|number} nanosecond the nanosecond for the new local time.
*/
constructor (hour, minute, second, nanosecond) {
this.hour = util.assertValidHour(hour)
this.minute = util.assertValidMinute(minute)
this.second = util.assertValidSecond(second)
this.nanosecond = util.assertValidNanosecond(nanosecond)
Object.freeze(this)
}
/**
* Create a local time object from the given standard JavaScript `Date` and optional nanoseconds.
* Year, month, day and time zone offset components of the given date are ignored.
* @param {global.Date} standardDate the standard JavaScript date to convert.
* @param {Integer|number|undefined} nanosecond the optional amount of nanoseconds.
* @return {LocalTime} new local time.
*/
static fromStandardDate (standardDate, nanosecond) {
verifyStandardDateAndNanos(standardDate, nanosecond)
return new LocalTime(
standardDate.getHours(),
standardDate.getMinutes(),
standardDate.getSeconds(),
util.totalNanoseconds(standardDate, nanosecond)
)
}
toString () {
return util.timeToIsoString(
this.hour,
this.minute,
this.second,
this.nanosecond
)
}
}
Object.defineProperty(
LocalTime.prototype,
LOCAL_TIME_IDENTIFIER_PROPERTY,
IDENTIFIER_PROPERTY_ATTRIBUTES
)
/**
* Test if given object is an instance of {@link LocalTime} class.
* @param {object} obj the object to test.
* @return {boolean} `true` if given object is a {@link LocalTime}, `false` otherwise.
*/
export function isLocalTime (obj) {
return hasIdentifierProperty(obj, LOCAL_TIME_IDENTIFIER_PROPERTY)
}
/**
* Represents an instant capturing the time of day, and the timezone offset in seconds, but not the date.
* Created `Time` objects are frozen with `Object.freeze()` in constructor and thus immutable.
*/
export class Time {
/**
* @constructor
* @param {Integer|number} hour the hour for the new local time.
* @param {Integer|number} minute the minute for the new local time.
* @param {Integer|number} second the second for the new local time.
* @param {Integer|number} nanosecond the nanosecond for the new local time.
* @param {Integer|number} timeZoneOffsetSeconds the time zone offset in seconds. Value represents the difference, in seconds, from UTC to local time.
* This is different from standard JavaScript `Date.getTimezoneOffset()` which is the difference, in minutes, from local time to UTC.
*/
constructor (hour, minute, second, nanosecond, timeZoneOffsetSeconds) {
this.hour = util.assertValidHour(hour)
this.minute = util.assertValidMinute(minute)
this.second = util.assertValidSecond(second)
this.nanosecond = util.assertValidNanosecond(nanosecond)
this.timeZoneOffsetSeconds = assertNumberOrInteger(
timeZoneOffsetSeconds,
'Time zone offset in seconds'
)
Object.freeze(this)
}
/**
* Create a time object from the given standard JavaScript `Date` and optional nanoseconds.
* Year, month and day components of the given date are ignored.
* @param {global.Date} standardDate the standard JavaScript date to convert.
* @param {Integer|number|undefined} nanosecond the optional amount of nanoseconds.
* @return {Time} new time.
*/
static fromStandardDate (standardDate, nanosecond) {
verifyStandardDateAndNanos(standardDate, nanosecond)
return new Time(
standardDate.getHours(),
standardDate.getMinutes(),
standardDate.getSeconds(),
util.totalNanoseconds(standardDate, nanosecond),
util.timeZoneOffsetInSeconds(standardDate)
)
}
toString () {
return (
util.timeToIsoString(
this.hour,
this.minute,
this.second,
this.nanosecond
) + util.timeZoneOffsetToIsoString(this.timeZoneOffsetSeconds)
)
}
}
Object.defineProperty(
Time.prototype,
TIME_IDENTIFIER_PROPERTY,
IDENTIFIER_PROPERTY_ATTRIBUTES
)
/**
* Test if given object is an instance of {@link Time} class.
* @param {object} obj the object to test.
* @return {boolean} `true` if given object is a {@link Time}, `false` otherwise.
*/
export function isTime (obj) {
return hasIdentifierProperty(obj, TIME_IDENTIFIER_PROPERTY)
}
/**
* Represents an instant capturing the date, but not the time, nor the timezone.
* Created `Date` objects are frozen with `Object.freeze()` in constructor and thus immutable.
*/
export class Date {
/**
* @constructor
* @param {Integer|number} year the year for the new local date.
* @param {Integer|number} month the month for the new local date.
* @param {Integer|number} day the day for the new local date.
*/
constructor (year, month, day) {
this.year = util.assertValidYear(year)
this.month = util.assertValidMonth(month)
this.day = util.assertValidDay(day)
Object.freeze(this)
}
/**
* Create a date object from the given standard JavaScript `Date`.
* Hour, minute, second, millisecond and time zone offset components of the given date are ignored.
* @param {global.Date} standardDate the standard JavaScript date to convert.
* @return {Date} new date.
*/
static fromStandardDate (standardDate) {
verifyStandardDateAndNanos(standardDate, null)
return new Date(
standardDate.getFullYear(),
standardDate.getMonth() + 1,
standardDate.getDate()
)
}
toString () {
return util.dateToIsoString(this.year, this.month, this.day)
}
}
Object.defineProperty(
Date.prototype,
DATE_IDENTIFIER_PROPERTY,
IDENTIFIER_PROPERTY_ATTRIBUTES
)
/**
* Test if given object is an instance of {@link Date} class.
* @param {object} obj the object to test.
* @return {boolean} `true` if given object is a {@link Date}, `false` otherwise.
*/
export function isDate (obj) {
return hasIdentifierProperty(obj, DATE_IDENTIFIER_PROPERTY)
}
/**
* Represents an instant capturing the date and the time, but not the timezone.
* Created `LocalDateTime` objects are frozen with `Object.freeze()` in constructor and thus immutable.
*/
export class LocalDateTime {
/**
* @constructor
* @param {Integer|number} year the year for the new local date.
* @param {Integer|number} month the month for the new local date.
* @param {Integer|number} day the day for the new local date.
* @param {Integer|number} hour the hour for the new local time.
* @param {Integer|number} minute the minute for the new local time.
* @param {Integer|number} second the second for the new local time.
* @param {Integer|number} nanosecond the nanosecond for the new local time.
*/
constructor (year, month, day, hour, minute, second, nanosecond) {
this.year = util.assertValidYear(year)
this.month = util.assertValidMonth(month)
this.day = util.assertValidDay(day)
this.hour = util.assertValidHour(hour)
this.minute = util.assertValidMinute(minute)
this.second = util.assertValidSecond(second)
this.nanosecond = util.assertValidNanosecond(nanosecond)
Object.freeze(this)
}
/**
* Create a local date-time object from the given standard JavaScript `Date` and optional nanoseconds.
* Time zone offset component of the given date is ignored.
* @param {global.Date} standardDate the standard JavaScript date to convert.
* @param {Integer|number|undefined} nanosecond the optional amount of nanoseconds.
* @return {LocalDateTime} new local date-time.
*/
static fromStandardDate (standardDate, nanosecond) {
verifyStandardDateAndNanos(standardDate, nanosecond)
return new LocalDateTime(
standardDate.getFullYear(),
standardDate.getMonth() + 1,
standardDate.getDate(),
standardDate.getHours(),
standardDate.getMinutes(),
standardDate.getSeconds(),
util.totalNanoseconds(standardDate, nanosecond)
)
}
toString () {
return localDateTimeToString(
this.year,
this.month,
this.day,
this.hour,
this.minute,
this.second,
this.nanosecond
)
}
}
Object.defineProperty(
LocalDateTime.prototype,
LOCAL_DATE_TIME_IDENTIFIER_PROPERTY,
IDENTIFIER_PROPERTY_ATTRIBUTES
)
/**
* Test if given object is an instance of {@link LocalDateTime} class.
* @param {object} obj the object to test.
* @return {boolean} `true` if given object is a {@link LocalDateTime}, `false` otherwise.
*/
export function isLocalDateTime (obj) {
return hasIdentifierProperty(obj, LOCAL_DATE_TIME_IDENTIFIER_PROPERTY)
}
/**
* Represents an instant capturing the date, the time and the timezone identifier.
* Created `DateTime` objects are frozen with `Object.freeze()` in constructor and thus immutable.
*/
export class DateTime {
/**
* @constructor
* @param {Integer|number} year the year for the new date-time.
* @param {Integer|number} month the month for the new date-time.
* @param {Integer|number} day the day for the new date-time.
* @param {Integer|number} hour the hour for the new date-time.
* @param {Integer|number} minute the minute for the new date-time.
* @param {Integer|number} second the second for the new date-time.
* @param {Integer|number} nanosecond the nanosecond for the new date-time.
* @param {Integer|number} timeZoneOffsetSeconds the time zone offset in seconds. Either this argument or `timeZoneId` should be defined.
* Value represents the difference, in seconds, from UTC to local time.
* This is different from standard JavaScript `Date.getTimezoneOffset()` which is the difference, in minutes, from local time to UTC.
* @param {string|null} timeZoneId the time zone id for the new date-time. Either this argument or `timeZoneOffsetSeconds` should be defined.
*/
constructor (
year,
month,
day,
hour,
minute,
second,
nanosecond,
timeZoneOffsetSeconds,
timeZoneId
) {
this.year = util.assertValidYear(year)
this.month = util.assertValidMonth(month)
this.day = util.assertValidDay(day)
this.hour = util.assertValidHour(hour)
this.minute = util.assertValidMinute(minute)
this.second = util.assertValidSecond(second)
this.nanosecond = util.assertValidNanosecond(nanosecond)
const [offset, id] = verifyTimeZoneArguments(
timeZoneOffsetSeconds,
timeZoneId
)
this.timeZoneOffsetSeconds = offset
this.timeZoneId = id
Object.freeze(this)
}
/**
* Create a date-time object from the given standard JavaScript `Date` and optional nanoseconds.
* @param {global.Date} standardDate the standard JavaScript date to convert.
* @param {Integer|number|undefined} nanosecond the optional amount of nanoseconds.
* @return {DateTime} new date-time.
*/
static fromStandardDate (standardDate, nanosecond) {
verifyStandardDateAndNanos(standardDate, nanosecond)
return new DateTime(
standardDate.getFullYear(),
standardDate.getMonth() + 1,
standardDate.getDate(),
standardDate.getHours(),
standardDate.getMinutes(),
standardDate.getSeconds(),
util.totalNanoseconds(standardDate, nanosecond),
util.timeZoneOffsetInSeconds(standardDate),
null /* no time zone id */
)
}
toString () {
const localDateTimeStr = localDateTimeToString(
this.year,
this.month,
this.day,
this.hour,
this.minute,
this.second,
this.nanosecond
)
const timeZoneStr = this.timeZoneId
? `[${this.timeZoneId}]`
: util.timeZoneOffsetToIsoString(this.timeZoneOffsetSeconds)
return localDateTimeStr + timeZoneStr
}
}
Object.defineProperty(
DateTime.prototype,
DATE_TIME_IDENTIFIER_PROPERTY,
IDENTIFIER_PROPERTY_ATTRIBUTES
)
/**
* Test if given object is an instance of {@link DateTime} class.
* @param {object} obj the object to test.
* @return {boolean} `true` if given object is a {@link DateTime}, `false` otherwise.
*/
export function isDateTime (obj) {
return hasIdentifierProperty(obj, DATE_TIME_IDENTIFIER_PROPERTY)
}
function hasIdentifierProperty (obj, property) {
return (obj && obj[property]) === true
}
function localDateTimeToString (
year,
month,
day,
hour,
minute,
second,
nanosecond
) {
return (
util.dateToIsoString(year, month, day) +
'T' +
util.timeToIsoString(hour, minute, second, nanosecond)
)
}
function verifyTimeZoneArguments (timeZoneOffsetSeconds, timeZoneId) {
const offsetDefined = timeZoneOffsetSeconds || timeZoneOffsetSeconds === 0
const idDefined = timeZoneId && timeZoneId !== ''
if (offsetDefined && !idDefined) {
assertNumberOrInteger(timeZoneOffsetSeconds, 'Time zone offset in seconds')
return [timeZoneOffsetSeconds, null]
} else if (!offsetDefined && idDefined) {
assertString(timeZoneId, 'Time zone ID')
return [null, timeZoneId]
} else if (offsetDefined && idDefined) {
throw newError(
`Unable to create DateTime with both time zone offset and id. Please specify either of them. Given offset: ${timeZoneOffsetSeconds} and id: ${timeZoneId}`
)
} else {
throw newError(
`Unable to create DateTime without either time zone offset or id. Please specify either of them. Given offset: ${timeZoneOffsetSeconds} and id: ${timeZoneId}`
)
}
}
function verifyStandardDateAndNanos (standardDate, nanosecond) {
assertValidDate(standardDate, 'Standard date')
if (nanosecond !== null && nanosecond !== undefined) {
assertNumberOrInteger(nanosecond, 'Nanosecond')
}
}