fix(common): date is not correctly formatted when year is between 0 and 99 (#40448)
use setFullYear method when parsing date to avoid javascript date factory behaviour Fixes #40377 PR Close #40448
This commit is contained in:
parent
8f2260a073
commit
03f0b157c1
|
@ -101,6 +101,38 @@ export function formatDate(
|
|||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Date object with the given date value, and the time set to midnight.
|
||||
*
|
||||
* We cannot use `new Date(year, month, date)` because it maps years between 0 and 99 to 1900-1999.
|
||||
* See: https://github.com/angular/angular/issues/40377
|
||||
*
|
||||
* Note that this function returns a Date object whose time is midnight in the current locale's
|
||||
* timezone. In the future we might want to change this to be midnight in UTC, but this would be a
|
||||
* considerable breaking change.
|
||||
*/
|
||||
function createDate(year: number, month: number, date: number): Date {
|
||||
// The `newDate` is set to midnight (UTC) on January 1st 1970.
|
||||
// - In PST this will be December 31st 1969 at 4pm.
|
||||
// - In GMT this will be January 1st 1970 at 1am.
|
||||
// Note that they even have different years, dates and months!
|
||||
const newDate = new Date(0);
|
||||
|
||||
// `setFullYear()` allows years like 0001 to be set correctly. This function does not
|
||||
// change the internal time of the date.
|
||||
// Consider calling `setFullYear(2019, 8, 20)` (September 20, 2019).
|
||||
// - In PST this will now be September 20, 2019 at 4pm
|
||||
// - In GMT this will now be September 20, 2019 at 1am
|
||||
|
||||
newDate.setFullYear(year, month, date);
|
||||
// We want the final date to be at local midnight, so we reset the time.
|
||||
// - In PST this will now be September 20, 2019 at 12am
|
||||
// - In GMT this will now be September 20, 2019 at 12am
|
||||
newDate.setHours(0, 0, 0);
|
||||
|
||||
return newDate;
|
||||
}
|
||||
|
||||
function getNamedFormat(locale: string, format: string): string {
|
||||
const localeId = getLocaleId(locale);
|
||||
NAMED_FORMATS[localeId] = NAMED_FORMATS[localeId] || {};
|
||||
|
@ -362,13 +394,13 @@ function timeZoneGetter(width: ZoneWidth): DateFormatter {
|
|||
const JANUARY = 0;
|
||||
const THURSDAY = 4;
|
||||
function getFirstThursdayOfYear(year: number) {
|
||||
const firstDayOfYear = (new Date(year, JANUARY, 1)).getDay();
|
||||
return new Date(
|
||||
const firstDayOfYear = createDate(year, JANUARY, 1).getDay();
|
||||
return createDate(
|
||||
year, 0, 1 + ((firstDayOfYear <= THURSDAY) ? THURSDAY : THURSDAY + 7) - firstDayOfYear);
|
||||
}
|
||||
|
||||
function getThursdayThisWeek(datetime: Date) {
|
||||
return new Date(
|
||||
return createDate(
|
||||
datetime.getFullYear(), datetime.getMonth(),
|
||||
datetime.getDate() + (THURSDAY - datetime.getDay()));
|
||||
}
|
||||
|
@ -720,7 +752,7 @@ export function toDate(value: string|number|Date): Date {
|
|||
is applied.
|
||||
Note: ISO months are 0 for January, 1 for February, ... */
|
||||
const [y, m = 1, d = 1] = value.split('-').map((val: string) => +val);
|
||||
return new Date(y, m - 1, d);
|
||||
return createDate(y, m - 1, d);
|
||||
}
|
||||
|
||||
const parsedNb = parseFloat(value);
|
||||
|
|
|
@ -114,7 +114,6 @@ import {invalidPipeArgumentError} from './invalid_pipe_argument_error';
|
|||
* | | O, OO & OOO | Short localized GMT format | GMT-8 |
|
||||
* | | OOOO | Long localized GMT format | GMT-08:00 |
|
||||
*
|
||||
* Note that timezone correction is not applied to an ISO string that has no time component, such as "2016-09-19"
|
||||
*
|
||||
* ### Format examples
|
||||
*
|
||||
|
|
|
@ -347,6 +347,32 @@ describe('Format date', () => {
|
|||
.toEqual('10:14 AM');
|
||||
});
|
||||
|
||||
// The following test is disabled because backwards compatibility requires that date-only ISO
|
||||
// strings are parsed with the local timezone.
|
||||
|
||||
// it('should create UTC date objects when an ISO string is passed with no time components',
|
||||
// () => {
|
||||
// expect(formatDate('2019-09-20', `MMM d, y, h:mm:ss a ZZZZZ`, ɵDEFAULT_LOCALE_ID))
|
||||
// .toEqual('Sep 20, 2019, 12:00:00 AM Z');
|
||||
// });
|
||||
|
||||
// This test is to ensure backward compatibility for parsing date-only ISO strings.
|
||||
it('should create local timezone date objects when an ISO string is passed with no time components',
|
||||
() => {
|
||||
// Dates created with individual components are evaluated against the local timezone. See
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date#Individual_date_and_time_component_values
|
||||
const localDate = new Date(2019, 8, 20, 0, 0, 0, 0);
|
||||
expect(formatDate('2019-09-20', `MMM d, y, h:mm:ss a ZZZZZ`, ɵDEFAULT_LOCALE_ID))
|
||||
.toEqual(formatDate(localDate, `MMM d, y, h:mm:ss a ZZZZZ`, ɵDEFAULT_LOCALE_ID));
|
||||
});
|
||||
|
||||
it('should create local timezone date objects when an ISO string is passed with time components',
|
||||
() => {
|
||||
const localDate = new Date(2019, 8, 20, 0, 0, 0, 0);
|
||||
expect(formatDate('2019-09-20T00:00:00', `MMM d, y, h:mm:ss a ZZZZZ`, ɵDEFAULT_LOCALE_ID))
|
||||
.toEqual(formatDate(localDate, `MMM d, y, h:mm:ss a ZZZZZ`, ɵDEFAULT_LOCALE_ID));
|
||||
});
|
||||
|
||||
it('should remove bidi control characters',
|
||||
() => expect(formatDate(date, 'MM/dd/yyyy', ɵDEFAULT_LOCALE_ID)!.length).toEqual(10));
|
||||
|
||||
|
@ -389,6 +415,17 @@ describe('Format date', () => {
|
|||
expect(formatDate('2013-12-29', 'YYYY', 'en')).toEqual('2014');
|
||||
expect(formatDate('2010-01-02', 'YYYY', 'en')).toEqual('2009');
|
||||
expect(formatDate('2010-01-04', 'YYYY', 'en')).toEqual('2010');
|
||||
expect(formatDate('0049-01-01', 'YYYY', 'en')).toEqual('0048');
|
||||
expect(formatDate('0049-01-04', 'YYYY', 'en')).toEqual('0049');
|
||||
});
|
||||
|
||||
// https://github.com/angular/angular/issues/40377
|
||||
it('should format date with year between 0 and 99 correctly', () => {
|
||||
expect(formatDate('0098-01-11', 'YYYY', ɵDEFAULT_LOCALE_ID)).toEqual('0098');
|
||||
expect(formatDate('0099-01-11', 'YYYY', ɵDEFAULT_LOCALE_ID)).toEqual('0099');
|
||||
expect(formatDate('0100-01-11', 'YYYY', ɵDEFAULT_LOCALE_ID)).toEqual('0100');
|
||||
expect(formatDate('0001-01-11', 'YYYY', ɵDEFAULT_LOCALE_ID)).toEqual('0001');
|
||||
expect(formatDate('0000-01-11', 'YYYY', ɵDEFAULT_LOCALE_ID)).toEqual('0000');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue