From a3da83e0ce5f988288cb3ff7a07060e65ab5b41d Mon Sep 17 00:00:00 2001 From: Michael Tsang Date: Thu, 23 Nov 2023 15:34:56 +0000 Subject: [PATCH] fix calendar generating start_date > end_date --- src/gtfs/native/Association.ts | 29 +++++++++++++++++++---------- src/gtfs/native/ScheduleCalendar.ts | 12 +++++++----- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/gtfs/native/Association.ts b/src/gtfs/native/Association.ts index be2f2528..8f28405b 100644 --- a/src/gtfs/native/Association.ts +++ b/src/gtfs/native/Association.ts @@ -51,25 +51,33 @@ export class Association implements OverlayRecord { */ public apply(base: Schedule, assoc: Schedule, idGenerator: IdGenerator): Schedule[] { const assocCalendar = this.dateIndicator === DateIndicator.Next ? this.calendar.shiftForward() : this.calendar; - const schedules = [this.mergeSchedules(base, assoc)]; + const mergedBase = this.mergeSchedules(base, assoc); + const schedules = mergedBase !== null ? [mergedBase] : []; // if the associated train starts running before the association, clone the associated schedule for those dates if (assoc.calendar.runsFrom.isBefore(assocCalendar.runsFrom)) { const before = assoc.calendar.clone(assoc.calendar.runsFrom, assocCalendar.runsFrom.clone().subtract(1, "days")); - schedules.push(assoc.clone(before, idGenerator.next().value)); + if (before !== null) { + schedules.push(assoc.clone(before, idGenerator.next().value)); + } } // if the associated train runs after the association has finished, clone the associated schedule for those dates if (assoc.calendar.runsTo.isAfter(assocCalendar.runsTo)) { const after = assoc.calendar.clone(assocCalendar.runsTo.clone().add(1, "days"), assoc.calendar.runsTo); - schedules.push(assoc.clone(after, idGenerator.next().value)); + if (after !== null) { + schedules.push(assoc.clone(after, idGenerator.next().value)); + } } // for each exclude day of the association for (const excludeDay of Object.values(assocCalendar.excludeDays)) { - schedules.push(assoc.clone(assoc.calendar.clone(excludeDay, excludeDay), idGenerator.next().value)); + const excludeCalendar = assoc.calendar.clone(excludeDay, excludeDay); + if (excludeCalendar !== null) { + schedules.push(assoc.clone(excludeCalendar, idGenerator.next().value)); + } } return schedules; @@ -78,7 +86,7 @@ export class Association implements OverlayRecord { /** * Apply the split or join to the given schedules */ - private mergeSchedules(base: Schedule, assoc: Schedule): Schedule { + private mergeSchedules(base: Schedule, assoc: Schedule): Schedule | null { let tuid: TUID; let start: StopTime[]; let assocStop: StopTime; @@ -117,16 +125,17 @@ export class Association implements OverlayRecord { const calendar = this.dateIndicator === DateIndicator.Next ? assoc.calendar.shiftBackward() : assoc.calendar; - return new Schedule( + const newCalendar = calendar.clone( + moment.max(this.calendar.runsFrom, calendar.runsFrom), + moment.min(this.calendar.runsTo, calendar.runsTo) + ); + return newCalendar === null ? null : new Schedule( assoc.id, stops, tuid, assoc.rsid, // only take the part of the schedule that the association applies to - calendar.clone( - moment.max(this.calendar.runsFrom, calendar.runsFrom), - moment.min(this.calendar.runsTo, calendar.runsTo) - ), + newCalendar, assoc.mode, assoc.operator, assoc.stp, diff --git a/src/gtfs/native/ScheduleCalendar.ts b/src/gtfs/native/ScheduleCalendar.ts index af0e5f28..3a5cf6bc 100644 --- a/src/gtfs/native/ScheduleCalendar.ts +++ b/src/gtfs/native/ScheduleCalendar.ts @@ -60,7 +60,7 @@ export class ScheduleCalendar { const calendar = this.clone(this.runsFrom, this.runsTo, NO_DAYS, excludeDays); - return calendar.runsFrom.isSameOrBefore(calendar.runsTo) ? [calendar] : []; + return calendar !== null ? [calendar] : []; } /** @@ -83,7 +83,7 @@ export class ScheduleCalendar { * Remove the given date range from this schedule and return one or two calendars */ public divideAround(calendar: ScheduleCalendar): ScheduleCalendar[] { - const calendars: ScheduleCalendar[] = [ + const calendars: (ScheduleCalendar|null)[] = [ this.clone(this.runsFrom.clone(), calendar.runsFrom.clone().subtract(1, "days")), this.clone(calendar.runsTo.clone().add(1, "days"), this.runsTo.clone()) ]; @@ -98,7 +98,7 @@ export class ScheduleCalendar { )); } - return calendars.filter(c => c.runsFrom.isSameOrBefore(c.runsTo)); + return calendars.filter((c) : c is ScheduleCalendar => c !== null); } /** @@ -107,9 +107,11 @@ export class ScheduleCalendar { public clone(start: Moment, end: Moment, removeDays: Days = NO_DAYS, - excludeDays: ExcludeDays = this.excludeDays): ScheduleCalendar { + excludeDays: ExcludeDays = this.excludeDays): ScheduleCalendar | null { const days = this.removeDays(removeDays); + start = start.clone(); + end = end.clone(); // skip forward to the first day the schedule is operating while (days[start.day()] === 0 || excludeDays[start.format("YYYYMMDD")] && start.isSameOrBefore(end)) { @@ -126,7 +128,7 @@ export class ScheduleCalendar { .filter(d => d.isBetween(start, end, "days", "[]")) .reduce((days: ExcludeDays, day: Moment) => { days[day.format("YYYYMMDD")] = day; return days; }, {}); - return new ScheduleCalendar(start, end, days, newExcludes); + return start.isSameOrBefore(end) ? new ScheduleCalendar(start, end, days, newExcludes) : null; } private removeDays(days: Days): Days {