1
1
import { formatOffset , parseZoneInfo , isUndefined , objToLocalTS } from "../impl/util.js" ;
2
2
import Zone from "../zone.js" ;
3
3
4
- let dtfCache = { } ;
5
- function makeDTF ( zone ) {
6
- if ( ! dtfCache [ zone ] ) {
7
- dtfCache [ zone ] = new Intl . DateTimeFormat ( "en-US" , {
4
+ let directOffsetDTFCache = { } ;
5
+ function makeDirectOffsetDTF ( zone ) {
6
+ if ( ! directOffsetDTFCache [ zone ] ) {
7
+ directOffsetDTFCache [ zone ] = new Intl . DateTimeFormat ( "en-US" , {
8
+ timeZone : zone ,
9
+ timeZoneName : "longOffset" ,
10
+ year : "numeric" ,
11
+ } ) ;
12
+ }
13
+ return directOffsetDTFCache [ zone ] ;
14
+ }
15
+
16
+ let calculatedOffsetDTFCache = { } ;
17
+ function makeCalculatedOffsetDTF ( zone ) {
18
+ if ( ! calculatedOffsetDTFCache [ zone ] ) {
19
+ calculatedOffsetDTFCache [ zone ] = new Intl . DateTimeFormat ( "en-US" , {
8
20
hour12 : false ,
9
21
timeZone : zone ,
10
22
year : "numeric" ,
@@ -16,7 +28,7 @@ function makeDTF(zone) {
16
28
era : "short" ,
17
29
} ) ;
18
30
}
19
- return dtfCache [ zone ] ;
31
+ return calculatedOffsetDTFCache [ zone ] ;
20
32
}
21
33
22
34
const typeToPos = {
@@ -52,7 +64,65 @@ function partsOffset(dtf, date) {
52
64
return filled ;
53
65
}
54
66
67
+ function calculatedOffset ( zone , ts ) {
68
+ const date = new Date ( ts ) ;
69
+
70
+ if ( isNaN ( date ) ) return NaN ;
71
+
72
+ const dtf = makeCalculatedOffsetDTF ( zone ) ;
73
+ let [ year , month , day , adOrBc , hour , minute , second ] = dtf . formatToParts
74
+ ? partsOffset ( dtf , date )
75
+ : hackyOffset ( dtf , date ) ;
76
+
77
+ if ( adOrBc === "BC" ) {
78
+ year = - Math . abs ( year ) + 1 ;
79
+ }
80
+
81
+ // because we're using hour12 and https://bugs.chromium.org/p/chromium/issues/detail?id=1025564&can=2&q=%2224%3A00%22%20datetimeformat
82
+ const adjustedHour = hour === 24 ? 0 : hour ;
83
+
84
+ const asUTC = objToLocalTS ( {
85
+ year,
86
+ month,
87
+ day,
88
+ hour : adjustedHour ,
89
+ minute,
90
+ second,
91
+ millisecond : 0 ,
92
+ } ) ;
93
+
94
+ let asTS = + date ;
95
+ const over = asTS % 1000 ;
96
+ asTS -= over >= 0 ? over : 1000 + over ;
97
+ return ( asUTC - asTS ) / ( 60 * 1000 ) ;
98
+ }
99
+
100
+ function directOffset ( zone , ts ) {
101
+ const dtf = makeDirectOffsetDTF ( zone ) ;
102
+
103
+ let formatted ;
104
+
105
+ try {
106
+ formatted = dtf . format ( ts ) ;
107
+ } catch ( e ) {
108
+ return NaN ;
109
+ }
110
+
111
+ const idx = formatted . search ( / G M T ( [ + - ] [ 0 - 9 ] [ 0 - 9 ] : [ 0 - 9 ] [ 0 - 9 ] ( : [ 0 - 9 ] [ 0 - 9 ] ) ? ) ? / ) ;
112
+ const sign = formatted . charCodeAt ( idx + 3 ) ;
113
+
114
+ if ( isNaN ( sign ) ) return 0 ;
115
+
116
+ return (
117
+ ( 44 - sign ) *
118
+ ( Number ( formatted . slice ( idx + 4 , idx + 6 ) ) * 60 +
119
+ Number ( formatted . slice ( idx + 7 , idx + 9 ) ) +
120
+ Number ( formatted . slice ( idx + 10 , idx + 12 ) ) / 60 )
121
+ ) ;
122
+ }
123
+
55
124
let ianaZoneCache = { } ;
125
+ let offsetFunc ;
56
126
/**
57
127
* A zone identified by an IANA identifier, like America/New_York
58
128
* @implements {Zone}
@@ -75,7 +145,8 @@ export default class IANAZone extends Zone {
75
145
*/
76
146
static resetCache ( ) {
77
147
ianaZoneCache = { } ;
78
- dtfCache = { } ;
148
+ calculatedOffsetDTFCache = { } ;
149
+ directOffsetDTFCache = { } ;
79
150
}
80
151
81
152
/**
@@ -145,36 +216,24 @@ export default class IANAZone extends Zone {
145
216
146
217
/** @override **/
147
218
offset ( ts ) {
148
- const date = new Date ( ts ) ;
149
-
150
- if ( isNaN ( date ) ) return NaN ;
151
-
152
- const dtf = makeDTF ( this . name ) ;
153
- let [ year , month , day , adOrBc , hour , minute , second ] = dtf . formatToParts
154
- ? partsOffset ( dtf , date )
155
- : hackyOffset ( dtf , date ) ;
156
-
157
- if ( adOrBc === "BC" ) {
158
- year = - Math . abs ( year ) + 1 ;
219
+ if ( offsetFunc === undefined ) {
220
+ try {
221
+ const ts = Date . now ( ) ;
222
+ // directOffset will raise an error if not supported by the engine
223
+ // also check it works correctly as it relies on a specific format
224
+ if (
225
+ directOffset ( "Etc/GMT" , ts ) !== 0 ||
226
+ directOffset ( "Etc/GMT+1" , ts ) !== - 60 ||
227
+ directOffset ( "Etc/GMT-1" , ts ) !== + 60
228
+ )
229
+ throw new Error ( "Invalid offset" ) ;
230
+ offsetFunc = directOffset ;
231
+ } catch ( e ) {
232
+ offsetFunc = calculatedOffset ;
233
+ }
159
234
}
160
235
161
- // because we're using hour12 and https://bugs.chromium.org/p/chromium/issues/detail?id=1025564&can=2&q=%2224%3A00%22%20datetimeformat
162
- const adjustedHour = hour === 24 ? 0 : hour ;
163
-
164
- const asUTC = objToLocalTS ( {
165
- year,
166
- month,
167
- day,
168
- hour : adjustedHour ,
169
- minute,
170
- second,
171
- millisecond : 0 ,
172
- } ) ;
173
-
174
- let asTS = + date ;
175
- const over = asTS % 1000 ;
176
- asTS -= over >= 0 ? over : 1000 + over ;
177
- return ( asUTC - asTS ) / ( 60 * 1000 ) ;
236
+ return offsetFunc ( this . name , ts ) ;
178
237
}
179
238
180
239
/** @override **/
0 commit comments