Skip to content

feat(common): add injection token for default date pipe timezone #43611

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions goldens/public-api/common/common.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,17 +73,20 @@ export class CurrencyPipe implements PipeTransform {
static ɵpipe: i0.ɵɵPipeDeclaration<CurrencyPipe, "currency">;
}

// @public
export const DATE_PIPE_DEFAULT_TIMEZONE: InjectionToken<string>;

// @public
export class DatePipe implements PipeTransform {
constructor(locale: string);
constructor(locale: string, defaultTimezone?: string | null | undefined);
// (undocumented)
transform(value: Date | string | number, format?: string, timezone?: string, locale?: string): string | null;
// (undocumented)
transform(value: null | undefined, format?: string, timezone?: string, locale?: string): null;
// (undocumented)
transform(value: Date | string | number | null | undefined, format?: string, timezone?: string, locale?: string): string | null;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration<DatePipe, never>;
static ɵfac: i0.ɵɵFactoryDeclaration<DatePipe, [null, { optional: true; }]>;
// (undocumented)
static ɵpipe: i0.ɵɵPipeDeclaration<DatePipe, "date">;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/common/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export {parseCookieValue as ɵparseCookieValue} from './cookie';
export {CommonModule} from './common_module';
export {NgClass, NgForOf, NgForOfContext, NgIf, NgIfContext, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet, NgComponentOutlet} from './directives/index';
export {DOCUMENT} from './dom_tokens';
export {AsyncPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe, TitleCasePipe, KeyValuePipe, KeyValue} from './pipes/index';
export {AsyncPipe, DatePipe, DATE_PIPE_DEFAULT_TIMEZONE, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe, TitleCasePipe, KeyValuePipe, KeyValue} from './pipes/index';
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';
export {VERSION} from './version';
export {ViewportScroller, NullViewportScroller as ɵNullViewportScroller} from './viewport_scroller';
Expand Down
28 changes: 21 additions & 7 deletions packages/common/src/pipes/date_pipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,23 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
import {Inject, InjectionToken, LOCALE_ID, Optional, Pipe, PipeTransform} from '@angular/core';
import {formatDate} from '../i18n/format_date';
import {invalidPipeArgumentError} from './invalid_pipe_argument_error';

/**
* Optionally-provided default timezone to use for all instances of `DatePipe` (such as `'+0430'`).
* If the value isn't provided, the `DatePipe` will use the end-user's local system timezone.
*/
export const DATE_PIPE_DEFAULT_TIMEZONE = new InjectionToken<string>('DATE_PIPE_DEFAULT_TIMEZONE');

// clang-format off
/**
* @ngModule CommonModule
* @description
*
* Formats a date value according to locale rules.
*
*
* `DatePipe` is executed only when it detects a pure change to the input value.
* A pure change is either a change to a primitive input value
* (such as `String`, `Number`, `Boolean`, or `Symbol`),
Expand All @@ -29,6 +35,11 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
* in another language, you must import the corresponding locale data.
* See the [I18n guide](guide/i18n-common-format-data-locale) for more information.
*
* The time zone of the formatted value can be specified either by passing it in as the second
* parameter of the pipe, or by setting the default through the `DATE_PIPE_DEFAULT_TIMEZONE`
* injection token. The value that is passed in as the second parameter takes precedence over
* the one defined using the injection token.
*
* @see `formatDate()`
*
*
Expand Down Expand Up @@ -166,16 +177,18 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
// clang-format on
@Pipe({name: 'date', pure: true})
export class DatePipe implements PipeTransform {
constructor(@Inject(LOCALE_ID) private locale: string) {}
constructor(
@Inject(LOCALE_ID) private locale: string,
@Inject(DATE_PIPE_DEFAULT_TIMEZONE) @Optional() private defaultTimezone?: string|null) {}

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

try {
return formatDate(value, format, locale || this.locale, timezone);
return formatDate(
value, format, locale || this.locale, timezone ?? this.defaultTimezone ?? undefined);
} catch (error) {
throw invalidPipeArgumentError(DatePipe, error.message);
}
Expand Down
3 changes: 2 additions & 1 deletion packages/common/src/pipes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
*/
import {AsyncPipe} from './async_pipe';
import {LowerCasePipe, TitleCasePipe, UpperCasePipe} from './case_conversion_pipes';
import {DatePipe} from './date_pipe';
import {DATE_PIPE_DEFAULT_TIMEZONE, DatePipe} from './date_pipe';
import {I18nPluralPipe} from './i18n_plural_pipe';
import {I18nSelectPipe} from './i18n_select_pipe';
import {JsonPipe} from './json_pipe';
Expand All @@ -24,6 +24,7 @@ import {SlicePipe} from './slice_pipe';
export {
AsyncPipe,
CurrencyPipe,
DATE_PIPE_DEFAULT_TIMEZONE,
DatePipe,
DecimalPipe,
I18nPluralPipe,
Expand Down
20 changes: 18 additions & 2 deletions packages/common/test/pipes/date_pipe_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@ import {ɵregisterLocaleData, ɵunregisterLocaleData} from '@angular/core';
import {JitReflector} from '@angular/platform-browser-dynamic/src/compiler_reflector';

{
let date: Date;
describe('DatePipe', () => {
const isoStringWithoutTime = '2015-01-01';
let pipe: DatePipe;
let date: Date;

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

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

it('should be marked as pure', () => {
Expand Down Expand Up @@ -100,6 +100,22 @@ import {JitReflector} from '@angular/platform-browser-dynamic/src/compiler_refle
expect(pipe.transform('2020-08-01T23:59:59.9999', 'yyyy-MM-dd, h:mm:ss SSS'))
.toEqual('2020-08-01, 11:59:59 999');
});

it('should take timezone into account', () => {
expect(pipe.transform('2017-01-11T00:00:00', 'mediumDate', '-1200'))
.toEqual('Jan 10, 2017');
});

it('should take the default timezone into account when no timezone is passed in', () => {
pipe = new DatePipe('en-US', '-1200');
expect(pipe.transform('2017-01-11T00:00:00', 'mediumDate')).toEqual('Jan 10, 2017');
});

it('should give precedence to the passed in timezone over the default one', () => {
pipe = new DatePipe('en-US', '-1200');
expect(pipe.transform('2017-01-11T00:00:00', 'mediumDate', '+0100'))
.toEqual('Jan 11, 2017');
});
});
});
}