gtfs-to-html 2.7.2 → 2.8.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.
Files changed (84) hide show
  1. package/package.json +21 -8
  2. package/.eslintrc.json +0 -28
  3. package/.husky/pre-commit +0 -4
  4. package/CHANGELOG.md +0 -1018
  5. package/app/index.js +0 -138
  6. package/bin/gtfs-to-html.js +0 -48
  7. package/config-sample.json +0 -59
  8. package/docker/Dockerfile +0 -14
  9. package/docker/README.md +0 -5
  10. package/docker/docker-compose.yml +0 -10
  11. package/examples/stop_attributes.txt +0 -6
  12. package/examples/timetable_notes.txt +0 -8
  13. package/examples/timetable_notes_references.txt +0 -8
  14. package/examples/timetable_pages.txt +0 -3
  15. package/examples/timetable_stop_order.txt +0 -16
  16. package/examples/timetables.txt +0 -9
  17. package/index.js +0 -1
  18. package/lib/file-utils.js +0 -202
  19. package/lib/formatters.js +0 -518
  20. package/lib/geojson-utils.js +0 -96
  21. package/lib/gtfs-to-html.js +0 -214
  22. package/lib/log-utils.js +0 -215
  23. package/lib/template-functions.js +0 -192
  24. package/lib/time-utils.js +0 -90
  25. package/lib/utils.js +0 -1702
  26. package/views/default/css/overview_styles.css +0 -197
  27. package/views/default/css/timetable_pdf_styles.css +0 -7
  28. package/views/default/css/timetable_styles.css +0 -447
  29. package/views/default/formatting_functions.pug +0 -113
  30. package/views/default/js/system-map.js +0 -594
  31. package/views/default/js/timetable-map.js +0 -358
  32. package/views/default/js/timetable-menu.js +0 -63
  33. package/views/default/layout.pug +0 -11
  34. package/views/default/overview.pug +0 -27
  35. package/views/default/overview_full.pug +0 -16
  36. package/views/default/timetable_continuation_as.pug +0 -7
  37. package/views/default/timetable_continuation_from.pug +0 -7
  38. package/views/default/timetable_horizontal.pug +0 -42
  39. package/views/default/timetable_hourly.pug +0 -30
  40. package/views/default/timetable_menu.pug +0 -48
  41. package/views/default/timetable_note_symbol.pug +0 -5
  42. package/views/default/timetable_stop_name.pug +0 -13
  43. package/views/default/timetable_stoptime.pug +0 -17
  44. package/views/default/timetable_vertical.pug +0 -67
  45. package/views/default/timetablepage.pug +0 -66
  46. package/views/default/timetablepage_full.pug +0 -22
  47. package/www/README.md +0 -33
  48. package/www/babel.config.js +0 -3
  49. package/www/blog/2020-07-07-New-Documentation.md +0 -12
  50. package/www/blog/2020-08-20-Version-1.0.0.md +0 -29
  51. package/www/blog/2021-11-06-CSV-Export.md +0 -26
  52. package/www/docs/additional-files.md +0 -24
  53. package/www/docs/configuration.md +0 -568
  54. package/www/docs/current-usage.md +0 -48
  55. package/www/docs/custom-templates.md +0 -13
  56. package/www/docs/introduction.md +0 -39
  57. package/www/docs/logging-sql-queries.md +0 -12
  58. package/www/docs/previewing-html-output.md +0 -24
  59. package/www/docs/processing-large-gtfs.md +0 -10
  60. package/www/docs/quick-start.md +0 -136
  61. package/www/docs/related-libraries.md +0 -54
  62. package/www/docs/reviewing-changes.md +0 -29
  63. package/www/docs/stop-attributes.md +0 -30
  64. package/www/docs/support.md +0 -12
  65. package/www/docs/timetable-notes-references.md +0 -44
  66. package/www/docs/timetable-notes.md +0 -33
  67. package/www/docs/timetable-pages.md +0 -37
  68. package/www/docs/timetable-stop-order.md +0 -63
  69. package/www/docs/timetables.md +0 -64
  70. package/www/docusaurus.config.js +0 -104
  71. package/www/package.json +0 -21
  72. package/www/sidebars.js +0 -10
  73. package/www/src/css/custom.css +0 -25
  74. package/www/src/pages/index.js +0 -270
  75. package/www/src/pages/styles.module.css +0 -53
  76. package/www/static/.nojekyll +0 -0
  77. package/www/static/img/favicon.ico +0 -0
  78. package/www/static/img/gtfs-to-html-logo.svg +0 -18
  79. package/www/static/img/overview-example.jpg +0 -0
  80. package/www/static/img/timetable-example.jpg +0 -0
  81. package/www/static/img/undraw_happy_music.svg +0 -1
  82. package/www/static/img/undraw_proud_coder.svg +0 -1
  83. package/www/static/img/undraw_spreadsheets.svg +0 -1
  84. package/www/yarn.lock +0 -8351
@@ -1,214 +0,0 @@
1
- import path from 'node:path';
2
- import { mkdir, writeFile } from 'node:fs/promises';
3
-
4
- import { map } from 'lodash-es';
5
- import { openDb, importGtfs } from 'gtfs';
6
- import sanitize from 'sanitize-filename';
7
- import Timer from 'timer-machine';
8
-
9
- import {
10
- prepDirectory,
11
- copyStaticAssets,
12
- generateFolderName,
13
- renderPdf,
14
- zipFolder,
15
- generateFileName,
16
- } from './file-utils.js';
17
- import {
18
- log,
19
- logWarning,
20
- logError,
21
- progressBar,
22
- generateLogText,
23
- logStats,
24
- } from './log-utils.js';
25
- import {
26
- setDefaultConfig,
27
- getTimetablePagesForAgency,
28
- getFormattedTimetablePage,
29
- generateTimetableHTML,
30
- generateTimetableCSV,
31
- generateOverviewHTML,
32
- generateStats,
33
- } from './utils.js';
34
-
35
- /*
36
- * Generate HTML timetables from GTFS.
37
- */
38
- /* eslint-disable complexity */
39
- const gtfsToHtml = async (initialConfig) => {
40
- const config = setDefaultConfig(initialConfig);
41
- const timer = new Timer();
42
-
43
- config.log = log(config);
44
- config.logWarning = logWarning(config);
45
- config.logError = logError(config);
46
-
47
- timer.start();
48
-
49
- try {
50
- openDb(config);
51
- } catch (error) {
52
- if (error instanceof Error && error.code === 'SQLITE_CANTOPEN') {
53
- config.logError(
54
- `Unable to open sqlite database "${config.sqlitePath}" defined as \`sqlitePath\` config.json. Ensure the parent directory exists or remove \`sqlitePath\` from config.json.`,
55
- );
56
- }
57
-
58
- throw error;
59
- }
60
-
61
- if (!config.agencies || config.agencies.length === 0) {
62
- throw new Error('No agencies defined in `config.json`');
63
- }
64
-
65
- if (!config.skipImport) {
66
- // Import GTFS
67
- await importGtfs(config);
68
- }
69
-
70
- const agencyKey = config.agencies
71
- .map((agency) => agency.agency_key ?? 'unknown')
72
- .join('-');
73
- const exportPath = path.join(process.cwd(), 'html', sanitize(agencyKey));
74
- const outputStats = {
75
- timetables: 0,
76
- timetablePages: 0,
77
- calendars: 0,
78
- trips: 0,
79
- routes: 0,
80
- stops: 0,
81
- warnings: [],
82
- };
83
-
84
- const timetablePages = [];
85
- const timetablePageIds = map(
86
- getTimetablePagesForAgency(config),
87
- 'timetable_page_id',
88
- );
89
- await prepDirectory(exportPath);
90
-
91
- if (config.noHead !== true && ['html', 'pdf'].includes(config.outputFormat)) {
92
- copyStaticAssets(config, exportPath);
93
- }
94
-
95
- const bar = progressBar(
96
- `${agencyKey}: Generating ${config.outputFormat.toUpperCase()} timetables {bar} {value}/{total}`,
97
- timetablePageIds.length,
98
- config,
99
- );
100
-
101
- /* eslint-disable no-await-in-loop */
102
- for (const timetablePageId of timetablePageIds) {
103
- try {
104
- const timetablePage = await getFormattedTimetablePage(
105
- timetablePageId,
106
- config,
107
- );
108
-
109
- for (const timetable of timetablePage.timetables) {
110
- for (const warning of timetable.warnings) {
111
- outputStats.warnings.push(warning);
112
- bar.interrupt(warning);
113
- }
114
- }
115
-
116
- if (timetablePage.consolidatedTimetables.length === 0) {
117
- throw new Error(
118
- `No timetables found for timetable_page_id=${timetablePage.timetable_page_id}`,
119
- );
120
- }
121
-
122
- outputStats.timetables += timetablePage.consolidatedTimetables.length;
123
- outputStats.timetablePages += 1;
124
-
125
- const datePath = generateFolderName(timetablePage);
126
-
127
- // Make directory if it doesn't exist
128
- await mkdir(path.join(exportPath, datePath), { recursive: true });
129
- config.assetPath = '../';
130
-
131
- timetablePage.relativePath = path.join(
132
- datePath,
133
- sanitize(timetablePage.filename),
134
- );
135
-
136
- if (config.outputFormat === 'csv') {
137
- for (const timetable of timetablePage.consolidatedTimetables) {
138
- const csv = await generateTimetableCSV(timetable);
139
- const csvPath = path.join(
140
- exportPath,
141
- datePath,
142
- generateFileName(timetable, config, 'csv'),
143
- );
144
- await writeFile(csvPath, csv);
145
- }
146
- } else {
147
- const html = await generateTimetableHTML(timetablePage, config);
148
- const htmlPath = path.join(
149
- exportPath,
150
- datePath,
151
- sanitize(timetablePage.filename),
152
- );
153
- await writeFile(htmlPath, html);
154
-
155
- if (config.outputFormat === 'pdf') {
156
- await renderPdf(htmlPath);
157
- }
158
- }
159
-
160
- timetablePages.push(timetablePage);
161
- const timetableStats = generateStats(timetablePage);
162
-
163
- for (const key of Object.keys(outputStats)) {
164
- if (timetableStats[key]) {
165
- outputStats[key] += timetableStats[key];
166
- }
167
- }
168
- } catch (error) {
169
- outputStats.warnings.push(error.message);
170
- bar.interrupt(error.message);
171
- }
172
-
173
- bar.increment();
174
- }
175
- /* eslint-enable no-await-in-loop */
176
-
177
- if (config.outputFormat === 'html') {
178
- // Generate route summary index.html
179
- config.assetPath = '';
180
- const html = await generateOverviewHTML(timetablePages, config);
181
- await writeFile(path.join(exportPath, 'index.html'), html);
182
- }
183
-
184
- // Generate output log.txt
185
- const logText = generateLogText(outputStats, config);
186
- await writeFile(path.join(exportPath, 'log.txt'), logText);
187
-
188
- // Zip output, if specified
189
- if (config.zipOutput) {
190
- await zipFolder(exportPath);
191
- }
192
-
193
- const fullExportPath = path.join(
194
- exportPath,
195
- config.zipOutput ? '/timetables.zip' : '',
196
- );
197
-
198
- // Print stats
199
- config.log(
200
- `${agencyKey}: ${config.outputFormat.toUpperCase()} timetables created at ${fullExportPath}`,
201
- );
202
-
203
- logStats(outputStats, config);
204
-
205
- const seconds = Math.round(timer.time() / 1000);
206
- config.log(
207
- `${agencyKey}: ${config.outputFormat.toUpperCase()} timetable generation required ${seconds} seconds`,
208
- );
209
-
210
- timer.stop();
211
- };
212
- /* eslint-enable complexity */
213
-
214
- export default gtfsToHtml;
package/lib/log-utils.js DELETED
@@ -1,215 +0,0 @@
1
- import { clearLine, cursorTo } from 'node:readline';
2
- import { noop } from 'lodash-es';
3
- import * as colors from 'yoctocolors';
4
- import { getFeedInfo } from 'gtfs';
5
- import Table from 'cli-table';
6
-
7
- /*
8
- * Creates text for a log of output details.
9
- */
10
- export function generateLogText(outputStats, config) {
11
- const feedInfo = getFeedInfo();
12
- const feedVersion =
13
- feedInfo.length > 0 && feedInfo[0].feed_version
14
- ? feedInfo[0].feed_version
15
- : 'Unknown';
16
-
17
- const logText = [
18
- `Feed Version: ${feedVersion}`,
19
- `GTFS-to-HTML Version: ${config.gtfsToHtmlVersion}`,
20
- `Date Generated: ${new Date().toISOString()}`,
21
- `Timetable Page Count: ${outputStats.timetablePages}`,
22
- `Timetable Count: ${outputStats.timetables}`,
23
- `Calendar Service ID Count: ${outputStats.calendars}`,
24
- `Route Count: ${outputStats.routes}`,
25
- `Trip Count: ${outputStats.trips}`,
26
- `Stop Count: ${outputStats.stops}`,
27
- ];
28
-
29
- for (const agency of config.agencies) {
30
- if (agency.url) {
31
- logText.push(`Source: ${agency.url}`);
32
- } else if (agency.path) {
33
- logText.push(`Source: ${agency.path}`);
34
- }
35
- }
36
-
37
- if (outputStats.warnings.length > 0) {
38
- logText.push('', 'Warnings:', ...outputStats.warnings);
39
- }
40
-
41
- return logText.join('\n');
42
- }
43
-
44
- /*
45
- * Returns a log function based on config settings
46
- */
47
- export function log(config) {
48
- if (config.verbose === false) {
49
- return noop;
50
- }
51
-
52
- if (config.logFunction) {
53
- return config.logFunction;
54
- }
55
-
56
- return (text, overwrite) => {
57
- if (overwrite === true && process.stdout.isTTY) {
58
- clearLine(process.stdout, 0);
59
- cursorTo(process.stdout, 0);
60
- } else {
61
- process.stdout.write('\n');
62
- }
63
-
64
- process.stdout.write(text);
65
- };
66
- }
67
-
68
- /*
69
- * Returns an warning log function based on config settings
70
- */
71
- export function logWarning(config) {
72
- if (config.logFunction) {
73
- return config.logFunction;
74
- }
75
-
76
- return (text) => {
77
- process.stdout.write(`\n${formatWarning(text)}\n`);
78
- };
79
- }
80
-
81
- /*
82
- * Returns an error log function based on config settings
83
- */
84
- export function logError(config) {
85
- if (config.logFunction) {
86
- return config.logFunction;
87
- }
88
-
89
- return (text) => {
90
- process.stdout.write(`\n${formatError(text)}\n`);
91
- };
92
- }
93
-
94
- /*
95
- * Format console warning text
96
- */
97
- export function formatWarning(text) {
98
- const warningMessage = `${colors.underline('Warning')}: ${text}`;
99
- return colors.yellow(warningMessage);
100
- }
101
-
102
- /*
103
- * Format console error text
104
- */
105
- export function formatError(error) {
106
- const messageText = error instanceof Error ? error.message : error;
107
- const errorMessage = `${colors.underline('Error')}: ${messageText.replace(
108
- 'Error: ',
109
- '',
110
- )}`;
111
- return colors.red(errorMessage);
112
- }
113
-
114
- /*
115
- * Print a table of stats to the console.
116
- */
117
- export function logStats(stats, config) {
118
- // Hide stats table from custom log functions
119
- if (config.logFunction) {
120
- return;
121
- }
122
-
123
- const table = new Table({
124
- colWidths: [40, 20],
125
- head: ['Item', 'Count'],
126
- });
127
-
128
- table.push(
129
- ['📄 Timetable Pages', stats.timetablePages],
130
- ['🕑 Timetables', stats.timetables],
131
- ['📅 Calendar Service IDs', stats.calendars],
132
- ['🔄 Routes', stats.routes],
133
- ['🚍 Trips', stats.trips],
134
- ['🛑 Stops', stats.stops],
135
- ['⛔️ Warnings', stats.warnings.length],
136
- );
137
-
138
- config.log(table.toString());
139
- }
140
-
141
- /*
142
- * Create progress bar text string
143
- */
144
- const generateProgressBarString = (barTotal, barProgress, size = 40) => {
145
- const line = '-';
146
- const slider = '=';
147
- if (!barTotal) {
148
- throw new Error('Total value is either not provided or invalid');
149
- }
150
-
151
- if (!barProgress && barProgress !== 0) {
152
- throw new Error('Current value is either not provided or invalid');
153
- }
154
-
155
- if (isNaN(barTotal)) {
156
- throw new Error('Total value is not an integer');
157
- }
158
-
159
- if (isNaN(barProgress)) {
160
- throw new Error('Current value is not an integer');
161
- }
162
-
163
- if (isNaN(size)) {
164
- throw new Error('Size is not an integer');
165
- }
166
-
167
- if (barProgress > barTotal) {
168
- return slider.repeat(size + 2);
169
- }
170
-
171
- const percentage = barProgress / barTotal;
172
- const progress = Math.round(size * percentage);
173
- const emptyProgress = size - progress;
174
- const progressText = slider.repeat(progress);
175
- const emptyProgressText = line.repeat(emptyProgress);
176
- return progressText + emptyProgressText;
177
- };
178
-
179
- /*
180
- * Print a progress bar to the console.
181
- */
182
- export function progressBar(formatString, barTotal, config) {
183
- let barProgress = 0;
184
-
185
- if (config.verbose === false) {
186
- return {
187
- increment: noop,
188
- interrupt: noop,
189
- };
190
- }
191
-
192
- if (barTotal === 0) {
193
- return null;
194
- }
195
-
196
- const renderProgressString = () =>
197
- formatString
198
- .replace('{value}', barProgress)
199
- .replace('{total}', barTotal)
200
- .replace('{bar}', generateProgressBarString(barTotal, barProgress));
201
-
202
- config.log(renderProgressString(), true);
203
-
204
- return {
205
- interrupt(text) {
206
- // Log two lines to avoid overwrite by progress bar
207
- config.logWarning(text);
208
- config.log('');
209
- },
210
- increment() {
211
- barProgress += 1;
212
- config.log(renderProgressString(), true);
213
- },
214
- };
215
- }
@@ -1,192 +0,0 @@
1
- import { every } from 'lodash-es';
2
-
3
- /*
4
- * Format an id to be used as an HTML attribute.
5
- */
6
- export function formatHtmlId(id) {
7
- return id.replace(/([^\w[\]{}.:-])\s?/g, '');
8
- }
9
-
10
- /*
11
- * Discern if a day list should be shown for a specific timetable (if some
12
- * trips happen on different days).
13
- */
14
- export function timetableHasDifferentDays(timetable) {
15
- return !every(timetable.orderedTrips, (trip, idx) => {
16
- if (idx === 0) {
17
- return true;
18
- }
19
-
20
- return trip.dayList === timetable.orderedTrips[idx - 1].dayList;
21
- });
22
- }
23
-
24
- /*
25
- * Discern if a day list should be shown for a specific timetable page's menu (if some
26
- * timetables are for different days).
27
- */
28
- export function timetablePageHasDifferentDays(timetablePage) {
29
- return !every(timetablePage.consolidatedTimetables, (timetable, idx) => {
30
- if (idx === 0) {
31
- return true;
32
- }
33
-
34
- return (
35
- timetable.dayListLong ===
36
- timetablePage.consolidatedTimetables[idx - 1].dayListLong
37
- );
38
- });
39
- }
40
-
41
- /*
42
- * Discern if individual timetable labels should be shown (if some
43
- * timetables have different labels).
44
- */
45
- export function timetablePageHasDifferentLabels(timetablePage) {
46
- return !every(timetablePage.consolidatedTimetables, (timetable, idx) => {
47
- if (idx === 0) {
48
- return true;
49
- }
50
-
51
- return (
52
- timetable.timetable_label ===
53
- timetablePage.consolidatedTimetables[idx - 1].timetable_label
54
- );
55
- });
56
- }
57
-
58
- /*
59
- * Discern if a timetable has any notes or notices to display.
60
- */
61
- export function hasNotesOrNotices(timetable) {
62
- return (
63
- timetable.requestPickupSymbolUsed ||
64
- timetable.noPickupSymbolUsed ||
65
- timetable.requestDropoffSymbolUsed ||
66
- timetable.noDropoffSymbolUsed ||
67
- timetable.noServiceSymbolUsed ||
68
- timetable.interpolatedStopSymbolUsed ||
69
- timetable.notes.length > 0
70
- );
71
- }
72
-
73
- /*
74
- * Return an array of all timetable notes that relate to the entire timetable or route.
75
- */
76
- export function getNotesForTimetableLabel(notes) {
77
- return notes.filter((note) => !note.stop_id && !note.trip_id);
78
- }
79
-
80
- /*
81
- * Return an array of all timetable notes for a specific stop and stop_sequence.
82
- */
83
- export function getNotesForStop(notes, stop) {
84
- return notes.filter((note) => {
85
- // Don't show if note applies only to a specific trip.
86
- if (note.trip_id) {
87
- return false;
88
- }
89
-
90
- // Don't show if note applies only to a specific stop_sequence that is not found.
91
- if (
92
- note.stop_sequence &&
93
- !stop.trips.some((trip) => trip.stop_sequence === note.stop_sequence)
94
- ) {
95
- return false;
96
- }
97
-
98
- return note.stop_id === stop.stop_id;
99
- });
100
- }
101
-
102
- /*
103
- * Return an array of all timetable notes for a specific trip.
104
- */
105
- export function getNotesForTrip(notes, trip) {
106
- return notes.filter((note) => {
107
- // Don't show if note applies only to a specific stop.
108
- if (note.stop_id) {
109
- return false;
110
- }
111
-
112
- return note.trip_id === trip.trip_id;
113
- });
114
- }
115
-
116
- /*
117
- * Return an array of all timetable notes for a specific stoptime.
118
- */
119
- export function getNotesForStoptime(notes, stoptime) {
120
- return notes.filter((note) => {
121
- // Show notes that apply to all trips at this stop if `show_on_stoptime` is true.
122
- if (
123
- !note.trip_id &&
124
- note.stop_id === stoptime.stop_id &&
125
- note.show_on_stoptime === 1
126
- ) {
127
- return true;
128
- }
129
-
130
- // Show notes that apply to all stops of this trip if `show_on_stoptime` is true.
131
- if (
132
- !note.stop_id &&
133
- note.trip_id === stoptime.trip_id &&
134
- note.show_on_stoptime === 1
135
- ) {
136
- return true;
137
- }
138
-
139
- return (
140
- note.trip_id === stoptime.trip_id && note.stop_id === stoptime.stop_id
141
- );
142
- });
143
- }
144
-
145
- /*
146
- * Formats a trip name.
147
- */
148
- export function formatTripName(trip, index, timetable) {
149
- let tripName;
150
- if (timetable.routes.length > 1) {
151
- tripName = trip.route_short_name;
152
- } else if (timetable.orientation === 'horizontal') {
153
- // Only add this to horizontal timetables.
154
- if (trip.trip_short_name) {
155
- tripName = trip.trip_short_name;
156
- } else {
157
- tripName = `Run #${index + 1}`;
158
- }
159
- }
160
-
161
- if (timetableHasDifferentDays(timetable)) {
162
- tripName += ` ${trip.dayList}`;
163
- }
164
-
165
- return tripName;
166
- }
167
-
168
- /*
169
- * Formats a trip name.
170
- */
171
- export function formatTripNameForCSV(trip, timetable) {
172
- let tripName = '';
173
- if (timetable.routes.length > 1) {
174
- tripName += `${trip.route_short_name} - `;
175
- }
176
-
177
- if (trip.trip_short_name) {
178
- tripName += trip.trip_short_name;
179
- } else {
180
- tripName += trip.trip_id;
181
- }
182
-
183
- if (trip.trip_headsign) {
184
- tripName += ` - ${trip.trip_headsign}`;
185
- }
186
-
187
- if (timetableHasDifferentDays(timetable)) {
188
- tripName += ` - ${trip.dayList}`;
189
- }
190
-
191
- return tripName;
192
- }
package/lib/time-utils.js DELETED
@@ -1,90 +0,0 @@
1
- import moment from 'moment';
2
-
3
- /*
4
- * Convert a GTFS formatted time string into a moment less than 24 hours.
5
- */
6
- export function fromGTFSTime(timeString) {
7
- const duration = moment.duration(timeString);
8
-
9
- return moment({
10
- hour: duration.hours(),
11
- minute: duration.minutes(),
12
- second: duration.seconds(),
13
- });
14
- }
15
-
16
- /*
17
- * Convert a moment into a GTFS formatted time string.
18
- */
19
- export function toGTFSTime(time) {
20
- return time.format('HH:mm:ss');
21
- }
22
-
23
- /*
24
- * Convert a GTFS formatted date string into a moment.
25
- */
26
- export function fromGTFSDate(gtfsDate) {
27
- return moment(gtfsDate, 'YYYYMMDD');
28
- }
29
-
30
- /*
31
- * Convert a moment date into a GTFS formatted date string.
32
- */
33
- export function toGTFSDate(date) {
34
- return moment(date).format('YYYYMMDD');
35
- }
36
-
37
- /*
38
- * Convert a object of weekdays into a a string containing 1s and 0s.
39
- */
40
- export function calendarToCalendarCode(c) {
41
- if (c.service_id) {
42
- return c.service_id;
43
- }
44
-
45
- return `${c.monday}${c.tuesday}${c.wednesday}${c.thursday}${c.friday}${c.saturday}${c.sunday}`;
46
- }
47
-
48
- /*
49
- * Convert a string of 1s and 0s representing a weekday to an object.
50
- */
51
- export function calendarCodeToCalendar(code) {
52
- const days = [
53
- 'monday',
54
- 'tuesday',
55
- 'wednesday',
56
- 'thursday',
57
- 'friday',
58
- 'saturday',
59
- 'sunday',
60
- ];
61
- const calendar = {};
62
-
63
- for (const [index, day] of days.entries()) {
64
- calendar[day] = code[index];
65
- }
66
-
67
- return calendar;
68
- }
69
-
70
- /*
71
- * Get number of seconds after midnight of a GTFS formatted time string.
72
- */
73
- export function secondsAfterMidnight(timeString) {
74
- return moment.duration(timeString).asSeconds();
75
- }
76
-
77
- /*
78
- * Get number of minutes after midnight of a GTFS formatted time string.
79
- */
80
- export function minutesAfterMidnight(timeString) {
81
- return moment.duration(timeString).asMinutes();
82
- }
83
-
84
- /*
85
- * Add specified number of seconds to a GTFS formatted time string.
86
- */
87
- export function updateTimeByOffset(timeString, offsetSeconds) {
88
- const newTime = fromGTFSTime(timeString);
89
- return toGTFSTime(newTime.add(offsetSeconds, 'seconds'));
90
- }