feat: timezoneOffset option to specify output timezone, see #375

This commit is contained in:
Harttle
2021-09-27 01:54:31 +08:00
committed by harttle
parent 91c721e650
commit 6b9f872bcc
8 changed files with 76 additions and 36 deletions
+29 -27
View File
@@ -1,5 +1,7 @@
import { changeCase, padStart, padEnd } from './underscore'
export const timezoneOffset = new Date().getTimezoneOffset()
const ISO8601_TIMEZONE_PATTERN = /([zZ]|([+-])(\d{2}):(\d{2}))$/
const rFormat = /%([-_0^#:]+)?(\d+)?([EO])?(.)/
const monthNames = [
'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August',
@@ -125,11 +127,10 @@ const formatCodes = {
y: (d: Date) => d.getFullYear().toString().substring(2, 4),
Y: (d: Date) => d.getFullYear(),
z: (d: Date, opts: FormatOptions) => {
const offset = d.getTimezoneOffset()
const nOffset = Math.abs(offset)
const nOffset = Math.abs(timezoneOffset)
const h = Math.floor(nOffset / 60)
const m = nOffset % 60
return (offset > 0 ? '-' : '+') +
return (timezoneOffset > 0 ? '-' : '+') +
padStart(h, 2, '0') +
(opts.flags[':'] ? ':' : '') +
padStart(m, 2, '0')
@@ -140,12 +141,7 @@ const formatCodes = {
};
(formatCodes as any).h = formatCodes.b
export default function (inputDate: Date, formatStr: string) {
let d = inputDate
if (d instanceof TimezoneDate) {
d = d.getDisplayDate()
}
export default function (d: Date, formatStr: string) {
let output = ''
let remaining = formatStr
let match
@@ -174,24 +170,30 @@ function format (d: Date, match: RegExpExecArray) {
return padStart(ret, padWidth, padChar)
}
export class TimezoneDate extends Date {
ISO8601_TIMEZONE_PATTERN = /([zZ]|([+-])(\d{2}):(\d{2}))$/;
inputTimezoneOffset = 0;
constructor (public dateString: string) {
super(dateString)
const m = dateString.match(this.ISO8601_TIMEZONE_PATTERN)
if (m && m[1] === 'Z') {
this.inputTimezoneOffset = this.getTimezoneOffset()
} else if (m && m[2] && m[3] && m[4]) {
const [, , sign, hours, minutes] = m
const delta = (sign === '+' ? 1 : -1) * (parseInt(hours, 10) * 60 + parseInt(minutes, 10))
this.inputTimezoneOffset = this.getTimezoneOffset() + delta
}
/**
* Create a Date object fixed to it's declared Timezone. Both
* - 2021-08-06T02:29:00.000Z and
* - 2021-08-06T02:29:00.000+08:00
* will always be displayed as
* - 2021-08-06 02:29:00
* regardless timezoneOffset in JavaScript realm
*
* The implementation hack:
* Instead of calling `.getMonth()`/`.getUTCMonth()` respect to `preserveTimezones`,
* we create a different Date to trick strftime, it's both simpler and more performant.
* Given that a template is expected to be parsed fewer times than rendered.
*/
export function createDateFixedToTimezone (dateString: string) {
const m = dateString.match(ISO8601_TIMEZONE_PATTERN)
// representing a UTC datetime
if (m && m[1] === 'Z') {
return new Date(+new Date(dateString) + timezoneOffset * 60000)
}
getDisplayDate (): Date {
return new Date((+this) + this.inputTimezoneOffset * 60 * 1000)
// has a timezone specified
if (m && m[2] && m[3] && m[4]) {
const [, , sign, hours, minutes] = m
const delta = (sign === '+' ? 1 : -1) * (parseInt(hours, 10) * 60 + parseInt(minutes, 10))
return new Date(+new Date(dateString) + (timezoneOffset + delta) * 60000)
}
return new Date(dateString)
}