diff --git a/docs/source/_data/sidebar.yml b/docs/source/_data/sidebar.yml index ac0631b11b..b44bef785c 100644 --- a/docs/source/_data/sidebar.yml +++ b/docs/source/_data/sidebar.yml @@ -36,6 +36,10 @@ filters: compact: compact.html concat: concat.html date: date.html + date_to_long_string: date_to_long_string.html + date_to_rfc822: date_to_rfc822.html + date_to_string: date_to_string.html + date_to_xmlschema: date_to_xmlschema.html default: default.html divided_by: divided_by.html downcase: downcase.html diff --git a/docs/source/filters/date_to_long_string.md b/docs/source/filters/date_to_long_string.md new file mode 100644 index 0000000000..a300797d73 --- /dev/null +++ b/docs/source/filters/date_to_long_string.md @@ -0,0 +1,30 @@ +--- +title: date_to_long_string +--- +{% since %}v10.13.0{% endsince %} + +Convert a date to long format. Same with Jekyll `date_to_long_string` filter. + +Input +```liquid +{{ site.time | date_to_long_string }} +``` + +Output +```text +07 November 2008 +``` + +Input +```liquid +{{ site.time | date_to_long_string: "ordinal" }} +``` + +Output +```text +7th November 2008 +``` + +Note that JavaScript `Date` has not timezone information, see [date][date] filter for details. + +[date]: ./date.html diff --git a/docs/source/filters/date_to_rfc822.md b/docs/source/filters/date_to_rfc822.md new file mode 100644 index 0000000000..afdd98cd63 --- /dev/null +++ b/docs/source/filters/date_to_rfc822.md @@ -0,0 +1,20 @@ +--- +title: date_to_rfc822 +--- +{% since %}v10.13.0{% endsince %} + +Convert a Date into the RFC-822 format used for RSS feeds, same as Jekyll filter `date_to_rfc822`. + +Input +```liquid +{{ site.time | date_to_rfc822 }} +``` + +Output +```text +Mon, 07 Nov 2008 13:07:54 -0800 +``` + +Note that JavaScript `Date` has not timezone information, see [date][date] filter for details. + +[date]: ./date.html diff --git a/docs/source/filters/date_to_string.md b/docs/source/filters/date_to_string.md new file mode 100644 index 0000000000..0534b8fe03 --- /dev/null +++ b/docs/source/filters/date_to_string.md @@ -0,0 +1,30 @@ +--- +title: date_to_string +--- +{% since %}v10.13.0{% endsince %} + +Convert a date to short format. Same with Jekyll `date_to_string` filter. + +Input +```liquid +{{ site.time | date_to_string }} +``` + +Output +```text +07 Nov 2008 +``` + +Input +```liquid +{{ site.time | date_to_string: "ordinal", "US" }} +``` + +Output +```text +Nov 7th, 2008 +``` + +Note that JavaScript `Date` has not timezone information, see [date][date] filter for details. + +[date]: ./date.html diff --git a/docs/source/filters/date_to_xmlschema.md b/docs/source/filters/date_to_xmlschema.md new file mode 100644 index 0000000000..dd2e870eb8 --- /dev/null +++ b/docs/source/filters/date_to_xmlschema.md @@ -0,0 +1,20 @@ +--- +title: date_to_xmlschema +--- +{% since %}v10.13.0{% endsince %} + +Convert a Date into XML Schema (ISO 8601) format, same as Jekyll filter `date_to_xmlschema`. + +Input +```liquid +{{ site.time | date_to_xmlschema }} +``` + +Output +```text +2008-11-07T13:07:54-08:00 +``` + +Note that JavaScript `Date` has not timezone information, see [date][date] filter for details. + +[date]: ./date.html diff --git a/docs/source/filters/overview.md b/docs/source/filters/overview.md index a5e11d3b8e..ded8bdb553 100644 --- a/docs/source/filters/overview.md +++ b/docs/source/filters/overview.md @@ -13,7 +13,7 @@ Math | plus, minus, modulo, times, floor, ceil, round, divided_by, abs, at_least String | append, prepend, capitalize, upcase, downcase, strip, lstrip, rstrip, strip_newlines, split, replace, replace_first, replace_last,remove, remove_first, remove_last, truncate, truncatewords, normalize_whitespace HTML/URI | escape, escape_once, url_encode, url_decode, strip_html, newline_to_br Array | slice, map, sort, sort_natural, uniq, where, where_exp, group_by, group_by_exp, find, find_exp, first, last, join, reverse, concat, compact, size, push, pop, shift, unshift -Date | date +Date | date, date_to_xmlschema, date_to_rfc822, date_to_string, date_to_long_string Misc | default, json, jsonify, inspect, raw, to_integer [shopify/liquid]: https://github.com/Shopify/liquid diff --git a/docs/source/zh-cn/filters/date_to_long_string.md b/docs/source/zh-cn/filters/date_to_long_string.md new file mode 100644 index 0000000000..0cb54dc74d --- /dev/null +++ b/docs/source/zh-cn/filters/date_to_long_string.md @@ -0,0 +1,31 @@ +--- +title: date_to_long_string +--- +{% since %}v10.13.0{% endsince %} + +把日期转换为长格式(只支持 US/UK 两种),与 Jekyll 的 `date_to_long_string` 过滤器一样。 + +输入 +```liquid +{{ site.time | date_to_long_string }} +``` + +输出 +```text +07 November 2008 +``` + +输入 +```liquid +{{ site.time | date_to_long_string: "ordinal" }} +``` + +输出 +```text +7th November 2008 +``` + + +注意 JavaScript `Date` 没有时区信息,详情请参考 [date][date] 过滤器。 + +[date]: ./date.html diff --git a/docs/source/zh-cn/filters/date_to_rfc822.md b/docs/source/zh-cn/filters/date_to_rfc822.md new file mode 100644 index 0000000000..fd704397e1 --- /dev/null +++ b/docs/source/zh-cn/filters/date_to_rfc822.md @@ -0,0 +1,20 @@ +--- +title: date_to_rfc822 +--- +{% since %}v10.13.0{% endsince %} + +把日期转换为 RFC-822 格式用于 RSS feed,与 Jekyll 的 `date_to_rfc822` 过滤器一样。 + +输入 +```liquid +{{ site.time | date_to_rfc822 }} +``` + +输入 +```text +Mon, 07 Nov 2008 13:07:54 -0800 +``` + +注意 JavaScript `Date` 没有时区信息,详情请参考 [date][date] 过滤器。 + +[date]: ./date.html diff --git a/docs/source/zh-cn/filters/date_to_string.md b/docs/source/zh-cn/filters/date_to_string.md new file mode 100644 index 0000000000..569454bf4c --- /dev/null +++ b/docs/source/zh-cn/filters/date_to_string.md @@ -0,0 +1,30 @@ +--- +title: date_to_string +--- +{% since %}v10.13.0{% endsince %} + +把日期转换为短格式(只支持 US/UK 两种),与 Jekyll 的 `date_to_string` 过滤器一样。 + +输入 +```liquid +{{ site.time | date_to_string }} +``` + +输出 +```text +07 Nov 2008 +``` + +输入 +```liquid +{{ site.time | date_to_string: "ordinal", "US" }} +``` + +输出 +```text +Nov 7th, 2008 +``` + +注意 JavaScript `Date` 没有时区信息,详情请参考 [date][date] 过滤器。 + +[date]: ./date.html diff --git a/docs/source/zh-cn/filters/date_to_xmlschema.md b/docs/source/zh-cn/filters/date_to_xmlschema.md new file mode 100644 index 0000000000..e1051cdc3d --- /dev/null +++ b/docs/source/zh-cn/filters/date_to_xmlschema.md @@ -0,0 +1,20 @@ +--- +title: date_to_xmlschema +--- +{% since %}v10.13.0{% endsince %} + +把日期转换为 XML Schema (ISO 8601) 格式,与 Jekyll 的 `date_to_xmlschema` 过滤器一样。 + +输入 +```liquid +{{ site.time | date_to_xmlschema }} +``` + +输出 +```text +2008-11-07T13:07:54-08:00 +``` + +注意 JavaScript `Date` 没有时区信息,详情请参考 [date][date] 过滤器。 + +[date]: ./date.html diff --git a/docs/source/zh-cn/filters/overview.md b/docs/source/zh-cn/filters/overview.md index ce13a9e598..c4bbfc7125 100644 --- a/docs/source/zh-cn/filters/overview.md +++ b/docs/source/zh-cn/filters/overview.md @@ -13,7 +13,7 @@ LiquidJS 共支持 40+ 个过滤器,可以分为如下几类: 字符串 | append, prepend, capitalize, upcase, downcase, strip, lstrip, rstrip, strip_newlines, split, replace, replace_first, replace_last, remove, remove_first, remove_last, truncate, truncatewords, normalize_whitespace HTML/URI | escape, escape_once, url_encode, url_decode, strip_html, newline_to_br 数组 | slice, map, sort, sort_natural, uniq, where, where_exp, group_by, group_by_exp, find, find_exp, first, last, join, reverse, concat, compact, size, push, pop, shift, unshift -日期 | date +日期 | date, date_to_xmlschema, date_to_rfc822, date_to_string, date_to_long_string 其他 | default, json, jsonify, inspect, raw, to_integer [shopify/liquid]: https://github.com/Shopify/liquid diff --git a/src/filters/date.ts b/src/filters/date.ts index 31ce2c890e..eede5fa915 100644 --- a/src/filters/date.ts +++ b/src/filters/date.ts @@ -1,13 +1,46 @@ import { toValue, stringify, isString, isNumber, TimezoneDate, LiquidDate, strftime, isNil } from '../util' import { FilterImpl } from '../template' +import { LiquidOptions } from '../liquid-options' export function date (this: FilterImpl, v: string | Date, format?: string, timezoneOffset?: number | string) { - const opts = this.context.opts + const date = parseDate(v, this.context.opts, timezoneOffset) + if (!date) return v + format = toValue(format) + format = isNil(format) ? this.context.opts.dateFormat : stringify(format) + return strftime(date, format) +} + +export function date_to_xmlschema (this: FilterImpl, v: string | Date) { + return date.call(this, v, '%Y-%m-%dT%H:%M:%S%:z') +} + +export function date_to_rfc822 (this: FilterImpl, v: string | Date) { + return date.call(this, v, '%a, %d %b %Y %H:%M:%S %z') +} + +export function date_to_string (this: FilterImpl, v: string | Date, type?: string, style?: string) { + return stringify_date.call(this, v, '%b', type, style) +} + +export function date_to_long_string (this: FilterImpl, v: string | Date, type?: string, style?: string) { + return stringify_date.call(this, v, '%B', type, style) +} + +function stringify_date (this: FilterImpl, v: string | Date, month_type: string, type?: string, style?: string) { + const date = parseDate(v, this.context.opts) + if (!date) return v + if (type === 'ordinal') { + const d = date.getDate() + return style === 'US' + ? strftime(date, `${month_type} ${d}%q, %Y`) + : strftime(date, `${d}%q ${month_type} %Y`) + } + return strftime(date, `%d ${month_type} %Y`) +} + +function parseDate (v: string | Date, opts: LiquidOptions, timezoneOffset?: number | string): LiquidDate | undefined { let date: LiquidDate v = toValue(v) - format = toValue(format) - if (isNil(format)) format = opts.dateFormat - else format = stringify(format) if (v === 'now' || v === 'today') { date = new Date() } else if (isNumber(v)) { @@ -23,13 +56,13 @@ export function date (this: FilterImpl, v: string | Date, format?: string, timez } else { date = v } - if (!isValidDate(date)) return v + if (!isValidDate(date)) return if (timezoneOffset !== undefined) { date = new TimezoneDate(date, timezoneOffset) } else if (!(date instanceof TimezoneDate) && opts.timezoneOffset !== undefined) { date = new TimezoneDate(date, opts.timezoneOffset) } - return strftime(date, format) + return date } function isValidDate (date: any): date is Date { diff --git a/src/util/strftime.ts b/src/util/strftime.ts index 335379d67a..b6bf2652f8 100644 --- a/src/util/strftime.ts +++ b/src/util/strftime.ts @@ -45,31 +45,16 @@ function isLeapYear (d: LiquidDate) { const year = d.getFullYear() return !!((year & 3) === 0 && (year % 100 || (year % 400 === 0 && year))) } -function getSuffix (d: LiquidDate) { +function ordinal (d: LiquidDate) { const date = d.getDate() + if ([11, 12, 13].includes(date)) return 'th' - let suffix = 'th' - - switch (date) { - case 11: - case 12: - case 13: - break - default: - switch (date % 10) { - case 1: - suffix = 'st' - break - case 2: - suffix = 'nd' - break - case 3: - suffix = 'rd' - break - } + switch (date % 10) { + case 1: return 'st' + case 2: return 'nd' + case 3: return 'rd' + default: return 'th' } - - return suffix } function century (d: LiquidDate) { return parseInt(d.getFullYear().toString().substring(0, 2), 10) @@ -138,7 +123,7 @@ const formatCodes = { }, p: (d: LiquidDate) => (d.getHours() < 12 ? 'AM' : 'PM'), P: (d: LiquidDate) => (d.getHours() < 12 ? 'am' : 'pm'), - q: (d: LiquidDate) => getSuffix(d), + q: (d: LiquidDate) => ordinal(d), s: (d: LiquidDate) => Math.round(d.getTime() / 1000), S: (d: LiquidDate) => d.getSeconds(), u: (d: LiquidDate) => d.getDay() || 7, diff --git a/test/integration/filters/date.spec.ts b/test/integration/filters/date.spec.ts index d944285e16..f2772402f7 100644 --- a/test/integration/filters/date.spec.ts +++ b/test/integration/filters/date.spec.ts @@ -146,3 +146,54 @@ describe('filters/date', function () { }) }) }) +describe('filters/date_to_xmlschema', function () { + const liquid = new Liquid() + it('should support literal date', function () { + const output = liquid.parseAndRenderSync('{{ "1990-10-15T23:00:00" | date_to_xmlschema }}') + expect(output).toMatch(/^1990-10-15T23:00:00[+-]\d\d:\d\d$/) + }) + it('should timezoned date', function () { + const liquid = new Liquid({ preserveTimezones: true }) + const output = liquid.parseAndRenderSync('{{ "2008-11-07T13:07:54-08:00" | date_to_xmlschema }}') + expect(output).toEqual('2008-11-07T13:07:54-08:00') + }) +}) +describe('filters/date_to_rfc822', function () { + const liquid = new Liquid() + it('should support literal date', function () { + const output = liquid.parseAndRenderSync('{{ "1990-10-15T23:00:00" | date_to_rfc822 }}') + expect(output).toMatch(/^Mon, 15 Oct 1990 23:00:00 [+-]\d{4}$/) + }) + it('should timezoned date', function () { + const liquid = new Liquid({ preserveTimezones: true }) + const output = liquid.parseAndRenderSync('{{ "2008-11-07T13:07:54-08:00" | date_to_rfc822 }}') + expect(output).toEqual('Fri, 07 Nov 2008 13:07:54 -0800') + }) +}) +describe('filters/date_to_string', function () { + const liquid = new Liquid({ preserveTimezones: true }) + it('should default to non-ordinal, UK', function () { + const output = liquid.parseAndRenderSync('{{ "2008-11-07T13:07:54-08:00" | date_to_string }}') + expect(output).toEqual('07 Nov 2008') + }) + it('should support ordinal, US', function () { + const output = liquid.parseAndRenderSync('{{ "2008-11-07T13:07:54-08:00" | date_to_string: "ordinal", "US" }}') + expect(output).toEqual('Nov 7th, 2008') + }) +}) + +describe('filters/date_to_long_string', function () { + const liquid = new Liquid({ preserveTimezones: true }) + it('should default to non-ordinal, UK', function () { + const output = liquid.parseAndRenderSync('{{ "2008-11-07T13:07:54-08:00" | date_to_long_string }}') + expect(output).toEqual('07 November 2008') + }) + it('should support ordinal, US', function () { + const output = liquid.parseAndRenderSync('{{ "2008-11-07T13:07:54-08:00" | date_to_long_string: "ordinal", "US" }}') + expect(output).toEqual('November 7th, 2008') + }) + it('should support ordinal, UK', function () { + const output = liquid.parseAndRenderSync('{{ "2008-11-07T13:07:54-08:00" | date_to_long_string: "ordinal" }}') + expect(output).toEqual('7th November 2008') + }) +})