AAE-43749 Added date formatting according to locale (#11769)

* [AAE-43749] added date formatting according to locale

* [AAE-43749] applied pr comments

* [AAE-43749] pipe use default date format reacting to locale
This commit is contained in:
Tomek Hanaj
2026-04-01 12:29:41 +01:00
committed by GitHub
parent 7b32eb49d8
commit d7a8f03dbf
2 changed files with 229 additions and 28 deletions
+222 -25
View File
@@ -15,47 +15,244 @@
* limitations under the License.
*/
import { TimeAgoPipe } from './time-ago.pipe';
import { DatePipe, registerLocaleData } from '@angular/common';
import localeDe from '@angular/common/locales/de';
import localeDeExtra from '@angular/common/locales/extra/de';
import { TestBed } from '@angular/core/testing';
import { AppConfigService } from '../app-config/app-config.service';
import { formatDistance } from 'date-fns';
import { UserPreferencesService } from '../common/services/user-preferences.service';
import { of } from 'rxjs';
import { DateFnsUtils } from '../common/utils/date-fns-utils';
import { TimeAgoPipe } from './time-ago.pipe';
import { AppConfigService } from '../app-config';
registerLocaleData(localeDe, 'de', localeDeExtra);
describe('TimeAgoPipe', () => {
let pipe: TimeAgoPipe;
let userPreferences: UserPreferencesService;
const NOW = new Date('2026-03-27T12:00:00.000Z');
beforeEach(() => {
const createPipe = (dateFormat, locale): TimeAgoPipe => {
TestBed.resetTestingModule();
TestBed.configureTestingModule({
providers: [TimeAgoPipe, UserPreferencesService, AppConfigService]
providers: [
TimeAgoPipe,
{
provide: UserPreferencesService,
useValue: {
localeSignal: jasmine.createSpy('localeSignal').and.returnValue(locale)
}
},
{
provide: AppConfigService,
useValue: {
get: jasmine.createSpy('get').and.returnValue(dateFormat)
}
}
]
});
userPreferences = TestBed.inject(UserPreferencesService);
spyOn(userPreferences, 'select').and.returnValue(of(''));
pipe = TestBed.inject(TimeAgoPipe);
const newPipe = TestBed.inject(TimeAgoPipe);
spyOn(newPipe, 'getCurrentDateTime').and.returnValue(NOW);
return newPipe;
};
describe('AppConfigService returns "MMM d, y, h:mm" and locale="en-US" ', () => {
let pipe: TimeAgoPipe;
beforeEach(() => {
pipe = createPipe('MMM d, y, h:mm', 'en-US');
});
it('should return time difference for a given date', () => {
const date = pipe.getCurrentDateTime();
expect(pipe.transform(date)).toBe('less than a minute ago');
});
it('should return exact date if given date is more than seven days for en locale ', () => {
const date = new Date('1990-11-02T15:25:42.749');
expect(pipe.transform(date)).toBe('Nov 2, 1990, 3:25');
});
it('should return empty string if given date is empty', () => {
expect(pipe.transform(null)).toBe('');
expect(pipe.transform(undefined)).toBe('');
});
it('should return relative distance when date is a few hours ago', () => {
const threeHoursAgo = new Date('2026-03-27T09:00:00.000Z');
const result = pipe.transform(threeHoursAgo);
const expected = formatDistance(threeHoursAgo, NOW, {
addSuffix: true,
locale: DateFnsUtils.getLocaleFromString('en-US')
});
expect(result).toBe(expected);
});
it('should return relative distance when date is 6 days ago', () => {
const sixDaysAgo = new Date('2026-03-21T12:00:00.000Z');
const result = pipe.transform(sixDaysAgo);
const expected = formatDistance(sixDaysAgo, NOW, {
addSuffix: true,
locale: DateFnsUtils.getLocaleFromString('en-US')
});
expect(result).toBe(expected);
});
it('should return relative distance when date is exactly 7 days ago', () => {
const sevenDaysAgo = new Date('2026-03-20T12:00:00.000Z');
const result = pipe.transform(sevenDaysAgo);
const expected = formatDistance(sevenDaysAgo, NOW, {
addSuffix: true,
locale: DateFnsUtils.getLocaleFromString('en-US')
});
expect(result).toBe(expected);
});
it('should use locale argument over user preference locale', () => {
const oldDate = new Date('2026-03-01T10:00:00.000Z');
const result = pipe.transform(oldDate, 'de');
expect(result).toBe('März 1, 2026, 10:00');
});
});
it('should return time difference for a given date', () => {
const date = new Date();
expect(pipe.transform(date)).toBe('less than a minute ago');
describe('AppConfigService return "MMM d, y, h:mm" and locale="de" ', () => {
let pipe: TimeAgoPipe;
beforeEach(() => {
pipe = createPipe('MMM d, y, h:mm', 'de');
});
it('should return time difference for a given date', () => {
const date = pipe.getCurrentDateTime();
expect(pipe.transform(date)).toBe('vor weniger als 1 Minute');
});
it('should return exact date if given date is more than seven days for de locale ', () => {
const date = new Date('1990-11-03T15:25:42.749');
expect(pipe.transform(date)).toBe('Nov. 3, 1990, 3:25');
});
it('should return empty string if given date is empty', () => {
expect(pipe.transform(null)).toBe('');
expect(pipe.transform(undefined)).toBe('');
});
it('should return relative distance when date is a few hours ago', () => {
const threeHoursAgo = new Date('2026-03-27T09:00:00.000Z');
const result = pipe.transform(threeHoursAgo);
const expected = formatDistance(threeHoursAgo, NOW, {
addSuffix: true,
locale: DateFnsUtils.getLocaleFromString('de')
});
expect(result).toBe(expected);
});
it('should return relative distance when date is 6 days ago', () => {
const sixDaysAgo = new Date('2026-03-21T12:00:00.000Z');
const result = pipe.transform(sixDaysAgo);
const expected = formatDistance(sixDaysAgo, NOW, {
addSuffix: true,
locale: DateFnsUtils.getLocaleFromString('de')
});
expect(result).toBe(expected);
});
it('should return relative distance when date is exactly 7 days ago', () => {
const sevenDaysAgo = new Date('2026-03-20T12:00:00.000Z');
const result = pipe.transform(sevenDaysAgo);
const expected = formatDistance(sevenDaysAgo, NOW, {
addSuffix: true,
locale: DateFnsUtils.getLocaleFromString('de')
});
expect(result).toBe(expected);
});
it('should use locale argument over user preference locale', () => {
const oldDate = new Date('2026-03-01T10:00:00.000Z');
const result = pipe.transform(oldDate, 'en-US');
expect(result).toBe('Mar 1, 2026, 10:00');
});
});
it('should return exact date if given date is more than seven days ', () => {
const date = new Date('1990-11-03T15:25:42.749');
expect(pipe.transform(date)).toBe('03/11/1990 15:25');
describe('AppConfigService has no value and returns default short and locale="en-US" ', () => {
let pipe: TimeAgoPipe;
beforeEach(() => {
pipe = createPipe('short', 'en-US');
});
it('should return time difference for a given date', () => {
const date = pipe.getCurrentDateTime();
expect(pipe.transform(date)).toBe('less than a minute ago');
});
it('should return exact date if given date is more than seven days for en locale ', () => {
const date = new Date('1990-11-04T15:25:42.749');
expect(pipe.transform(date)).toBe('11/4/90, 3:25 PM');
});
it('should return empty string if given date is empty', () => {
expect(pipe.transform(null)).toBe('');
expect(pipe.transform(undefined)).toBe('');
});
it('should use locale argument over user preference locale', () => {
const oldDate = new Date('2026-03-01T10:00:00.000Z');
const result = pipe.transform(oldDate, 'de');
expect(result).toBe('01.03.26, 10:00');
});
});
it('should return empty string if given date is empty', () => {
expect(pipe.transform(null)).toBe('');
expect(pipe.transform(undefined)).toBe('');
describe('AppConfigService has no value and returns default short and locale="de" ', () => {
let pipe: TimeAgoPipe;
beforeEach(() => {
pipe = createPipe('short', 'de');
});
it('should return time difference for a given date', () => {
const date = pipe.getCurrentDateTime();
expect(pipe.transform(date)).toBe('vor weniger als 1 Minute');
});
it('should return exact date if given date is more than seven days for de locale ', () => {
const date = new Date('1990-11-04T15:25:42.749');
expect(pipe.transform(date)).toBe('04.11.90, 15:25');
});
it('should return empty string if given date is empty', () => {
expect(pipe.transform(null)).toBe('');
expect(pipe.transform(undefined)).toBe('');
});
it('should use locale argument over user preference locale', () => {
const oldDate = new Date('2026-03-01T10:00:00.000Z');
const result = pipe.transform(oldDate, 'en-US');
expect(result).toBe('3/1/26, 10:00 AM');
});
});
describe('When a locale is given', () => {
it('should return a localised message', () => {
const date = new Date();
const transformedDate = pipe.transform(date, 'de');
/* cspell:disable-next-line */
expect(transformedDate).toBe('vor weniger als 1 Minute');
describe('locale handling', () => {
let pipe: TimeAgoPipe;
beforeEach(() => {
pipe = createPipe('short', undefined);
});
it('should return localized relative message when locale argument is provided', () => {
const date = new Date(NOW);
const result = pipe.transform(date, 'de');
const expected = formatDistance(date, NOW, {
addSuffix: true,
locale: DateFnsUtils.getLocaleFromString('de')
});
expect(result).toBe(expected);
});
it('should fall back to DEFAULT_LOCALE when user preference locale is empty', () => {
const oldDate = new Date('2026-03-01T10:00:00.000Z');
const result = pipe.transform(oldDate);
const expected = new DatePipe('en-US').transform(oldDate, 'short');
expect(result).toBe(expected);
});
});
});
+7 -3
View File
@@ -32,7 +32,7 @@ export class TimeAgoPipe implements PipeTransform {
appConfig = inject(AppConfigService);
static DEFAULT_LOCALE = 'en-US';
static DEFAULT_DATE_TIME_FORMAT = 'dd/MM/yyyy HH:mm';
static DEFAULT_DATE_TIME_FORMAT = 'short';
defaultDateTimeFormat: string;
@@ -40,18 +40,22 @@ export class TimeAgoPipe implements PipeTransform {
this.defaultDateTimeFormat = this.appConfig.get<string>('dateValues.defaultDateTimeFormat', TimeAgoPipe.DEFAULT_DATE_TIME_FORMAT);
}
getCurrentDateTime(): Date {
return new Date();
}
transform(value: Date, locale?: string) {
if (value !== null && value !== undefined) {
// Use signal directly - no subscription needed!
const defaultLocale = this.userPreferenceService.localeSignal() || TimeAgoPipe.DEFAULT_LOCALE;
const actualLocale = locale || defaultLocale;
const diff = differenceInDays(new Date(), new Date(value));
const diff = differenceInDays(this.getCurrentDateTime(), new Date(value));
if (diff > 7) {
const datePipe: DatePipe = new DatePipe(actualLocale);
return datePipe.transform(value, this.defaultDateTimeFormat);
} else {
const dateFnsLocale = DateFnsUtils.getLocaleFromString(actualLocale);
return formatDistance(new Date(value), new Date(), { addSuffix: true, locale: dateFnsLocale });
return formatDistance(new Date(value), this.getCurrentDateTime(), { addSuffix: true, locale: dateFnsLocale });
}
}
return '';