mirror of
https://github.com/harttle/liquidjs.git
synced 2026-09-18 22:10:41 -07:00
feat: timezoneOffset option to specify output timezone, see #375
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import strftime, { TimezoneDate } from '../../util/strftime'
|
||||
import strftime, { createDateFixedToTimezone } from '../../util/strftime'
|
||||
import { isString, isNumber } from '../../util/underscore'
|
||||
import { FilterImpl } from '../../template/filter/filter-impl'
|
||||
|
||||
export function date (this: FilterImpl, v: string | Date, arg: string) {
|
||||
let date = v
|
||||
let date: Date
|
||||
if (v === 'now' || v === 'today') {
|
||||
date = new Date()
|
||||
} else if (isNumber(v)) {
|
||||
@@ -12,10 +12,12 @@ export function date (this: FilterImpl, v: string | Date, arg: string) {
|
||||
if (/^\d+$/.test(v)) {
|
||||
date = new Date(+v * 1000)
|
||||
} else if (this.context.opts.preserveTimezones) {
|
||||
date = new TimezoneDate(v)
|
||||
date = createDateFixedToTimezone(v)
|
||||
} else {
|
||||
date = new Date(v)
|
||||
}
|
||||
} else {
|
||||
date = v
|
||||
}
|
||||
return isValidDate(date) ? strftime(date, arg) : v
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { FS } from './fs/fs'
|
||||
import * as fs from './fs/node'
|
||||
import { defaultOperators, Operators } from './render/operator'
|
||||
import { createTrie, Trie } from './util/operator-trie'
|
||||
import { timezoneOffset } from './util/strftime'
|
||||
|
||||
export interface LiquidOptions {
|
||||
/** A directory or an array of directories from where to resolve layout and include templates, and the filename passed to `.renderFile()`. If it's an array, the files are looked up in the order they occur in the array. Defaults to `["."]` */
|
||||
@@ -24,6 +25,8 @@ export interface LiquidOptions {
|
||||
strictVariables?: 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;
|
||||
/** 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. */
|
||||
@@ -108,6 +111,7 @@ export const defaultOptions: NormalizedFullOptions = {
|
||||
lenientIf: false,
|
||||
globals: {},
|
||||
keepOutputType: false,
|
||||
timezoneOffset: timezoneOffset,
|
||||
operators: defaultOperators,
|
||||
operatorsTrie: createTrie(defaultOperators)
|
||||
}
|
||||
|
||||
+29
-27
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user