From 3fb66465c6fe1bf4dc2e1ace9157c23d0fc8f859 Mon Sep 17 00:00:00 2001 From: prassie Date: Wed, 22 Feb 2023 21:26:33 +0530 Subject: [PATCH] feat: LiquidOptions.dateFormat to override default date format (#587) doc: updates related to LiquidOptions.dateFormat doc: revamped the date filter doc for easier reference --- docs/source/filters/date.md | 105 +++++++++++-------------- docs/source/tutorials/options.md | 2 + docs/source/zh-cn/tutorials/options.md | 1 + src/filters/date.ts | 2 +- src/liquid-options.ts | 2 + test/integration/filters/date.ts | 18 +++++ 6 files changed, 71 insertions(+), 59 deletions(-) diff --git a/docs/source/filters/date.md b/docs/source/filters/date.md index f180e412b..b449b60d2 100644 --- a/docs/source/filters/date.md +++ b/docs/source/filters/date.md @@ -1,77 +1,66 @@ --- title: date --- - {% since %}v1.9.1{% endsince %} -Converts a timestamp into another date format. LiquidJS tries to be conformant with Shopify/Liquid which is using Ruby's core [Time#strftime(string)](http://www.ruby-doc.org/core/Time.html#method-i-strftime). The input is firstly converted to `Date` object via [new Date()][newDate]. +# Format +* Converts a timestamp into another date format +* LiquidJS tries to be conformant with Shopify/Liquid which is using Ruby's core [Time#strftime(string)](http://www.ruby-doc.org/core/Time.html#method-i-strftime) + * Refer [format flags](https://ruby-doc.org/core/strftime_formatting_rdoc.html) + * Not all options are supported though - refer [differences here](/tutorials/differences.html#Differences) +* The input is firstly converted to `Date` object via [new Date()][jsDate] +* Date format can be provided individually as a filter option + * If not provided, then `%A, %B %-e, %Y at %-l:%M %P %z` format will be used as default format + * Override this using [`dateFormat`](/api/interfaces/liquid_options_.liquidoptions.html#Optional-dateFormat) LiquidJS option, to set your preferred default format for all date filters -Input +### Examples ```liquid -{{ article.published_at | date: "%a, %b %d, %y" }} +{{ article.published_at | date: '%a, %b %d, %y' }} => Fri, Jul 17, 15 +{{ "now" | date: "%Y-%m-%d %H:%M" }} => 2020-03-25 15:57 + +// equivalent to setting options.dateFormat = %d%q of %b %Y at %I:%M %P %Z +{{ '1990-12-31T23:30:28Z' | date: '%d%q of %b %Y at %I:%M %P %Z', -330 }} => 01st of Jan 1991 at 05:00 am +0530; ``` -Output -```text -Fri, Jul 17, 15 -``` +# TimeZone +* By default, dates will be converted to local timezone before output +* You can override that by, + * setting a timezone for each individual `date` filter via the second parameter + * using the [`timezoneOffset`](/api/interfaces/liquid_options_.liquidoptions.html#Optional-timezoneOffset) LiquidJS option + * Its default value is your local timezone offset which can be obtained by `new Date().getTimezoneOffset()` + * Offset can be set as, + * minutes: `-360` means `'+06:00'` and `360` means `'-06:00'` + * timeZone ID: `Asia/Colombo` or `America/New_York` + * Use minutes for better performance with repeated processing of templates with many dates like, converting template for each email recipient + * Refer [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) for TZ database values -{% note info TimeZone %} -Date will be converted to local time before output. To avoid that, you can set `timezoneOffset` LiquidJS option to `0`, its default value is your local timezone offset which can be obtained by `new Date().getTimezoneOffset()`. -{% endnote %} - -And you can set a timezone for each individual `date` filter via the second parameter: - -Input +### Examples ```liquid -{{ "1990-12-31T23:00:00Z" | date: "%Y-%m-%dT%H:%M:%S", 360}} // equivalent to setting `options.timezoneOffset` to `360`. -{{ "1990-12-31T23:00:00Z" | date: "%Y-%m-%dT%H:%M:%S", "Asia/Colombo" }} +// equivalent to setting `options.timezoneOffset` to `360` +{{ "1990-12-31T23:00:00Z" | date: "%Y-%m-%dT%H:%M:%S", 360 }} => 1990-12-31T17:00:00 +{{ "1990-12-31T23:00:00Z" | date: "%Y-%m-%dT%H:%M:%S", "Asia/Colombo" }} => 1991-01-01T04:30:00 ``` -Output + +# Input +* `date` works on strings if they contain well-formatted dates +* Note that LiquidJS is using [JavaScript Date][jsDate] to parse the input string, that means [IETF-compliant RFC 2822 timestamps](https://datatracker.ietf.org/doc/html/rfc2822#page-14) and strings in [a version of ISO8601](https://www.ecma-international.org/ecma-262/11.0/#sec-date.parse) are supported. + +### Examples ```liquid -1990-12-31T17:00:00 -1991-01-01T04:30:00 +{{ "1990-12-31T23:00:00Z" | date: "%Y-%m-%dT%H:%M:%S", 360 }} => 1990-12-31T17:00:00 +{{ "March 14, 2016" | date: "%b %d, %y" }} => Mar 14, 16 ``` -Input + +# Current Date +* To get the current time, pass the special word `"now"` or `"today"` as input +* Note that the value will be the current time of when the page was last generated from the template, not when the page is presented to a user if caching or static site generation is involved + +### Example ```liquid -{{ article.published_at | date: "%Y" }} +Last updated on: {{ "now" | date: "%Y-%m-%d %H:%M" }} => Last updated on: 2020-03-25 15:57 +Last updated on: {{ "today" | date: "%Y-%m-%d %H:%M" }} => Last updated on: 2020-03-25 15:57 ``` -Output -```text -2015 -``` - -`date` works on strings if they contain well-formatted dates: - -Input -```liquid -{{ "March 14, 2016" | date: "%b %d, %y" }} -``` - -Output -```text -Mar 14, 16 -``` - -{% note info Timestamp Strings %} -Note that LiquidJS is using JavaScript [Date][newDate] to parse the input string, that means [IETF-compliant RFC 2822 timestamps](https://datatracker.ietf.org/doc/html/rfc2822#page-14) and strings in [a version of ISO8601](https://www.ecma-international.org/ecma-262/11.0/#sec-date.parse) are supported. -{% endnote %} - -To get the current time, pass the special word `"now"` (or `"today"`) to `date`: - -Input -```liquid -This page was last updated at {{ "now" | date: "%Y-%m-%d %H:%M" }}. -``` - -Output -```text -This page was last updated at 2020-03-25 15:57. -``` - -{% note info now %}Note that the value will be the current time of when the page was last generated from the template, not when the page is presented to a user if caching or static site generation is involved.{% endnote %} - -[newDate]: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Date +[jsDate]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date diff --git a/docs/source/tutorials/options.md b/docs/source/tutorials/options.md index 144818f1e..6ccc4caa8 100644 --- a/docs/source/tutorials/options.md +++ b/docs/source/tutorials/options.md @@ -108,6 +108,8 @@ it defaults to false. For example, when set to true, a blank string would evalu **preserveTimezones** is a boolean effects only literal timestamps. When set to `true`, all literal timestamps will remain the same when output. This is a parser option, so Date objects passed to LiquidJS as data will not be affected. Note that `preserveTimezones` has a higher priority than `timezoneOffset`. +**dateFormat** is used to specify a default format to output dates. `%A, %B %-e, %Y at %-l:%M %P %z` will be used if not specified. For example, set `dateFormat: %Y-%m-%dT%H:%M:%S:%LZ` to output all dates in [JavaScript Date.toJson()][https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toJSON] format. + ## Trimming **greedy**, **trimOutputLeft**, **trimOutputRight**, **trimTagLeft**, **trimTagRight** options are used to eliminate extra newlines and indents in templates around Liquid Constructs. See [Whitespace Control][wc] for details. diff --git a/docs/source/zh-cn/tutorials/options.md b/docs/source/zh-cn/tutorials/options.md index bd9ce651b..a6f100049 100644 --- a/docs/source/zh-cn/tutorials/options.md +++ b/docs/source/zh-cn/tutorials/options.md @@ -106,6 +106,7 @@ LiquidJS 把这个选项默认值设为 true 以兼容于 shopify/l **preserveTimezones** 是一个布尔值,只影响时间戳字面量。当设置为 `true` 时,所有字面量的时间戳字符串会在输出时保持原状,即不论输入时采取怎样的时区,输出时仍然采用那一时区(和 Shopify Liquid 的行为一致)。注意这是一个解析器参数,渲染时传入的数据中的日期的输出不会受此参数影响。注意 `preserveTimezones` 比 `timezoneOffset` 的优先级更高。 +**dateFormat** 用于指定输出日期的默认格式. `%A, %B %-e, %Y at %-l:%M %P %z` 如果未指定,将使用. 例如,设置 `dateFormat: %Y-%m-%dT%H:%M:%S:%LZ` 以输出 [JavaSrcipt Date.toJson()][https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Date/toJSON] 格式. ## 换行和缩进 diff --git a/src/filters/date.ts b/src/filters/date.ts index 62156a1cc..6483590c7 100644 --- a/src/filters/date.ts +++ b/src/filters/date.ts @@ -8,7 +8,7 @@ export function date (this: FilterImpl, v: string | Date, format?: string, timez let date: LiquidDate v = toValue(v) format = toValue(format) - if (isNil(format)) format = DEFAULT_FMT + if (isNil(format)) format = opts.dateFormat ?? DEFAULT_FMT else format = stringify(format) if (v === 'now' || v === 'today') { date = new Date() diff --git a/src/liquid-options.ts b/src/liquid-options.ts index c0ff85671..5e45620f2 100644 --- a/src/liquid-options.ts +++ b/src/liquid-options.ts @@ -38,6 +38,8 @@ export interface LiquidOptions { lenientIf?: boolean; /** 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; + /** Default date format to use if the date filter doesn't include a format. Defaults to `%A, %B %-e, %Y at %-l:%M %P %z`. */ + dateFormat?: 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 62123bf84..bc4216255 100644 --- a/test/integration/filters/date.ts +++ b/test/integration/filters/date.ts @@ -103,4 +103,22 @@ describe('filters/date', function () { return test('{{ "1990-12-31T23:00:00+02:30" | date: "%Y-%m-%dT%H:%M:%S"}}', '1990-12-31T23:00:00', undefined, opts) }) }) + describe('dateFormat', function () { + const optsWithoutDateFormat: LiquidOptions = { timezoneOffset: 360 } // -06:00 + // date.DEFAULT_FMT: '%A, %B %-e, %Y at %-l:%M %P %z' + it('should use default format for date filters without format argument', function () { + return test('{{ "2022-12-08T03:22:18.000Z" | date }}', 'Wednesday, December 7, 2022 at 9:22 pm -0600', undefined, optsWithoutDateFormat) + }) + it('should use given date filter format argument and NOT default format', function () { + return test('{{ "1990-12-31T23:00:00Z" | date: "%Y-%m-%dT%H:%M:%S" }}', '1990-12-31T17:00:00', undefined, optsWithoutDateFormat) + }) + + const optsWithDateFormat: LiquidOptions = { timezoneOffset: -330, dateFormat: '%d%q of %b %Y at %I:%M %P' } // -06:00, 31st of Dec 1990 at 11:00 pm + it('should use configured `options.dateFormat` for date filters without format argument', function () { + return test('{{ "2022-12-08T13:30:18.000Z" | date }}', '08th of Dec 2022 at 07:00 pm', undefined, optsWithDateFormat) + }) + it('should use given date filter format argument and NOT `options.dateFormat`', function () { + return test('{{ "1990-12-31T23:00:00Z" | date: "%Y-%m-%dT%H:%M:%S" }}', '1991-01-01T04:30:00', undefined, optsWithDateFormat) + }) + }) })