move setLocale and add comment

This commit is contained in:
Mees Frensel 2026-06-10 14:20:35 +02:00
parent b430f5188b
commit 439b7fe3be
6 changed files with 41 additions and 20 deletions

View file

@ -54,7 +54,7 @@ class AlbumResponseDto {
/// Album description
String description;
/// End date (latest asset)
/// UTC representation of (local) end date (latest asset)
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
@ -92,7 +92,7 @@ class AlbumResponseDto {
/// Is shared album
bool shared;
/// Start date (earliest asset)
/// UTC representation of (local) start date (earliest asset)
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated

View file

@ -16235,7 +16235,7 @@
"type": "string"
},
"endDate": {
"description": "End date (latest asset)",
"description": "UTC representation of (local) end date (latest asset)",
"format": "date-time",
"type": "string"
},
@ -16264,7 +16264,7 @@
"type": "boolean"
},
"startDate": {
"description": "Start date (earliest asset)",
"description": "UTC representation of (local) start date (earliest asset)",
"format": "date-time",
"type": "string"
},

View file

@ -484,7 +484,7 @@ export type AlbumResponseDto = {
createdAt: string;
/** Album description */
description: string;
/** End date (latest asset) */
/** UTC representation of (local) end date (latest asset) */
endDate?: string;
/** Has shared link */
hasSharedLink: boolean;
@ -497,7 +497,7 @@ export type AlbumResponseDto = {
order?: AssetOrder;
/** Is shared album */
shared: boolean;
/** Start date (earliest asset) */
/** UTC representation of (local) start date (earliest asset) */
startDate?: string;
/** Last update date */
updatedAt: string;

View file

@ -131,9 +131,17 @@ export const AlbumResponseSchema = z
.optional()
.describe('Last modified asset timestamp'),
// TODO: use `isoDatetimeToDate` when using `ZodSerializerDto` on the controllers.
startDate: z.string().meta({ format: 'date-time' }).optional().describe('Start date (earliest asset)'),
startDate: z
.string()
.meta({ format: 'date-time' })
.optional()
.describe('UTC representation of (local) start date (earliest asset)'),
// TODO: use `isoDatetimeToDate` when using `ZodSerializerDto` on the controllers.
endDate: z.string().meta({ format: 'date-time' }).optional().describe('End date (latest asset)'),
endDate: z
.string()
.meta({ format: 'date-time' })
.optional()
.describe('UTC representation of (local) end date (latest asset)'),
isActivityEnabled: z.boolean().describe('Activity feed enabled'),
order: AssetOrderSchema.optional(),
contributorCounts: z.array(ContributorCountResponseSchema).optional(),

View file

@ -34,6 +34,11 @@ describe('getShortDateRange', () => {
expect(getShortDateRange('2022-01-01T00:00:00.000Z', '2022-01-31T00:00:00.000Z')).toEqual('January 2022');
});
it('should correctly return long month if start and end date are within the same month, ignoring local time zone', () => {
vi.stubEnv('TZ', 'UTC-6');
expect(getShortDateRange('2022-01-01T00:00:00.000Z', '2022-01-31T00:00:00.000Z')).toEqual('January 2022');
});
it('should correctly return month range if start and end date are in separate months within the same year, ignoring local time zone', () => {
vi.stubEnv('TZ', 'UTC+6');
expect(getShortDateRange('2022-01-01T00:00:00.000Z', '2022-02-01T00:00:00.000Z')).toEqual('JanFeb 2022');
@ -61,7 +66,7 @@ describe('getShortDateRange', () => {
});
describe('getAlbumDateRange', () => {
beforeAll(() => {
beforeEach(() => {
vi.stubEnv('TZ', 'UTC');
});
@ -77,17 +82,19 @@ describe('getAlbumDateRange', () => {
expect(getAlbumDateRange('2021-01-01T09:00:00Z', '2021-01-01T10:00:00Z')).toEqual('Jan 1, 2021');
});
it('should work with positive time zone present', () => {
expect(getAlbumDateRange('2021-01-01T00:00:00+05:00', '2021-01-01T00:00:00+05:00')).toEqual('Jan 1, 2021');
});
it('should work with negative time zone present', () => {
expect(getAlbumDateRange('2021-01-01T00:00:00-05:00', '2021-01-01T00:00:00-05:00')).toEqual('Jan 1, 2021');
});
it('should use the proper locale', () => {
locale.set('fr');
expect(getAlbumDateRange('2020-03-26T12:00:00Z', '2021-12-01T00:00:00Z')).toEqual('26 mars 20201 déc. 2021');
locale.set('en');
});
it('should correctly return range if start and end date are in separate months and years, ignoring local time zone', () => {
vi.stubEnv('TZ', 'UTC+6');
expect(getAlbumDateRange('2021-12-01T00:00:00Z', '2022-01-01T00:00:00Z')).toEqual('Dec 1, 2021Jan 1, 2022');
});
it('should correctly return range if start and end date are in separate months and years, ignoring local time zone', () => {
vi.stubEnv('TZ', 'UTC-6');
expect(getAlbumDateRange('2021-12-01T00:00:00Z', '2022-01-01T00:00:00Z')).toEqual('Dec 1, 2021Jan 1, 2022');
});
});

View file

@ -8,12 +8,14 @@ export function parseUtcDate(date: string) {
}
const getDateRange = (startTimestamp: string, endTimestamp: string, format: 'short' | 'long') => {
// We don't need to check if the locale is set/nonempty. MDN's Intl docs:
// "If the application doesn't provide a locales argument, or the runtime doesn't have a locale that matches the request, then the runtime's default locale is used."
const userLocale = get(locale);
const startDate = DateTime.fromISO(startTimestamp).setZone('UTC');
const endDate = DateTime.fromISO(endTimestamp).setZone('UTC').setLocale(userLocale);
const endDate = DateTime.fromISO(endTimestamp).setZone('UTC');
if (startDate.year === endDate.year && startDate.month === endDate.month && format === 'short') {
return endDate.toLocaleString({ month: 'long', year: 'numeric' });
return endDate.setLocale(userLocale).toLocaleString({ month: 'long', year: 'numeric' });
}
const formatter = new Intl.DateTimeFormat(
@ -24,10 +26,14 @@ const getDateRange = (startTimestamp: string, endTimestamp: string, format: 'sho
};
/**
* Get localized date range in short format like 'Oct Nov 2026', with full month if start and end are the same: 'October 2026'
* Get localized date range in short format like 'Oct Nov 2026', with full month if start and end are the same: 'October 2026'.
* Timestamps are expected to be date-only in UTC.
*/
export const getShortDateRange = (start: string, end: string) => getDateRange(start, end, 'short');
/**
* Get localized date range in long format. Timestamps are expected to be date-only in UTC.
*/
export const getAlbumDateRange = (start: string, end: string) => getDateRange(start, end, 'long');
/**