gtfs 4.1.1 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/README.md +2 -2
- package/lib/gtfs/shapes.js +8 -0
- package/lib/gtfs/stops.js +7 -1
- package/lib/import.js +12 -2
- package/models/gtfs/agency.js +1 -5
- package/models/gtfs/attributions.js +1 -0
- package/models/gtfs/calendar-dates.js +2 -7
- package/models/gtfs/fare-leg-rules.js +4 -0
- package/models/gtfs/fare-products.js +5 -0
- package/models/gtfs/fare-rules.js +0 -5
- package/models/gtfs/fare-transfer-rules.js +6 -0
- package/models/gtfs/feed-info.js +0 -5
- package/models/gtfs/frequencies.js +2 -6
- package/models/gtfs/shapes.js +2 -6
- package/models/gtfs/stop-times.js +2 -7
- package/models/gtfs/transfers.js +7 -9
- package/models/gtfs/translations.js +6 -5
- package/models/gtfs-plus/directions.js +2 -7
- package/models/gtfs-plus/stop-attributes.js +1 -6
- package/models/gtfs-realtime/service-alert-targets.js +0 -1
- package/package.json +1 -1
- package/test/mocha/advanced-query.js +7 -7
- package/test/mocha/get-agencies.js +0 -2
- package/test/mocha/get-calendar-dates.js +0 -4
- package/test/mocha/open-db.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [4.2.0] - 2023-04-13
|
|
9
|
+
|
|
10
|
+
### Updated
|
|
11
|
+
|
|
12
|
+
- Add route_attributes to getShapesAsGeoJSON() function as geojson properties
|
|
13
|
+
- Add stop_attributes to getStopsAsGeoJSON() function as geojson properties
|
|
14
|
+
- Support for multi-column primary keys
|
|
15
|
+
|
|
8
16
|
## [4.1.1] - 2023-04-12
|
|
9
17
|
|
|
10
18
|
### Added
|
package/README.md
CHANGED
|
@@ -692,7 +692,7 @@ const stops = getStops({
|
|
|
692
692
|
|
|
693
693
|
#### getStopsAsGeoJSON(query, options)
|
|
694
694
|
|
|
695
|
-
Returns geoJSON object of stops that match query parameters. All valid queries for `getStops()` work for `getStopsAsGeoJSON()`.
|
|
695
|
+
Returns geoJSON object of stops that match query parameters. Stops will include all properties of each stop from stops.txt and stop_attributes.txt if present. All valid queries for `getStops()` work for `getStopsAsGeoJSON()`.
|
|
696
696
|
|
|
697
697
|
```js
|
|
698
698
|
import { getStopsAsGeoJSON } from 'gtfs';
|
|
@@ -804,7 +804,7 @@ const shapes = getShapes({
|
|
|
804
804
|
|
|
805
805
|
#### getShapesAsGeoJSON(query, options)
|
|
806
806
|
|
|
807
|
-
Returns a geoJSON object of shapes that match query parameters. All valid queries for `getShapes()` work for `getShapesAsGeoJSON()`.
|
|
807
|
+
Returns a geoJSON object of shapes that match query parameters. Shapes will include all properties of each route from routes.txt and route_attributes.txt if present. All valid queries for `getShapes()` work for `getShapesAsGeoJSON()`.
|
|
808
808
|
|
|
809
809
|
```js
|
|
810
810
|
import { getShapesAsGeoJSON } from 'gtfs';
|
package/lib/gtfs/shapes.js
CHANGED
|
@@ -14,6 +14,7 @@ import { shapesToGeoJSONFeatures } from '../geojson-utils.js';
|
|
|
14
14
|
import shapes from '../../models/gtfs/shapes.js';
|
|
15
15
|
import { getAgencies } from './agencies.js';
|
|
16
16
|
import { getRoutes } from './routes.js';
|
|
17
|
+
import { getRouteAttributes } from '../gtfs-plus/route-attributes.js';
|
|
17
18
|
|
|
18
19
|
function buildTripSubquery(query) {
|
|
19
20
|
const whereClause = formatWhereClauses(query);
|
|
@@ -92,6 +93,12 @@ export function getShapesAsGeoJSON(query = {}, options = {}) {
|
|
|
92
93
|
[],
|
|
93
94
|
options
|
|
94
95
|
);
|
|
96
|
+
const routeAttributes = getRouteAttributes(
|
|
97
|
+
{ route_id: route.route_id },
|
|
98
|
+
[],
|
|
99
|
+
[],
|
|
100
|
+
options
|
|
101
|
+
);
|
|
95
102
|
|
|
96
103
|
const agency = agencies.find(
|
|
97
104
|
(agency) => agency.agency_id === route.agency_id
|
|
@@ -101,6 +108,7 @@ export function getShapesAsGeoJSON(query = {}, options = {}) {
|
|
|
101
108
|
agency_name: agency ? agency.agency_name : undefined,
|
|
102
109
|
shape_id: query.shape_id,
|
|
103
110
|
...route,
|
|
111
|
+
...(routeAttributes?.[0] || []),
|
|
104
112
|
};
|
|
105
113
|
features.push(...shapesToGeoJSONFeatures(shapes, geojsonProperties));
|
|
106
114
|
}
|
package/lib/gtfs/stops.js
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { stopsToGeoJSON } from '../geojson-utils.js';
|
|
13
13
|
import stops from '../../models/gtfs/stops.js';
|
|
14
14
|
import { getAgencies } from './agencies.js';
|
|
15
|
+
import { getStopAttributes } from '../gtfs-plus/stop-attributes.js';
|
|
15
16
|
|
|
16
17
|
function buildTripSubquery(query) {
|
|
17
18
|
const whereClause = formatWhereClauses(query);
|
|
@@ -89,12 +90,17 @@ export function getStopsAsGeoJSON(query = {}, options = {}) {
|
|
|
89
90
|
.prepare(`SELECT * FROM routes WHERE route_id IN (${routeSubquery})`)
|
|
90
91
|
.all(stop.stop_id);
|
|
91
92
|
|
|
93
|
+
const stopAttributes = getStopAttributes({ stop_id: stop.stop_id });
|
|
94
|
+
|
|
92
95
|
stop.routes = orderBy(routes, (route) =>
|
|
93
96
|
Number.parseInt(route.route_short_name, 10)
|
|
94
97
|
);
|
|
95
98
|
stop.agency_name = agencies[0].agency_name;
|
|
96
99
|
|
|
97
|
-
return
|
|
100
|
+
return {
|
|
101
|
+
...stop,
|
|
102
|
+
...(stopAttributes?.[0] || []),
|
|
103
|
+
};
|
|
98
104
|
});
|
|
99
105
|
|
|
100
106
|
// Exclude stops not part of any route
|
package/lib/import.js
CHANGED
|
@@ -329,13 +329,23 @@ const createTables = (db) => {
|
|
|
329
329
|
check = `CHECK( ${column.name} <= ${column.max} )`;
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
-
const primary = column.primary ? 'PRIMARY KEY' : '';
|
|
333
332
|
const required = column.required ? 'NOT NULL' : '';
|
|
334
333
|
const columnDefault = column.default ? 'DEFAULT ' + column.default : '';
|
|
335
334
|
const columnCollation = column.nocase ? 'COLLATE NOCASE' : '';
|
|
336
|
-
return `${column.name} ${column.type} ${check} ${
|
|
335
|
+
return `${column.name} ${column.type} ${check} ${required} ${columnDefault} ${columnCollation}`;
|
|
337
336
|
});
|
|
338
337
|
|
|
338
|
+
// Find Primary Key fields
|
|
339
|
+
const primaryColumns = model.schema.filter((column) => column.primary);
|
|
340
|
+
|
|
341
|
+
if (primaryColumns.length > 0) {
|
|
342
|
+
columns.push(
|
|
343
|
+
`PRIMARY KEY (${primaryColumns
|
|
344
|
+
.map((column) => column.name)
|
|
345
|
+
.join(', ')})`
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
|
|
339
349
|
db.prepare(`DROP TABLE IF EXISTS ${model.filenameBase};`).run();
|
|
340
350
|
|
|
341
351
|
db.prepare(
|
package/models/gtfs/agency.js
CHANGED
|
@@ -1,22 +1,17 @@
|
|
|
1
1
|
const model = {
|
|
2
2
|
filenameBase: 'calendar_dates',
|
|
3
3
|
schema: [
|
|
4
|
-
{
|
|
5
|
-
name: 'id',
|
|
6
|
-
type: 'integer',
|
|
7
|
-
primary: true,
|
|
8
|
-
},
|
|
9
4
|
{
|
|
10
5
|
name: 'service_id',
|
|
11
6
|
type: 'varchar(255)',
|
|
12
7
|
required: true,
|
|
13
|
-
|
|
8
|
+
primary: true,
|
|
14
9
|
},
|
|
15
10
|
{
|
|
16
11
|
name: 'date',
|
|
17
12
|
type: 'integer',
|
|
18
13
|
required: true,
|
|
19
|
-
|
|
14
|
+
primary: true,
|
|
20
15
|
},
|
|
21
16
|
{
|
|
22
17
|
name: 'exception_type',
|
|
@@ -8,19 +8,23 @@ const model = {
|
|
|
8
8
|
{
|
|
9
9
|
name: 'network_id',
|
|
10
10
|
type: 'varchar(255)',
|
|
11
|
+
primary: true,
|
|
11
12
|
},
|
|
12
13
|
{
|
|
13
14
|
name: 'from_area_id',
|
|
14
15
|
type: 'varchar(255)',
|
|
16
|
+
primary: true,
|
|
15
17
|
},
|
|
16
18
|
{
|
|
17
19
|
name: 'to_area_id',
|
|
18
20
|
type: 'varchar(255)',
|
|
21
|
+
primary: true,
|
|
19
22
|
},
|
|
20
23
|
{
|
|
21
24
|
name: 'fare_product_id',
|
|
22
25
|
type: 'varchar(255)',
|
|
23
26
|
required: true,
|
|
27
|
+
primary: true,
|
|
24
28
|
},
|
|
25
29
|
],
|
|
26
30
|
};
|
|
@@ -4,15 +4,18 @@ const model = {
|
|
|
4
4
|
{
|
|
5
5
|
name: 'from_leg_group_id',
|
|
6
6
|
type: 'varchar(255)',
|
|
7
|
+
primary: true,
|
|
7
8
|
},
|
|
8
9
|
{
|
|
9
10
|
name: 'to_leg_group_id',
|
|
10
11
|
type: 'varchar(255)',
|
|
12
|
+
primary: true,
|
|
11
13
|
},
|
|
12
14
|
{
|
|
13
15
|
name: 'transfer_count',
|
|
14
16
|
type: 'integer',
|
|
15
17
|
min: -1,
|
|
18
|
+
primary: true,
|
|
16
19
|
},
|
|
17
20
|
{
|
|
18
21
|
name: 'transfer_id',
|
|
@@ -22,6 +25,7 @@ const model = {
|
|
|
22
25
|
name: 'duration_limit',
|
|
23
26
|
type: 'integer',
|
|
24
27
|
min: 0,
|
|
28
|
+
primary: true,
|
|
25
29
|
},
|
|
26
30
|
{
|
|
27
31
|
name: 'duration_limit_type',
|
|
@@ -34,10 +38,12 @@ const model = {
|
|
|
34
38
|
type: 'integer',
|
|
35
39
|
min: 0,
|
|
36
40
|
max: 2,
|
|
41
|
+
required: true,
|
|
37
42
|
},
|
|
38
43
|
{
|
|
39
44
|
name: 'fare_product_id',
|
|
40
45
|
type: 'varchar(255)',
|
|
46
|
+
primary: true,
|
|
41
47
|
},
|
|
42
48
|
],
|
|
43
49
|
};
|
package/models/gtfs/feed-info.js
CHANGED
|
@@ -1,21 +1,17 @@
|
|
|
1
1
|
const model = {
|
|
2
2
|
filenameBase: 'frequencies',
|
|
3
3
|
schema: [
|
|
4
|
-
{
|
|
5
|
-
name: 'id',
|
|
6
|
-
type: 'integer',
|
|
7
|
-
primary: true,
|
|
8
|
-
},
|
|
9
4
|
{
|
|
10
5
|
name: 'trip_id',
|
|
11
6
|
type: 'varchar(255)',
|
|
12
7
|
required: true,
|
|
13
|
-
|
|
8
|
+
primary: true,
|
|
14
9
|
},
|
|
15
10
|
{
|
|
16
11
|
name: 'start_time',
|
|
17
12
|
type: 'varchar(255)',
|
|
18
13
|
required: true,
|
|
14
|
+
primary: true,
|
|
19
15
|
},
|
|
20
16
|
{
|
|
21
17
|
name: 'start_timestamp',
|
package/models/gtfs/shapes.js
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
const model = {
|
|
2
2
|
filenameBase: 'shapes',
|
|
3
3
|
schema: [
|
|
4
|
-
{
|
|
5
|
-
name: 'id',
|
|
6
|
-
type: 'integer',
|
|
7
|
-
primary: true,
|
|
8
|
-
},
|
|
9
4
|
{
|
|
10
5
|
name: 'shape_id',
|
|
11
6
|
type: 'varchar(255)',
|
|
12
7
|
required: true,
|
|
13
|
-
|
|
8
|
+
primary: true,
|
|
14
9
|
},
|
|
15
10
|
{
|
|
16
11
|
name: 'shape_pt_lat',
|
|
@@ -30,6 +25,7 @@ const model = {
|
|
|
30
25
|
name: 'shape_pt_sequence',
|
|
31
26
|
type: 'integer',
|
|
32
27
|
required: true,
|
|
28
|
+
primary: true,
|
|
33
29
|
min: 0,
|
|
34
30
|
},
|
|
35
31
|
{
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
const model = {
|
|
2
2
|
filenameBase: 'stop_times',
|
|
3
3
|
schema: [
|
|
4
|
-
{
|
|
5
|
-
name: 'id',
|
|
6
|
-
type: 'integer',
|
|
7
|
-
primary: true,
|
|
8
|
-
},
|
|
9
4
|
{
|
|
10
5
|
name: 'trip_id',
|
|
11
6
|
type: 'varchar(255)',
|
|
12
7
|
required: true,
|
|
13
|
-
|
|
8
|
+
primary: true,
|
|
14
9
|
},
|
|
15
10
|
{
|
|
16
11
|
name: 'arrival_time',
|
|
@@ -39,8 +34,8 @@ const model = {
|
|
|
39
34
|
name: 'stop_sequence',
|
|
40
35
|
type: 'integer',
|
|
41
36
|
required: true,
|
|
37
|
+
primary: true,
|
|
42
38
|
min: 0,
|
|
43
|
-
index: true,
|
|
44
39
|
},
|
|
45
40
|
{
|
|
46
41
|
name: 'stop_headsign',
|
package/models/gtfs/transfers.js
CHANGED
|
@@ -1,44 +1,42 @@
|
|
|
1
1
|
const model = {
|
|
2
2
|
filenameBase: 'transfers',
|
|
3
3
|
schema: [
|
|
4
|
-
{
|
|
5
|
-
name: 'id',
|
|
6
|
-
type: 'integer',
|
|
7
|
-
primary: true,
|
|
8
|
-
},
|
|
9
4
|
{
|
|
10
5
|
name: 'from_stop_id',
|
|
11
6
|
type: 'varchar(255)',
|
|
12
|
-
|
|
7
|
+
primary: true,
|
|
13
8
|
},
|
|
14
9
|
{
|
|
15
10
|
name: 'to_stop_id',
|
|
16
11
|
type: 'varchar(255)',
|
|
17
|
-
|
|
12
|
+
primary: true,
|
|
18
13
|
},
|
|
19
14
|
{
|
|
20
15
|
name: 'from_route_id',
|
|
21
16
|
type: 'varchar(255)',
|
|
17
|
+
primary: true,
|
|
22
18
|
},
|
|
23
19
|
{
|
|
24
20
|
name: 'to_route_id',
|
|
25
21
|
type: 'varchar(255)',
|
|
22
|
+
primary: true,
|
|
26
23
|
},
|
|
27
24
|
{
|
|
28
25
|
name: 'from_trip_id',
|
|
29
26
|
type: 'varchar(255)',
|
|
27
|
+
primary: true,
|
|
30
28
|
},
|
|
31
29
|
{
|
|
32
30
|
name: 'to_trip_id',
|
|
33
31
|
type: 'varchar(255)',
|
|
32
|
+
primary: true,
|
|
34
33
|
},
|
|
35
34
|
{
|
|
36
35
|
name: 'transfer_type',
|
|
37
36
|
type: 'integer',
|
|
38
37
|
min: 0,
|
|
39
38
|
max: 5,
|
|
40
|
-
|
|
41
|
-
default: 0
|
|
39
|
+
default: 0,
|
|
42
40
|
},
|
|
43
41
|
{
|
|
44
42
|
name: 'min_transfer_time',
|
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
const model = {
|
|
2
2
|
filenameBase: 'translations',
|
|
3
3
|
schema: [
|
|
4
|
-
{
|
|
5
|
-
name: 'id',
|
|
6
|
-
type: 'integer',
|
|
7
|
-
primary: true,
|
|
8
|
-
},
|
|
9
4
|
{
|
|
10
5
|
name: 'table_name',
|
|
11
6
|
type: 'varchar(255)',
|
|
7
|
+
primary: true,
|
|
12
8
|
required: true,
|
|
13
9
|
},
|
|
14
10
|
{
|
|
15
11
|
name: 'field_name',
|
|
16
12
|
type: 'varchar(255)',
|
|
13
|
+
primary: true,
|
|
17
14
|
required: true,
|
|
18
15
|
},
|
|
19
16
|
{
|
|
20
17
|
name: 'language',
|
|
21
18
|
type: 'varchar(255)',
|
|
19
|
+
primary: true,
|
|
22
20
|
required: true,
|
|
23
21
|
},
|
|
24
22
|
{
|
|
@@ -29,14 +27,17 @@ const model = {
|
|
|
29
27
|
{
|
|
30
28
|
name: 'record_id',
|
|
31
29
|
type: 'varchar(255)',
|
|
30
|
+
primary: true,
|
|
32
31
|
},
|
|
33
32
|
{
|
|
34
33
|
name: 'record_sub_id',
|
|
35
34
|
type: 'varchar(255)',
|
|
35
|
+
primary: true,
|
|
36
36
|
},
|
|
37
37
|
{
|
|
38
38
|
name: 'field_value',
|
|
39
39
|
type: 'varchar(2047)',
|
|
40
|
+
primary: true,
|
|
40
41
|
},
|
|
41
42
|
],
|
|
42
43
|
};
|
|
@@ -3,23 +3,18 @@ const model = {
|
|
|
3
3
|
nonstandard: true,
|
|
4
4
|
extension: 'gtfs-plus',
|
|
5
5
|
schema: [
|
|
6
|
-
{
|
|
7
|
-
name: 'id',
|
|
8
|
-
type: 'integer',
|
|
9
|
-
primary: true,
|
|
10
|
-
},
|
|
11
6
|
{
|
|
12
7
|
name: 'route_id',
|
|
13
8
|
type: 'varchar(255)',
|
|
14
9
|
required: true,
|
|
15
|
-
|
|
10
|
+
primary: true,
|
|
16
11
|
},
|
|
17
12
|
{
|
|
18
13
|
name: 'direction_id',
|
|
19
14
|
type: 'integer',
|
|
20
15
|
min: 0,
|
|
21
16
|
max: 1,
|
|
22
|
-
|
|
17
|
+
primary: true,
|
|
23
18
|
},
|
|
24
19
|
{
|
|
25
20
|
name: 'direction',
|
|
@@ -3,16 +3,11 @@ const model = {
|
|
|
3
3
|
nonstandard: true,
|
|
4
4
|
extension: 'gtfs-plus',
|
|
5
5
|
schema: [
|
|
6
|
-
{
|
|
7
|
-
name: 'id',
|
|
8
|
-
type: 'integer',
|
|
9
|
-
primary: true,
|
|
10
|
-
},
|
|
11
6
|
{
|
|
12
7
|
name: 'stop_id',
|
|
13
8
|
type: 'varchar(255)',
|
|
14
9
|
required: true,
|
|
15
|
-
|
|
10
|
+
primary: true,
|
|
16
11
|
},
|
|
17
12
|
{
|
|
18
13
|
name: 'accessibility_id',
|
package/package.json
CHANGED
|
@@ -57,14 +57,14 @@ describe('advancedQuery():', () => {
|
|
|
57
57
|
const results = advancedQuery('stop_times', advancedQueryOptions);
|
|
58
58
|
|
|
59
59
|
const expectedResults = [
|
|
60
|
-
{ trip_id: '329', arrival_time: '9:09:00' },
|
|
61
|
-
{ trip_id: '329', arrival_time: '8:52:00' },
|
|
62
|
-
{ trip_id: '329', arrival_time: '8:44:00' },
|
|
63
|
-
{ trip_id: '329', arrival_time: '8:35:00' },
|
|
64
|
-
{ trip_id: '329', arrival_time: '8:27:00' },
|
|
65
|
-
{ trip_id: '329', arrival_time: '8:16:00' },
|
|
66
|
-
{ trip_id: '329', arrival_time: '8:03:00' },
|
|
67
60
|
{ trip_id: '329', arrival_time: '7:56:00' },
|
|
61
|
+
{ trip_id: '329', arrival_time: '8:03:00' },
|
|
62
|
+
{ trip_id: '329', arrival_time: '8:16:00' },
|
|
63
|
+
{ trip_id: '329', arrival_time: '8:27:00' },
|
|
64
|
+
{ trip_id: '329', arrival_time: '8:35:00' },
|
|
65
|
+
{ trip_id: '329', arrival_time: '8:44:00' },
|
|
66
|
+
{ trip_id: '329', arrival_time: '8:52:00' },
|
|
67
|
+
{ trip_id: '329', arrival_time: '9:09:00' },
|
|
68
68
|
];
|
|
69
69
|
|
|
70
70
|
should.exist(results);
|
|
@@ -28,7 +28,6 @@ describe('getAgencies():', () => {
|
|
|
28
28
|
const results = getAgencies();
|
|
29
29
|
|
|
30
30
|
const expectedResult = {
|
|
31
|
-
id: 1,
|
|
32
31
|
agency_id: 'CT',
|
|
33
32
|
agency_name: 'Caltrain',
|
|
34
33
|
agency_url: 'http://www.caltrain.com',
|
|
@@ -54,7 +53,6 @@ describe('getAgencies():', () => {
|
|
|
54
53
|
});
|
|
55
54
|
|
|
56
55
|
const expectedResult = {
|
|
57
|
-
id: 1,
|
|
58
56
|
agency_id: 'CT',
|
|
59
57
|
agency_name: 'Caltrain',
|
|
60
58
|
agency_url: 'http://www.caltrain.com',
|
|
@@ -38,28 +38,24 @@ describe('getCalendarDates():', () => {
|
|
|
38
38
|
|
|
39
39
|
const expectedResults = [
|
|
40
40
|
{
|
|
41
|
-
id: 2,
|
|
42
41
|
service_id: 'CT-16APR-Caltrain-Weekday-01',
|
|
43
42
|
date: 20161124,
|
|
44
43
|
exception_type: 2,
|
|
45
44
|
holiday_name: null,
|
|
46
45
|
},
|
|
47
46
|
{
|
|
48
|
-
id: 4,
|
|
49
47
|
service_id: 'CT-16APR-Caltrain-Weekday-01',
|
|
50
48
|
date: 20160905,
|
|
51
49
|
exception_type: 2,
|
|
52
50
|
holiday_name: null,
|
|
53
51
|
},
|
|
54
52
|
{
|
|
55
|
-
id: 6,
|
|
56
53
|
service_id: 'CT-16APR-Caltrain-Weekday-01',
|
|
57
54
|
date: 20160704,
|
|
58
55
|
exception_type: 2,
|
|
59
56
|
holiday_name: null,
|
|
60
57
|
},
|
|
61
58
|
{
|
|
62
|
-
id: 8,
|
|
63
59
|
service_id: 'CT-16APR-Caltrain-Weekday-01',
|
|
64
60
|
date: 20160530,
|
|
65
61
|
exception_type: 2,
|