@escape-game-over/atlas 0.1.98 → 0.1.99
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/package.json +1 -1
- package/src/hours.ts +93 -19
- package/src/index.ts +2 -0
package/package.json
CHANGED
package/src/hours.ts
CHANGED
|
@@ -217,6 +217,71 @@ export function formatHours(
|
|
|
217
217
|
): readonly FormattedHours[] {
|
|
218
218
|
const between = format.between ?? "–";
|
|
219
219
|
const and = format.and ?? ", ";
|
|
220
|
+
const { dayName, time } = printer(format);
|
|
221
|
+
|
|
222
|
+
return groupHours(hours, format.weekStart).map((group) => ({
|
|
223
|
+
days: group.runs.map((run) => runName(run, dayName, between)).join(and),
|
|
224
|
+
weekdays: group.days,
|
|
225
|
+
times: timesOf(group, format.closed, time, between, and),
|
|
226
|
+
}));
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* How to say a week in a sentence: `HoursFormat` without the separators it
|
|
231
|
+
* picks from `Intl`, plus the one `Intl` has no word for.
|
|
232
|
+
*/
|
|
233
|
+
export interface HoursSentenceFormat extends Omit<HoursFormat, "and"> {
|
|
234
|
+
/**
|
|
235
|
+
* Between one group of days and the next: `"; "` in English, `"؛ "` in
|
|
236
|
+
* Arabic. Copy, so the caller translates it; `Intl` has no list form
|
|
237
|
+
* for it.
|
|
238
|
+
*/
|
|
239
|
+
readonly separator: string;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* The week as one sentence, for copy that quotes it: `Monday–Thursday and
|
|
244
|
+
* Sunday 14:00–23:30; Friday–Saturday 14:00–00:45`.
|
|
245
|
+
*
|
|
246
|
+
* Days and times join with the locale's own "and" (`Intl.ListFormat`), so a
|
|
247
|
+
* sentence in Arabic reads `الاثنين–الخميس والأحد` rather than borrowing an
|
|
248
|
+
* English comma. No closing full stop: the copy around it decides that.
|
|
249
|
+
*/
|
|
250
|
+
export function hoursSentence(
|
|
251
|
+
hours: WeeklyHours,
|
|
252
|
+
format: HoursSentenceFormat
|
|
253
|
+
): string {
|
|
254
|
+
const between = format.between ?? "–";
|
|
255
|
+
const { dayName, time } = printer(format);
|
|
256
|
+
const list = new Intl.ListFormat(format.locale, {
|
|
257
|
+
type: "conjunction",
|
|
258
|
+
style: "long",
|
|
259
|
+
});
|
|
260
|
+
const joined = (parts: readonly string[]) => list.format(parts);
|
|
261
|
+
|
|
262
|
+
return groupHours(hours, format.weekStart)
|
|
263
|
+
.map((group) => {
|
|
264
|
+
const days = joined(
|
|
265
|
+
group.runs.map((run) => runName(run, dayName, between))
|
|
266
|
+
);
|
|
267
|
+
const times =
|
|
268
|
+
group.hours === "closed"
|
|
269
|
+
? format.closed
|
|
270
|
+
: joined(
|
|
271
|
+
group.hours.map(
|
|
272
|
+
(range) =>
|
|
273
|
+
`${time(range.opens)}${between}${time(range.closes)}`
|
|
274
|
+
)
|
|
275
|
+
);
|
|
276
|
+
return `${days} ${times}`;
|
|
277
|
+
})
|
|
278
|
+
.join(format.separator);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** Day names and times as `format` prints them. */
|
|
282
|
+
function printer(
|
|
283
|
+
format: Pick<HoursFormat, "locale" | "weekday" | "hourCycle">
|
|
284
|
+
) {
|
|
220
285
|
// 2024-01-01 was a Monday, so `WEEK[i]` falls on the 1st plus `i`.
|
|
221
286
|
const names = new Intl.DateTimeFormat(format.locale, {
|
|
222
287
|
weekday: format.weekday ?? "short",
|
|
@@ -247,26 +312,35 @@ export function formatHours(
|
|
|
247
312
|
)
|
|
248
313
|
)
|
|
249
314
|
);
|
|
315
|
+
return { dayName, time };
|
|
316
|
+
}
|
|
250
317
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
318
|
+
/** A run of one day prints as one name — `Sun`, not `Sun–Sun`. */
|
|
319
|
+
function runName(
|
|
320
|
+
[first, last]: HoursGroup["runs"][number],
|
|
321
|
+
dayName: (day: Weekday) => string,
|
|
322
|
+
between: string
|
|
323
|
+
): string {
|
|
324
|
+
return first === last
|
|
325
|
+
? dayName(first)
|
|
326
|
+
: `${dayName(first)}${between}${dayName(last)}`;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function timesOf(
|
|
330
|
+
group: HoursGroup,
|
|
331
|
+
closed: string,
|
|
332
|
+
time: (at: TimeOfDay) => string,
|
|
333
|
+
between: string,
|
|
334
|
+
and: string
|
|
335
|
+
): string {
|
|
336
|
+
return group.hours === "closed"
|
|
337
|
+
? closed
|
|
338
|
+
: group.hours
|
|
339
|
+
.map(
|
|
340
|
+
(range) =>
|
|
341
|
+
`${time(range.opens)}${between}${time(range.closes)}`
|
|
342
|
+
)
|
|
343
|
+
.join(and);
|
|
270
344
|
}
|
|
271
345
|
|
|
272
346
|
/** Minutes since midnight, for comparing two times. */
|