fix(common): do not round factional seconds (#24831)
fixes #24384 PR Close #24831
This commit is contained in:
parent
265489518e
commit
0746485136
@ -29,7 +29,7 @@ enum DateType {
|
|||||||
Hours,
|
Hours,
|
||||||
Minutes,
|
Minutes,
|
||||||
Seconds,
|
Seconds,
|
||||||
Milliseconds,
|
FractionalSeconds,
|
||||||
Day
|
Day
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,8 +57,6 @@ enum TranslationType {
|
|||||||
* If not specified, host system settings are used.
|
* If not specified, host system settings are used.
|
||||||
*
|
*
|
||||||
* See {@link DatePipe} for more details.
|
* See {@link DatePipe} for more details.
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
export function formatDate(
|
export function formatDate(
|
||||||
value: string | number | Date, format: string, locale: string, timezone?: string): string {
|
value: string | number | Date, format: string, locale: string, timezone?: string): string {
|
||||||
@ -195,6 +193,22 @@ function padNumber(
|
|||||||
return neg + strNum;
|
return neg + strNum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trim a fractional part to `digits` number of digits.
|
||||||
|
* Right pads with "0" to fit the requested number of digits if needed.
|
||||||
|
*
|
||||||
|
* @param num The fractional part value
|
||||||
|
* @param digits The width of the output
|
||||||
|
*/
|
||||||
|
function trimRPadFractional(num: number, digits: number): string {
|
||||||
|
let strNum = String(num);
|
||||||
|
// Add padding at the end
|
||||||
|
while (strNum.length < digits) {
|
||||||
|
strNum = strNum + 0;
|
||||||
|
}
|
||||||
|
return strNum.substr(0, digits);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a date formatter that transforms a date into its locale digit representation
|
* Returns a date formatter that transforms a date into its locale digit representation
|
||||||
*/
|
*/
|
||||||
@ -202,20 +216,26 @@ function dateGetter(
|
|||||||
name: DateType, size: number, offset: number = 0, trim = false,
|
name: DateType, size: number, offset: number = 0, trim = false,
|
||||||
negWrap = false): DateFormatter {
|
negWrap = false): DateFormatter {
|
||||||
return function(date: Date, locale: string): string {
|
return function(date: Date, locale: string): string {
|
||||||
let part = getDatePart(name, date, size);
|
let part = getDatePart(name, date);
|
||||||
if (offset > 0 || part > -offset) {
|
if (offset > 0 || part > -offset) {
|
||||||
part += offset;
|
part += offset;
|
||||||
}
|
}
|
||||||
if (name === DateType.Hours && part === 0 && offset === -12) {
|
|
||||||
part = 12;
|
if (name === DateType.Hours) {
|
||||||
|
if (part === 0 && offset === -12) {
|
||||||
|
part = 12;
|
||||||
|
}
|
||||||
|
} else if (name === DateType.FractionalSeconds) {
|
||||||
|
return trimRPadFractional(part, size);
|
||||||
}
|
}
|
||||||
return padNumber(
|
|
||||||
part, size, getLocaleNumberSymbol(locale, NumberSymbol.MinusSign), trim, negWrap);
|
const localeMinus = getLocaleNumberSymbol(locale, NumberSymbol.MinusSign);
|
||||||
|
return padNumber(part, size, localeMinus, trim, negWrap);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDatePart(name: DateType, date: Date, size: number): number {
|
function getDatePart(part: DateType, date: Date): number {
|
||||||
switch (name) {
|
switch (part) {
|
||||||
case DateType.FullYear:
|
case DateType.FullYear:
|
||||||
return date.getFullYear();
|
return date.getFullYear();
|
||||||
case DateType.Month:
|
case DateType.Month:
|
||||||
@ -228,13 +248,12 @@ function getDatePart(name: DateType, date: Date, size: number): number {
|
|||||||
return date.getMinutes();
|
return date.getMinutes();
|
||||||
case DateType.Seconds:
|
case DateType.Seconds:
|
||||||
return date.getSeconds();
|
return date.getSeconds();
|
||||||
case DateType.Milliseconds:
|
case DateType.FractionalSeconds:
|
||||||
const div = size === 1 ? 100 : (size === 2 ? 10 : 1);
|
return date.getMilliseconds();
|
||||||
return Math.round(date.getMilliseconds() / div);
|
|
||||||
case DateType.Day:
|
case DateType.Day:
|
||||||
return date.getDay();
|
return date.getDay();
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown DateType value "${name}".`);
|
throw new Error(`Unknown DateType value "${part}".`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -561,16 +580,15 @@ function getDateFormatter(format: string): DateFormatter|null {
|
|||||||
formatter = dateGetter(DateType.Seconds, 2);
|
formatter = dateGetter(DateType.Seconds, 2);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Fractional second padded (0-9)
|
// Fractional second
|
||||||
case 'S':
|
case 'S':
|
||||||
formatter = dateGetter(DateType.Milliseconds, 1);
|
formatter = dateGetter(DateType.FractionalSeconds, 1);
|
||||||
break;
|
break;
|
||||||
case 'SS':
|
case 'SS':
|
||||||
formatter = dateGetter(DateType.Milliseconds, 2);
|
formatter = dateGetter(DateType.FractionalSeconds, 2);
|
||||||
break;
|
break;
|
||||||
// = millisecond
|
|
||||||
case 'SSS':
|
case 'SSS':
|
||||||
formatter = dateGetter(DateType.Milliseconds, 3);
|
formatter = dateGetter(DateType.FractionalSeconds, 3);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ describe('Format date', () => {
|
|||||||
|
|
||||||
// Check the transformation of a date into a pattern
|
// Check the transformation of a date into a pattern
|
||||||
function expectDateFormatAs(date: Date | string, pattern: any, output: string): void {
|
function expectDateFormatAs(date: Date | string, pattern: any, output: string): void {
|
||||||
expect(formatDate(date, pattern, defaultLocale)).toEqual(output);
|
expect(formatDate(date, pattern, defaultLocale)).toEqual(output, `pattern: "${pattern}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
@ -105,7 +105,7 @@ describe('Format date', () => {
|
|||||||
mm: '03',
|
mm: '03',
|
||||||
s: '1',
|
s: '1',
|
||||||
ss: '01',
|
ss: '01',
|
||||||
S: '6',
|
S: '5',
|
||||||
SS: '55',
|
SS: '55',
|
||||||
SSS: '550',
|
SSS: '550',
|
||||||
a: 'AM',
|
a: 'AM',
|
||||||
@ -233,7 +233,6 @@ describe('Format date', () => {
|
|||||||
Object.keys(dateFixtures).forEach((pattern: string) => {
|
Object.keys(dateFixtures).forEach((pattern: string) => {
|
||||||
expectDateFormatAs(date, pattern, dateFixtures[pattern]);
|
expectDateFormatAs(date, pattern, dateFixtures[pattern]);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should format with pattern aliases', () => {
|
it('should format with pattern aliases', () => {
|
||||||
@ -266,14 +265,13 @@ describe('Format date', () => {
|
|||||||
() => expect(formatDate('2017-01-20T12:00:00+0000', defaultFormat, defaultLocale))
|
() => expect(formatDate('2017-01-20T12:00:00+0000', defaultFormat, defaultLocale))
|
||||||
.toEqual('Jan 20, 2017'));
|
.toEqual('Jan 20, 2017'));
|
||||||
|
|
||||||
// test for the following bugs:
|
|
||||||
// https://github.com/angular/angular/issues/9524
|
// https://github.com/angular/angular/issues/9524
|
||||||
// https://github.com/angular/angular/issues/9524
|
// https://github.com/angular/angular/issues/9524
|
||||||
it('should format correctly with iso strings that contain time',
|
it('should format correctly with iso strings that contain time',
|
||||||
() => expect(formatDate('2017-05-07T22:14:39', 'dd-MM-yyyy HH:mm', defaultLocale))
|
() => expect(formatDate('2017-05-07T22:14:39', 'dd-MM-yyyy HH:mm', defaultLocale))
|
||||||
.toMatch(/07-05-2017 \d{2}:\d{2}/));
|
.toMatch(/07-05-2017 \d{2}:\d{2}/));
|
||||||
|
|
||||||
// test for issue https://github.com/angular/angular/issues/21491
|
// https://github.com/angular/angular/issues/21491
|
||||||
it('should not assume UTC for iso strings in Safari if the timezone is not defined', () => {
|
it('should not assume UTC for iso strings in Safari if the timezone is not defined', () => {
|
||||||
// this test only works if the timezone is not in UTC
|
// this test only works if the timezone is not in UTC
|
||||||
// which is the case for BrowserStack when we test Safari
|
// which is the case for BrowserStack when we test Safari
|
||||||
@ -283,7 +281,6 @@ describe('Format date', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// test for the following bugs:
|
|
||||||
// https://github.com/angular/angular/issues/16624
|
// https://github.com/angular/angular/issues/16624
|
||||||
// https://github.com/angular/angular/issues/17478
|
// https://github.com/angular/angular/issues/17478
|
||||||
it('should show the correct time when the timezone is fixed', () => {
|
it('should show the correct time when the timezone is fixed', () => {
|
||||||
@ -311,5 +308,17 @@ describe('Format date', () => {
|
|||||||
expect(() => formatDate(date, 'b', 'de'))
|
expect(() => formatDate(date, 'b', 'de'))
|
||||||
.toThrowError(/Missing extra locale data for the locale "de"/);
|
.toThrowError(/Missing extra locale data for the locale "de"/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// https://github.com/angular/angular/issues/24384
|
||||||
|
it('should not round fractional seconds', () => {
|
||||||
|
expect(formatDate(3999, 'm:ss', 'en')).toEqual('0:03');
|
||||||
|
expect(formatDate(3999, 'm:ss.S', 'en')).toEqual('0:03.9');
|
||||||
|
expect(formatDate(3999, 'm:ss.SS', 'en')).toEqual('0:03.99');
|
||||||
|
expect(formatDate(3999, 'm:ss.SSS', 'en')).toEqual('0:03.999');
|
||||||
|
expect(formatDate(3000, 'm:ss', 'en')).toEqual('0:03');
|
||||||
|
expect(formatDate(3000, 'm:ss.S', 'en')).toEqual('0:03.0');
|
||||||
|
expect(formatDate(3000, 'm:ss.SS', 'en')).toEqual('0:03.00');
|
||||||
|
expect(formatDate(3000, 'm:ss.SSS', 'en')).toEqual('0:03.000');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user