diff --git a/src/filters/date.ts b/src/filters/date.ts index e4ca440203..33bed77248 100644 --- a/src/filters/date.ts +++ b/src/filters/date.ts @@ -1,7 +1,7 @@ import { toValue, stringify, isString, isNumber, TimezoneDate, LiquidDate, strftime } from '../util' import { FilterImpl } from '../template' -export function date (this: FilterImpl, v: string | Date, format: string, timeZoneOffset?: number) { +export function date (this: FilterImpl, v: string | Date, format: string, timezoneOffset?: number | string) { const opts = this.context.opts let date: LiquidDate v = toValue(v) @@ -22,10 +22,10 @@ export function date (this: FilterImpl, v: string | Date, format: string, timeZo date = v } if (!isValidDate(date)) return v - if (timeZoneOffset !== undefined) { - date = new TimezoneDate(date, timeZoneOffset) + if (timezoneOffset !== undefined) { + date = new TimezoneDate(date, parseTimezoneOffset(date, timezoneOffset)) } else if (opts.timezoneOffset !== undefined) { - date = new TimezoneDate(date, opts.timezoneOffset!) + date = new TimezoneDate(date, parseTimezoneOffset(date, opts.timezoneOffset)) } return strftime(date, format) } @@ -33,3 +33,13 @@ export function date (this: FilterImpl, v: string | Date, format: string, timeZo function isValidDate (date: any): date is Date { return (date instanceof Date || date instanceof TimezoneDate) && !isNaN(date.getTime()) } + +/** + * need pass in a `date` because offset is dependent on whether DST is active + */ +function parseTimezoneOffset (date: Date, timeZone: string | number) { + if (isNumber(timeZone)) return timeZone + const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' })) + const tzDate = new Date(date.toLocaleString('en-US', { timeZone })) + return (utcDate.getTime() - tzDate.getTime()) / 6e4 +} diff --git a/src/liquid-options.ts b/src/liquid-options.ts index 1bc261584b..ce1cf6ad41 100644 --- a/src/liquid-options.ts +++ b/src/liquid-options.ts @@ -35,8 +35,8 @@ export interface LiquidOptions { ownPropertyOnly?: boolean; /** Modifies the behavior of `strictVariables`. If set, a single undefined variable will *not* cause an exception in the context of the `if`/`elsif`/`unless` tag and the `default` filter. Instead, it will evaluate to `false` and `null`, respectively. Irrelevant if `strictVariables` is not set. Defaults to `false`. **/ lenientIf?: boolean; - /** JavaScript timezoneOffset for `date` filter, default to local time. That means if you're in Australia (UTC+10), it'll default to -600 */ - timezoneOffset?: number; + /** JavaScript timezone name or timezoneOffset for `date` filter, default to local time. That means if you're in Australia (UTC+10), it'll default to `-600` or `Australia/Lindeman` */ + timezoneOffset?: number | string; /** Strip blank characters (including ` `, `\t`, and `\r`) from the right of tags (`{% %}`) until `\n` (inclusive). Defaults to `false`. */ trimTagRight?: boolean; /** Similar to `trimTagRight`, whereas the `\n` is exclusive. Defaults to `false`. See Whitespace Control for details. */ diff --git a/test/integration/filters/date.ts b/test/integration/filters/date.ts index 55783469fa..ea6902d3cb 100644 --- a/test/integration/filters/date.ts +++ b/test/integration/filters/date.ts @@ -64,16 +64,29 @@ describe('filters/date', function () { it('should support timezone offset argument', function () { return test('{{ "1990-12-31T23:00:00Z" | date: "%Y-%m-%dT%H:%M:%S", 360}}', '1990-12-31T17:00:00') }) + it('should support timezone name argument', function () { + return test('{{ "1990-12-31T23:00:00Z" | date: "%Y-%m-%dT%H:%M:%S", "Asia/Colombo" }}', '1991-01-01T04:30:00') + }) + it('should support timezone name argument when DST is not active', function () { + return test('{{ "2021-01-01T23:00:00Z" | date: "%Y-%m-%dT%H:%M:%S", "America/New_York" }}', '2021-01-01T18:00:00') + }) + it('should support timezone name argument when DST is active', function () { + return test('{{ "2021-06-01T23:00:00Z" | date: "%Y-%m-%dT%H:%M:%S", "America/New_York" }}', '2021-06-01T19:00:00') + }) it('should offset date literal with timezone 00:00 specified', function () { return test('{{ "1990-12-31T23:00:00+00:00" | date: "%Y-%m-%dT%H:%M:%S"}}', '1990-12-31T17:00:00', undefined, opts) }) it('should offset date literal with timezone -01:00 specified', function () { return test('{{ "1990-12-31T23:00:00-01:00" | date: "%Y-%m-%dT%H:%M:%S"}}', '1990-12-31T18:00:00', undefined, opts) }) - it('should offset date from scope', function () { + it('should offset date from scope (timezone offset)', function () { const scope = { date: new Date('1990-12-31T23:00:00Z') } return test('{{ date | date: "%Y-%m-%dT%H:%M:%S"}}', scope, '1990-12-31T17:00:00', opts) }) + it('should offset date from scope (timezone name)', function () { + const scope = { date: new Date('1990-12-31T23:00:00Z') } + return test('{{ date | date: "%Y-%m-%dT%H:%M:%S"}}', scope, '1990-12-31T17:00:00', { timezoneOffset: 'America/Merida' }) + }) it('should reflect timezoneOffset', function () { const scope = { date: new Date('1990-12-31T23:00:00Z') } return test('{{ date | date: "%z"}}', scope, '-0600', opts)