cronli5 0.2.0 → 0.3.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 (57) hide show
  1. package/CHANGELOG.md +90 -0
  2. package/README.md +4 -4
  3. package/cronli5.min.js +2 -2
  4. package/dist/cronli5.cjs +514 -407
  5. package/dist/cronli5.js +514 -407
  6. package/dist/lang/de.cjs +296 -225
  7. package/dist/lang/de.js +296 -225
  8. package/dist/lang/en.cjs +471 -364
  9. package/dist/lang/en.js +471 -364
  10. package/dist/lang/es.cjs +318 -281
  11. package/dist/lang/es.js +318 -281
  12. package/dist/lang/fi.cjs +326 -276
  13. package/dist/lang/fi.js +326 -276
  14. package/dist/lang/zh.cjs +308 -236
  15. package/dist/lang/zh.js +308 -236
  16. package/package.json +1 -1
  17. package/src/core/analyze.ts +22 -21
  18. package/src/core/cadence.ts +164 -0
  19. package/src/core/index.ts +3 -1
  20. package/src/core/normalize.ts +3 -3
  21. package/src/core/parse.ts +1 -1
  22. package/src/core/{ir.ts → schedule.ts} +23 -24
  23. package/src/core/shapes.ts +8 -1
  24. package/src/core/specs.ts +1 -1
  25. package/src/core/util.ts +4 -83
  26. package/src/core/validate.ts +2 -2
  27. package/src/core/weekday.ts +54 -0
  28. package/src/cronli5.ts +7 -7
  29. package/src/lang/de/index.ts +329 -288
  30. package/src/lang/en/dialects.ts +1 -1
  31. package/src/lang/en/index.ts +640 -516
  32. package/src/lang/es/index.ts +342 -374
  33. package/src/lang/es/notes.md +1 -1
  34. package/src/lang/fi/dialects.ts +1 -1
  35. package/src/lang/fi/index.ts +367 -372
  36. package/src/lang/fi/notes.md +23 -8
  37. package/src/lang/fi/status.json +1 -1
  38. package/src/lang/zh/index.ts +344 -262
  39. package/src/types.ts +6 -6
  40. package/types/core/analyze.d.ts +4 -4
  41. package/types/core/cadence.d.ts +33 -0
  42. package/types/core/index.d.ts +3 -1
  43. package/types/core/normalize.d.ts +1 -1
  44. package/types/core/parse.d.ts +1 -1
  45. package/types/core/{ir.d.ts → schedule.d.ts} +16 -21
  46. package/types/core/shapes.d.ts +2 -1
  47. package/types/core/specs.d.ts +1 -1
  48. package/types/core/util.d.ts +1 -15
  49. package/types/core/weekday.d.ts +10 -0
  50. package/types/lang/de/index.d.ts +1 -1
  51. package/types/lang/en/dialects.d.ts +1 -1
  52. package/types/lang/en/index.d.ts +1 -1
  53. package/types/lang/es/index.d.ts +1 -1
  54. package/types/lang/fi/dialects.d.ts +1 -1
  55. package/types/lang/fi/index.d.ts +1 -1
  56. package/types/lang/zh/index.d.ts +1 -1
  57. package/types/types.d.ts +5 -5
package/dist/lang/fi.cjs CHANGED
@@ -57,6 +57,16 @@ function isNonNegativeInteger(value) {
57
57
  const digits = /^\d+$/;
58
58
  return digits.test(value);
59
59
  }
60
+ function toFieldNumber(token, numberMap) {
61
+ return isNonNegativeInteger(token) ? +token : numberMap[token.toUpperCase()];
62
+ }
63
+
64
+ // src/core/shapes.ts
65
+ function isOpenStep(field) {
66
+ return field.indexOf("/") !== -1 && field.indexOf("-") === -1 && field.indexOf(",") === -1;
67
+ }
68
+
69
+ // src/core/cadence.ts
60
70
  function arithmeticStep(values) {
61
71
  if (values.length < 5) {
62
72
  return null;
@@ -72,6 +82,56 @@ function arithmeticStep(values) {
72
82
  }
73
83
  return { start: values[0], interval, last: values[values.length - 1] };
74
84
  }
85
+ function segmentsOf(schedule, field) {
86
+ return schedule.analyses.segments[field] ?? [];
87
+ }
88
+ function stepSegment(schedule, field) {
89
+ return segmentsOf(schedule, field)[0];
90
+ }
91
+ function singleValues(segments) {
92
+ const values = [];
93
+ for (const segment of segments) {
94
+ if (segment.kind !== "single") {
95
+ return null;
96
+ }
97
+ values.push(+segment.value);
98
+ }
99
+ return values;
100
+ }
101
+ function offsetCleanStride(stride) {
102
+ return stride.start < stride.interval && 24 % stride.interval === 0;
103
+ }
104
+ function renderStride(spec, parts) {
105
+ const { start, interval, cycle } = spec;
106
+ const tiles = cycle % interval === 0;
107
+ if (start === 0 && tiles) {
108
+ return parts.bare();
109
+ }
110
+ if (start < interval && tiles) {
111
+ return parts.offset();
112
+ }
113
+ return parts.bounded();
114
+ }
115
+ function hourListStride(values) {
116
+ if (values.length < 2) {
117
+ return null;
118
+ }
119
+ const interval = values[1] - values[0];
120
+ if (interval < 2) {
121
+ return null;
122
+ }
123
+ for (let i = 2; i < values.length; i += 1) {
124
+ if (values[i] - values[i - 1] !== interval) {
125
+ return null;
126
+ }
127
+ }
128
+ if (values[0] !== 0 && values.length < 5) {
129
+ return null;
130
+ }
131
+ return { interval, last: values[values.length - 1], start: values[0] };
132
+ }
133
+
134
+ // src/core/weekday.ts
75
135
  function weekdayDisplayKey(value) {
76
136
  return value === 0 ? 7 : value;
77
137
  }
@@ -92,9 +152,6 @@ function orderWeekdaysForDisplay(segments) {
92
152
  return pair[0];
93
153
  });
94
154
  }
95
- function toFieldNumber(token, numberMap) {
96
- return isNonNegativeInteger(token) ? +token : numberMap[token.toUpperCase()];
97
- }
98
155
 
99
156
  // src/lang/fi/dialects.ts
100
157
  var dialects = {
@@ -113,9 +170,6 @@ function resolveDialect(dialect) {
113
170
  }
114
171
 
115
172
  // src/lang/fi/index.ts
116
- function stepSegment(segments) {
117
- return segments[0];
118
- }
119
173
  var genitives = [
120
174
  null,
121
175
  "yhden",
@@ -253,94 +307,108 @@ function normalizeOptions(options) {
253
307
  years: !!options.years
254
308
  };
255
309
  }
256
- function restrictedMonthUnion(ir) {
257
- return ir.pattern.date !== "*" && ir.pattern.weekday !== "*" && ir.pattern.month !== "*";
310
+ function restrictedMonthUnion(schedule) {
311
+ return schedule.pattern.date !== "*" && schedule.pattern.weekday !== "*" && schedule.pattern.month !== "*";
258
312
  }
259
- function unionDateArm(ir) {
260
- return quartzDatePhrase(ir.pattern.date) || dateWords(ir) + " p\xE4iv\xE4n\xE4";
313
+ function oddDayUnion(dateField) {
314
+ if (!isOpenStep(dateField)) {
315
+ return null;
316
+ }
317
+ const [start, step] = dateField.split("/");
318
+ return (start === "*" || start === "1") && +step === 2 ? "kuukauden parittomina p\xE4ivin\xE4" : null;
319
+ }
320
+ function unionDateArm(schedule) {
321
+ return quartzDatePhrase(schedule.pattern.date) || oddDayUnion(schedule.pattern.date) || dateWords(schedule) + " p\xE4iv\xE4n\xE4";
322
+ }
323
+ function unionWeekdayArm(schedule) {
324
+ const segments = segmentsOf(schedule, "weekday");
325
+ if (segments.length === 1 && segments[0].kind === "range" && segments[0].bounds[0] === "1" && segments[0].bounds[1] === "5") {
326
+ return "arkisin";
327
+ }
328
+ return weekdayQualifier(schedule);
261
329
  }
262
- function describe(ir, opts) {
263
- if (restrictedMonthUnion(ir)) {
264
- const timePart = render(ir, ir.plan, opts);
330
+ function describe(schedule, opts) {
331
+ if (restrictedMonthUnion(schedule)) {
332
+ const timePart = render(schedule, schedule.plan, opts);
265
333
  return applyYear(
266
- monthPhrase(ir) + " " + timePart + " joko " + unionDateArm(ir) + " tai " + weekdayQualifier(ir),
267
- ir,
334
+ monthPhrase(schedule) + " " + timePart + " " + unionDateArm(schedule) + " tai " + unionWeekdayArm(schedule),
335
+ schedule,
268
336
  opts
269
337
  );
270
338
  }
271
- return applyYear(render(ir, ir.plan, opts), ir, opts);
339
+ return applyYear(render(schedule, schedule.plan, opts), schedule, opts);
272
340
  }
273
- function render(ir, plan, opts) {
341
+ function render(schedule, plan, opts) {
274
342
  const renderer = renderers[plan.kind];
275
- return renderer(ir, plan, opts);
343
+ return renderer(schedule, plan, opts);
276
344
  }
277
- function renderEverySecond(ir, plan, opts) {
278
- return "joka sekunti" + trailingQualifier(ir, opts);
345
+ function renderEverySecond(schedule, plan, opts) {
346
+ return "joka sekunti" + trailingQualifier(schedule, opts);
279
347
  }
280
- function renderStandaloneSeconds(ir, plan, opts) {
281
- return secondsLeadClause(ir, opts) + trailingQualifier(ir, opts);
348
+ function renderStandaloneSeconds(schedule, plan, opts) {
349
+ return secondsLeadClause(schedule, opts) + trailingQualifier(schedule, opts);
282
350
  }
283
- function renderSecondPastMinute(ir, plan, opts) {
284
- return atMarks(ir.pattern.second, units.second, true) + trailingQualifier(ir, opts);
351
+ function renderSecondPastMinute(schedule, plan, opts) {
352
+ return atMarks(schedule.pattern.second, units.second, true) + trailingQualifier(schedule, opts);
285
353
  }
286
- function renderSecondsWithinMinute(ir, plan, opts) {
287
- const minuteField = ir.pattern.minute;
354
+ function renderSecondsWithinMinute(schedule, plan, opts) {
355
+ const minuteField = schedule.pattern.minute;
288
356
  if (plan.singleSecond) {
289
- return units.minute.mark + " " + minuteField + " " + units.minute.gen + " ja " + ir.pattern.second + " " + units.second.gen + " kohdalla" + trailingQualifier(ir, opts);
357
+ return units.minute.mark + " " + minuteField + " " + units.minute.gen + " ja " + schedule.pattern.second + " " + units.second.gen + " kohdalla" + trailingQualifier(schedule, opts);
290
358
  }
291
- return secondsLeadClause(ir, opts) + ", " + atMarks(minuteField, units.minute, true) + trailingQualifier(ir, opts);
359
+ return secondsLeadClause(schedule, opts) + ", " + atMarks(minuteField, units.minute, true) + trailingQualifier(schedule, opts);
292
360
  }
293
- function composeSecondsOverMinuteStep(ir, freq, opts) {
294
- const seg = stepSegment(ir.analyses.segments.minute);
361
+ function composeSecondsOverMinuteStep(schedule, freq, opts) {
362
+ const seg = stepSegment(schedule, "minute");
295
363
  const stepPhrase = stepCycle60(seg, units.minute, opts);
296
364
  if (freq.hours.kind === "during" && minuteStepIsAnchored(seg)) {
297
- const bareHours = kloFromTimes(ir, freq.hours.times, opts);
298
- return hoursFirstMinutes(bareHours, ir, opts) + ", " + secondsLeadClause(ir, opts) + trailingQualifier(ir, opts);
365
+ const bareHours = kloFromTimes(schedule, freq.hours.times, opts);
366
+ return hoursFirstMinutes(bareHours, schedule, opts) + ", " + secondsLeadClause(schedule, opts) + trailingQualifier(schedule, opts);
299
367
  }
300
368
  let hourClause = "";
301
369
  if (freq.hours.kind === "during") {
302
- hourClause = " " + hourWindowsFromTimes(ir, freq.hours.times, opts);
370
+ hourClause = " " + hourWindowsFromTimes(schedule, freq.hours.times, opts);
303
371
  } else if (freq.hours.kind === "window") {
304
372
  hourClause = " " + hourWindow(freq.hours, opts);
305
373
  } else if (freq.hours.kind === "step") {
306
- hourClause = " " + everyNthHour(stepSegment(ir.analyses.segments.hour), opts);
374
+ hourClause = " " + everyNthHour(stepSegment(schedule, "hour"), opts);
307
375
  }
308
- return stepPhrase + ", " + secondsLeadClause(ir, opts) + hourClause + trailingQualifier(ir, opts);
376
+ return stepPhrase + ", " + secondsLeadClause(schedule, opts) + hourClause + trailingQualifier(schedule, opts);
309
377
  }
310
- function composeHourCadence(ir, plan, opts) {
378
+ function composeHourCadence(schedule, plan, opts) {
311
379
  const clockRest = plan.rest.kind === "clockTimes" || plan.rest.kind === "compactClockTimes";
312
- if (!clockRest || ir.shapes.minute !== "single") {
380
+ if (!clockRest || schedule.shapes.minute !== "single") {
313
381
  return null;
314
382
  }
315
- const minute = +ir.pattern.minute;
316
- return hourCadence(ir, minute, opts) ?? hourRangeCadence(ir, minute, opts);
383
+ const minute = +schedule.pattern.minute;
384
+ return hourCadence(schedule, minute, opts) ?? hourRangeCadence(schedule, minute, opts);
317
385
  }
318
- function renderComposeSeconds(ir, plan, opts) {
319
- const cadence = composeHourCadence(ir, plan, opts);
386
+ function renderComposeSeconds(schedule, plan, opts) {
387
+ const cadence = composeHourCadence(schedule, plan, opts);
320
388
  if (cadence !== null) {
321
389
  return cadence;
322
390
  }
323
- if (plan.rest.kind === "minuteFrequency" && ir.pattern.second !== "*") {
324
- return composeSecondsOverMinuteStep(ir, plan.rest, opts);
391
+ if (plan.rest.kind === "minuteFrequency" && schedule.pattern.second !== "*") {
392
+ return composeSecondsOverMinuteStep(schedule, plan.rest, opts);
325
393
  }
326
394
  if (plan.rest.kind === "clockTimes" && plan.rest.times.every((time) => +time.minute === 0)) {
327
- return composeMinuteZero(ir, plan.rest, opts);
395
+ return composeMinuteZero(schedule, plan.rest, opts);
328
396
  }
329
- if (isEveryOtherMinuteSeconds(ir, plan)) {
330
- return secondsLeadClause(ir, opts) + " joka toisena minuuttina";
397
+ if (isEveryOtherMinuteSeconds(schedule, plan)) {
398
+ return secondsLeadClause(schedule, opts) + " joka toisena minuuttina";
331
399
  }
332
- const restOwnsLead = plan.rest.kind === "compactClockTimes" && ir.analyses.clockSecond;
333
- const lead = restOwnsLead ? "" : secondsLeadClause(ir, opts) + ", ";
334
- return lead + render(ir, plan.rest, opts);
400
+ const restOwnsLead = plan.rest.kind === "compactClockTimes" && schedule.analyses.clockSecond;
401
+ const lead = restOwnsLead ? "" : secondsLeadClause(schedule, opts) + ", ";
402
+ return lead + render(schedule, plan.rest, opts);
335
403
  }
336
- function isEveryOtherMinuteSeconds(ir, plan) {
337
- if (plan.rest.kind !== "minuteFrequency" || ir.pattern.second !== "*" || ir.shapes.hour !== "wildcard") {
404
+ function isEveryOtherMinuteSeconds(schedule, plan) {
405
+ if (plan.rest.kind !== "minuteFrequency" || schedule.pattern.second !== "*" || schedule.shapes.hour !== "wildcard") {
338
406
  return false;
339
407
  }
340
- const seg = stepSegment(ir.analyses.segments.minute);
408
+ const seg = stepSegment(schedule, "minute");
341
409
  return seg.startToken === "*" && seg.interval === 2;
342
410
  }
343
- function composeMinuteZero(ir, rest, opts) {
411
+ function composeMinuteZero(schedule, rest, opts) {
344
412
  const clocks = rest.times.map(function clock(time) {
345
413
  return clockDigits(
346
414
  { hour: time.hour, minute: time.minute },
@@ -348,54 +416,66 @@ function composeMinuteZero(ir, rest, opts) {
348
416
  );
349
417
  });
350
418
  const frame = clocks.length === 1 ? "minuutin " + clocks[0] : "minuuttien " + joinList(clocks);
351
- const dayTrail = leadingQualifier(ir, opts).trimEnd();
352
- return secondsLeadClause(ir, opts) + " " + frame + " aikana" + (dayTrail ? ", " + dayTrail : "");
419
+ const dayTrail = leadingQualifier(schedule, opts).trimEnd();
420
+ return secondsLeadClause(schedule, opts) + " " + frame + " aikana" + (dayTrail ? ", " + dayTrail : "");
353
421
  }
354
- function secondsLeadClause(ir, opts) {
355
- const secondField = ir.pattern.second;
356
- const shape = ir.shapes.second;
422
+ function secondsLeadClause(schedule, opts) {
423
+ const secondField = schedule.pattern.second;
424
+ const shape = schedule.shapes.second;
357
425
  if (secondField === "*") {
358
426
  return "joka sekunti";
359
427
  }
360
428
  if (shape === "step") {
361
429
  return stepCycle60(
362
- stepSegment(ir.analyses.segments.second),
430
+ stepSegment(schedule, "second"),
363
431
  units.second,
364
432
  opts
365
433
  );
366
434
  }
367
- const marked = ir.pattern.minute === "*";
435
+ const marked = schedule.pattern.minute === "*";
368
436
  if (shape === "single") {
369
437
  return atMarks(secondField, units.second, marked);
370
438
  }
371
- return strideFromSegments(ir.analyses.segments.second, units.second, opts) ?? atMarks(
372
- joinList(segmentWords(ir.analyses.segments.second)),
439
+ return strideFromSegments(
440
+ segmentsOf(schedule, "second"),
441
+ units.second,
442
+ opts
443
+ ) ?? atMarks(
444
+ joinList(segmentWords(segmentsOf(schedule, "second"))),
373
445
  units.second,
374
446
  marked
375
447
  );
376
448
  }
377
- function renderEveryMinute(ir, plan, opts) {
378
- return "joka minuutti" + trailingQualifier(ir, opts);
449
+ function renderEveryMinute(schedule, plan, opts) {
450
+ return "joka minuutti" + trailingQualifier(schedule, opts);
379
451
  }
380
- function renderSingleMinute(ir, plan, opts) {
381
- return atMarks(ir.pattern.minute, units.minute, true) + trailingQualifier(ir, opts);
452
+ function renderSingleMinute(schedule, plan, opts) {
453
+ return atMarks(schedule.pattern.minute, units.minute, true) + trailingQualifier(schedule, opts);
382
454
  }
383
- function renderRangeOfMinutes(ir, plan, opts) {
384
- return minutesList(ir, opts) + trailingQualifier(ir, opts);
455
+ function renderRangeOfMinutes(schedule, plan, opts) {
456
+ return minutesList(schedule, opts) + trailingQualifier(schedule, opts);
385
457
  }
386
- function renderMultipleMinutes(ir, plan, opts) {
387
- return minutesList(ir, opts) + trailingQualifier(ir, opts);
458
+ function renderMultipleMinutes(schedule, plan, opts) {
459
+ return minutesList(schedule, opts) + trailingQualifier(schedule, opts);
388
460
  }
389
- function minutesList(ir, opts) {
390
- return strideFromSegments(ir.analyses.segments.minute, units.minute, opts) ?? atMarks(
391
- joinList(segmentWords(ir.analyses.segments.minute)),
461
+ function minutesList(schedule, opts) {
462
+ return strideFromSegments(
463
+ segmentsOf(schedule, "minute"),
464
+ units.minute,
465
+ opts
466
+ ) ?? atMarks(
467
+ joinList(segmentWords(segmentsOf(schedule, "minute"))),
392
468
  units.minute,
393
469
  true
394
470
  );
395
471
  }
396
- function bareMinutes(ir, opts) {
397
- return strideFromSegments(ir.analyses.segments.minute, units.minute, opts) ?? atMarks(
398
- joinList(segmentWords(ir.analyses.segments.minute)),
472
+ function bareMinutes(schedule, opts) {
473
+ return strideFromSegments(
474
+ segmentsOf(schedule, "minute"),
475
+ units.minute,
476
+ opts
477
+ ) ?? atMarks(
478
+ joinList(segmentWords(segmentsOf(schedule, "minute"))),
399
479
  units.minute,
400
480
  false
401
481
  );
@@ -425,16 +505,16 @@ function hoursAreRangeIsolated(segments) {
425
505
  }
426
506
  return hasRange && hasSingle;
427
507
  }
428
- function hoursFirstMinutes(hoursStr, ir, opts) {
429
- const stride = strideFromSegments(ir.analyses.segments.minute, units.minute, opts);
508
+ function hoursFirstMinutes(hoursStr, schedule, opts) {
509
+ const stride = strideFromSegments(segmentsOf(schedule, "minute"), units.minute, opts);
430
510
  if (stride) {
431
511
  return hoursStr + " aina " + stride;
432
512
  }
433
- return hoursStr + " aina minuuttien " + joinList(segmentWords(ir.analyses.segments.minute)) + " kohdalla";
513
+ return hoursStr + " aina minuuttien " + joinList(segmentWords(segmentsOf(schedule, "minute"))) + " kohdalla";
434
514
  }
435
- function hourSegmentTimesWithSeka(ir, minute, second, opts) {
515
+ function hourSegmentTimesWithSeka(schedule, minute, second, opts) {
436
516
  const pieces = [];
437
- ir.analyses.segments.hour.forEach(function clock(segment) {
517
+ segmentsOf(schedule, "hour").forEach(function clock(segment) {
438
518
  if (segment.kind === "range") {
439
519
  pieces.push(rangeDigits(
440
520
  { hour: +segment.bounds[0], minute, second },
@@ -447,61 +527,61 @@ function hourSegmentTimesWithSeka(ir, minute, second, opts) {
447
527
  });
448
528
  return "klo " + pieces.slice(0, -1).join(", ") + " sek\xE4 klo " + pieces[pieces.length - 1];
449
529
  }
450
- function renderMinuteFrequency(ir, plan, opts) {
451
- const seg = stepSegment(ir.analyses.segments.minute);
530
+ function renderMinuteFrequency(schedule, plan, opts) {
531
+ const seg = stepSegment(schedule, "minute");
452
532
  if (plan.hours.kind === "during") {
453
- const cadence = unevenHourCadence(ir, opts);
533
+ const cadence = unevenHourCadence(schedule, opts);
454
534
  if (cadence !== null) {
455
- return stepCycle60(seg, units.minute, opts) + ", " + cadence + trailingQualifier(ir, opts);
535
+ return stepCycle60(seg, units.minute, opts) + ", " + cadence + trailingQualifier(schedule, opts);
456
536
  }
457
537
  if (minuteStepIsAnchored(seg)) {
458
- const bareHours = kloFromTimes(ir, plan.hours.times, opts);
459
- return hoursFirstMinutes(bareHours, ir, opts) + trailingQualifier(ir, opts);
538
+ const bareHours = kloFromTimes(schedule, plan.hours.times, opts);
539
+ return hoursFirstMinutes(bareHours, schedule, opts) + trailingQualifier(schedule, opts);
460
540
  }
461
- return stepCycle60(seg, units.minute, opts) + " " + hourWindowsFromTimes(ir, plan.hours.times, opts) + trailingQualifier(ir, opts);
541
+ return stepCycle60(seg, units.minute, opts) + " " + hourWindowsFromTimes(schedule, plan.hours.times, opts) + trailingQualifier(schedule, opts);
462
542
  }
463
543
  let phrase = stepCycle60(seg, units.minute, opts);
464
544
  if (plan.hours.kind === "window") {
465
545
  phrase += " " + hourWindow(plan.hours, opts);
466
546
  } else if (plan.hours.kind === "step") {
467
- phrase += " " + everyNthHour(stepSegment(ir.analyses.segments.hour), opts);
547
+ phrase += " " + everyNthHour(stepSegment(schedule, "hour"), opts);
468
548
  }
469
- return phrase + trailingQualifier(ir, opts);
549
+ return phrase + trailingQualifier(schedule, opts);
470
550
  }
471
- function renderMinuteSpanInHour(ir, plan, opts) {
472
- if (ir.pattern.minute === "*") {
473
- return "joka minuutti kello " + plan.hour + " aikana" + trailingQualifier(ir, opts);
551
+ function renderMinuteSpanInHour(schedule, plan, opts) {
552
+ if (schedule.pattern.minute === "*") {
553
+ return "joka minuutti kello " + plan.hour + " aikana" + trailingQualifier(schedule, opts);
474
554
  }
475
555
  return "joka minuutti " + kloRange(
476
556
  { hour: plan.hour, minute: plan.span[0] },
477
557
  { hour: plan.hour, minute: plan.span[1] },
478
558
  opts
479
- ) + trailingQualifier(ir, opts);
559
+ ) + trailingQualifier(schedule, opts);
480
560
  }
481
- function renderMinutesAcrossHours(ir, plan, opts) {
482
- const cadence = unevenHourCadence(ir, opts);
561
+ function renderMinutesAcrossHours(schedule, plan, opts) {
562
+ const cadence = unevenHourCadence(schedule, opts);
483
563
  if (plan.form === "wildcard") {
484
- return cadence ? "joka minuutti, " + cadence + trailingQualifier(ir, opts) : "joka minuutti " + hourWindowsFromTimes(ir, plan.times, opts) + trailingQualifier(ir, opts);
564
+ return cadence ? "joka minuutti, " + cadence + trailingQualifier(schedule, opts) : "joka minuutti " + hourWindowsFromTimes(schedule, plan.times, opts) + trailingQualifier(schedule, opts);
485
565
  }
486
566
  if (cadence !== null) {
487
- return bareMinutes(ir, opts) + ", " + cadence + trailingQualifier(ir, opts);
567
+ return bareMinutes(schedule, opts) + ", " + cadence + trailingQualifier(schedule, opts);
488
568
  }
489
- if (hoursAreRangeIsolated(ir.analyses.segments.hour)) {
490
- return bareMinutes(ir, opts) + " " + hourSegmentTimesWithSeka(ir, 0, null, opts) + trailingQualifier(ir, opts);
569
+ if (hoursAreRangeIsolated(segmentsOf(schedule, "hour"))) {
570
+ return bareMinutes(schedule, opts) + " " + hourSegmentTimesWithSeka(schedule, 0, null, opts) + trailingQualifier(schedule, opts);
491
571
  }
492
- const hoursStr = kloFromTimes(ir, plan.times, opts);
493
- return hoursFirstMinutes(hoursStr, ir, opts) + trailingQualifier(ir, opts);
572
+ const hoursStr = kloFromTimes(schedule, plan.times, opts);
573
+ return hoursFirstMinutes(hoursStr, schedule, opts) + trailingQualifier(schedule, opts);
494
574
  }
495
- function renderMinuteSpanAcrossHourStep(ir, plan, opts) {
496
- const segment = stepSegment(ir.analyses.segments.hour);
497
- const cadence = unevenHourCadence(ir, opts);
575
+ function renderMinuteSpanAcrossHourStep(schedule, plan, opts) {
576
+ const segment = stepSegment(schedule, "hour");
577
+ const cadence = unevenHourCadence(schedule, opts);
498
578
  if (plan.form === "wildcard") {
499
- return "joka minuutti " + everyNthHour(segment, opts) + trailingQualifier(ir, opts);
579
+ return "joka minuutti " + everyNthHour(segment, opts) + trailingQualifier(schedule, opts);
500
580
  }
501
581
  if (cadence !== null) {
502
- return bareMinutes(ir, opts) + ", " + cadence + trailingQualifier(ir, opts);
582
+ return bareMinutes(schedule, opts) + ", " + cadence + trailingQualifier(schedule, opts);
503
583
  }
504
- return bareMinutes(ir, opts) + hourStepTail(segment, opts) + trailingQualifier(ir, opts);
584
+ return bareMinutes(schedule, opts) + hourStepTail(segment, opts) + trailingQualifier(schedule, opts);
505
585
  }
506
586
  function plainHourStep(segment) {
507
587
  return segment.startToken === "*" && 24 % segment.interval === 0;
@@ -550,35 +630,35 @@ function hourStepTail(segment, opts) {
550
630
  const sep = plainHourStep(segment) ? " " : ", ";
551
631
  return sep + plainOrFullHourStep(segment, opts);
552
632
  }
553
- function renderEveryHour(ir, plan, opts) {
554
- return "joka tunti" + trailingQualifier(ir, opts);
633
+ function renderEveryHour(schedule, plan, opts) {
634
+ return "joka tunti" + trailingQualifier(schedule, opts);
555
635
  }
556
- function renderHourRange(ir, plan, opts) {
636
+ function renderHourRange(schedule, plan, opts) {
557
637
  const window = hourWindow(boundedWindow(plan), opts);
558
638
  if (plan.minuteForm === "wildcard") {
559
- return "joka minuutti " + window + trailingQualifier(ir, opts);
639
+ return "joka minuutti " + window + trailingQualifier(schedule, opts);
560
640
  }
561
641
  if (plan.minuteForm === "range") {
562
- return hoursFirstMinutes(window, ir, opts) + trailingQualifier(ir, opts);
642
+ return hoursFirstMinutes(window, schedule, opts) + trailingQualifier(schedule, opts);
563
643
  }
564
- if (ir.pattern.minute === "0") {
565
- return "joka tunti " + window + trailingQualifier(ir, opts);
644
+ if (schedule.pattern.minute === "0") {
645
+ return "joka tunti " + window + trailingQualifier(schedule, opts);
566
646
  }
567
- if (ir.shapes.minute === "single") {
568
- return atMarks(ir.pattern.minute, units.minute, false) + " " + kloRange(
569
- { hour: plan.from, minute: +ir.pattern.minute },
647
+ if (schedule.shapes.minute === "single") {
648
+ return atMarks(schedule.pattern.minute, units.minute, false) + " " + kloRange(
649
+ { hour: plan.from, minute: +schedule.pattern.minute },
570
650
  { hour: plan.to, minute: plan.last },
571
651
  opts
572
- ) + trailingQualifier(ir, opts);
652
+ ) + trailingQualifier(schedule, opts);
573
653
  }
574
- return hoursFirstMinutes(window, ir, opts) + trailingQualifier(ir, opts);
654
+ return hoursFirstMinutes(window, schedule, opts) + trailingQualifier(schedule, opts);
575
655
  }
576
- function renderHourStep(ir, plan, opts) {
577
- const cadence = unevenHourCadence(ir, opts);
656
+ function renderHourStep(schedule, plan, opts) {
657
+ const cadence = unevenHourCadence(schedule, opts);
578
658
  if (cadence !== null) {
579
- return cadence + trailingQualifier(ir, opts);
659
+ return cadence + trailingQualifier(schedule, opts);
580
660
  }
581
- return stepHours(stepSegment(ir.analyses.segments.hour), opts) + trailingQualifier(ir, opts);
661
+ return stepHours(stepSegment(schedule, "hour"), opts) + trailingQualifier(schedule, opts);
582
662
  }
583
663
  function boundedWindow(plan) {
584
664
  return { from: plan.from, last: plan.boundMinute ?? 0, to: plan.to };
@@ -590,53 +670,62 @@ function hourWindow(window, opts) {
590
670
  opts
591
671
  );
592
672
  }
593
- function renderClockTimes(ir, plan, opts) {
594
- if (ir.shapes.minute === "single") {
595
- const minute = +ir.pattern.minute;
596
- const cadence = hourCadence(ir, minute, opts) ?? hourRangeCadence(ir, minute, opts);
673
+ function renderClockTimes(schedule, plan, opts) {
674
+ if (schedule.shapes.minute === "single") {
675
+ const minute = +schedule.pattern.minute;
676
+ const cadence = hourCadence(schedule, minute, opts) ?? hourRangeCadence(schedule, minute, opts);
597
677
  if (cadence !== null) {
598
678
  return cadence;
599
679
  }
600
680
  }
601
681
  if (plan.times.length === 1) {
602
682
  const time = plan.times[0];
603
- return leadingQualifier(ir, opts) + timeWord(time.hour, time.minute, time.second, opts);
683
+ return leadingQualifier(schedule, opts) + timeWord(time.hour, time.minute, time.second, opts);
604
684
  }
605
685
  const digits = plan.times.map(function clock(time) {
606
686
  return timeDigits(time.hour, time.minute, time.second, opts);
607
687
  });
608
- return leadingQualifier(ir, opts) + "klo " + joinList(digits);
688
+ return leadingQualifier(schedule, opts) + "klo " + joinList(digits);
609
689
  }
610
- function renderCompactClockTimes(ir, plan, opts) {
690
+ function renderCompactClockTimes(schedule, plan, opts) {
611
691
  if (plan.fold) {
612
- const cadence2 = hourCadence(ir, plan.minute, opts) ?? hourRangeCadence(ir, plan.minute, opts);
692
+ const cadence2 = hourCadence(schedule, plan.minute, opts) ?? hourRangeCadence(schedule, plan.minute, opts);
613
693
  if (cadence2 !== null) {
614
694
  return cadence2;
615
695
  }
616
696
  }
617
- const hourSegs = ir.analyses.segments.hour;
697
+ const hourSegs = segmentsOf(schedule, "hour");
618
698
  if (hoursAreRangeIsolated(hourSegs)) {
619
699
  if (plan.fold) {
620
- return leadingQualifier(ir, opts) + hourSegmentTimesWithSeka(
621
- ir,
700
+ return leadingQualifier(schedule, opts) + hourSegmentTimesWithSeka(
701
+ schedule,
622
702
  plan.minute,
623
- ir.analyses.clockSecond,
703
+ schedule.analyses.clockSecond,
624
704
  opts
625
705
  );
626
706
  }
627
- const phrase2 = bareMinutes(ir, opts) + " " + hourSegmentTimesWithSeka(ir, 0, null, opts) + trailingQualifier(ir, opts);
628
- return ir.analyses.clockSecond ? secondsLeadClause(ir, opts) + ", " + phrase2 : phrase2;
707
+ const phrase2 = bareMinutes(schedule, opts) + " " + hourSegmentTimesWithSeka(schedule, 0, null, opts) + trailingQualifier(schedule, opts);
708
+ return schedule.analyses.clockSecond ? secondsLeadClause(schedule, opts) + ", " + phrase2 : phrase2;
629
709
  }
630
710
  if (plan.fold) {
631
- return leadingQualifier(ir, opts) + hourSegmentTimes(ir, plan.minute, ir.analyses.clockSecond, opts);
711
+ return leadingQualifier(schedule, opts) + hourSegmentTimes(
712
+ schedule,
713
+ plan.minute,
714
+ schedule.analyses.clockSecond,
715
+ opts
716
+ );
632
717
  }
633
- const cadence = unevenHourCadence(ir, opts);
634
- const phrase = cadence ? bareMinutes(ir, opts) + ", " + cadence + trailingQualifier(ir, opts) : (
718
+ const cadence = unevenHourCadence(schedule, opts);
719
+ const phrase = cadence ? bareMinutes(schedule, opts) + ", " + cadence + trailingQualifier(schedule, opts) : (
635
720
  // A minute list over purely enumerated hours (step fires, all singles) —
636
721
  // hours-first, drop "joka tunti".
637
- hoursFirstMinutes(hourSegmentTimes(ir, 0, null, opts), ir, opts) + trailingQualifier(ir, opts)
722
+ hoursFirstMinutes(
723
+ hourSegmentTimes(schedule, 0, null, opts),
724
+ schedule,
725
+ opts
726
+ ) + trailingQualifier(schedule, opts)
638
727
  );
639
- return ir.analyses.clockSecond ? secondsLeadClause(ir, opts) + ", " + phrase : phrase;
728
+ return schedule.analyses.clockSecond ? secondsLeadClause(schedule, opts) + ", " + phrase : phrase;
640
729
  }
641
730
  var renderers = {
642
731
  clockTimes: renderClockTimes,
@@ -658,32 +747,19 @@ var renderers = {
658
747
  singleMinute: renderSingleMinute,
659
748
  standaloneSeconds: renderStandaloneSeconds
660
749
  };
661
- function renderStride(stride, opts) {
750
+ function renderStride2(stride, opts) {
662
751
  const { interval, start, last, cycle, unit } = stride;
663
752
  const cadence = genitive(interval, opts) + " " + unit.gen + " v\xE4lein";
664
- const tiles = cycle % interval === 0;
665
- if (start === 0 && tiles) {
666
- return cadence;
667
- }
668
- if (start < interval && tiles) {
669
- return cadence + " " + unit.anchor + " " + unit.ela + " " + start + " alkaen";
670
- }
671
- return cadence + " " + unit.ela + " " + start + " " + unit.ill + " " + last;
753
+ return renderStride({ start, interval, cycle }, {
754
+ bare: () => cadence,
755
+ offset: () => cadence + " " + unit.anchor + " " + unit.ela + " " + start + " alkaen",
756
+ bounded: () => cadence + " " + unit.ela + " " + start + " " + unit.ill + " " + last
757
+ });
672
758
  }
673
759
  function strideFromSegments(segments, unit, opts) {
674
760
  const values = singleValues(segments);
675
761
  const step = values && arithmeticStep(values);
676
- return step ? renderStride({ ...step, cycle: 60, unit }, opts) : null;
677
- }
678
- function singleValues(segments) {
679
- const values = [];
680
- for (const segment of segments) {
681
- if (segment.kind !== "single") {
682
- return null;
683
- }
684
- values.push(+segment.value);
685
- }
686
- return values;
762
+ return step ? renderStride2({ ...step, cycle: 60, unit }, opts) : null;
687
763
  }
688
764
  function stepCycle60(segment, unit, opts) {
689
765
  if (segment.startToken.indexOf("-") !== -1) {
@@ -693,7 +769,7 @@ function stepCycle60(segment, unit, opts) {
693
769
  if (start !== 0 && segment.fires.length <= 3) {
694
770
  return atMarks(joinList(wordList(segment.fires)), unit, true);
695
771
  }
696
- return renderStride({
772
+ return renderStride2({
697
773
  interval: segment.interval,
698
774
  start,
699
775
  last: segment.fires[segment.fires.length - 1],
@@ -719,38 +795,14 @@ function stepHours(segment, opts) {
719
795
  function hourStrideCadence(stride, opts) {
720
796
  const { start, interval, last } = stride;
721
797
  const cadence = genitive(interval, opts) + " tunnin v\xE4lein";
722
- const tiles = 24 % interval === 0;
723
- if (start === 0 && tiles) {
724
- return cadence;
725
- }
726
- if (start < interval && tiles) {
727
- return cadence + " klo " + hourElatives[start] + " alkaen";
728
- }
729
- return cadence + " " + kloRange({ hour: start, minute: 0 }, { hour: last, minute: 0 }, opts);
730
- }
731
- function hourListStride(values) {
732
- if (values.length < 2) {
733
- return null;
734
- }
735
- const interval = values[1] - values[0];
736
- if (interval < 2) {
737
- return null;
738
- }
739
- for (let i = 2; i < values.length; i += 1) {
740
- if (values[i] - values[i - 1] !== interval) {
741
- return null;
742
- }
743
- }
744
- if (values[0] !== 0 && values.length < 5) {
745
- return null;
746
- }
747
- return { interval, last: values[values.length - 1], start: values[0] };
748
- }
749
- function offsetCleanStride(stride) {
750
- return stride.start < stride.interval && 24 % stride.interval === 0;
798
+ return renderStride({ start, interval, cycle: 24 }, {
799
+ bare: () => cadence,
800
+ offset: () => cadence + " klo " + hourElatives[start] + " alkaen",
801
+ bounded: () => cadence + " " + kloRange({ hour: start, minute: 0 }, { hour: last, minute: 0 }, opts)
802
+ });
751
803
  }
752
- function hourStride(ir) {
753
- const segments = ir.analyses.segments.hour;
804
+ function hourStride(schedule) {
805
+ const segments = schedule.analyses.segments.hour;
754
806
  if (!segments) {
755
807
  return null;
756
808
  }
@@ -765,47 +817,47 @@ function hourStride(ir) {
765
817
  const values = singleValues(segments);
766
818
  return values && hourListStride(values);
767
819
  }
768
- function unevenHourCadence(ir, opts) {
769
- const stride = hourStride(ir);
820
+ function unevenHourCadence(schedule, opts) {
821
+ const stride = hourStride(schedule);
770
822
  if (!stride || offsetCleanStride(stride)) {
771
823
  return null;
772
824
  }
773
825
  return hourStrideCadence(stride, opts);
774
826
  }
775
- function subMinuteSecond(ir) {
776
- return ir.pattern.second === "*" || ir.shapes.second === "step";
827
+ function subMinuteSecond(schedule) {
828
+ return schedule.pattern.second === "*" || schedule.shapes.second === "step";
777
829
  }
778
- function hourCadenceLead(ir, minute, opts) {
830
+ function hourCadenceLead(schedule, minute, opts) {
779
831
  if (minute === 0) {
780
- if (subMinuteSecond(ir)) {
781
- return secondsLeadClause(ir, opts) + " minuutin ajan";
832
+ if (subMinuteSecond(schedule)) {
833
+ return secondsLeadClause(schedule, opts) + " minuutin ajan";
782
834
  }
783
- return secondsLeadClause(ir, opts);
835
+ return secondsLeadClause(schedule, opts);
784
836
  }
785
837
  const minutePhrase = atMarks(String(minute), units.minute, false);
786
- if (ir.pattern.second === "0") {
838
+ if (schedule.pattern.second === "0") {
787
839
  return minutePhrase;
788
840
  }
789
- return secondsLeadClause(ir, opts) + ", " + minutePhrase;
841
+ return secondsLeadClause(schedule, opts) + ", " + minutePhrase;
790
842
  }
791
- function hourCadence(ir, minute, opts) {
792
- const stride = hourStride(ir);
843
+ function hourCadence(schedule, minute, opts) {
844
+ const stride = hourStride(schedule);
793
845
  if (!stride) {
794
846
  return null;
795
847
  }
796
848
  const fires = (stride.last - stride.start) / stride.interval + 1;
797
- if (ir.pattern.second === "0" && fires <= maxClockTimes && offsetCleanStride(stride)) {
849
+ if (schedule.pattern.second === "0" && fires <= maxClockTimes && offsetCleanStride(stride)) {
798
850
  return null;
799
851
  }
800
- const segment = ir.analyses.segments.hour[0];
801
- const confined = minute === 0 && subMinuteSecond(ir) && ir.analyses.segments.hour.length === 1 && segment.kind === "step" && cleanHourStride(segment);
852
+ const segment = segmentsOf(schedule, "hour")[0];
853
+ const confined = minute === 0 && subMinuteSecond(schedule) && segmentsOf(schedule, "hour").length === 1 && segment.kind === "step" && cleanHourStride(segment);
802
854
  if (confined) {
803
- return secondsLeadClause(ir, opts) + " minuutin ajan " + everyNthHour(segment, opts) + trailingQualifier(ir, opts);
855
+ return secondsLeadClause(schedule, opts) + " minuutin ajan " + everyNthHour(segment, opts) + trailingQualifier(schedule, opts);
804
856
  }
805
- if (minute === 0 && ir.pattern.second === "0") {
806
- return hourStrideCadence(stride, opts) + trailingQualifier(ir, opts);
857
+ if (minute === 0 && schedule.pattern.second === "0") {
858
+ return hourStrideCadence(stride, opts) + trailingQualifier(schedule, opts);
807
859
  }
808
- return hourCadenceLead(ir, minute, opts) + ", " + hourStrideCadence(stride, opts) + trailingQualifier(ir, opts);
860
+ return hourCadenceLead(schedule, minute, opts) + ", " + hourStrideCadence(stride, opts) + trailingQualifier(schedule, opts);
809
861
  }
810
862
  function cleanHourStride(segment) {
811
863
  if (segment.startToken.indexOf("-") !== -1) {
@@ -814,22 +866,22 @@ function cleanHourStride(segment) {
814
866
  const start = segment.startToken === "*" ? 0 : +segment.startToken;
815
867
  return 24 % segment.interval === 0 && start < segment.interval;
816
868
  }
817
- function hasHourWindow(ir) {
818
- const segments = ir.analyses.segments.hour;
869
+ function hasHourWindow(schedule) {
870
+ const segments = schedule.analyses.segments.hour;
819
871
  return !!segments && segments.some(function range(segment) {
820
872
  return segment.kind === "range";
821
873
  });
822
874
  }
823
- function hourRangeWindowTail(ir, opts) {
824
- return ir.analyses.segments.hour.length === 1 ? hourSegmentTimes(ir, 0, null, opts) : hourSegmentTimesWithSeka(ir, 0, null, opts);
875
+ function hourRangeWindowTail(schedule, opts) {
876
+ return segmentsOf(schedule, "hour").length === 1 ? hourSegmentTimes(schedule, 0, null, opts) : hourSegmentTimesWithSeka(schedule, 0, null, opts);
825
877
  }
826
- function hourRangeCadence(ir, minute, opts) {
827
- if (minute !== 0 || !hasHourWindow(ir) || ir.pattern.second === "0") {
878
+ function hourRangeCadence(schedule, minute, opts) {
879
+ if (minute !== 0 || !hasHourWindow(schedule) || schedule.pattern.second === "0") {
828
880
  return null;
829
881
  }
830
- const tail = hourRangeWindowTail(ir, opts);
831
- const joiner = subMinuteSecond(ir) ? " " : ", ";
832
- return hourCadenceLead(ir, minute, opts) + joiner + tail + trailingQualifier(ir, opts);
882
+ const tail = hourRangeWindowTail(schedule, opts);
883
+ const joiner = subMinuteSecond(schedule) ? " " : ", ";
884
+ return hourCadenceLead(schedule, minute, opts) + joiner + tail + trailingQualifier(schedule, opts);
833
885
  }
834
886
  function kloList(hours, opts) {
835
887
  if (hours.length === 1) {
@@ -839,17 +891,17 @@ function kloList(hours, opts) {
839
891
  return timeDigits(hour, 0, null, opts);
840
892
  }));
841
893
  }
842
- function kloFromTimes(ir, times, opts) {
894
+ function kloFromTimes(schedule, times, opts) {
843
895
  if (times.kind === "fires") {
844
896
  return kloList(times.fires, opts);
845
897
  }
846
- return hourSegmentTimes(ir, 0, null, opts);
898
+ return hourSegmentTimes(schedule, 0, null, opts);
847
899
  }
848
- function hourWindowsFromTimes(ir, times, opts) {
900
+ function hourWindowsFromTimes(schedule, times, opts) {
849
901
  if (times.kind === "fires") {
850
902
  return kloList(times.fires, opts);
851
903
  }
852
- const segments = ir.analyses.segments.hour;
904
+ const segments = segmentsOf(schedule, "hour");
853
905
  if (!segments.some(function ranged(segment) {
854
906
  return segment.kind === "range";
855
907
  })) {
@@ -887,9 +939,9 @@ function hourSegmentFires(segments) {
887
939
  function hourWindowDigits(hour, opts) {
888
940
  return rangeDigits({ hour, minute: 0 }, { hour, minute: 59 }, opts);
889
941
  }
890
- function hourSegmentTimes(ir, minute, second, opts) {
942
+ function hourSegmentTimes(schedule, minute, second, opts) {
891
943
  const pieces = [];
892
- ir.analyses.segments.hour.forEach(function clock(segment) {
944
+ segmentsOf(schedule, "hour").forEach(function clock(segment) {
893
945
  if (segment.kind === "step") {
894
946
  pieces.push(...segment.fires.map(function each(hour) {
895
947
  return timeDigits(hour, minute, second, opts);
@@ -939,53 +991,54 @@ function timeDigits(hour, minute, second, opts) {
939
991
  { lean: true, sep: opts.style.sep }
940
992
  );
941
993
  }
942
- function leadingQualifier(ir, opts) {
943
- const pattern = ir.pattern;
944
- if (restrictedMonthUnion(ir)) {
994
+ function leadingQualifier(schedule, opts) {
995
+ const pattern = schedule.pattern;
996
+ if (restrictedMonthUnion(schedule)) {
945
997
  return "";
946
998
  }
947
999
  if (pattern.date !== "*" && pattern.weekday !== "*") {
948
- return dateOrWeekday(ir, opts) + " ";
1000
+ return dateOrWeekday(schedule, opts) + " ";
949
1001
  }
950
1002
  if (pattern.date !== "*") {
951
- return datePhrase(ir, opts) + " ";
1003
+ return datePhrase(schedule, opts) + " ";
952
1004
  }
953
1005
  if (pattern.weekday !== "*") {
954
- return weekdayQualifier(ir) + monthScope(ir) + " ";
1006
+ return weekdayQualifier(schedule) + monthScope(schedule) + " ";
955
1007
  }
956
1008
  if (pattern.month !== "*") {
957
- return "joka p\xE4iv\xE4 " + monthPhrase(ir) + " ";
1009
+ return "joka p\xE4iv\xE4 " + monthPhrase(schedule) + " ";
958
1010
  }
959
1011
  return "joka p\xE4iv\xE4 ";
960
1012
  }
961
- function trailingQualifier(ir, opts) {
962
- const pattern = ir.pattern;
963
- if (restrictedMonthUnion(ir)) {
1013
+ function trailingQualifier(schedule, opts) {
1014
+ const pattern = schedule.pattern;
1015
+ if (restrictedMonthUnion(schedule)) {
964
1016
  return "";
965
1017
  }
966
1018
  if (pattern.date !== "*" && pattern.weekday !== "*") {
967
- return " " + dateOrWeekday(ir, opts);
1019
+ return " " + dateOrWeekday(schedule, opts);
968
1020
  }
969
1021
  if (pattern.date !== "*") {
970
- return " " + datePhrase(ir, opts);
1022
+ return " " + datePhrase(schedule, opts);
971
1023
  }
972
1024
  if (pattern.weekday !== "*") {
973
- return " " + weekdayQualifier(ir) + monthScope(ir);
1025
+ return " " + weekdayQualifier(schedule) + monthScope(schedule);
974
1026
  }
975
1027
  if (pattern.month !== "*") {
976
- return " " + monthPhrase(ir);
1028
+ return " " + monthPhrase(schedule);
977
1029
  }
978
1030
  return "";
979
1031
  }
980
- function dateOrWeekday(ir, opts) {
981
- return datePhrase(ir, opts) + " tai " + weekdayQualifier(ir) + monthScope(ir);
1032
+ function dateOrWeekday(schedule, opts) {
1033
+ const dateArm = oddDayUnion(schedule.pattern.date) || datePhrase(schedule, opts);
1034
+ return dateArm + " tai " + unionWeekdayArm(schedule) + monthScope(schedule);
982
1035
  }
983
- function weekdayQualifier(ir) {
984
- const quartz = quartzWeekdayPhrase(ir.pattern.weekday);
1036
+ function weekdayQualifier(schedule) {
1037
+ const quartz = quartzWeekdayPhrase(schedule.pattern.weekday);
985
1038
  if (quartz) {
986
1039
  return quartz;
987
1040
  }
988
- const segments = orderWeekdaysForDisplay(ir.analyses.segments.weekday);
1041
+ const segments = orderWeekdaysForDisplay(segmentsOf(schedule, "weekday"));
989
1042
  return joinList(segments.map(function piece(segment) {
990
1043
  if (segment.kind === "range") {
991
1044
  return weekdays[weekdayNumber(segment.bounds[0])].ela + " " + weekdays[weekdayNumber(segment.bounds[1])].ill;
@@ -993,8 +1046,8 @@ function weekdayQualifier(ir) {
993
1046
  return weekdays[weekdayNumber(segment.value)].isin;
994
1047
  }));
995
1048
  }
996
- function monthPhrase(ir) {
997
- const segments = flattenSteps(ir.analyses.segments.month);
1049
+ function monthPhrase(schedule) {
1050
+ const segments = flattenSteps(segmentsOf(schedule, "month"));
998
1051
  return joinList(segments.map(function piece(segment) {
999
1052
  if (segment.kind === "range") {
1000
1053
  return monthStems[monthNumber(segment.bounds[0])] + "kuusta " + monthStems[monthNumber(segment.bounds[1])] + "kuuhun";
@@ -1002,11 +1055,11 @@ function monthPhrase(ir) {
1002
1055
  return monthStems[monthNumber(segment.value)] + "kuussa";
1003
1056
  }));
1004
1057
  }
1005
- function monthScope(ir) {
1006
- if (ir.pattern.month === "*") {
1058
+ function monthScope(schedule) {
1059
+ if (schedule.pattern.month === "*") {
1007
1060
  return "";
1008
1061
  }
1009
- return " " + monthPhrase(ir);
1062
+ return " " + monthPhrase(schedule);
1010
1063
  }
1011
1064
  function flattenSteps(segments) {
1012
1065
  return segments.flatMap(function flat(segment) {
@@ -1015,16 +1068,16 @@ function flattenSteps(segments) {
1015
1068
  }) : [segment];
1016
1069
  });
1017
1070
  }
1018
- function datePhrase(ir, opts) {
1019
- const pattern = ir.pattern;
1071
+ function datePhrase(schedule, opts) {
1072
+ const pattern = schedule.pattern;
1020
1073
  const quartz = quartzDatePhrase(pattern.date);
1021
1074
  if (quartz) {
1022
- return quartz + monthScope(ir);
1075
+ return quartz + monthScope(schedule);
1023
1076
  }
1024
1077
  if (isOpenStep(pattern.date)) {
1025
- return stepDates(pattern.date, opts) + monthScope(ir);
1078
+ return stepDates(pattern.date, opts) + monthScope(schedule);
1026
1079
  }
1027
- return monthAnchor(ir, opts) + " " + dateWords(ir) + " p\xE4iv\xE4n\xE4" + foldedYear(ir) + monthStepStart(pattern.month) + rangedMonthScope(ir);
1080
+ return monthAnchor(schedule, opts) + " " + dateWords(schedule) + " p\xE4iv\xE4n\xE4" + foldedYear(schedule) + monthStepStart(pattern.month) + rangedMonthScope(schedule);
1028
1081
  }
1029
1082
  function monthStepStart(monthField) {
1030
1083
  if (!isOpenStep(monthField)) {
@@ -1036,30 +1089,30 @@ function monthStepStart(monthField) {
1036
1089
  }
1037
1090
  return " " + monthStems[monthNumber(start)] + "kuusta alkaen";
1038
1091
  }
1039
- function monthAnchor(ir, opts) {
1040
- const monthField = ir.pattern.month;
1041
- if (monthField === "*" || monthRanged(ir)) {
1092
+ function monthAnchor(schedule, opts) {
1093
+ const monthField = schedule.pattern.month;
1094
+ if (monthField === "*" || monthRanged(schedule)) {
1042
1095
  return "kuukauden";
1043
1096
  }
1044
1097
  if (isOpenStep(monthField)) {
1045
1098
  return stepMonths(monthField, opts);
1046
1099
  }
1047
- const segments = flattenSteps(ir.analyses.segments.month);
1100
+ const segments = flattenSteps(segmentsOf(schedule, "month"));
1048
1101
  return joinList(segments.map(function genitiveOf(segment) {
1049
1102
  const single = segment;
1050
1103
  return monthStems[monthNumber(single.value)] + "kuun";
1051
1104
  }));
1052
1105
  }
1053
- function rangedMonthScope(ir) {
1054
- return monthRanged(ir) ? " " + monthPhrase(ir) : "";
1106
+ function rangedMonthScope(schedule) {
1107
+ return monthRanged(schedule) ? " " + monthPhrase(schedule) : "";
1055
1108
  }
1056
- function monthRanged(ir) {
1057
- return ir.pattern.month !== "*" && ir.analyses.segments.month.some(function range(segment) {
1109
+ function monthRanged(schedule) {
1110
+ return schedule.pattern.month !== "*" && segmentsOf(schedule, "month").some(function range(segment) {
1058
1111
  return segment.kind === "range";
1059
1112
  });
1060
1113
  }
1061
- function dateWords(ir) {
1062
- return joinList(ir.analyses.segments.date.flatMap(
1114
+ function dateWords(schedule) {
1115
+ return joinList(segmentsOf(schedule, "date").flatMap(
1063
1116
  function word(segment) {
1064
1117
  if (segment.kind === "range") {
1065
1118
  return [segment.bounds[0] + ".\u2013" + segment.bounds[1] + "."];
@@ -1115,15 +1168,15 @@ function weekdayNumber(token) {
1115
1168
  function monthNumber(token) {
1116
1169
  return +token;
1117
1170
  }
1118
- function applyYear(description, ir, opts) {
1119
- const yearField = ir.pattern.year;
1171
+ function applyYear(description, schedule, opts) {
1172
+ const yearField = schedule.pattern.year;
1120
1173
  if (yearField === "*") {
1121
1174
  return description;
1122
1175
  }
1123
1176
  if (yearField.indexOf("/") !== -1) {
1124
1177
  return description + " " + stepYears(yearField, opts);
1125
1178
  }
1126
- if (foldedYear(ir) && ir.pattern.date !== "*") {
1179
+ if (foldedYear(schedule) && schedule.pattern.date !== "*") {
1127
1180
  return description;
1128
1181
  }
1129
1182
  if (yearField.indexOf(",") !== -1) {
@@ -1143,8 +1196,8 @@ function stepYears(yearField, opts) {
1143
1196
  }
1144
1197
  return phrase;
1145
1198
  }
1146
- function foldedYear(ir) {
1147
- const yearField = ir.pattern.year;
1199
+ function foldedYear(schedule) {
1200
+ const yearField = schedule.pattern.year;
1148
1201
  if (yearField === "*" || yearField.indexOf("/") !== -1 || yearField.indexOf("-") !== -1 || yearField.indexOf(",") !== -1) {
1149
1202
  return "";
1150
1203
  }
@@ -1161,9 +1214,6 @@ function segmentWords(segments) {
1161
1214
  return [segment.value];
1162
1215
  });
1163
1216
  }
1164
- function isOpenStep(field) {
1165
- return field.indexOf("/") !== -1 && field.indexOf("-") === -1 && field.indexOf(",") === -1;
1166
- }
1167
1217
  function wordList(fires) {
1168
1218
  return fires.map(function digit(value) {
1169
1219
  return "" + value;