fix(common): format day-periods that cross midnight (#36611)
When formatting a time with the `b` or `B` format codes, the rendered string was not correctly handling day periods that spanned midnight. Instead the logic was falling back to the default case of `AM`. Now the logic has been updated so that it matches times that are within a day period that spans midnight, so it will now render the correct output, such as `at night` in the case of English. Applications that are using either `formatDate()` or `DatePipe` and any of the `b` or `B` format codes will be affected by this change. Fixes #36566 PR Close #36611
This commit is contained in:
parent
074266b896
commit
c6e5fc4fbe
@ -277,26 +277,40 @@ function getDateTranslation(
|
|||||||
if (extended) {
|
if (extended) {
|
||||||
const rules = getLocaleExtraDayPeriodRules(locale);
|
const rules = getLocaleExtraDayPeriodRules(locale);
|
||||||
const dayPeriods = getLocaleExtraDayPeriods(locale, form, width);
|
const dayPeriods = getLocaleExtraDayPeriods(locale, form, width);
|
||||||
let result;
|
const index = rules.findIndex(rule => {
|
||||||
rules.forEach((rule: Time|[Time, Time], index: number) => {
|
|
||||||
if (Array.isArray(rule)) {
|
if (Array.isArray(rule)) {
|
||||||
// morning, afternoon, evening, night
|
// morning, afternoon, evening, night
|
||||||
const {hours: hoursFrom, minutes: minutesFrom} = rule[0];
|
const [from, to] = rule;
|
||||||
const {hours: hoursTo, minutes: minutesTo} = rule[1];
|
const afterFrom = currentHours >= from.hours && currentMinutes >= from.minutes;
|
||||||
if (currentHours >= hoursFrom && currentMinutes >= minutesFrom &&
|
const beforeTo =
|
||||||
(currentHours < hoursTo ||
|
(currentHours < to.hours ||
|
||||||
(currentHours === hoursTo && currentMinutes < minutesTo))) {
|
(currentHours === to.hours && currentMinutes < to.minutes));
|
||||||
result = dayPeriods[index];
|
// We must account for normal rules that span a period during the day (e.g. 6am-9am)
|
||||||
|
// where `from` is less (earlier) than `to`. But also rules that span midnight (e.g.
|
||||||
|
// 10pm - 5am) where `from` is greater (later!) than `to`.
|
||||||
|
//
|
||||||
|
// In the first case the current time must be BOTH after `from` AND before `to`
|
||||||
|
// (e.g. 8am is after 6am AND before 10am).
|
||||||
|
//
|
||||||
|
// In the second case the current time must be EITHER after `from` OR before `to`
|
||||||
|
// (e.g. 4am is before 5am but not after 10pm; and 11pm is not before 5am but it is
|
||||||
|
// after 10pm).
|
||||||
|
if (from.hours < to.hours) {
|
||||||
|
if (afterFrom && beforeTo) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (afterFrom || beforeTo) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
} else { // noon or midnight
|
} else { // noon or midnight
|
||||||
const {hours, minutes} = rule;
|
if (rule.hours === currentHours && rule.minutes === currentMinutes) {
|
||||||
if (hours === currentHours && minutes === currentMinutes) {
|
return true;
|
||||||
result = dayPeriods[index];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
if (result) {
|
if (index !== -1) {
|
||||||
return result;
|
return dayPeriods[index];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if no rules for the day periods, we use am/pm by default
|
// if no rules for the day periods, we use am/pm by default
|
||||||
|
@ -202,6 +202,19 @@ describe('Format date', () => {
|
|||||||
BBBBB: 'mi',
|
BBBBB: 'mi',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const midnightCrossingPeriods: any = {
|
||||||
|
b: 'night',
|
||||||
|
bb: 'night',
|
||||||
|
bbb: 'night',
|
||||||
|
bbbb: 'night',
|
||||||
|
bbbbb: 'night',
|
||||||
|
B: 'at night',
|
||||||
|
BB: 'at night',
|
||||||
|
BBB: 'at night',
|
||||||
|
BBBB: 'at night',
|
||||||
|
BBBBB: 'at night',
|
||||||
|
};
|
||||||
|
|
||||||
Object.keys(dateFixtures).forEach((pattern: string) => {
|
Object.keys(dateFixtures).forEach((pattern: string) => {
|
||||||
expectDateFormatAs(date, pattern, dateFixtures[pattern]);
|
expectDateFormatAs(date, pattern, dateFixtures[pattern]);
|
||||||
});
|
});
|
||||||
@ -209,6 +222,11 @@ describe('Format date', () => {
|
|||||||
Object.keys(isoStringWithoutTimeFixtures).forEach((pattern: string) => {
|
Object.keys(isoStringWithoutTimeFixtures).forEach((pattern: string) => {
|
||||||
expectDateFormatAs(isoStringWithoutTime, pattern, isoStringWithoutTimeFixtures[pattern]);
|
expectDateFormatAs(isoStringWithoutTime, pattern, isoStringWithoutTimeFixtures[pattern]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const nightTime = new Date(2015, 5, 15, 2, 3, 1, 550);
|
||||||
|
Object.keys(midnightCrossingPeriods).forEach(pattern => {
|
||||||
|
expectDateFormatAs(nightTime, pattern, midnightCrossingPeriods[pattern]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should format with timezones', () => {
|
it('should format with timezones', () => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user