@internationalized/date 3.0.0-alpha.0 → 3.0.0-alpha.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/package.json +4 -3
- package/src/CalendarDate.ts +302 -0
- package/src/DateFormatter.ts +190 -0
- package/src/calendars/BuddhistCalendar.ts +45 -0
- package/src/calendars/EthiopicCalendar.ts +169 -0
- package/src/calendars/GregorianCalendar.ts +114 -0
- package/src/calendars/HebrewCalendar.ts +196 -0
- package/src/calendars/IndianCalendar.ts +120 -0
- package/src/calendars/IslamicCalendar.ts +203 -0
- package/src/calendars/JapaneseCalendar.ts +160 -0
- package/src/calendars/PersianCalendar.ts +92 -0
- package/src/calendars/TaiwanCalendar.ts +70 -0
- package/src/conversion.ts +274 -0
- package/src/createCalendar.ts +54 -0
- package/src/index.ts +28 -0
- package/src/manipulation.ts +445 -0
- package/src/queries.ts +217 -0
- package/src/string.ts +180 -0
- package/src/types.ts +97 -0
- package/src/utils.ts +37 -0
- package/src/weekStartData.ts +108 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2020 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// Portions of the code in this file are based on code from the TC39 Temporal proposal.
|
|
14
|
+
// Original licensing can be found in the NOTICE file in the root directory of this source tree.
|
|
15
|
+
|
|
16
|
+
import {AnyCalendarDate, AnyDateTime, AnyTime, Calendar, DateFields, Disambiguation, TimeFields} from './types';
|
|
17
|
+
import {CalendarDate, CalendarDateTime, Time, ZonedDateTime} from './CalendarDate';
|
|
18
|
+
import {getLocalTimeZone} from './queries';
|
|
19
|
+
import {GregorianCalendar} from './calendars/GregorianCalendar';
|
|
20
|
+
import {Mutable} from './utils';
|
|
21
|
+
|
|
22
|
+
export function epochFromDate(date: AnyDateTime) {
|
|
23
|
+
date = toCalendar(date, new GregorianCalendar());
|
|
24
|
+
return epochFromParts(date.year, date.month, date.day, date.hour, date.minute, date.second, date.millisecond);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function epochFromParts(year: number, month: number, day: number, hour: number, minute: number, second: number, millisecond: number) {
|
|
28
|
+
// Note: Date.UTC() interprets one and two-digit years as being in the
|
|
29
|
+
// 20th century, so don't use it
|
|
30
|
+
let date = new Date();
|
|
31
|
+
date.setUTCHours(hour, minute, second, millisecond);
|
|
32
|
+
date.setUTCFullYear(year, month - 1, day);
|
|
33
|
+
return date.getTime();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function getTimeZoneOffset(ms: number, timeZone: string) {
|
|
37
|
+
// Fast path: for local timezone, use native Date.
|
|
38
|
+
if (timeZone === getLocalTimeZone()) {
|
|
39
|
+
return new Date(ms).getTimezoneOffset() * -60 * 1000;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let {year, month, day, hour, minute, second} = getTimeZoneParts(ms, timeZone);
|
|
43
|
+
let utc = epochFromParts(year, month, day, hour, minute, second, 0);
|
|
44
|
+
return utc - Math.floor(ms / 1000) * 1000;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const formattersByTimeZone = new Map<string, Intl.DateTimeFormat>();
|
|
48
|
+
|
|
49
|
+
function getTimeZoneParts(ms: number, timeZone: string) {
|
|
50
|
+
let formatter = formattersByTimeZone.get(timeZone);
|
|
51
|
+
if (!formatter) {
|
|
52
|
+
formatter = new Intl.DateTimeFormat('en-US', {
|
|
53
|
+
timeZone,
|
|
54
|
+
hour12: false,
|
|
55
|
+
era: 'short',
|
|
56
|
+
year: 'numeric',
|
|
57
|
+
month: 'numeric',
|
|
58
|
+
day: 'numeric',
|
|
59
|
+
hour: 'numeric',
|
|
60
|
+
minute: 'numeric',
|
|
61
|
+
second: 'numeric'
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
formattersByTimeZone.set(timeZone, formatter);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let parts = formatter.formatToParts(new Date(ms));
|
|
68
|
+
let namedParts: {[name: string]: string} = {};
|
|
69
|
+
for (let part of parts) {
|
|
70
|
+
if (part.type !== 'literal') {
|
|
71
|
+
namedParts[part.type] = part.value;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
year: namedParts.era === 'BC' ? -namedParts.year + 1 : +namedParts.year,
|
|
77
|
+
month: +namedParts.month,
|
|
78
|
+
day: +namedParts.day,
|
|
79
|
+
hour: namedParts.hour === '24' ? 0 : +namedParts.hour, // bugs.chromium.org/p/chromium/issues/detail?id=1045791
|
|
80
|
+
minute: +namedParts.minute,
|
|
81
|
+
second: +namedParts.second
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const DAYMILLIS = 86400000;
|
|
86
|
+
|
|
87
|
+
export function possibleAbsolutes(date: CalendarDateTime, timeZone: string): number[] {
|
|
88
|
+
let ms = epochFromDate(date);
|
|
89
|
+
let earlier = ms - getTimeZoneOffset(ms - DAYMILLIS, timeZone);
|
|
90
|
+
let later = ms - getTimeZoneOffset(ms + DAYMILLIS, timeZone);
|
|
91
|
+
return getValidWallTimes(date, timeZone, earlier, later);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function getValidWallTimes(date: CalendarDateTime, timeZone: string, earlier: number, later: number): number[] {
|
|
95
|
+
let found = earlier === later ? [earlier] : [earlier, later];
|
|
96
|
+
return found.filter(absolute => isValidWallTime(date, timeZone, absolute));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function isValidWallTime(date: CalendarDateTime, timeZone: string, absolute: number) {
|
|
100
|
+
let parts = getTimeZoneParts(absolute, timeZone);
|
|
101
|
+
return date.year === parts.year
|
|
102
|
+
&& date.month === parts.month
|
|
103
|
+
&& date.day === parts.day
|
|
104
|
+
&& date.hour === parts.hour
|
|
105
|
+
&& date.minute === parts.minute
|
|
106
|
+
&& date.second === parts.second;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function toAbsolute(date: CalendarDate | CalendarDateTime, timeZone: string, disambiguation: Disambiguation = 'compatible'): number {
|
|
110
|
+
let dateTime = toCalendarDateTime(date);
|
|
111
|
+
|
|
112
|
+
// Fast path: if the time zone is the local timezone and disambiguation is compatible, use native Date.
|
|
113
|
+
if (timeZone === getLocalTimeZone() && disambiguation === 'compatible') {
|
|
114
|
+
dateTime = toCalendar(dateTime, new GregorianCalendar());
|
|
115
|
+
|
|
116
|
+
// Don't use Date constructor here because two-digit years are interpreted in the 20th century.
|
|
117
|
+
let date = new Date();
|
|
118
|
+
date.setFullYear(dateTime.year, dateTime.month - 1, dateTime.day);
|
|
119
|
+
date.setHours(dateTime.hour, dateTime.minute, dateTime.second, dateTime.millisecond);
|
|
120
|
+
return date.getTime();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
let ms = epochFromDate(dateTime);
|
|
124
|
+
let offsetBefore = getTimeZoneOffset(ms - DAYMILLIS, timeZone);
|
|
125
|
+
let offsetAfter = getTimeZoneOffset(ms + DAYMILLIS, timeZone);
|
|
126
|
+
let valid = getValidWallTimes(dateTime, timeZone, ms - offsetBefore, ms - offsetAfter);
|
|
127
|
+
|
|
128
|
+
if (valid.length === 1) {
|
|
129
|
+
return valid[0];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (valid.length > 1) {
|
|
133
|
+
switch (disambiguation) {
|
|
134
|
+
// 'compatible' means 'earlier' for "fall back" transitions
|
|
135
|
+
case 'compatible':
|
|
136
|
+
case 'earlier':
|
|
137
|
+
return valid[0];
|
|
138
|
+
case 'later':
|
|
139
|
+
return valid[valid.length - 1];
|
|
140
|
+
case 'reject':
|
|
141
|
+
throw new RangeError('Multiple possible absolute times found');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
switch (disambiguation) {
|
|
146
|
+
case 'earlier':
|
|
147
|
+
return Math.min(ms - offsetBefore, ms - offsetAfter);
|
|
148
|
+
// 'compatible' means 'later' for "spring forward" transitions
|
|
149
|
+
case 'compatible':
|
|
150
|
+
case 'later':
|
|
151
|
+
return Math.max(ms - offsetBefore, ms - offsetAfter);
|
|
152
|
+
case 'reject':
|
|
153
|
+
throw new RangeError('No such absolute time found');
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function toDate(dateTime: CalendarDate | CalendarDateTime, timeZone: string, disambiguation: Disambiguation = 'compatible'): Date {
|
|
158
|
+
return new Date(toAbsolute(dateTime, timeZone, disambiguation));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function fromAbsolute(ms: number, timeZone: string): ZonedDateTime {
|
|
162
|
+
let offset = getTimeZoneOffset(ms, timeZone);
|
|
163
|
+
let date = new Date(ms + offset);
|
|
164
|
+
let year = date.getUTCFullYear();
|
|
165
|
+
let month = date.getUTCMonth() + 1;
|
|
166
|
+
let day = date.getUTCDate();
|
|
167
|
+
let hour = date.getUTCHours();
|
|
168
|
+
let minute = date.getUTCMinutes();
|
|
169
|
+
let second = date.getUTCSeconds();
|
|
170
|
+
let millisecond = date.getUTCMilliseconds();
|
|
171
|
+
|
|
172
|
+
return new ZonedDateTime(year, month, day, timeZone, offset, hour, minute, second, millisecond);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function fromDate(date: Date, timeZone: string): ZonedDateTime {
|
|
176
|
+
return fromAbsolute(date.getTime(), timeZone);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function fromDateToLocal(date: Date): ZonedDateTime {
|
|
180
|
+
return fromDate(date, getLocalTimeZone());
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function toCalendarDate(dateTime: AnyCalendarDate): CalendarDate {
|
|
184
|
+
return new CalendarDate(dateTime.calendar, dateTime.era, dateTime.year, dateTime.month, dateTime.day);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function toDateFields(date: AnyCalendarDate): DateFields {
|
|
188
|
+
return {
|
|
189
|
+
era: date.era,
|
|
190
|
+
year: date.year,
|
|
191
|
+
month: date.month,
|
|
192
|
+
day: date.day
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export function toTimeFields(date: AnyTime): TimeFields {
|
|
197
|
+
return {
|
|
198
|
+
hour: date.hour,
|
|
199
|
+
minute: date.minute,
|
|
200
|
+
second: date.second,
|
|
201
|
+
millisecond: date.millisecond
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function toCalendarDateTime(date: CalendarDate | CalendarDateTime | ZonedDateTime, time?: AnyTime): CalendarDateTime {
|
|
206
|
+
let hour = 0, minute = 0, second = 0, millisecond = 0;
|
|
207
|
+
if ('timeZone' in date) {
|
|
208
|
+
({hour, minute, second, millisecond} = date);
|
|
209
|
+
} else if ('hour' in date && !time) {
|
|
210
|
+
return date;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (time) {
|
|
214
|
+
({hour, minute, second, millisecond} = time);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return new CalendarDateTime(
|
|
218
|
+
date.calendar,
|
|
219
|
+
date.era,
|
|
220
|
+
date.year,
|
|
221
|
+
date.month,
|
|
222
|
+
date.day,
|
|
223
|
+
hour,
|
|
224
|
+
minute,
|
|
225
|
+
second,
|
|
226
|
+
millisecond
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function toTime(dateTime: CalendarDateTime): Time {
|
|
231
|
+
return new Time(dateTime.hour, dateTime.minute, dateTime.second, dateTime.millisecond);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export function toCalendar<T extends AnyCalendarDate>(date: T, calendar: Calendar): T {
|
|
235
|
+
if (date.calendar.identifier === calendar.identifier) {
|
|
236
|
+
return date;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
let calendarDate = calendar.fromJulianDay(date.calendar.toJulianDay(date));
|
|
240
|
+
let copy: Mutable<T> = date.copy();
|
|
241
|
+
copy.calendar = calendar;
|
|
242
|
+
copy.era = calendarDate.era;
|
|
243
|
+
copy.year = calendarDate.year;
|
|
244
|
+
copy.month = calendarDate.month;
|
|
245
|
+
copy.day = calendarDate.day;
|
|
246
|
+
return copy;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function toZoned(date: CalendarDate | CalendarDateTime | ZonedDateTime, timeZone: string, disambiguation?: Disambiguation) {
|
|
250
|
+
if (date instanceof ZonedDateTime) {
|
|
251
|
+
if (date.timeZone === timeZone) {
|
|
252
|
+
return date;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return toTimeZone(date, timeZone);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
let ms = toAbsolute(date, timeZone, disambiguation);
|
|
259
|
+
return fromAbsolute(ms, timeZone);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export function zonedToDate(date: ZonedDateTime) {
|
|
263
|
+
let ms = epochFromDate(date) - date.offset;
|
|
264
|
+
return new Date(ms);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export function toTimeZone(date: ZonedDateTime, timeZone: string): ZonedDateTime {
|
|
268
|
+
let ms = epochFromDate(date) - date.offset;
|
|
269
|
+
return toCalendar(fromAbsolute(ms, timeZone), date.calendar);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export function toLocalTimeZone(date: ZonedDateTime) {
|
|
273
|
+
return toTimeZone(date, getLocalTimeZone());
|
|
274
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2020 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {BuddhistCalendar} from './calendars/BuddhistCalendar';
|
|
14
|
+
import {Calendar} from './types';
|
|
15
|
+
import {CopticCalendar, EthiopicAmeteAlemCalendar, EthiopicCalendar} from './calendars/EthiopicCalendar';
|
|
16
|
+
import {GregorianCalendar} from './calendars/GregorianCalendar';
|
|
17
|
+
import {HebrewCalendar} from './calendars/HebrewCalendar';
|
|
18
|
+
import {IndianCalendar} from './calendars/IndianCalendar';
|
|
19
|
+
import {IslamicCivilCalendar, IslamicTabularCalendar, IslamicUmalquraCalendar} from './calendars/IslamicCalendar';
|
|
20
|
+
import {JapaneseCalendar} from './calendars/JapaneseCalendar';
|
|
21
|
+
import {PersianCalendar} from './calendars/PersianCalendar';
|
|
22
|
+
import {TaiwanCalendar} from './calendars/TaiwanCalendar';
|
|
23
|
+
|
|
24
|
+
export function createCalendar(name: string): Calendar {
|
|
25
|
+
switch (name) {
|
|
26
|
+
case 'buddhist':
|
|
27
|
+
return new BuddhistCalendar();
|
|
28
|
+
case 'ethiopic':
|
|
29
|
+
return new EthiopicCalendar();
|
|
30
|
+
case 'ethioaa':
|
|
31
|
+
return new EthiopicAmeteAlemCalendar();
|
|
32
|
+
case 'coptic':
|
|
33
|
+
return new CopticCalendar();
|
|
34
|
+
case 'hebrew':
|
|
35
|
+
return new HebrewCalendar();
|
|
36
|
+
case 'indian':
|
|
37
|
+
return new IndianCalendar();
|
|
38
|
+
case 'islamic-civil':
|
|
39
|
+
return new IslamicCivilCalendar();
|
|
40
|
+
case 'islamic-tbla':
|
|
41
|
+
return new IslamicTabularCalendar();
|
|
42
|
+
case 'islamic-umalqura':
|
|
43
|
+
return new IslamicUmalquraCalendar();
|
|
44
|
+
case 'japanese':
|
|
45
|
+
return new JapaneseCalendar();
|
|
46
|
+
case 'persian':
|
|
47
|
+
return new PersianCalendar();
|
|
48
|
+
case 'roc':
|
|
49
|
+
return new TaiwanCalendar();
|
|
50
|
+
case 'gregory':
|
|
51
|
+
default:
|
|
52
|
+
return new GregorianCalendar();
|
|
53
|
+
}
|
|
54
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2020 Adobe. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export * from './CalendarDate';
|
|
14
|
+
export {GregorianCalendar} from './calendars/GregorianCalendar';
|
|
15
|
+
export {JapaneseCalendar} from './calendars/JapaneseCalendar';
|
|
16
|
+
export {BuddhistCalendar} from './calendars/BuddhistCalendar';
|
|
17
|
+
export {TaiwanCalendar} from './calendars/TaiwanCalendar';
|
|
18
|
+
export {PersianCalendar} from './calendars/PersianCalendar';
|
|
19
|
+
export {IndianCalendar} from './calendars/IndianCalendar';
|
|
20
|
+
export {IslamicCivilCalendar, IslamicTabularCalendar, IslamicUmalquraCalendar} from './calendars/IslamicCalendar';
|
|
21
|
+
export {HebrewCalendar} from './calendars/HebrewCalendar';
|
|
22
|
+
export {EthiopicCalendar, EthiopicAmeteAlemCalendar, CopticCalendar} from './calendars/EthiopicCalendar';
|
|
23
|
+
export {createCalendar} from './createCalendar';
|
|
24
|
+
export * from './conversion';
|
|
25
|
+
export * from './queries';
|
|
26
|
+
export * from './types';
|
|
27
|
+
export * from './string';
|
|
28
|
+
export * from './DateFormatter';
|