mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-02 08:15:55 +00:00
fix: keep event cards whose last day ends after a daylight saving change
This commit is contained in:
@@ -122,6 +122,20 @@ describe('resolveCards', () => {
|
||||
expect((card as { checkin: { time?: string } }).checkin.time).toBeUndefined();
|
||||
});
|
||||
|
||||
it('ends a date-only event end at midnight local time even across a DST change', async () => {
|
||||
const event = (country?: string) => ({
|
||||
'@context': ctx, '@type': 'EventReservation', reservationNumber: 'EV1',
|
||||
reservationFor: {
|
||||
'@type': 'Event', name: 'Herbstmesse', startDate: '2026-10-23T18:00:00+02:00', endDate: '2026-10-26',
|
||||
location: { '@type': 'Place', name: 'Messe', ...(country ? { address: { addressCountry: country } } : {}) },
|
||||
},
|
||||
});
|
||||
const [withVenue] = await resolved(event('DE'));
|
||||
expect(withVenue).toMatchObject({ end: { date: '2026-10-26', time: '23:59:59', zone: 'Europe/Berlin', utc: '2026-10-26T22:59:59Z' } });
|
||||
const [noVenueCountry] = await resolved(event());
|
||||
expect(noVenueCountry).toMatchObject({ end: { date: '2026-10-26', time: '23:59:59', zone: 'Etc/GMT-2', utc: '2026-10-26T21:59:59Z' } });
|
||||
});
|
||||
|
||||
it('reads bus times in the single zone of the stop\'s country', async () => {
|
||||
const [card] = (await resolved({
|
||||
'@context': ctx, '@type': 'BusReservation', reservationNumber: 'B',
|
||||
|
||||
@@ -231,12 +231,6 @@ function base(kind: BookingKind, res: Obj, source: CardSource) {
|
||||
|
||||
const MISSING: Moment = { date: '' };
|
||||
|
||||
function offsetSuffix(minutes: number): string {
|
||||
const sign = minutes < 0 ? '-' : '+';
|
||||
const abs = Math.abs(minutes);
|
||||
return `${sign}${String(Math.floor(abs / 60)).padStart(2, '0')}:${String(abs % 60).padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
function reservation(kind: BookingKind, res: Obj, source: CardSource): StructuredCard | undefined {
|
||||
if (kind === 'flight' || kind === 'train' || kind === 'bus') return transport(kind, res, source);
|
||||
const target = obj(res.reservationFor) ?? {};
|
||||
@@ -275,8 +269,9 @@ function reservation(kind: BookingKind, res: Obj, source: CardSource): Structure
|
||||
let end = moment(target.endDate);
|
||||
// An end date without a time means the end of that day, when the
|
||||
// event starts at a time; with a date-only start it's an all-day span.
|
||||
if (end && !end.time && start?.time) end = { ...end, time: '23:59:59', ...(start.offsetMinutes !== undefined ? { offsetMinutes: start.offsetMinutes } : {}) };
|
||||
if (end?.offsetMinutes !== undefined && end.time && !end.utc) end = moment(`${end.date}T${end.time}${offsetSuffix(end.offsetMinutes)}`);
|
||||
// The end carries no offset: the day may end on the other side of a
|
||||
// DST change, so resolve.ts reads it in the zone the start resolves to.
|
||||
if (end && !end.time && start?.time) end = { ...end, time: '23:59:59' };
|
||||
return {
|
||||
...base(kind, res, source),
|
||||
kind,
|
||||
|
||||
@@ -116,8 +116,14 @@ function resolveCard(card: StructuredCard, zoneOf: ZoneOf | undefined): Structur
|
||||
case 'event': {
|
||||
const named = (m: Moment) => namedZone(card.venue, card.kind, zoneOf, m.offsetMinutes === undefined);
|
||||
const start = resolveMoment(card.start, named(card.start));
|
||||
const end = card.end ? resolveMoment(card.end, named(card.end)) : undefined;
|
||||
if (start === 'drop' || end === 'drop') return null;
|
||||
if (start === 'drop') return null;
|
||||
// An end without its own offset (a date-only end turned into the end of
|
||||
// that day) belongs to the start's zone when the venue names none.
|
||||
const endNamed = card.end && card.end.offsetMinutes === undefined && !named(card.end) && start.zone
|
||||
? { zone: start.zone, source: 'country' as const }
|
||||
: card.end ? named(card.end) : undefined;
|
||||
const end = card.end ? resolveMoment(card.end, endNamed) : undefined;
|
||||
if (end === 'drop') return null;
|
||||
if (end && !notBefore(end, start)) return null;
|
||||
return { ...card, start, ...(end ? { end } : {}) };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user