@opentripplanner/core-utils 5.0.3-alpha.1 → 7.0.0-alpha.1

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 (45) hide show
  1. package/esm/itinerary.js +74 -11
  2. package/esm/itinerary.js.map +1 -1
  3. package/esm/map.js +20 -239
  4. package/esm/map.js.map +1 -1
  5. package/esm/query.js +14 -11
  6. package/esm/query.js.map +1 -1
  7. package/esm/time.js +5 -56
  8. package/esm/time.js.map +1 -1
  9. package/lib/itinerary.d.ts +14 -7
  10. package/lib/itinerary.d.ts.map +1 -1
  11. package/lib/itinerary.js +79 -65
  12. package/lib/itinerary.js.map +1 -1
  13. package/lib/map.d.ts +4 -13
  14. package/lib/map.d.ts.map +1 -1
  15. package/lib/map.js +18 -251
  16. package/lib/map.js.map +1 -1
  17. package/lib/query.js +11 -15
  18. package/lib/query.js.map +1 -1
  19. package/lib/time.d.ts +1 -16
  20. package/lib/time.d.ts.map +1 -1
  21. package/lib/time.js +4 -72
  22. package/lib/time.js.map +1 -1
  23. package/package.json +4 -5
  24. package/src/__tests__/__snapshots__/itinerary.js.snap +2 -38
  25. package/src/__tests__/__snapshots__/time.js.snap +7 -13
  26. package/src/__tests__/itinerary.js +6 -29
  27. package/src/__tests__/time.js +7 -27
  28. package/src/itinerary.ts +72 -27
  29. package/src/map.ts +19 -287
  30. package/src/query.js +19 -16
  31. package/src/time.ts +11 -76
  32. package/tsconfig.tsbuildinfo +1381 -1288
  33. package/esm/deprecated-with-types.js +0 -47
  34. package/esm/deprecated-with-types.js.map +0 -1
  35. package/esm/deprecated.js +0 -325
  36. package/esm/deprecated.js.map +0 -1
  37. package/lib/deprecated-with-types.d.ts +0 -23
  38. package/lib/deprecated-with-types.d.ts.map +0 -1
  39. package/lib/deprecated-with-types.js +0 -61
  40. package/lib/deprecated-with-types.js.map +0 -1
  41. package/lib/deprecated.js +0 -355
  42. package/lib/deprecated.js.map +0 -1
  43. package/src/__tests__/__mocks__/multi-currency-itinerary.json +0 -1728
  44. package/src/deprecated-with-types.ts +0 -62
  45. package/src/deprecated.js +0 -334
package/src/itinerary.ts CHANGED
@@ -7,33 +7,37 @@ import {
7
7
  Itinerary,
8
8
  LatLngArray,
9
9
  Leg,
10
- Step
10
+ Money,
11
+ Step,
12
+ TncFare
11
13
  } from "@opentripplanner/types";
12
14
  import turfAlong from "@turf/along";
13
15
 
16
+ /*
14
17
  import {
15
- calculateFares,
16
- getLegModeLabel,
17
- getModeForPlace,
18
- getPlaceName,
19
- getStepDirection,
20
- getStepInstructions,
21
- getStepStreetName,
22
- getTimeZoneOffset,
23
- getTransitFare
18
+ // calculateFares,
19
+ // getLegModeLabel,
20
+ // getModeForPlace,
21
+ // getPlaceName,
22
+ // getStepDirection,
23
+ // getStepInstructions,
24
+ // getStepStreetName,
25
+ // getTimeZoneOffset,
26
+ // getTransitFare
24
27
  } from "./deprecated";
25
28
 
26
29
  export {
27
- calculateFares,
28
- getLegModeLabel,
29
- getModeForPlace,
30
- getPlaceName,
31
- getStepDirection,
32
- getStepInstructions,
33
- getStepStreetName,
34
- getTimeZoneOffset,
35
- getTransitFare
30
+ // calculateFares,
31
+ // getLegModeLabel,
32
+ // getModeForPlace,
33
+ // getPlaceName,
34
+ // getStepDirection,
35
+ // getStepInstructions,
36
+ // getStepStreetName,
37
+ // getTimeZoneOffset,
38
+ // getTransitFare
36
39
  };
40
+ */
37
41
 
38
42
  // All OTP transit modes
39
43
  export const transitModes = [
@@ -445,12 +449,53 @@ export function calculatePhysicalActivity(
445
449
  };
446
450
  }
447
451
 
448
- export function calculateTncFares(itinerary) {
449
- // TODO: don't rely on deprecated methods!
450
- // At the moment this is safe as none of these exported variables contain strings
451
- const { maxTNCFare, minTNCFare, tncCurrencyCode } = calculateFares(
452
- itinerary,
453
- true
454
- );
455
- return { maxTNCFare, minTNCFare, tncCurrencyCode };
452
+ /**
453
+ * For an itinerary, calculates the TNC fares and returns an object with
454
+ * these values and currency info.
455
+ * It is assumed that the same currency is used for all TNC legs.
456
+ */
457
+ export function calculateTncFares(itinerary: Itinerary): TncFare {
458
+ let minTNCFare = 0;
459
+ let maxTNCFare = 0;
460
+ let currencyCode;
461
+ itinerary.legs.forEach(({ hailedCar, mode, tncData }) => {
462
+ if (mode === "CAR" && hailedCar && tncData) {
463
+ const { currency, maxCost, minCost } = tncData;
464
+ minTNCFare += minCost;
465
+ maxTNCFare += maxCost;
466
+ // Assumes a single currency for entire itinerary.
467
+ currencyCode = currency;
468
+ }
469
+ });
470
+
471
+ return {
472
+ currencyCode,
473
+ maxTNCFare,
474
+ minTNCFare
475
+ };
476
+ }
477
+
478
+ /**
479
+ * For a given fare component (either total fare or component parts), returns
480
+ * an object with the fare value (in cents).
481
+ */
482
+ export function getTransitFare(
483
+ fareComponent: Money
484
+ ): {
485
+ currencyCode: string;
486
+ transitFare: number;
487
+ } {
488
+ // Default values (if fare component is not valid).
489
+ let transitFare = 0;
490
+ let currencyCode = "USD";
491
+ if (fareComponent) {
492
+ // Assign values without declaration.
493
+ // See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#assignment_without_declaration
494
+ ({ currencyCode } = fareComponent.currency);
495
+ transitFare = fareComponent.cents;
496
+ }
497
+ return {
498
+ currencyCode,
499
+ transitFare
500
+ };
456
501
  }
package/src/map.ts CHANGED
@@ -1,28 +1,16 @@
1
- import {
2
- Company,
3
- Itinerary,
4
- LatLngArray,
5
- Leg,
6
- Location,
7
- TransitiveData,
8
- UserPosition
9
- } from "@opentripplanner/types";
10
- import {
11
- getPlaceName,
12
- isAccessMode,
13
- isFlex,
14
- isTransit,
15
- toSentenceCase
16
- } from "./itinerary";
1
+ import { LatLngArray, Location, UserPosition } from "@opentripplanner/types";
2
+ import { toSentenceCase } from "./itinerary";
17
3
 
4
+ /*
18
5
  import {
19
- coordsToString,
20
- getDetailText,
21
- latlngToString,
22
- logDeprecationWarning
6
+ // coordsToString,
7
+ // getDetailText //,
8
+ // latlngToString,
9
+ // logDeprecationWarning
23
10
  } from "./deprecated";
24
11
 
25
- export { coordsToString, getDetailText, latlngToString };
12
+ export { coordsToString, getDetailText , latlngToString };
13
+ */
26
14
 
27
15
  export function currentPositionToLocation(
28
16
  currentPosition: UserPosition
@@ -40,6 +28,12 @@ export function currentPositionToLocation(
40
28
  };
41
29
  }
42
30
 
31
+ // TRICKY: It is used in query.js and in the context of
32
+ // otp-rr actions where the intl context is not available.
33
+ export function coordsToString(coords: number[]): string {
34
+ return coords.length && coords.map(c => (+c).toFixed(5)).join(", ");
35
+ }
36
+
43
37
  export function stringToCoords(str: string): number[] {
44
38
  return (str && str.split(",").map(c => +c)) || [];
45
39
  }
@@ -54,23 +48,10 @@ export function constructLocation(latlng: {
54
48
  };
55
49
  }
56
50
 
57
- export function formatStoredPlaceName(
58
- location: Location,
59
- withDetails = true
60
- ): string {
61
- if (withDetails) {
62
- logDeprecationWarning("the formatStoredPlaceName withDetails parameter");
63
- }
64
-
65
- let displayName =
66
- location.type === "home" || location.type === "work"
67
- ? toSentenceCase(location.type)
68
- : location.name;
69
- if (withDetails) {
70
- const detailText = getDetailText(location);
71
- if (detailText) displayName += ` (${detailText})`;
72
- }
73
- return displayName;
51
+ export function formatStoredPlaceName(location: Location): string {
52
+ return location.type === "home" || location.type === "work"
53
+ ? toSentenceCase(location.type)
54
+ : location.name;
74
55
  }
75
56
 
76
57
  export function matchLatLon(location1: Location, location2: Location): boolean {
@@ -78,255 +59,6 @@ export function matchLatLon(location1: Location, location2: Location): boolean {
78
59
  return location1.lat === location2.lat && location1.lon === location2.lon;
79
60
  }
80
61
 
81
- /**
82
- * Converts an OTP itinerary object to a transtive.js itinerary object.
83
- * @param {*} itin Required OTP itinerary (see @opentripplanner/core-utils/types#itineraryType) to convert.
84
- * @param {*} companies Optional list of companies, used for labeling vehicle rental locations.
85
- * @param {*} getRouteLabel Optional function that takes an itinerary leg (see @opentripplanner/core-utils/types#legType)
86
- * and returns a string representing the route label to display for that leg.
87
- * @returns An itinerary in the transitive.js format.
88
- */
89
- export function itineraryToTransitive(
90
- itin: Itinerary,
91
- companies: Company[],
92
- getRouteLabel: (leg: Leg) => string,
93
- disableFlexArc: boolean
94
- ): TransitiveData {
95
- const tdata = {
96
- journeys: [],
97
- streetEdges: [],
98
- places: [],
99
- patterns: [],
100
- routes: [],
101
- stops: []
102
- };
103
- const routes = {};
104
- const stops = {};
105
- let streetEdgeId = 0;
106
- let patternId = 0;
107
-
108
- const journey = {
109
- journey_id: "itin",
110
- // This string is not shown in the UI
111
- journey_name: "Iterarary-derived Journey",
112
- segments: []
113
- };
114
-
115
- // add 'from' and 'to' places to the tdata places array
116
- tdata.places.push({
117
- place_id: "from",
118
- place_lat: itin.legs[0].from.lat,
119
- place_lon: itin.legs[0].from.lon
120
- });
121
- tdata.places.push({
122
- place_id: "to",
123
- place_lat: itin.legs[itin.legs.length - 1].to.lat,
124
- place_lon: itin.legs[itin.legs.length - 1].to.lon
125
- });
126
-
127
- itin.legs.forEach((leg, idx) => {
128
- if (isAccessMode(leg.mode)) {
129
- let fromPlaceId: string;
130
- if (leg.from.bikeShareId) {
131
- fromPlaceId = `bicycle_rent_station_${leg.from.bikeShareId}`;
132
- if (
133
- // OTP2 Scooter case
134
- leg.mode === "SCOOTER"
135
- ) {
136
- fromPlaceId = `escooter_rent_station_${leg.from.bikeShareId}`;
137
- }
138
- } else if (leg.from.vertexType === "VEHICLERENTAL") {
139
- // OTP1 Scooter case
140
- fromPlaceId = `escooter_rent_station_${leg.from.name}`;
141
- } else if (
142
- leg.mode === "CAR" &&
143
- idx > 0 &&
144
- itin.legs[idx - 1].mode === "WALK"
145
- ) {
146
- // create a special place ID for car legs preceded by walking legs
147
- fromPlaceId = `itin_car_${streetEdgeId}_from`;
148
- } else if (!fromPlaceId) {
149
- fromPlaceId = `itin_street_${streetEdgeId}_from`;
150
- }
151
-
152
- let toPlaceId;
153
- if (leg.to.bikeShareId) {
154
- toPlaceId = `bicycle_rent_station_${leg.to.bikeShareId}`;
155
- // OTP2 scooter case
156
- // Need to check next leg since this is a "to" place "
157
- if (leg.mode === "SCOOTER" || itin.legs?.[idx + 1].mode === "SCOOTER") {
158
- toPlaceId = `escooter_rent_station_${leg.to.bikeShareId}`;
159
- }
160
- } else if (leg.to.vertexType === "VEHICLERENTAL") {
161
- toPlaceId = `escooter_rent_station_${leg.to.name}`;
162
- } else if (
163
- leg.mode === "CAR" &&
164
- idx < itin.legs.length - 1 &&
165
- itin.legs[idx + 1].mode === "WALK"
166
- ) {
167
- // create a special place ID for car legs followed by walking legs
168
- toPlaceId = `itin_car_${streetEdgeId}_to`;
169
- } else if (!toPlaceId) {
170
- toPlaceId = `itin_street_${streetEdgeId}_to`;
171
- }
172
-
173
- const segment = {
174
- arc: false,
175
- type: leg.mode,
176
- streetEdges: [streetEdgeId],
177
- from: { type: "PLACE", place_id: fromPlaceId },
178
- to: { type: "PLACE", place_id: toPlaceId }
179
- };
180
- // For TNC segments, draw using an arc
181
- if (leg.mode === "CAR" && leg.hailedCar) segment.arc = true;
182
- journey.segments.push(segment);
183
-
184
- tdata.streetEdges.push({
185
- edge_id: streetEdgeId,
186
- geometry: leg.legGeometry
187
- });
188
- tdata.places.push({
189
- place_id: fromPlaceId,
190
- // Do not label the from place in addition to the to place. Otherwise,
191
- // in some cases (bike rental station) the label for a single place will
192
- // appear twice on the rendered transitive view.
193
- // See https://github.com/conveyal/trimet-mod-otp/issues/152
194
- // place_name: leg.from.name,
195
- place_lat: leg.from.lat,
196
- place_lon: leg.from.lon
197
- });
198
- tdata.places.push({
199
- place_id: toPlaceId,
200
- // This string is not shown in the UI
201
- place_name: getPlaceName(leg.to, companies),
202
- place_lat: leg.to.lat,
203
- place_lon: leg.to.lon
204
- });
205
- streetEdgeId++;
206
- }
207
-
208
- if (isTransit(leg.mode)) {
209
- // Flex routes sometimes have the same from and to IDs, but
210
- // these stops still need to be rendered separately!
211
- if (leg.from.stopId === leg.to.stopId) {
212
- leg.to.stopId = `${leg.to.stopId}_flexed_to`;
213
- }
214
- // determine if we have valid inter-stop geometry
215
- const hasInterStopGeometry = !!leg.interStopGeometry;
216
- const hasLegGeometry = !!leg.legGeometry?.points;
217
- const hasIntermediateStopGeometry =
218
- hasInterStopGeometry &&
219
- leg.intermediateStops &&
220
- leg.interStopGeometry.length === leg.intermediateStops.length + 1;
221
-
222
- // create leg-specific pattern
223
- const ptnId = `ptn_${patternId}`;
224
- const pattern = {
225
- pattern_id: ptnId,
226
- // This string is not shown in the UI
227
- pattern_name: `Pattern ${patternId}`,
228
- route_id: leg.routeId,
229
- stops: []
230
- };
231
-
232
- // add 'from' stop to stops dictionary and pattern object
233
- stops[leg.from.stopId] = {
234
- stop_id: leg.from.stopId,
235
- stop_name: leg.from.name,
236
- stop_lat: leg.from.lat,
237
- stop_lon: leg.from.lon
238
- };
239
- pattern.stops.push({ stop_id: leg.from.stopId });
240
-
241
- // add intermediate stops to stops dictionary and pattern object
242
- // If there is no intermediateStopGeometry, do not add the intermediate stops
243
- // as it will be straight lines instead of the nice legGeometry (but only if
244
- // the legGeometry exists).
245
- if (
246
- leg.intermediateStops &&
247
- (hasIntermediateStopGeometry || !hasLegGeometry)
248
- ) {
249
- leg.intermediateStops.forEach((stop, i) => {
250
- stops[stop.stopId] = {
251
- stop_id: stop.stopId,
252
- stop_name: stop.name,
253
- stop_lat: stop.lat,
254
- stop_lon: stop.lon
255
- };
256
- pattern.stops.push({
257
- stop_id: stop.stopId,
258
- geometry:
259
- hasIntermediateStopGeometry && leg.interStopGeometry[i].points
260
- });
261
- });
262
- }
263
-
264
- // add 'to' stop to stops dictionary and pattern object
265
- stops[leg.to.stopId] = {
266
- stop_id: leg.to.stopId,
267
- stop_name: leg.to.name,
268
- stop_lat: leg.to.lat,
269
- stop_lon: leg.to.lon
270
- };
271
- pattern.stops.push({
272
- stop_id: leg.to.stopId,
273
- geometry:
274
- // Some legs don't have intermediateStopGeometry, but do have valid legGeometry
275
- (hasInterStopGeometry || hasLegGeometry) &&
276
- (hasIntermediateStopGeometry
277
- ? leg.interStopGeometry[leg.interStopGeometry.length - 1].points
278
- : leg.legGeometry.points)
279
- });
280
-
281
- // add route to the route dictionary
282
- // with a custom route label if specified.
283
- const routeLabel =
284
- typeof getRouteLabel === "function"
285
- ? getRouteLabel(leg)
286
- : leg.routeShortName;
287
- routes[leg.routeId] = {
288
- agency_id: leg.agencyId,
289
- route_id: leg.routeId,
290
- route_short_name: routeLabel || "",
291
- route_long_name: leg.routeLongName || "",
292
- route_type: leg.routeType,
293
- route_color: leg.routeColor
294
- };
295
-
296
- // add the pattern to the tdata patterns array
297
- tdata.patterns.push(pattern);
298
-
299
- // add the pattern reference to the journey object
300
- journey.segments.push({
301
- arc:
302
- typeof disableFlexArc === "undefined" ? isFlex(leg) : !disableFlexArc,
303
- type: "TRANSIT",
304
- patterns: [
305
- {
306
- pattern_id: ptnId,
307
- from_stop_index: 0,
308
- to_stop_index: hasIntermediateStopGeometry
309
- ? leg.intermediateStops.length + 2 - 1
310
- : 1
311
- }
312
- ]
313
- });
314
-
315
- patternId++;
316
- }
317
- });
318
-
319
- // add the routes and stops to the tdata arrays
320
- tdata.routes.push(...Object.values(routes));
321
- tdata.stops.push(...Object.values(stops));
322
-
323
- // add the journey to the tdata journeys array
324
- tdata.journeys.push(journey);
325
-
326
- // console.log('derived tdata', tdata);
327
- return tdata;
328
- }
329
-
330
62
  type TransitivePlaceRaw = {
331
63
  place_id: string;
332
64
  };
package/src/query.js CHANGED
@@ -1,20 +1,20 @@
1
- import moment from "moment";
1
+ import { format, isMatch, parse } from "date-fns";
2
2
  import getGeocoder from "@opentripplanner/geocoder/lib";
3
3
  import qs from "qs";
4
4
 
5
5
  import { getTransitModes, hasCar, isAccessMode } from "./itinerary";
6
- import { stringToCoords } from "./map";
6
+ import { coordsToString, stringToCoords } from "./map";
7
7
  import queryParams from "./query-params";
8
8
  import {
9
9
  getCurrentTime,
10
10
  getCurrentDate,
11
- OTP_API_DATE_FORMAT,
12
- OTP_API_TIME_FORMAT
11
+ OTP_API_TIME_FORMAT,
12
+ OTP_API_DATE_FORMAT_DATE_FNS
13
13
  } from "./time";
14
14
 
15
- import { coordsToString, summarizeQuery } from "./deprecated";
15
+ // import { coordsToString, summarizeQuery } from "./deprecated";
16
16
 
17
- export { summarizeQuery };
17
+ // export { summarizeQuery };
18
18
 
19
19
  /* The list of default parameters considered in the settings panel */
20
20
 
@@ -315,9 +315,15 @@ export function planParamsToQuery(params) {
315
315
  break;
316
316
  case "time":
317
317
  {
318
- const parsedTime = moment(params.time, TIME_FORMATS);
319
- query.time = parsedTime.isValid()
320
- ? parsedTime.format(OTP_API_TIME_FORMAT)
318
+ // Match one of the supported time formats
319
+ const matchedTimeFormat = TIME_FORMATS.find(timeFormat =>
320
+ isMatch(params.time, timeFormat)
321
+ );
322
+ query.time = matchedTimeFormat
323
+ ? format(
324
+ parse(params.time, matchedTimeFormat, new Date()),
325
+ OTP_API_TIME_FORMAT
326
+ )
321
327
  : getCurrentTime();
322
328
  }
323
329
  break;
@@ -421,8 +427,8 @@ export function getRoutingParams(config, currentQuery, ignoreRealtimeUpdates) {
421
427
  }
422
428
 
423
429
  // check date/time validity; ignore both if either is invalid
424
- const dateValid = moment(params.date, OTP_API_DATE_FORMAT).isValid();
425
- const timeValid = moment(params.time, OTP_API_TIME_FORMAT).isValid();
430
+ const dateValid = isMatch(params.date, OTP_API_DATE_FORMAT_DATE_FNS);
431
+ const timeValid = isMatch(params.time, OTP_API_TIME_FORMAT);
426
432
 
427
433
  if (!dateValid || !timeValid) {
428
434
  delete params.time;
@@ -447,11 +453,8 @@ export function getRoutingParams(config, currentQuery, ignoreRealtimeUpdates) {
447
453
  // Additional processing specific to PROFILE mode
448
454
  } else {
449
455
  // check start and end time validity; ignore both if either is invalid
450
- const startTimeValid = moment(
451
- params.startTime,
452
- OTP_API_TIME_FORMAT
453
- ).isValid();
454
- const endTimeValid = moment(params.endTime, OTP_API_TIME_FORMAT).isValid();
456
+ const startTimeValid = isMatch(params.startTime, OTP_API_TIME_FORMAT);
457
+ const endTimeValid = isMatch(params.endTime, OTP_API_TIME_FORMAT);
455
458
 
456
459
  if (!startTimeValid || !endTimeValid) {
457
460
  delete params.startTimeValid;
package/src/time.ts CHANGED
@@ -1,21 +1,7 @@
1
1
  import { Config } from "@opentripplanner/types";
2
- import {
3
- startOfDay,
4
- add,
5
- format,
6
- formatDuration as dateFnsFormatDuration
7
- } from "date-fns";
2
+ import { startOfDay, add, format } from "date-fns";
8
3
  import { utcToZonedTime } from "date-fns-tz";
9
4
 
10
- /* eslint-disable import/no-cycle */
11
- import {
12
- formatTime,
13
- formatDurationWithSeconds,
14
- formatDuration
15
- } from "./deprecated-with-types";
16
-
17
- export { formatTime, formatDuration, formatDurationWithSeconds };
18
-
19
5
  // special constants for making sure the following date format is always sent to
20
6
  // OTP regardless of whatever the user has configured as the display format
21
7
  export const OTP_API_DATE_FORMAT = "YYYY-MM-DD";
@@ -24,73 +10,22 @@ export const OTP_API_DATE_FORMAT = "YYYY-MM-DD";
24
10
  export const OTP_API_DATE_FORMAT_DATE_FNS = "yyyy-MM-dd";
25
11
  export const OTP_API_TIME_FORMAT = "HH:mm";
26
12
 
27
- /**
28
- * To ease the transition away from moment.js, this method uses date-fns to format durations
29
- * the way moment.js did.
30
- * @param {number} seconds The number of seconds to format
31
- * @param {boolean} showSeconds Whether to render seconds or not
32
- * @param {boolean} localize If true, will create output like moment.js using date-fns locale.
33
- * Otherwise, uses date-fns default
34
- * @returns Formatted duration
35
- */
36
- export function formatDurationLikeMoment(
37
- seconds: number,
38
- showSeconds: boolean,
39
- localize: { enabled: boolean; code: string } = {
40
- enabled: true,
41
- code: "en-US"
42
- }
43
- ): string {
44
- // date-fns doesn't do this automatically
45
- if ((!showSeconds && seconds < 60) || seconds === 0) {
46
- return "0 min";
47
- }
48
-
49
- const hours = Math.floor(seconds / 3600);
50
- const minutes = Math.floor((seconds - hours * 3600) / 60);
51
- const secondsLeftOver = showSeconds
52
- ? seconds - hours * 3600 - minutes * 60
53
- : 0;
54
- const specLookup = {
55
- xHours: "hr",
56
- xMinutes: "min",
57
- xSeconds: "sec"
58
- };
59
- const locale = localize
60
- ? {
61
- // Maintain backwards compatibility when called with localize=true
62
- code: localize?.code || "en-US",
63
- formatDistance: (spec, val) => {
64
- return `${val} ${specLookup[spec]}`;
65
- }
66
- }
67
- : undefined;
68
-
69
- return dateFnsFormatDuration(
70
- {
71
- hours,
72
- minutes,
73
- seconds: secondsLeftOver
74
- },
75
- {
76
- format: ["hours", "minutes", "seconds"],
77
- locale
78
- }
79
- );
80
- }
81
-
82
13
  /**
83
14
  * Breaks up a duration in seconds into hours, minutes, and seconds.
84
15
  * @param {number} seconds The number of seconds to break up
85
16
  * @returns an object with fields with the corresponding, hours, minutes, seconds.
86
17
  */
87
- export function toHoursMinutesSeconds(seconds) {
88
- const hours = Math.floor(seconds / 3600);
89
- const minutes = Math.floor((seconds - hours * 3600) / 60);
18
+ export function toHoursMinutesSeconds(
19
+ seconds: number
20
+ ): {
21
+ hours: number;
22
+ minutes: number;
23
+ seconds: number;
24
+ } {
90
25
  return {
91
- hours,
92
- minutes,
93
- seconds: seconds - hours * 3600 - minutes * 60
26
+ hours: Math.floor(seconds / 3600),
27
+ minutes: Math.floor(seconds / 60) % 60,
28
+ seconds: seconds % 60
94
29
  };
95
30
  }
96
31