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
This commit is contained in:
Ajit Singh 2020-07-13 08:38:30 +05:30 committed by Andrew Kushnir
parent ce1efc1af2
commit 26f28200bf
2 changed files with 11 additions and 1 deletions

View File

@ -734,7 +734,10 @@ export function isoStringToDate(match: RegExpMatchArray): Date {
const h = Number(match[4] || 0) - tzHour; const h = Number(match[4] || 0) - tzHour;
const m = Number(match[5] || 0) - tzMin; const m = Number(match[5] || 0) - tzMin;
const s = Number(match[6] || 0); 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); timeSetter.call(date, h, m, s, ms);
return date; return date;
} }

View File

@ -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-30T00:00:00', 'w')).toEqual('1');
expect(pipe.transform('2012-12-31T00: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');
});
}); });
}); });
} }