Skip to content

Commit

Permalink
Merge pull request #10 from cedarbaum/zero_out_time_in_gtfs_date_conv…
Browse files Browse the repository at this point in the history
…ersion

Zero out time in GTFS date string conversions
  • Loading branch information
linusnorton authored Oct 28, 2023
2 parents 9dad965 + d2e2032 commit 3ee3e97
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 40 deletions.
61 changes: 41 additions & 20 deletions src/gtfs/calendar/CalendarFactory.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,36 @@
import { Calendar, CalendarDate } from "../GTFS";
import { toGTFSDate } from "./toGTFSDate";
import { toGTFSDate, getDateFromGTFSString } from "./gtfsDateUtils";

/**
* Creates calendars based on a set of calendar dates.
*/
export class CalendarFactory {

/**
* Count the number of times a service runs or does not run on each day of the week and then decide whether to
* create a calendar with that day enabled with exclude days when it is not running, or disabled with include
* days when it is running.
*/
public create(serviceId: string, calendarDates: CalendarDate[]): [Calendar, CalendarDate[]] {
public create(
serviceId: string,
calendarDates: CalendarDate[]
): [Calendar, CalendarDate[]] {
calendarDates.sort((a, b) => +a.date - +b.date);

const startDate = calendarDates[0];
const endDate = calendarDates[calendarDates.length - 1];
const [daysRunning, daysNotRunning] = this.splitCalendarDates(calendarDates, serviceId, startDate, endDate);
const calendar = this.createCalendar(serviceId, startDate, endDate, daysRunning, daysNotRunning);
const [daysRunning, daysNotRunning] = this.splitCalendarDates(
calendarDates,
serviceId,
startDate,
endDate
);
const calendar = this.createCalendar(
serviceId,
startDate,
endDate,
daysRunning,
daysNotRunning
);
const newCalendarDates = this.getCalendarDates(daysRunning, daysNotRunning);

return [calendar, newCalendarDates];
Expand All @@ -29,22 +42,28 @@ export class CalendarFactory {
startDate: CalendarDate,
endDate: CalendarDate
): [CalendarDate[][], CalendarDate[][]] {

const daysNotRunning: CalendarDate[][] = [[], [], [], [], [], [], []];
const daysRunning: CalendarDate[][] = [[], [], [], [], [], [], []];
const calendarDateIndex = this.indexCalendarDates(calendarDates);

let i = startDate.date;
let date = this.getDateFromGTFSString(i);
let date = getDateFromGTFSString(i);

while (i <= endDate.date) {
const dow = date.getDay();

if (calendarDateIndex[i]) {
daysRunning[dow].push({ exception_type: 1, service_id: serviceId, date: i });
}
else {
daysNotRunning[dow].push({ exception_type: 2, service_id: serviceId, date: i });
daysRunning[dow].push({
exception_type: 1,
service_id: serviceId,
date: i,
});
} else {
daysNotRunning[dow].push({
exception_type: 2,
service_id: serviceId,
date: i,
});
}

date.setDate(date.getDate() + 1);
Expand All @@ -54,18 +73,16 @@ export class CalendarFactory {
return [daysRunning, daysNotRunning];
}

private indexCalendarDates(calendarDates: CalendarDate[]): Record<string, CalendarDate> {
private indexCalendarDates(
calendarDates: CalendarDate[]
): Record<string, CalendarDate> {
return calendarDates.reduce((index, calendarDate) => {
index[calendarDate.date] = calendarDate;

return index;
}, {});
}

private getDateFromGTFSString(i: string): Date {
return new Date(i.substr(0, 4) + "-" + i.substr(4, 2) + "-" + i.substr(6, 2));
}

private createCalendar(
serviceId: string,
startDate: CalendarDate,
Expand All @@ -83,18 +100,22 @@ export class CalendarFactory {
wednesday: daysRunning[3].length > daysNotRunning[3].length ? 1 : 0,
thursday: daysRunning[4].length > daysNotRunning[4].length ? 1 : 0,
friday: daysRunning[5].length > daysNotRunning[5].length ? 1 : 0,
saturday: daysRunning[6].length > daysNotRunning[6].length ? 1 : 0
saturday: daysRunning[6].length > daysNotRunning[6].length ? 1 : 0,
};
}

/**
* For each day of the week check if the service runs more often than not. If it does, return the exclude days as
* the calendar will have the day set to 1, if not then return the include days.
*/
private getCalendarDates(daysRunning: CalendarDate[][], daysNotRunning: CalendarDate[][]): CalendarDate[] {
private getCalendarDates(
daysRunning: CalendarDate[][],
daysNotRunning: CalendarDate[][]
): CalendarDate[] {
return daysRunning.flatMap((runningDates, i) => {
return runningDates.length > daysNotRunning[i].length ? daysNotRunning[i] : runningDates;
return runningDates.length > daysNotRunning[i].length
? daysNotRunning[i]
: runningDates;
});
}

}
34 changes: 34 additions & 0 deletions src/gtfs/calendar/gftsDateUtils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as chai from "chai";
import { toGTFSDate, getDateFromGTFSString } from "./gtfsDateUtils";

describe("toGTFSDate", () => {
it("returns a GTFS date string", () => {
const date = new Date("2019-06-04T00:00:00");
const result = toGTFSDate(date);

chai.expect(result).to.equal("20190604");
});
});

describe("getDateFromGTFSString", () => {
it("returns a JS Date from GTFS string", () => {
const result = getDateFromGTFSString("20190604");
const dateDiff = result.getTime() - new Date(2019, 5, 4, 0, 0, 0).getTime();
chai.expect(dateDiff).to.equal(0);
});
});

describe("GTFS date roundtrip", () => {
it("Preserves GTFS dates when converting to/from JS dates", () => {
function validateGtfsDateRoundtrip(gtfsDate: string) {
const date = getDateFromGTFSString(gtfsDate);
const result = toGTFSDate(date);
chai.expect(result).to.equal(gtfsDate);
}

validateGtfsDateRoundtrip("20190601");
validateGtfsDateRoundtrip("20190604");
validateGtfsDateRoundtrip("20191231");
validateGtfsDateRoundtrip("20200101");
});
});
14 changes: 14 additions & 0 deletions src/gtfs/calendar/gtfsDateUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export function toGTFSDate(date: Date): string {
return (
date.getFullYear() +
(date.getMonth() + 1).toString().padStart(2, "0") +
date.getDate().toString().padStart(2, "0")
);
}

export function getDateFromGTFSString(i: string): Date {
// Zero out the time portion of the date to avoid timezone issues
return new Date(
i.substr(0, 4) + "-" + i.substr(4, 2) + "-" + i.substr(6, 2) + "T00:00:00"
);
}
13 changes: 0 additions & 13 deletions src/gtfs/calendar/toGTFSDate.spec.ts

This file was deleted.

6 changes: 0 additions & 6 deletions src/gtfs/calendar/toGTFSDate.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as yargs from "yargs";
import { Arguments } from "yargs";
import { Container } from "./Container";
import { toGTFSDate } from "./gtfs/calendar/toGTFSDate";
import { toGTFSDate } from "./gtfs/calendar/gtfsDateUtils";
import { RouteType } from "./gtfs/GTFS";

const args = yargs.argv as Arguments<{
Expand Down

0 comments on commit 3ee3e97

Please sign in to comment.