1
- import { padStart , roundTo , hasRelative } from "./util.js" ;
1
+ import { padStart , roundTo , hasRelative , formatOffset } from "./util.js" ;
2
2
import * as English from "./english.js" ;
3
3
import Settings from "../settings.js" ;
4
4
import DateTime from "../datetime.js" ;
@@ -197,9 +197,13 @@ class PolyNumberFormatter {
197
197
class PolyDateFormatter {
198
198
constructor ( dt , intl , opts ) {
199
199
this . opts = opts ;
200
+ this . originalZone = undefined ;
200
201
201
202
let z = undefined ;
202
- if ( dt . zone . isUniversal ) {
203
+ if ( this . opts . timeZone ) {
204
+ // Don't apply any workarounds if a timeZone is explicitly provided in opts
205
+ this . dt = dt ;
206
+ } else if ( dt . zone . type === "fixed" ) {
203
207
// UTC-8 or Etc/UTC-8 are not part of tzdata, only Etc/GMT+8 and the like.
204
208
// That is why fixed-offset TZ is set to that unless it is:
205
209
// 1. Representing offset 0 when UTC is used to maintain previous behavior and does not become GMT.
@@ -212,25 +216,23 @@ class PolyDateFormatter {
212
216
z = offsetZ ;
213
217
this . dt = dt ;
214
218
} else {
215
- // Not all fixed-offset zones like Etc/+4:30 are present in tzdata.
216
- // So we have to make do. Two cases:
217
- // 1. The format options tell us to show the zone. We can't do that, so the best
218
- // we can do is format the date in UTC.
219
- // 2. The format options don't tell us to show the zone. Then we can adjust them
220
- // the time and tell the formatter to show it to us in UTC, so that the time is right
221
- // and the bad zone doesn't show up.
219
+ // Not all fixed-offset zones like Etc/+4:30 are present in tzdata so
220
+ // we manually apply the offset and substitute the zone as needed.
222
221
z = "UTC" ;
223
- if ( opts . timeZoneName ) {
224
- this . dt = dt ;
225
- } else {
226
- this . dt = dt . offset === 0 ? dt : DateTime . fromMillis ( dt . ts + dt . offset * 60 * 1000 ) ;
227
- }
222
+ this . dt = dt . offset === 0 ? dt : dt . setZone ( "UTC" ) . plus ( { minutes : dt . offset } ) ;
223
+ this . originalZone = dt . zone ;
228
224
}
229
225
} else if ( dt . zone . type === "system" ) {
230
226
this . dt = dt ;
231
- } else {
227
+ } else if ( dt . zone . type === "iana" ) {
232
228
this . dt = dt ;
233
229
z = dt . zone . name ;
230
+ } else {
231
+ // Custom zones can have any offset / offsetName so we just manually
232
+ // apply the offset and substitute the zone as needed.
233
+ z = "UTC" ;
234
+ this . dt = dt . setZone ( "UTC" ) . plus ( { minutes : dt . offset } ) ;
235
+ this . originalZone = dt . zone ;
234
236
}
235
237
236
238
const intlOpts = { ...this . opts } ;
@@ -239,11 +241,35 @@ class PolyDateFormatter {
239
241
}
240
242
241
243
format ( ) {
244
+ if ( this . originalZone ) {
245
+ // If we have to substitute in the actual zone name, we have to use
246
+ // formatToParts so that the timezone can be replaced.
247
+ return this . formatToParts ( )
248
+ . map ( ( { value } ) => value )
249
+ . join ( "" ) ;
250
+ }
242
251
return this . dtf . format ( this . dt . toJSDate ( ) ) ;
243
252
}
244
253
245
254
formatToParts ( ) {
246
- return this . dtf . formatToParts ( this . dt . toJSDate ( ) ) ;
255
+ const parts = this . dtf . formatToParts ( this . dt . toJSDate ( ) ) ;
256
+ if ( this . originalZone ) {
257
+ return parts . map ( ( part ) => {
258
+ if ( part . type === "timeZoneName" ) {
259
+ const offsetName = this . originalZone . offsetName ( this . dt . ts , {
260
+ locale : this . dt . locale ,
261
+ format : this . opts . timeZoneName ,
262
+ } ) ;
263
+ return {
264
+ ...part ,
265
+ value : offsetName ,
266
+ } ;
267
+ } else {
268
+ return part ;
269
+ }
270
+ } ) ;
271
+ }
272
+ return parts ;
247
273
}
248
274
249
275
resolvedOptions ( ) {
0 commit comments