From c56ecab515c28d2ad45c630080bfbf549675528a Mon Sep 17 00:00:00 2001 From: kirjs Date: Tue, 9 Feb 2021 14:53:48 -0500 Subject: [PATCH] feat(common): support ICU standard "stand alone day of week" with `DatePipe` (#40766) This commit adds support for Finnish full date formatting, as well as `c/cc/ccc/cccc/ccccc/cccccc` date formats in the `DatePipe`. Fixes #26922 PR Close #40766 --- .../size-tracking/integration-payloads.json | 2 +- packages/common/src/i18n/format_date.ts | 24 +++- packages/common/src/pipes/date_pipe.ts | 123 +++++++++--------- packages/common/test/i18n/format_date_spec.ts | 19 +++ 4 files changed, 106 insertions(+), 62 deletions(-) diff --git a/goldens/size-tracking/integration-payloads.json b/goldens/size-tracking/integration-payloads.json index 38791a2991..3a456cfc71 100644 --- a/goldens/size-tracking/integration-payloads.json +++ b/goldens/size-tracking/integration-payloads.json @@ -30,7 +30,7 @@ "master": { "uncompressed": { "runtime-es2015": 1485, - "main-es2015": 136703, + "main-es2015": 137236, "polyfills-es2015": 37641 } } diff --git a/packages/common/src/i18n/format_date.ts b/packages/common/src/i18n/format_date.ts index 9e2b9c9a38..75e94af556 100644 --- a/packages/common/src/i18n/format_date.ts +++ b/packages/common/src/i18n/format_date.ts @@ -13,7 +13,7 @@ export const ISO8601_DATE_REGEX = // 1 2 3 4 5 6 7 8 9 10 11 const NAMED_FORMATS: {[localeId: string]: {[format: string]: string}} = {}; const DATE_FORMATS_SPLIT = - /((?:[^GyYMLwWdEabBhHmsSzZO']+)|(?:'(?:[^']|'')*')|(?:G{1,5}|y{1,4}|Y{1,4}|M{1,5}|L{1,5}|w{1,2}|W{1}|d{1,2}|E{1,6}|a{1,5}|b{1,5}|B{1,5}|h{1,2}|H{1,2}|m{1,2}|s{1,2}|S{1,3}|z{1,4}|Z{1,5}|O{1,4}))([\s\S]*)/; + /((?:[^BEGHLMOSWYZabcdhmswyz']+)|(?:'(?:[^']|'')*')|(?:G{1,5}|y{1,4}|Y{1,4}|M{1,5}|L{1,5}|w{1,2}|W{1}|d{1,2}|E{1,6}|c{1,6}|a{1,5}|b{1,5}|B{1,5}|h{1,2}|H{1,2}|m{1,2}|s{1,2}|S{1,3}|z{1,4}|Z{1,5}|O{1,4}))([\s\S]*)/; enum ZoneWidth { Short, @@ -445,7 +445,7 @@ const DATE_FORMATS: {[format: string]: DateFormatter} = {}; // Based on CLDR formats: // See complete list: http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table // See also explanations: http://cldr.unicode.org/translation/date-time -// TODO(ocombe): support all missing cldr formats: Y, U, Q, D, F, e, c, j, J, C, A, v, V, X, x +// TODO(ocombe): support all missing cldr formats: U, Q, D, F, e, j, J, C, A, v, V, X, x function getDateFormatter(format: string): DateFormatter|null { if (DATE_FORMATS[format]) { return DATE_FORMATS[format]; @@ -557,6 +557,26 @@ function getDateFormatter(format: string): DateFormatter|null { formatter = dateGetter(DateType.Date, 2); break; + // Day of the Week StandAlone (1, 1, Mon, Monday, M, Mo) + case 'c': + case 'cc': + formatter = dateGetter(DateType.Day, 1); + break; + case 'ccc': + formatter = + dateStrGetter(TranslationType.Days, TranslationWidth.Abbreviated, FormStyle.Standalone); + break; + case 'cccc': + formatter = dateStrGetter(TranslationType.Days, TranslationWidth.Wide, FormStyle.Standalone); + break; + case 'ccccc': + formatter = + dateStrGetter(TranslationType.Days, TranslationWidth.Narrow, FormStyle.Standalone); + break; + case 'cccccc': + formatter = dateStrGetter(TranslationType.Days, TranslationWidth.Short, FormStyle.Standalone); + break; + // Day of the Week case 'E': case 'EE': diff --git a/packages/common/src/pipes/date_pipe.ts b/packages/common/src/pipes/date_pipe.ts index 3109d9f77b..5fced0e337 100644 --- a/packages/common/src/pipes/date_pipe.ts +++ b/packages/common/src/pipes/date_pipe.ts @@ -54,65 +54,70 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error'; * Format details depend on the locale. * Fields marked with (*) are only available in the extra data set for the given locale. * - * | Field type | Format | Description | Example Value | - * |--------------------|-------------|---------------------------------------------------------------|------------------------------------------------------------| - * | Era | G, GG & GGG | Abbreviated | AD | - * | | GGGG | Wide | Anno Domini | - * | | GGGGG | Narrow | A | - * | Year | y | Numeric: minimum digits | 2, 20, 201, 2017, 20173 | - * | | yy | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 | - * | | yyy | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 | - * | | yyyy | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 | - * | Week-numbering year| Y | Numeric: minimum digits | 2, 20, 201, 2017, 20173 | - * | | YY | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 | - * | | YYY | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 | - * | | YYYY | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 | - * | Month | M | Numeric: 1 digit | 9, 12 | - * | | MM | Numeric: 2 digits + zero padded | 09, 12 | - * | | MMM | Abbreviated | Sep | - * | | MMMM | Wide | September | - * | | MMMMM | Narrow | S | - * | Month standalone | L | Numeric: 1 digit | 9, 12 | - * | | LL | Numeric: 2 digits + zero padded | 09, 12 | - * | | LLL | Abbreviated | Sep | - * | | LLLL | Wide | September | - * | | LLLLL | Narrow | S | - * | Week of year | w | Numeric: minimum digits | 1... 53 | - * | | ww | Numeric: 2 digits + zero padded | 01... 53 | - * | Week of month | W | Numeric: 1 digit | 1... 5 | - * | Day of month | d | Numeric: minimum digits | 1 | - * | | dd | Numeric: 2 digits + zero padded | 01 | - * | Week day | E, EE & EEE | Abbreviated | Tue | - * | | EEEE | Wide | Tuesday | - * | | EEEEE | Narrow | T | - * | | EEEEEE | Short | Tu | - * | Period | a, aa & aaa | Abbreviated | am/pm or AM/PM | - * | | aaaa | Wide (fallback to `a` when missing) | ante meridiem/post meridiem | - * | | aaaaa | Narrow | a/p | - * | Period* | B, BB & BBB | Abbreviated | mid. | - * | | BBBB | Wide | am, pm, midnight, noon, morning, afternoon, evening, night | - * | | BBBBB | Narrow | md | - * | Period standalone* | b, bb & bbb | Abbreviated | mid. | - * | | bbbb | Wide | am, pm, midnight, noon, morning, afternoon, evening, night | - * | | bbbbb | Narrow | md | - * | Hour 1-12 | h | Numeric: minimum digits | 1, 12 | - * | | hh | Numeric: 2 digits + zero padded | 01, 12 | - * | Hour 0-23 | H | Numeric: minimum digits | 0, 23 | - * | | HH | Numeric: 2 digits + zero padded | 00, 23 | - * | Minute | m | Numeric: minimum digits | 8, 59 | - * | | mm | Numeric: 2 digits + zero padded | 08, 59 | - * | Second | s | Numeric: minimum digits | 0... 59 | - * | | ss | Numeric: 2 digits + zero padded | 00... 59 | - * | Fractional seconds | S | Numeric: 1 digit | 0... 9 | - * | | SS | Numeric: 2 digits + zero padded | 00... 99 | - * | | SSS | Numeric: 3 digits + zero padded (= milliseconds) | 000... 999 | - * | Zone | z, zz & zzz | Short specific non location format (fallback to O) | GMT-8 | - * | | zzzz | Long specific non location format (fallback to OOOO) | GMT-08:00 | - * | | Z, ZZ & ZZZ | ISO8601 basic format | -0800 | - * | | ZZZZ | Long localized GMT format | GMT-8:00 | - * | | ZZZZZ | ISO8601 extended format + Z indicator for offset 0 (= XXXXX) | -08:00 | - * | | O, OO & OOO | Short localized GMT format | GMT-8 | - * | | OOOO | Long localized GMT format | GMT-08:00 | + * | Field type | Format | Description | Example Value | + * |-------------------- |-------------|---------------------------------------------------------------|------------------------------------------------------------| + * | Era | G, GG & GGG | Abbreviated | AD | + * | | GGGG | Wide | Anno Domini | + * | | GGGGG | Narrow | A | + * | Year | y | Numeric: minimum digits | 2, 20, 201, 2017, 20173 | + * | | yy | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 | + * | | yyy | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 | + * | | yyyy | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 | + * | Week-numbering year | Y | Numeric: minimum digits | 2, 20, 201, 2017, 20173 | + * | | YY | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 | + * | | YYY | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 | + * | | YYYY | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 | + * | Month | M | Numeric: 1 digit | 9, 12 | + * | | MM | Numeric: 2 digits + zero padded | 09, 12 | + * | | MMM | Abbreviated | Sep | + * | | MMMM | Wide | September | + * | | MMMMM | Narrow | S | + * | Month standalone | L | Numeric: 1 digit | 9, 12 | + * | | LL | Numeric: 2 digits + zero padded | 09, 12 | + * | | LLL | Abbreviated | Sep | + * | | LLLL | Wide | September | + * | | LLLLL | Narrow | S | + * | Week of year | w | Numeric: minimum digits | 1... 53 | + * | | ww | Numeric: 2 digits + zero padded | 01... 53 | + * | Week of month | W | Numeric: 1 digit | 1... 5 | + * | Day of month | d | Numeric: minimum digits | 1 | + * | | dd | Numeric: 2 digits + zero padded | 01 | + * | Week day | E, EE & EEE | Abbreviated | Tue | + * | | EEEE | Wide | Tuesday | + * | | EEEEE | Narrow | T | + * | | EEEEEE | Short | Tu | + * | Week day standalone | c, cc | Numeric: 1 digit | 2 | + * | | ccc | Abbreviated | Tue | + * | | cccc | Wide | Tuesday | + * | | ccccc | Narrow | T | + * | | cccccc | Short | Tu | + * | Period | a, aa & aaa | Abbreviated | am/pm or AM/PM | + * | | aaaa | Wide (fallback to `a` when missing) | ante meridiem/post meridiem | + * | | aaaaa | Narrow | a/p | + * | Period* | B, BB & BBB | Abbreviated | mid. | + * | | BBBB | Wide | am, pm, midnight, noon, morning, afternoon, evening, night | + * | | BBBBB | Narrow | md | + * | Period standalone* | b, bb & bbb | Abbreviated | mid. | + * | | bbbb | Wide | am, pm, midnight, noon, morning, afternoon, evening, night | + * | | bbbbb | Narrow | md | + * | Hour 1-12 | h | Numeric: minimum digits | 1, 12 | + * | | hh | Numeric: 2 digits + zero padded | 01, 12 | + * | Hour 0-23 | H | Numeric: minimum digits | 0, 23 | + * | | HH | Numeric: 2 digits + zero padded | 00, 23 | + * | Minute | m | Numeric: minimum digits | 8, 59 | + * | | mm | Numeric: 2 digits + zero padded | 08, 59 | + * | Second | s | Numeric: minimum digits | 0... 59 | + * | | ss | Numeric: 2 digits + zero padded | 00... 59 | + * | Fractional seconds | S | Numeric: 1 digit | 0... 9 | + * | | SS | Numeric: 2 digits + zero padded | 00... 99 | + * | | SSS | Numeric: 3 digits + zero padded (= milliseconds) | 000... 999 | + * | Zone | z, zz & zzz | Short specific non location format (fallback to O) | GMT-8 | + * | | zzzz | Long specific non location format (fallback to OOOO) | GMT-08:00 | + * | | Z, ZZ & ZZZ | ISO8601 basic format | -0800 | + * | | ZZZZ | Long localized GMT format | GMT-8:00 | + * | | ZZZZZ | ISO8601 extended format + Z indicator for offset 0 (= XXXXX) | -08:00 | + * | | O, OO & OOO | Short localized GMT format | GMT-8 | + * | | OOOO | Long localized GMT format | GMT-08:00 | * * * ### Format examples diff --git a/packages/common/test/i18n/format_date_spec.ts b/packages/common/test/i18n/format_date_spec.ts index 56d16197f9..c1fda24481 100644 --- a/packages/common/test/i18n/format_date_spec.ts +++ b/packages/common/test/i18n/format_date_spec.ts @@ -9,6 +9,7 @@ import localeAr from '@angular/common/locales/ar'; import localeDe from '@angular/common/locales/de'; import localeEn from '@angular/common/locales/en'; import localeEnExtra from '@angular/common/locales/extra/en'; +import localeFi from '@angular/common/locales/fi'; import localeHu from '@angular/common/locales/hu'; import localeSr from '@angular/common/locales/sr'; import localeTh from '@angular/common/locales/th'; @@ -78,6 +79,7 @@ describe('Format date', () => { ɵregisterLocaleData(localeSr); ɵregisterLocaleData(localeTh); ɵregisterLocaleData(localeAr); + ɵregisterLocaleData(localeFi); }); afterAll(() => ɵunregisterLocaleData()); @@ -116,6 +118,12 @@ describe('Format date', () => { W: '3', d: '15', dd: '15', + c: '1', + cc: '1', + ccc: 'Mon', + cccc: 'Monday', + ccccc: 'M', + cccccc: 'Mo', E: 'Mon', EE: 'Mon', EEE: 'Mon', @@ -178,6 +186,12 @@ describe('Format date', () => { W: '1', d: '1', dd: '01', + c: '4', + cc: '4', + ccc: 'Thu', + cccc: 'Thursday', + ccccc: 'T', + cccccc: 'Th', E: 'Thu', EE: 'Thu', EEE: 'Thu', @@ -427,5 +441,10 @@ describe('Format date', () => { expect(formatDate('0001-01-11', 'YYYY', ɵDEFAULT_LOCALE_ID)).toEqual('0001'); expect(formatDate('0000-01-11', 'YYYY', ɵDEFAULT_LOCALE_ID)).toEqual('0000'); }); + + // https://github.com/angular/angular/issues/26922 + it('should support fullDate in finnish, which uses standalone week day', () => { + expect(formatDate(date, 'fullDate', 'fi')).toMatch('maanantai 15. kesäkuuta 2015'); + }); }); });