From 26f28200bf3a909024184c61111f4cd20ac41edd Mon Sep 17 00:00:00 2001 From: Ajit Singh Date: Mon, 13 Jul 2020 08:38:30 +0530 Subject: [PATCH] fix(common): do not round up fractions of a millisecond in `DatePipe` (#38009) Currently, the `DatePipe` (via `formatDate()`) rounds fractions of a millisecond to the nearest millisecond. This can cause dates that are less than a millisecond before midnight to be incremented to the following day. The [ECMAScript specification](https://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.11) defines that `DateTime` milliseconds should always be rounded down, so that `999.9ms` becomes `999ms`. This change brings `formatDate()` and so `DatePipe` inline with the ECMAScript specification. Fixes #37989 BREAKING CHANGE: When passing a date-time formatted string to the `DatePipe` in a format that contains fractions of a millisecond, the milliseconds will now always be rounded down rather than to the nearest millisecond. Most applications will not be affected by this change. If this is not the desired behaviour then consider pre-processing the string to round the millisecond part before passing it to the `DatePipe`. PR Close #38009 --- packages/common/src/i18n/format_date.ts | 5 ++++- packages/common/test/pipes/date_pipe_spec.ts | 7 +++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/common/src/i18n/format_date.ts b/packages/common/src/i18n/format_date.ts index 355298a60f..c9ecd65c20 100644 --- a/packages/common/src/i18n/format_date.ts +++ b/packages/common/src/i18n/format_date.ts @@ -734,7 +734,10 @@ export function isoStringToDate(match: RegExpMatchArray): Date { const h = Number(match[4] || 0) - tzHour; const m = Number(match[5] || 0) - tzMin; const s = Number(match[6] || 0); - const ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000); + // The ECMAScript specification (https://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.11) + // defines that `DateTime` milliseconds should always be rounded down, so that `999.9ms` + // becomes `999ms`. + const ms = Math.floor(parseFloat('0.' + (match[7] || 0)) * 1000); timeSetter.call(date, h, m, s, ms); return date; } diff --git a/packages/common/test/pipes/date_pipe_spec.ts b/packages/common/test/pipes/date_pipe_spec.ts index ba532d5e25..21cf55ab7b 100644 --- a/packages/common/test/pipes/date_pipe_spec.ts +++ b/packages/common/test/pipes/date_pipe_spec.ts @@ -85,6 +85,13 @@ import {JitReflector} from '@angular/platform-browser-dynamic/src/compiler_refle expect(pipe.transform('2012-12-30T00:00:00', 'w')).toEqual('1'); expect(pipe.transform('2012-12-31T00:00:00', 'w')).toEqual('1'); }); + + + it('should round milliseconds down to the nearest millisecond', () => { + expect(pipe.transform('2020-08-01T23:59:59.999', 'yyyy-MM-dd')).toEqual('2020-08-01'); + expect(pipe.transform('2020-08-01T23:59:59.9999', 'yyyy-MM-dd, h:mm:ss SSS')) + .toEqual('2020-08-01, 11:59:59 999'); + }); }); }); }