Skip to content

Commit adf4481

Browse files
crisbetodylhunn
authored andcommitted
feat(common): add injection token for default date pipe timezone (#43611)
Adds a new injection token called `DATE_PIPE_DEFAULT_TIMEZONE` that allows for the default timezone for all `DatePipe` instances to be specified. Fixes #43031. PR Close #43611
1 parent 5d7229e commit adf4481

File tree

5 files changed

+47
-13
lines changed

5 files changed

+47
-13
lines changed

goldens/public-api/common/common.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,17 +73,20 @@ export class CurrencyPipe implements PipeTransform {
7373
static ɵpipe: i0.ɵɵPipeDeclaration<CurrencyPipe, "currency">;
7474
}
7575

76+
// @public
77+
export const DATE_PIPE_DEFAULT_TIMEZONE: InjectionToken<string>;
78+
7679
// @public
7780
export class DatePipe implements PipeTransform {
78-
constructor(locale: string);
81+
constructor(locale: string, defaultTimezone?: string | null | undefined);
7982
// (undocumented)
8083
transform(value: Date | string | number, format?: string, timezone?: string, locale?: string): string | null;
8184
// (undocumented)
8285
transform(value: null | undefined, format?: string, timezone?: string, locale?: string): null;
8386
// (undocumented)
8487
transform(value: Date | string | number | null | undefined, format?: string, timezone?: string, locale?: string): string | null;
8588
// (undocumented)
86-
static ɵfac: i0.ɵɵFactoryDeclaration<DatePipe, never>;
89+
static ɵfac: i0.ɵɵFactoryDeclaration<DatePipe, [null, { optional: true; }]>;
8790
// (undocumented)
8891
static ɵpipe: i0.ɵɵPipeDeclaration<DatePipe, "date">;
8992
}

packages/common/src/common.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export {parseCookieValue as ɵparseCookieValue} from './cookie';
2222
export {CommonModule} from './common_module';
2323
export {NgClass, NgForOf, NgForOfContext, NgIf, NgIfContext, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet, NgComponentOutlet} from './directives/index';
2424
export {DOCUMENT} from './dom_tokens';
25-
export {AsyncPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe, TitleCasePipe, KeyValuePipe, KeyValue} from './pipes/index';
25+
export {AsyncPipe, DatePipe, DATE_PIPE_DEFAULT_TIMEZONE, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe, TitleCasePipe, KeyValuePipe, KeyValue} from './pipes/index';
2626
export {PLATFORM_BROWSER_ID as ɵPLATFORM_BROWSER_ID, PLATFORM_SERVER_ID as ɵPLATFORM_SERVER_ID, PLATFORM_WORKER_APP_ID as ɵPLATFORM_WORKER_APP_ID, PLATFORM_WORKER_UI_ID as ɵPLATFORM_WORKER_UI_ID, isPlatformBrowser, isPlatformServer, isPlatformWorkerApp, isPlatformWorkerUi} from './platform_id';
2727
export {VERSION} from './version';
2828
export {ViewportScroller, NullViewportScroller as ɵNullViewportScroller} from './viewport_scroller';

packages/common/src/pipes/date_pipe.ts

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,23 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
9+
import {Inject, InjectionToken, LOCALE_ID, Optional, Pipe, PipeTransform} from '@angular/core';
1010
import {formatDate} from '../i18n/format_date';
1111
import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
1212

13+
/**
14+
* Optionally-provided default timezone to use for all instances of `DatePipe` (such as `'+0430'`).
15+
* If the value isn't provided, the `DatePipe` will use the end-user's local system timezone.
16+
*/
17+
export const DATE_PIPE_DEFAULT_TIMEZONE = new InjectionToken<string>('DATE_PIPE_DEFAULT_TIMEZONE');
18+
1319
// clang-format off
1420
/**
1521
* @ngModule CommonModule
1622
* @description
1723
*
1824
* Formats a date value according to locale rules.
19-
*
25+
*
2026
* `DatePipe` is executed only when it detects a pure change to the input value.
2127
* A pure change is either a change to a primitive input value
2228
* (such as `String`, `Number`, `Boolean`, or `Symbol`),
@@ -29,6 +35,11 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
2935
* in another language, you must import the corresponding locale data.
3036
* See the [I18n guide](guide/i18n-common-format-data-locale) for more information.
3137
*
38+
* The time zone of the formatted value can be specified either by passing it in as the second
39+
* parameter of the pipe, or by setting the default through the `DATE_PIPE_DEFAULT_TIMEZONE`
40+
* injection token. The value that is passed in as the second parameter takes precedence over
41+
* the one defined using the injection token.
42+
*
3243
* @see `formatDate()`
3344
*
3445
*
@@ -166,16 +177,18 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
166177
// clang-format on
167178
@Pipe({name: 'date', pure: true})
168179
export class DatePipe implements PipeTransform {
169-
constructor(@Inject(LOCALE_ID) private locale: string) {}
180+
constructor(
181+
@Inject(LOCALE_ID) private locale: string,
182+
@Inject(DATE_PIPE_DEFAULT_TIMEZONE) @Optional() private defaultTimezone?: string|null) {}
170183

171184
/**
172185
* @param value The date expression: a `Date` object, a number
173186
* (milliseconds since UTC epoch), or an ISO string (https://www.w3.org/TR/NOTE-datetime).
174187
* @param format The date/time components to include, using predefined options or a
175188
* custom format string.
176-
* @param timezone A timezone offset (such as `'+0430'`), or a standard
177-
* UTC/GMT or continental US timezone abbreviation.
178-
* When not supplied, uses the end-user's local system timezone.
189+
* @param timezone A timezone offset (such as `'+0430'`), or a standard UTC/GMT, or continental US
190+
* timezone abbreviation. When not supplied, either the value of the `DATE_PIPE_DEFAULT_TIMEZONE`
191+
* injection token is used or the end-user's local system timezone.
179192
* @param locale A locale code for the locale format rules to use.
180193
* When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default.
181194
* See [Setting your app locale](guide/i18n-common-locale-id).
@@ -193,7 +206,8 @@ export class DatePipe implements PipeTransform {
193206
if (value == null || value === '' || value !== value) return null;
194207

195208
try {
196-
return formatDate(value, format, locale || this.locale, timezone);
209+
return formatDate(
210+
value, format, locale || this.locale, timezone ?? this.defaultTimezone ?? undefined);
197211
} catch (error) {
198212
throw invalidPipeArgumentError(DatePipe, error.message);
199213
}

packages/common/src/pipes/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
*/
1414
import {AsyncPipe} from './async_pipe';
1515
import {LowerCasePipe, TitleCasePipe, UpperCasePipe} from './case_conversion_pipes';
16-
import {DatePipe} from './date_pipe';
16+
import {DATE_PIPE_DEFAULT_TIMEZONE, DatePipe} from './date_pipe';
1717
import {I18nPluralPipe} from './i18n_plural_pipe';
1818
import {I18nSelectPipe} from './i18n_select_pipe';
1919
import {JsonPipe} from './json_pipe';
@@ -24,6 +24,7 @@ import {SlicePipe} from './slice_pipe';
2424
export {
2525
AsyncPipe,
2626
CurrencyPipe,
27+
DATE_PIPE_DEFAULT_TIMEZONE,
2728
DatePipe,
2829
DecimalPipe,
2930
I18nPluralPipe,

packages/common/test/pipes/date_pipe_spec.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,17 @@ import {ɵregisterLocaleData, ɵunregisterLocaleData} from '@angular/core';
1414
import {JitReflector} from '@angular/platform-browser-dynamic/src/compiler_reflector';
1515

1616
{
17-
let date: Date;
1817
describe('DatePipe', () => {
1918
const isoStringWithoutTime = '2015-01-01';
2019
let pipe: DatePipe;
20+
let date: Date;
2121

2222
beforeAll(() => ɵregisterLocaleData(localeEn, localeEnExtra));
2323
afterAll(() => ɵunregisterLocaleData());
2424

2525
beforeEach(() => {
2626
date = new Date(2015, 5, 15, 9, 3, 1, 550);
27-
pipe = new DatePipe('en-US');
27+
pipe = new DatePipe('en-US', null);
2828
});
2929

3030
it('should be marked as pure', () => {
@@ -100,6 +100,22 @@ import {JitReflector} from '@angular/platform-browser-dynamic/src/compiler_refle
100100
expect(pipe.transform('2020-08-01T23:59:59.9999', 'yyyy-MM-dd, h:mm:ss SSS'))
101101
.toEqual('2020-08-01, 11:59:59 999');
102102
});
103+
104+
it('should take timezone into account', () => {
105+
expect(pipe.transform('2017-01-11T00:00:00', 'mediumDate', '-1200'))
106+
.toEqual('Jan 10, 2017');
107+
});
108+
109+
it('should take the default timezone into account when no timezone is passed in', () => {
110+
pipe = new DatePipe('en-US', '-1200');
111+
expect(pipe.transform('2017-01-11T00:00:00', 'mediumDate')).toEqual('Jan 10, 2017');
112+
});
113+
114+
it('should give precedence to the passed in timezone over the default one', () => {
115+
pipe = new DatePipe('en-US', '-1200');
116+
expect(pipe.transform('2017-01-11T00:00:00', 'mediumDate', '+0100'))
117+
.toEqual('Jan 11, 2017');
118+
});
103119
});
104120
});
105121
}

0 commit comments

Comments
 (0)