fix(common): no TZ Offset added by DatePipe for dates without time (#12380)

This commit is contained in:
Bruno da Silva joão 2016-11-10 20:57:04 -02:00 committed by Victor Berchet
parent 77dc1ab675
commit 2aba8b0ff2
2 changed files with 120 additions and 43 deletions

View File

@ -54,6 +54,9 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
*
* Timezone of the formatted text will be the local system timezone of the end-user's machine.
*
* When the expression is a ISO string without time (e.g. 2016-09-19) the time zone offset is not
* applied and the formatted text will have the same day, month and year of the expression.
*
* WARNINGS:
* - this pipe is marked as pure hence it will not be re-evaluated when the input is mutated.
* Instead users should treat the date as an immutable object and change the reference when the
@ -95,17 +98,30 @@ export class DatePipe implements PipeTransform {
constructor(@Inject(LOCALE_ID) private _locale: string) {}
transform(value: any, pattern: string = 'mediumDate'): string {
let date: Date;
if (isBlank(value)) return null;
if (typeof value === 'string') {
value = value.trim();
}
let date: Date;
if (isDate(value)) {
date = value;
} else if (NumberWrapper.isNumeric(value)) {
date = new Date(parseFloat(value));
} else if (typeof value === 'string' && /^(\d{4}-\d{1,2}-\d{1,2})$/.test(value)) {
/**
* For ISO Strings without time the day, month and year must be extracted from the ISO String
* before Date creation to avoid time offset and errors in the new Date.
* If we only replace '-' with ',' in the ISO String ("2015,01,01"), and try to create a new
* date, some browsers (e.g. IE 9) will throw an invalid Date error
* If we leave the '-' ("2015-01-01") and try to create a new Date("2015-01-01") the timeoffset
* is applied
* Note: ISO months are 0 for January, 1 for February, ...
*/
const [y, m, d] = value.split('-').map((val: string) => parseInt(val, 10));
date = new Date(y, m - 1, d);
} else {
date = new Date(value);
}

View File

@ -13,8 +13,14 @@ import {browserDetection} from '@angular/platform-browser/testing/browser_util';
export function main() {
describe('DatePipe', () => {
let date: Date;
const isoStringWithoutTime = '2015-01-01';
let pipe: DatePipe;
// Check the transformation of a date into a pattern
function expectDateFormatAs(date: Date | string, pattern: any, output: string): void {
expect(pipe.transform(date, pattern)).toEqual(output);
}
// TODO: reactivate the disabled expectations once emulators are fixed in SauceLabs
// In some old versions of Chrome in Android emulators, time formatting returns dates in the
// timezone of the VM host,
@ -47,84 +53,139 @@ export function main() {
it('should return null for empty string', () => expect(pipe.transform('')).toEqual(null));
it('should support ISO string without time',
() => { expect(() => pipe.transform(isoStringWithoutTime)).not.toThrow(); });
it('should not support other objects',
() => { expect(() => pipe.transform({})).toThrowError(); });
});
describe('transform', () => {
it('should format each component correctly', () => {
expect(pipe.transform(date, 'y')).toEqual('2015');
expect(pipe.transform(date, 'yy')).toEqual('15');
expect(pipe.transform(date, 'M')).toEqual('6');
expect(pipe.transform(date, 'MM')).toEqual('06');
expect(pipe.transform(date, 'MMM')).toEqual('Jun');
expect(pipe.transform(date, 'MMMM')).toEqual('June');
expect(pipe.transform(date, 'd')).toEqual('15');
expect(pipe.transform(date, 'EEE')).toEqual('Mon');
expect(pipe.transform(date, 'EEEE')).toEqual('Monday');
let dateFixtures: any = {
'y': '2015',
'yy': '15',
'M': '6',
'MM': '06',
'MMM': 'Jun',
'MMMM': 'June',
'd': '15',
'dd': '15',
'EEE': 'Mon',
'EEEE': 'Monday'
};
let isoStringWithoutTimeFixtures: any = {
'y': '2015',
'yy': '15',
'M': '1',
'MM': '01',
'MMM': 'Jan',
'MMMM': 'January',
'd': '1',
'dd': '01',
'EEE': 'Thu',
'EEEE': 'Thursday'
};
if (!browserDetection.isOldChrome) {
expect(pipe.transform(date, 'h')).toEqual('9');
expect(pipe.transform(date, 'hh')).toEqual('09');
expect(pipe.transform(date, 'j')).toEqual('9 AM');
dateFixtures['h'] = '9';
dateFixtures['hh'] = '09';
dateFixtures['j'] = '9 AM';
isoStringWithoutTimeFixtures['h'] = '12';
isoStringWithoutTimeFixtures['hh'] = '12';
isoStringWithoutTimeFixtures['j'] = '12 AM';
}
// IE and Edge can't format a date to minutes and seconds without hours
if (!browserDetection.isEdge && !browserDetection.isIE ||
!browserDetection.supportsNativeIntlApi) {
if (!browserDetection.isOldChrome) {
expect(pipe.transform(date, 'HH')).toEqual('09');
dateFixtures['HH'] = '09';
isoStringWithoutTimeFixtures['HH'] = '00';
}
dateFixtures['E'] = 'M';
dateFixtures['L'] = 'J';
dateFixtures['m'] = '3';
dateFixtures['s'] = '1';
dateFixtures['mm'] = '03';
dateFixtures['ss'] = '01';
isoStringWithoutTimeFixtures['m'] = '0';
isoStringWithoutTimeFixtures['s'] = '0';
isoStringWithoutTimeFixtures['mm'] = '00';
isoStringWithoutTimeFixtures['ss'] = '00';
}
expect(pipe.transform(date, 'E')).toEqual('M');
expect(pipe.transform(date, 'L')).toEqual('J');
expect(pipe.transform(date, 'm')).toEqual('3');
expect(pipe.transform(date, 's')).toEqual('1');
expect(pipe.transform(date, 'mm')).toEqual('03');
expect(pipe.transform(date, 'ss')).toEqual('01');
}
Object.keys(dateFixtures).forEach((pattern: string) => {
expectDateFormatAs(date, pattern, dateFixtures[pattern]);
});
Object.keys(isoStringWithoutTimeFixtures).forEach((pattern: string) => {
expectDateFormatAs(isoStringWithoutTime, pattern, isoStringWithoutTimeFixtures[pattern]);
});
expect(pipe.transform(date, 'Z')).toBeDefined();
});
it('should format common multi component patterns', () => {
expect(pipe.transform(date, 'EEE, M/d/y')).toEqual('Mon, 6/15/2015');
expect(pipe.transform(date, 'EEE, M/d')).toEqual('Mon, 6/15');
expect(pipe.transform(date, 'MMM d')).toEqual('Jun 15');
expect(pipe.transform(date, 'dd/MM/yyyy')).toEqual('15/06/2015');
expect(pipe.transform(date, 'MM/dd/yyyy')).toEqual('06/15/2015');
expect(pipe.transform(date, 'yMEEEd')).toEqual('20156Mon15');
expect(pipe.transform(date, 'MEEEd')).toEqual('6Mon15');
expect(pipe.transform(date, 'MMMd')).toEqual('Jun15');
expect(pipe.transform(date, 'yMMMMEEEEd')).toEqual('Monday, June 15, 2015');
let dateFixtures: any = {
'EEE, M/d/y': 'Mon, 6/15/2015',
'EEE, M/d': 'Mon, 6/15',
'MMM d': 'Jun 15',
'dd/MM/yyyy': '15/06/2015',
'MM/dd/yyyy': '06/15/2015',
'yMEEEd': '20156Mon15',
'MEEEd': '6Mon15',
'MMMd': 'Jun15',
'yMMMMEEEEd': 'Monday, June 15, 2015'
};
// IE and Edge can't format a date to minutes and seconds without hours
if (!browserDetection.isEdge && !browserDetection.isIE ||
!browserDetection.supportsNativeIntlApi) {
expect(pipe.transform(date, 'ms')).toEqual('31');
dateFixtures['ms'] = '31';
}
if (!browserDetection.isOldChrome) {
expect(pipe.transform(date, 'jm')).toEqual('9:03 AM');
dateFixtures['jm'] = '9:03 AM';
}
Object.keys(dateFixtures).forEach((pattern: string) => {
expectDateFormatAs(date, pattern, dateFixtures[pattern]);
});
});
it('should format with pattern aliases', () => {
let dateFixtures: any = {
'MM/dd/yyyy': '06/15/2015',
'fullDate': 'Monday, June 15, 2015',
'longDate': 'June 15, 2015',
'mediumDate': 'Jun 15, 2015',
'shortDate': '6/15/2015'
};
if (!browserDetection.isOldChrome) {
// IE and Edge do not add a coma after the year in these 2 cases
if ((browserDetection.isEdge || browserDetection.isIE) &&
browserDetection.supportsNativeIntlApi) {
expect(pipe.transform(date, 'medium')).toEqual('Jun 15, 2015 9:03:01 AM');
expect(pipe.transform(date, 'short')).toEqual('6/15/2015 9:03 AM');
dateFixtures['medium'] = 'Jun 15, 2015 9:03:01 AM';
dateFixtures['short'] = '6/15/2015 9:03 AM';
} else {
expect(pipe.transform(date, 'medium')).toEqual('Jun 15, 2015, 9:03:01 AM');
expect(pipe.transform(date, 'short')).toEqual('6/15/2015, 9:03 AM');
dateFixtures['medium'] = 'Jun 15, 2015, 9:03:01 AM';
dateFixtures['short'] = '6/15/2015, 9:03 AM';
}
}
expect(pipe.transform(date, 'MM/dd/yyyy')).toEqual('06/15/2015');
expect(pipe.transform(date, 'fullDate')).toEqual('Monday, June 15, 2015');
expect(pipe.transform(date, 'longDate')).toEqual('June 15, 2015');
expect(pipe.transform(date, 'mediumDate')).toEqual('Jun 15, 2015');
expect(pipe.transform(date, 'shortDate')).toEqual('6/15/2015');
if (!browserDetection.isOldChrome) {
expect(pipe.transform(date, 'mediumTime')).toEqual('9:03:01 AM');
expect(pipe.transform(date, 'shortTime')).toEqual('9:03 AM');
dateFixtures['mediumTime'] = '9:03:01 AM';
dateFixtures['shortTime'] = '9:03 AM';
}
Object.keys(dateFixtures).forEach((pattern: string) => {
expectDateFormatAs(date, pattern, dateFixtures[pattern]);
});
});
it('should remove bidi control characters',