cronli5 0.2.1 → 0.3.4

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 (55) hide show
  1. package/CHANGELOG.md +109 -0
  2. package/README.md +4 -4
  3. package/cronli5.min.js +2 -2
  4. package/dist/cronli5.cjs +471 -383
  5. package/dist/cronli5.js +471 -383
  6. package/dist/lang/de.cjs +286 -215
  7. package/dist/lang/de.js +286 -215
  8. package/dist/lang/en.cjs +413 -327
  9. package/dist/lang/en.js +413 -327
  10. package/dist/lang/es.cjs +303 -265
  11. package/dist/lang/es.js +303 -265
  12. package/dist/lang/fi.cjs +311 -266
  13. package/dist/lang/fi.js +311 -266
  14. package/dist/lang/zh.cjs +320 -240
  15. package/dist/lang/zh.js +320 -240
  16. package/package.json +6 -6
  17. package/src/core/analyze.ts +12 -12
  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} +17 -18
  23. package/src/core/specs.ts +1 -1
  24. package/src/core/util.ts +3 -165
  25. package/src/core/validate.ts +1 -1
  26. package/src/core/weekday.ts +54 -0
  27. package/src/cronli5.ts +5 -5
  28. package/src/lang/de/index.ts +329 -219
  29. package/src/lang/en/dialects.ts +1 -1
  30. package/src/lang/en/index.ts +521 -372
  31. package/src/lang/es/index.ts +338 -286
  32. package/src/lang/es/notes.md +1 -1
  33. package/src/lang/fi/dialects.ts +1 -1
  34. package/src/lang/fi/index.ts +365 -299
  35. package/src/lang/fi/notes.md +23 -8
  36. package/src/lang/fi/status.json +1 -1
  37. package/src/lang/zh/index.ts +386 -245
  38. package/src/types.ts +6 -6
  39. package/types/core/analyze.d.ts +3 -3
  40. package/types/core/cadence.d.ts +33 -0
  41. package/types/core/index.d.ts +3 -1
  42. package/types/core/normalize.d.ts +1 -1
  43. package/types/core/parse.d.ts +1 -1
  44. package/types/core/{ir.d.ts → schedule.d.ts} +11 -16
  45. package/types/core/specs.d.ts +1 -1
  46. package/types/core/util.d.ts +1 -30
  47. package/types/core/weekday.d.ts +10 -0
  48. package/types/lang/de/index.d.ts +1 -1
  49. package/types/lang/en/dialects.d.ts +1 -1
  50. package/types/lang/en/index.d.ts +1 -1
  51. package/types/lang/es/index.d.ts +1 -1
  52. package/types/lang/fi/dialects.d.ts +1 -1
  53. package/types/lang/fi/index.d.ts +1 -1
  54. package/types/lang/zh/index.d.ts +1 -1
  55. package/types/types.d.ts +5 -5
package/dist/lang/de.cjs CHANGED
@@ -42,11 +42,7 @@ var weekdayNumbers = {
42
42
  };
43
43
  var maxClockTimes = 6;
44
44
 
45
- // src/core/util.ts
46
- function isNonNegativeInteger(value) {
47
- const digits = /^\d+$/;
48
- return digits.test(value);
49
- }
45
+ // src/core/cadence.ts
50
46
  function arithmeticStep(values) {
51
47
  if (values.length < 5) {
52
48
  return null;
@@ -62,34 +58,11 @@ function arithmeticStep(values) {
62
58
  }
63
59
  return { start: values[0], interval, last: values[values.length - 1] };
64
60
  }
65
- function weekdayDisplayKey(value) {
66
- return value === 0 ? 7 : value;
67
- }
68
- function orderWeekdaysForDisplay(segments) {
69
- const flattened = segments.flatMap(function flat(segment) {
70
- return segment.kind === "step" ? segment.fires.map(function single(value) {
71
- return { kind: "single", value: "" + value };
72
- }) : [segment];
73
- });
74
- function key(segment) {
75
- return segment.kind === "range" ? weekdayDisplayKey(+segment.bounds[0]) : weekdayDisplayKey(+segment.value);
76
- }
77
- return flattened.map(function index(segment, position) {
78
- return [segment, position];
79
- }).sort(function byDisplayKey(a, b) {
80
- return key(a[0]) - key(b[0]) || a[1] - b[1];
81
- }).map(function unwrap(pair) {
82
- return pair[0];
83
- });
84
- }
85
- function toFieldNumber(token, numberMap) {
86
- return isNonNegativeInteger(token) ? +token : numberMap[token.toUpperCase()];
61
+ function segmentsOf(schedule, field) {
62
+ return schedule.analyses.segments[field] ?? [];
87
63
  }
88
- function segmentsOf(ir, field) {
89
- return ir.analyses.segments[field] ?? [];
90
- }
91
- function stepSegment(ir, field) {
92
- return segmentsOf(ir, field)[0];
64
+ function stepSegment(schedule, field) {
65
+ return segmentsOf(schedule, field)[0];
93
66
  }
94
67
  function singleValues(segments) {
95
68
  const values = [];
@@ -104,6 +77,17 @@ function singleValues(segments) {
104
77
  function offsetCleanStride(stride) {
105
78
  return stride.start < stride.interval && 24 % stride.interval === 0;
106
79
  }
80
+ function renderStride(spec, parts) {
81
+ const { start, interval, cycle } = spec;
82
+ const tiles = cycle % interval === 0;
83
+ if (start === 0 && tiles) {
84
+ return parts.bare();
85
+ }
86
+ if (start < interval && tiles) {
87
+ return parts.offset();
88
+ }
89
+ return parts.bounded();
90
+ }
107
91
  function hourListStride(values) {
108
92
  if (values.length < 2) {
109
93
  return null;
@@ -123,6 +107,42 @@ function hourListStride(values) {
123
107
  return { interval, last: values[values.length - 1], start: values[0] };
124
108
  }
125
109
 
110
+ // src/core/weekday.ts
111
+ function weekdayDisplayKey(value) {
112
+ return value === 0 ? 7 : value;
113
+ }
114
+ function orderWeekdaysForDisplay(segments) {
115
+ const flattened = segments.flatMap(function flat(segment) {
116
+ return segment.kind === "step" ? segment.fires.map(function single(value) {
117
+ return { kind: "single", value: "" + value };
118
+ }) : [segment];
119
+ });
120
+ function key(segment) {
121
+ return segment.kind === "range" ? weekdayDisplayKey(+segment.bounds[0]) : weekdayDisplayKey(+segment.value);
122
+ }
123
+ return flattened.map(function index(segment, position) {
124
+ return [segment, position];
125
+ }).sort(function byDisplayKey(a, b) {
126
+ return key(a[0]) - key(b[0]) || a[1] - b[1];
127
+ }).map(function unwrap(pair) {
128
+ return pair[0];
129
+ });
130
+ }
131
+
132
+ // src/core/util.ts
133
+ function isNonNegativeInteger(value) {
134
+ const digits = /^\d+$/;
135
+ return digits.test(value);
136
+ }
137
+ function toFieldNumber(token, numberMap) {
138
+ return isNonNegativeInteger(token) ? +token : numberMap[token.toUpperCase()];
139
+ }
140
+
141
+ // src/core/shapes.ts
142
+ function isOpenStep(field) {
143
+ return field.indexOf("/") !== -1 && field.indexOf("-") === -1 && field.indexOf(",") === -1;
144
+ }
145
+
126
146
  // src/lang/de/dialects.ts
127
147
  var months = [
128
148
  null,
@@ -185,18 +205,15 @@ function withAnchor(clause, anchor) {
185
205
  function cleanStep(segment, cycle) {
186
206
  return (segment.startToken === "*" || +segment.startToken === 0) && cycle % segment.interval === 0;
187
207
  }
188
- function renderStride(stride) {
208
+ function renderStride2(stride) {
189
209
  const { interval, start, last, cycle, unit, anchor } = stride;
190
210
  const cadence = everyN(interval, unit);
191
- const tiles = cycle % interval === 0;
192
- if (start === 0 && tiles) {
193
- return cadence;
194
- }
195
211
  const tail = anchor ? " " + anchor : "";
196
- if (start < interval && tiles) {
197
- return cadence + " ab " + unit.singular + " " + start + tail;
198
- }
199
- return cadence + " von " + unit.singular + " " + start + " bis " + last + tail;
212
+ return renderStride({ start, interval, cycle }, {
213
+ bare: () => cadence,
214
+ offset: () => cadence + " ab " + unit.singular + " " + start + tail,
215
+ bounded: () => cadence + " von " + unit.singular + " " + start + " bis " + last + tail
216
+ });
200
217
  }
201
218
  function stepClause(segment, unit, anchor) {
202
219
  const start = segment.startToken === "*" ? 0 : +segment.startToken;
@@ -207,7 +224,7 @@ function stepClause(segment, unit, anchor) {
207
224
  anchor
208
225
  );
209
226
  }
210
- return renderStride({
227
+ return renderStride2({
211
228
  interval: segment.interval,
212
229
  start,
213
230
  last: segment.fires[segment.fires.length - 1],
@@ -219,7 +236,7 @@ function stepClause(segment, unit, anchor) {
219
236
  function strideFromSegments(segments, unit, anchor) {
220
237
  const values = singleValues(segments);
221
238
  const step = values && arithmeticStep(values);
222
- return step ? renderStride({ ...step, cycle: 60, unit, anchor }) : null;
239
+ return step ? renderStride2({ ...step, cycle: 60, unit, anchor }) : null;
223
240
  }
224
241
  var weekdayNames = [
225
242
  "sonntags",
@@ -252,8 +269,8 @@ function weekdayName(token) {
252
269
  function weekdayRange(bounds) {
253
270
  return weekdayName(bounds[0]) + " bis " + weekdayName(bounds[1]);
254
271
  }
255
- function weekdayQualifier(ir) {
256
- const segments = orderWeekdaysForDisplay(segmentsOf(ir, "weekday"));
272
+ function weekdayQualifier(schedule) {
273
+ const segments = orderWeekdaysForDisplay(segmentsOf(schedule, "weekday"));
257
274
  if (segments.length === 1 && segments[0].kind === "range") {
258
275
  return weekdayRange(segments[0].bounds);
259
276
  }
@@ -316,26 +333,39 @@ function quartzDate(field) {
316
333
  }
317
334
  return null;
318
335
  }
336
+ function oddEvenDay(dateField) {
337
+ if (!isOpenStep(dateField)) {
338
+ return null;
339
+ }
340
+ const [start, step] = dateField.split("/");
341
+ if (+step !== 2) {
342
+ return null;
343
+ }
344
+ if (start === "*" || start === "1") {
345
+ return "an jedem ungeraden Tag des Monats";
346
+ }
347
+ return start === "2" ? "an jedem geraden Tag des Monats" : null;
348
+ }
319
349
  function monthName(token, months2) {
320
350
  return months2[+token];
321
351
  }
322
352
  function monthRange(bounds, months2) {
323
353
  return "von " + monthName(bounds[0], months2) + " bis " + monthName(bounds[1], months2);
324
354
  }
325
- function monthNamesList(ir, months2) {
326
- return joinList(flattenSteps(segmentsOf(ir, "month")).map(function name(segment) {
355
+ function monthNamesList(schedule, months2) {
356
+ return joinList(flattenSteps(segmentsOf(schedule, "month")).map(function name(segment) {
327
357
  return segment.kind === "range" ? monthRange(segment.bounds, months2) : monthName(segment.value, months2);
328
358
  }));
329
359
  }
330
- function monthClause(ir, months2) {
331
- const segments = flattenSteps(segmentsOf(ir, "month"));
360
+ function monthClause(schedule, months2) {
361
+ const segments = flattenSteps(segmentsOf(schedule, "month"));
332
362
  if (segments.length === 1 && segments[0].kind === "range") {
333
363
  return monthRange(segments[0].bounds, months2);
334
364
  }
335
- return "im " + monthNamesList(ir, months2);
365
+ return "im " + monthNamesList(schedule, months2);
336
366
  }
337
- function monthScope(ir, months2) {
338
- return ir.pattern.month === "*" ? "" : " " + monthClause(ir, months2);
367
+ function monthScope(schedule, months2) {
368
+ return schedule.pattern.month === "*" ? "" : " " + monthClause(schedule, months2);
339
369
  }
340
370
  function ordinalDay(value) {
341
371
  return value + ".";
@@ -343,8 +373,8 @@ function ordinalDay(value) {
343
373
  function dateRange(bounds) {
344
374
  return "vom " + ordinalDay(bounds[0]) + " bis zum " + ordinalDay(bounds[1]);
345
375
  }
346
- function dateClauseBare(ir) {
347
- const segments = flattenSteps(segmentsOf(ir, "date"));
376
+ function dateClauseBare(schedule) {
377
+ const segments = flattenSteps(segmentsOf(schedule, "date"));
348
378
  if (segments.length === 1 && segments[0].kind === "range") {
349
379
  return dateRange(segments[0].bounds);
350
380
  }
@@ -357,13 +387,13 @@ function dateClauseBare(ir) {
357
387
  return segment.kind === "range" ? dateRange(segment.bounds) : "am " + ordinalDay(segment.value);
358
388
  }));
359
389
  }
360
- function datePhrase(ir, months2) {
361
- const clause = dateClauseBare(ir);
362
- if (ir.pattern.month === "*") {
390
+ function datePhrase(schedule, months2) {
391
+ const clause = dateClauseBare(schedule);
392
+ if (schedule.pattern.month === "*") {
363
393
  return clause;
364
394
  }
365
- const monthRanged = flattenSteps(segmentsOf(ir, "month")).some((segment) => segment.kind === "range");
366
- return monthRanged ? clause + ", " + monthClause(ir, months2) : clause + " " + monthNamesList(ir, months2);
395
+ const monthRanged = flattenSteps(segmentsOf(schedule, "month")).some((segment) => segment.kind === "range");
396
+ return monthRanged ? clause + ", " + monthClause(schedule, months2) : clause + " " + monthNamesList(schedule, months2);
367
397
  }
368
398
  function bareTime(time, sep) {
369
399
  if (time.second) {
@@ -382,32 +412,35 @@ function timesPhrase(times, sep) {
382
412
  function hourWindow(from, to, last, sep) {
383
413
  return "von " + bareTime({ hour: from, minute: 0 }, sep) + " bis " + bareTime({ hour: to, minute: last }, sep) + " Uhr";
384
414
  }
385
- function fieldValues(ir, field) {
386
- return flattenSteps(segmentsOf(ir, field)).map(function value(segment) {
415
+ function fieldValues(schedule, field) {
416
+ return flattenSteps(segmentsOf(schedule, field)).map(function value(segment) {
387
417
  return segment.kind === "range" ? segment.bounds[0] + " bis " + segment.bounds[1] : String(segment.value);
388
418
  });
389
419
  }
390
- function countedPhrase(ir, field, singular, plural) {
391
- if (ir.shapes[field] === "single") {
392
- return "in " + singular + " " + ir.pattern[field];
420
+ function countedPhrase(schedule, field, singular, plural) {
421
+ if (schedule.shapes[field] === "single") {
422
+ return "in " + singular + " " + schedule.pattern[field];
393
423
  }
394
- return "in den " + plural + " " + joinList(fieldValues(ir, field));
424
+ return "in den " + plural + " " + joinList(fieldValues(schedule, field));
395
425
  }
396
- function minuteAnchor(ir) {
397
- return ir.pattern.minute === "*" ? "jeder Minute" : "";
426
+ function minuteAnchor(schedule) {
427
+ return schedule.pattern.minute === "*" ? "jeder Minute" : "";
398
428
  }
399
- function secondsLead(ir) {
400
- return secondsClause(ir, minuteAnchor(ir));
429
+ function secondsLead(schedule) {
430
+ return secondsClause(schedule, minuteAnchor(schedule));
401
431
  }
402
- function secondsClause(ir, anchor) {
403
- if (ir.pattern.second === "*") {
432
+ function secondsClause(schedule, anchor) {
433
+ if (schedule.pattern.second === "*") {
404
434
  return "jede Sekunde";
405
435
  }
406
- const segments = ir.analyses.segments.second;
407
- if (ir.shapes.second === "step") {
408
- return stepClause(stepSegment(ir, "second"), UNITS.second, anchor);
436
+ const segments = schedule.analyses.segments.second;
437
+ if (schedule.shapes.second === "step") {
438
+ return stepClause(stepSegment(schedule, "second"), UNITS.second, anchor);
409
439
  }
410
- return strideFromSegments(segments, UNITS.second, anchor) ?? withAnchor(countedPhrase(ir, "second", "Sekunde", "Sekunden"), anchor);
440
+ return strideFromSegments(segments, UNITS.second, anchor) ?? withAnchor(
441
+ countedPhrase(schedule, "second", "Sekunde", "Sekunden"),
442
+ anchor
443
+ );
411
444
  }
412
445
  function spanTime(hour, minute, sep) {
413
446
  return hour + sep + pad(minute);
@@ -415,8 +448,8 @@ function spanTime(hour, minute, sep) {
415
448
  function atHours(hours) {
416
449
  return "um " + joinList(hours.map(String)) + " Uhr";
417
450
  }
418
- function hourFires(ir) {
419
- return flattenSteps(segmentsOf(ir, "hour")).map(function fire(segment) {
451
+ function hourFires(schedule) {
452
+ return flattenSteps(segmentsOf(schedule, "hour")).map(function fire(segment) {
420
453
  return segment.kind === "range" ? +segment.bounds[0] : +segment.value;
421
454
  });
422
455
  }
@@ -426,8 +459,8 @@ function partTime(hour, minute, second, sep) {
426
459
  }
427
460
  return minute === 0 ? String(hour) : hour + sep + pad(minute);
428
461
  }
429
- function hourSegmentParts(ir, minute, second, sep) {
430
- return segmentsOf(ir, "hour").map(function part(segment) {
462
+ function hourSegmentParts(schedule, minute, second, sep) {
463
+ return segmentsOf(schedule, "hour").map(function part(segment) {
431
464
  if (segment.kind === "range") {
432
465
  return "von " + partTime(+segment.bounds[0], minute, second, sep) + " bis " + partTime(+segment.bounds[1], minute, second, sep) + " Uhr";
433
466
  }
@@ -439,13 +472,13 @@ function hourSegmentParts(ir, minute, second, sep) {
439
472
  return "um " + partTime(+segment.value, minute, second, sep) + " Uhr";
440
473
  });
441
474
  }
442
- function duringWindows(ir, times, sep) {
475
+ function duringWindows(schedule, times, sep) {
443
476
  if (times.kind === "fires") {
444
477
  return times.fires.map(function each(hour) {
445
478
  return hourWindow(hour, hour, 59, sep);
446
479
  });
447
480
  }
448
- return segmentsOf(ir, "hour").flatMap(function part(segment) {
481
+ return segmentsOf(schedule, "hour").flatMap(function part(segment) {
449
482
  if (segment.kind === "range") {
450
483
  return [hourWindow(+segment.bounds[0], +segment.bounds[1], 59, sep)];
451
484
  }
@@ -457,8 +490,8 @@ function duringWindows(ir, times, sep) {
457
490
  return [hourWindow(+segment.value, +segment.value, 59, sep)];
458
491
  });
459
492
  }
460
- function duringHours(ir, times, sep) {
461
- const windows = duringWindows(ir, times, sep);
493
+ function duringHours(schedule, times, sep) {
494
+ const windows = duringWindows(schedule, times, sep);
462
495
  if (windows.length <= 3 || times.kind !== "fires") {
463
496
  return joinList(windows);
464
497
  }
@@ -473,24 +506,24 @@ function renderEveryMinute() {
473
506
  function renderEveryHour() {
474
507
  return everyUnit(UNITS.hour);
475
508
  }
476
- function renderSeconds(ir) {
477
- return secondsLead(ir);
509
+ function renderSeconds(schedule) {
510
+ return secondsLead(schedule);
478
511
  }
479
- function minutePastClause(ir) {
512
+ function minutePastClause(schedule) {
480
513
  return strideFromSegments(
481
- segmentsOf(ir, "minute"),
514
+ segmentsOf(schedule, "minute"),
482
515
  UNITS.minute,
483
516
  "jeder Stunde"
484
- ) ?? countedPhrase(ir, "minute", "Minute", "Minuten") + " jeder Stunde";
517
+ ) ?? countedPhrase(schedule, "minute", "Minute", "Minuten") + " jeder Stunde";
485
518
  }
486
- function renderMinutePast(ir) {
487
- return minutePastClause(ir);
519
+ function renderMinutePast(schedule) {
520
+ return minutePastClause(schedule);
488
521
  }
489
- function renderSecondsWithinMinute(ir, plan) {
522
+ function renderSecondsWithinMinute(schedule, plan) {
490
523
  if (plan.singleSecond) {
491
- return "in Minute " + ir.pattern.minute + " und Sekunde " + ir.pattern.second + " jeder Stunde";
524
+ return "in Minute " + schedule.pattern.minute + " und Sekunde " + schedule.pattern.second + " jeder Stunde";
492
525
  }
493
- return secondsLead(ir) + ", in Minute " + ir.pattern.minute + " jeder Stunde";
526
+ return secondsLead(schedule) + ", in Minute " + schedule.pattern.minute + " jeder Stunde";
494
527
  }
495
528
  function wholeHour(hour) {
496
529
  if (hour === 0) {
@@ -501,39 +534,39 @@ function wholeHour(hour) {
501
534
  }
502
535
  return "der " + hour + "-Uhr-Stunde";
503
536
  }
504
- function renderMinuteSpanInHour(ir, plan, opts) {
505
- if (ir.pattern.minute === "*") {
537
+ function renderMinuteSpanInHour(schedule, plan, opts) {
538
+ if (schedule.pattern.minute === "*") {
506
539
  return "jede Minute " + wholeHour(plan.hour);
507
540
  }
508
541
  const sep = opts.style.sep;
509
542
  return "jede Minute von " + spanTime(plan.hour, plan.span[0], sep) + " bis " + spanTime(plan.hour, plan.span[1], sep) + " Uhr";
510
543
  }
511
- function isEveryOtherMinuteSeconds(ir, plan) {
512
- if (plan.rest.kind !== "minuteFrequency" || ir.shapes.second !== "wildcard" || ir.shapes.hour !== "wildcard") {
544
+ function isEveryOtherMinuteSeconds(schedule, plan) {
545
+ if (plan.rest.kind !== "minuteFrequency" || schedule.shapes.second !== "wildcard" || schedule.shapes.hour !== "wildcard") {
513
546
  return false;
514
547
  }
515
- const minuteStep = stepSegment(ir, "minute");
548
+ const minuteStep = stepSegment(schedule, "minute");
516
549
  return minuteStep.startToken === "*" && minuteStep.interval === 2;
517
550
  }
518
- function renderComposeSeconds(ir, plan, opts) {
519
- if ((plan.rest.kind === "clockTimes" || plan.rest.kind === "compactClockTimes") && ir.shapes.minute === "single") {
520
- const minute = +ir.pattern.minute;
521
- const cadence = hourCadence(ir, minute) ?? hourRangeCadence(ir, minute);
551
+ function renderComposeSeconds(schedule, plan, opts) {
552
+ if ((plan.rest.kind === "clockTimes" || plan.rest.kind === "compactClockTimes") && schedule.shapes.minute === "single") {
553
+ const minute = +schedule.pattern.minute;
554
+ const cadence = hourCadence(schedule, minute) ?? hourRangeCadence(schedule, minute);
522
555
  if (cadence !== null) {
523
556
  return cadence;
524
557
  }
525
558
  }
526
- if (composeMinuteZero(ir, plan)) {
527
- return secondsLead(ir) + " " + clockMinuteGenitive(plan.rest.times, opts.style.sep);
559
+ if (composeMinuteZero(schedule, plan)) {
560
+ return secondsLead(schedule) + " " + clockMinuteGenitive(plan.rest.times, opts.style.sep);
528
561
  }
529
- if (isEveryOtherMinuteSeconds(ir, plan)) {
530
- return secondsLead(ir) + " jeder zweiten Minute";
562
+ if (isEveryOtherMinuteSeconds(schedule, plan)) {
563
+ return secondsLead(schedule) + " jeder zweiten Minute";
531
564
  }
532
- const restOwnsLead = plan.rest.kind === "compactClockTimes" && ir.analyses.clockSecond;
533
- const lead = restOwnsLead ? "" : secondsLead(ir) + ", ";
534
- return lead + render(ir, plan.rest, opts);
565
+ const restOwnsLead = plan.rest.kind === "compactClockTimes" && schedule.analyses.clockSecond;
566
+ const lead = restOwnsLead ? "" : secondsLead(schedule) + ", ";
567
+ return lead + render(schedule, plan.rest, opts);
535
568
  }
536
- function composeMinuteZero(ir, plan) {
569
+ function composeMinuteZero(schedule, plan) {
537
570
  return plan.rest.kind === "clockTimes" && plan.rest.times.every((time) => +time.minute === 0);
538
571
  }
539
572
  function clockMinuteGenitive(times, sep) {
@@ -542,44 +575,49 @@ function clockMinuteGenitive(times, sep) {
542
575
  });
543
576
  return clocks.length === 1 ? "der Minute " + clocks[0] : "der Minuten " + joinList(clocks);
544
577
  }
545
- function renderMinutesAcrossHours(ir, plan, opts) {
578
+ function renderMinutesAcrossHours(schedule, plan, opts) {
546
579
  const sep = opts.style.sep;
547
- const cadence = unevenHourCadence(ir);
580
+ const cadence = unevenHourCadence(schedule);
548
581
  if (plan.form === "wildcard") {
549
- return cadence ? "jede Minute, " + cadence : "jede Minute " + duringHours(ir, plan.times, sep);
582
+ return cadence ? "jede Minute, " + cadence : "jede Minute " + duringHours(schedule, plan.times, sep);
550
583
  }
551
- const minuteLead = strideFromSegments(segmentsOf(ir, "minute"), UNITS.minute, "") ?? countedPhrase(ir, "minute", "Minute", "Minuten");
584
+ const minuteLead = strideFromSegments(segmentsOf(schedule, "minute"), UNITS.minute, "") ?? countedPhrase(schedule, "minute", "Minute", "Minuten");
552
585
  if (cadence !== null) {
553
586
  return minuteLead + ", " + cadence;
554
587
  }
555
- const hours = plan.times.kind === "fires" ? atHours(plan.times.fires) : joinList(hourSegmentParts(ir, 0, 0, sep));
588
+ const hours = plan.times.kind === "fires" ? atHours(plan.times.fires) : joinList(hourSegmentParts(schedule, 0, 0, sep));
556
589
  return minuteLead + ", " + hours;
557
590
  }
558
- function renderMinuteSpanAcrossHourStep(ir, plan) {
559
- const cadence = unevenHourCadence(ir);
591
+ function renderMinuteSpanAcrossHourStep(schedule, plan) {
592
+ const cadence = unevenHourCadence(schedule);
560
593
  if (plan.form === "wildcard") {
561
- return "jede Minute " + everyNthHour(stepSegment(ir, "hour"));
594
+ return "jede Minute " + everyNthHour(stepSegment(schedule, "hour"));
562
595
  }
563
- const segment = stepSegment(ir, "hour");
596
+ const segment = stepSegment(schedule, "hour");
564
597
  const hours = cadence ?? (confinedHourStride(segment) ? everyNthHour(segment) : atHours(segment.fires));
565
- return (strideFromSegments(segmentsOf(ir, "minute"), UNITS.minute, "") ?? countedPhrase(ir, "minute", "Minute", "Minuten")) + ", " + hours;
598
+ return (strideFromSegments(segmentsOf(schedule, "minute"), UNITS.minute, "") ?? countedPhrase(schedule, "minute", "Minute", "Minuten")) + ", " + hours;
566
599
  }
567
- function renderCompactClockTimes(ir, plan, opts) {
600
+ function renderCompactClockTimes(schedule, plan, opts) {
568
601
  const sep = opts.style.sep;
569
602
  if (plan.fold) {
570
- const cadence = hourCadence(ir, plan.minute) ?? hourRangeCadence(ir, plan.minute);
603
+ const cadence = hourCadence(schedule, plan.minute) ?? hourRangeCadence(schedule, plan.minute);
571
604
  if (cadence !== null) {
572
605
  return cadence;
573
606
  }
574
- const hourly = segmentsOf(ir, "hour").some((segment) => segment.kind === "range");
575
- return (hourly ? "st\xFCndlich " : "t\xE4glich ") + joinList(hourSegmentParts(ir, plan.minute, ir.analyses.clockSecond, sep));
607
+ const hourly = segmentsOf(schedule, "hour").some((segment) => segment.kind === "range");
608
+ return (hourly ? "st\xFCndlich " : "t\xE4glich ") + joinList(hourSegmentParts(
609
+ schedule,
610
+ plan.minute,
611
+ schedule.analyses.clockSecond,
612
+ sep
613
+ ));
576
614
  }
577
- const hours = unevenHourCadence(ir) ?? (segmentsOf(ir, "hour").some((segment) => segment.kind === "range") ? joinList(hourSegmentParts(ir, 0, 0, sep)) : atHours(hourFires(ir)));
578
- const lead = ir.analyses.clockSecond ? countedPhrase(ir, "second", "Sekunde", "Sekunden") + ", " : "";
579
- return lead + (strideFromSegments(segmentsOf(ir, "minute"), UNITS.minute, "") ?? countedPhrase(ir, "minute", "Minute", "Minuten")) + ", " + hours;
615
+ const hours = unevenHourCadence(schedule) ?? (segmentsOf(schedule, "hour").some((segment) => segment.kind === "range") ? joinList(hourSegmentParts(schedule, 0, 0, sep)) : atHours(hourFires(schedule)));
616
+ const lead = schedule.analyses.clockSecond ? countedPhrase(schedule, "second", "Sekunde", "Sekunden") + ", " : "";
617
+ return lead + (strideFromSegments(segmentsOf(schedule, "minute"), UNITS.minute, "") ?? countedPhrase(schedule, "minute", "Minute", "Minuten")) + ", " + hours;
580
618
  }
581
- function renderMinuteFrequency(ir, plan, opts) {
582
- const segment = stepSegment(ir, "minute");
619
+ function renderMinuteFrequency(schedule, plan, opts) {
620
+ const segment = stepSegment(schedule, "minute");
583
621
  const sep = opts.style.sep;
584
622
  const clean = cleanStep(segment, 60);
585
623
  if (plan.hours.kind === "window") {
@@ -599,47 +637,44 @@ function renderMinuteFrequency(ir, plan, opts) {
599
637
  }
600
638
  const base = stepClause(segment, UNITS.minute, "jeder Stunde");
601
639
  if (plan.hours.kind === "during") {
602
- const cadence = unevenHourCadence(ir);
603
- return cadence ? base + ", " + cadence : base + " " + duringHours(ir, plan.hours.times, sep);
640
+ const cadence = unevenHourCadence(schedule);
641
+ return cadence ? base + ", " + cadence : base + " " + duringHours(schedule, plan.hours.times, sep);
604
642
  }
605
643
  if (plan.hours.kind === "step") {
606
- return base + " " + everyNthHour(stepSegment(ir, "hour"));
644
+ return base + " " + everyNthHour(stepSegment(schedule, "hour"));
607
645
  }
608
646
  return base;
609
647
  }
610
- function hourStepPhrase(ir) {
611
- const cadence = unevenHourCadence(ir);
648
+ function hourStepPhrase(schedule) {
649
+ const cadence = unevenHourCadence(schedule);
612
650
  if (cadence !== null) {
613
651
  return cadence;
614
652
  }
615
- const segment = stepSegment(ir, "hour");
653
+ const segment = stepSegment(schedule, "hour");
616
654
  if (cleanStep(segment, 24)) {
617
655
  return everyN(segment.interval, UNITS.hour);
618
656
  }
619
- const stride = openOffsetCleanStride(ir, segment);
657
+ const stride = openOffsetCleanStride(schedule, segment);
620
658
  return stride ? hourStrideCadence(stride) : atHours(segment.fires);
621
659
  }
622
- function openOffsetCleanStride(ir, segment) {
660
+ function openOffsetCleanStride(schedule, segment) {
623
661
  if (segment.startToken.indexOf("-") !== -1) {
624
662
  return null;
625
663
  }
626
- const stride = hourStride(ir);
664
+ const stride = hourStride(schedule);
627
665
  return stride && offsetCleanStride(stride) ? stride : null;
628
666
  }
629
667
  function hourStrideCadence(stride) {
630
668
  const { start, interval, last } = stride;
631
669
  const cadence = everyN(interval, UNITS.hour);
632
- const tiles = 24 % interval === 0;
633
- if (start === 0 && tiles) {
634
- return cadence;
635
- }
636
- if (start < interval && tiles) {
637
- return cadence + " ab " + start + " Uhr";
638
- }
639
- return cadence + " von " + start + " bis " + last + " Uhr";
670
+ return renderStride({ start, interval, cycle: 24 }, {
671
+ bare: () => cadence,
672
+ offset: () => cadence + " ab " + start + " Uhr",
673
+ bounded: () => cadence + " von " + start + " bis " + last + " Uhr"
674
+ });
640
675
  }
641
- function hourStride(ir) {
642
- const segments = segmentsOf(ir, "hour");
676
+ function hourStride(schedule) {
677
+ const segments = segmentsOf(schedule, "hour");
643
678
  if (!segments) {
644
679
  return null;
645
680
  }
@@ -654,89 +689,95 @@ function hourStride(ir) {
654
689
  const values = singleValues(segments);
655
690
  return values && hourListStride(values);
656
691
  }
657
- function unevenHourCadence(ir) {
658
- const stride = hourStride(ir);
692
+ function unevenHourCadence(schedule) {
693
+ const stride = hourStride(schedule);
659
694
  if (!stride || offsetCleanStride(stride)) {
660
695
  return null;
661
696
  }
662
697
  return hourStrideCadence(stride);
663
698
  }
664
- function subMinuteSecond(ir) {
665
- return ir.pattern.second === "*" || ir.shapes.second === "step";
699
+ function subMinuteSecond(schedule) {
700
+ return schedule.pattern.second === "*" || schedule.shapes.second === "step";
666
701
  }
667
- function hourCadenceLead(ir, minute) {
702
+ function hourCadenceLead(schedule, minute) {
668
703
  if (minute === 0) {
669
- if (subMinuteSecond(ir)) {
670
- return withAnchor(secondsClause(ir, minuteAnchor(ir)), "f\xFCr eine Minute");
704
+ if (subMinuteSecond(schedule)) {
705
+ return withAnchor(
706
+ secondsClause(schedule, minuteAnchor(schedule)),
707
+ "f\xFCr eine Minute"
708
+ );
671
709
  }
672
- return secondsClause(ir, "jeder Stunde");
710
+ return secondsClause(schedule, "jeder Stunde");
673
711
  }
674
712
  const minutePhrase = "in Minute " + minute;
675
- if (ir.pattern.second === "0") {
713
+ if (schedule.pattern.second === "0") {
676
714
  return minutePhrase;
677
715
  }
678
- return secondsClause(ir, minuteAnchor(ir)) + ", " + minutePhrase;
716
+ return secondsClause(schedule, minuteAnchor(schedule)) + ", " + minutePhrase;
679
717
  }
680
- function hourCadence(ir, minute) {
681
- const stride = hourStride(ir);
718
+ function hourCadence(schedule, minute) {
719
+ const stride = hourStride(schedule);
682
720
  if (!stride) {
683
721
  return null;
684
722
  }
685
723
  const fires = (stride.last - stride.start) / stride.interval + 1;
686
- if (ir.pattern.second === "0" && fires <= maxClockTimes && offsetCleanStride(stride)) {
724
+ if (schedule.pattern.second === "0" && fires <= maxClockTimes && offsetCleanStride(stride)) {
687
725
  return null;
688
726
  }
689
- const segment = segmentsOf(ir, "hour")[0];
690
- const confined = minute === 0 && subMinuteSecond(ir) && segmentsOf(ir, "hour").length === 1 && segment.kind === "step" && confinedHourStride(segment);
727
+ const segment = segmentsOf(schedule, "hour")[0];
728
+ const confined = minute === 0 && subMinuteSecond(schedule) && segmentsOf(schedule, "hour").length === 1 && segment.kind === "step" && confinedHourStride(segment);
691
729
  if (confined) {
692
- return withAnchor(secondsClause(ir, minuteAnchor(ir)), "f\xFCr eine Minute") + " " + everyNthHour(segment);
730
+ return withAnchor(
731
+ secondsClause(schedule, minuteAnchor(schedule)),
732
+ "f\xFCr eine Minute"
733
+ ) + " " + everyNthHour(segment);
693
734
  }
694
- if (minute === 0 && ir.pattern.second === "0") {
735
+ if (minute === 0 && schedule.pattern.second === "0") {
695
736
  return hourStrideCadence(stride);
696
737
  }
697
- return hourCadenceLead(ir, minute) + ", " + hourStrideCadence(stride);
738
+ return hourCadenceLead(schedule, minute) + ", " + hourStrideCadence(stride);
698
739
  }
699
- function hourCadenceApplies(ir) {
700
- if (ir.shapes.minute !== "single") {
740
+ function hourCadenceApplies(schedule) {
741
+ if (schedule.shapes.minute !== "single") {
701
742
  return false;
702
743
  }
703
- const minute = +ir.pattern.minute;
704
- return hourCadence(ir, minute) !== null || hourRangeCadence(ir, minute) !== null;
744
+ const minute = +schedule.pattern.minute;
745
+ return hourCadence(schedule, minute) !== null || hourRangeCadence(schedule, minute) !== null;
705
746
  }
706
- function hasHourWindow(ir) {
707
- const segments = segmentsOf(ir, "hour");
747
+ function hasHourWindow(schedule) {
748
+ const segments = segmentsOf(schedule, "hour");
708
749
  return !!segments && segments.some(function range(segment) {
709
750
  return segment.kind === "range";
710
751
  });
711
752
  }
712
- function hourRangeCadence(ir, minute) {
713
- if (minute !== 0 || !hasHourWindow(ir) || ir.pattern.second === "0") {
753
+ function hourRangeCadence(schedule, minute) {
754
+ if (minute !== 0 || !hasHourWindow(schedule) || schedule.pattern.second === "0") {
714
755
  return null;
715
756
  }
716
- return hourCadenceLead(ir, minute) + ", " + hourRangeWindowTail(ir);
757
+ return hourCadenceLead(schedule, minute) + ", " + hourRangeWindowTail(schedule);
717
758
  }
718
- function hourRangeWindowTail(ir) {
719
- return joinList(hourSegmentParts(ir, 0, 0, ":"));
759
+ function hourRangeWindowTail(schedule) {
760
+ return joinList(hourSegmentParts(schedule, 0, 0, ":"));
720
761
  }
721
- function renderHourRange(ir, plan, opts) {
762
+ function renderHourRange(schedule, plan, opts) {
722
763
  const last = plan.minuteForm === "wildcard" ? plan.boundMinute ?? 0 : 0;
723
764
  const window = hourWindow(plan.from, plan.to, last, opts.style.sep);
724
765
  if (plan.minuteForm === "wildcard") {
725
766
  return "jede Minute " + window;
726
767
  }
727
- if (plan.minuteForm === "lead" && ir.pattern.minute === "0") {
768
+ if (plan.minuteForm === "lead" && schedule.pattern.minute === "0") {
728
769
  return "st\xFCndlich " + window;
729
770
  }
730
771
  return (strideFromSegments(
731
- segmentsOf(ir, "minute"),
772
+ segmentsOf(schedule, "minute"),
732
773
  UNITS.minute,
733
774
  "jeder Stunde"
734
- ) ?? countedPhrase(ir, "minute", "Minute", "Minuten") + " jeder Stunde") + ", " + window;
775
+ ) ?? countedPhrase(schedule, "minute", "Minute", "Minuten") + " jeder Stunde") + ", " + window;
735
776
  }
736
- function renderClockTimes(ir, plan, opts) {
737
- if (ir.shapes.minute === "single") {
738
- const minute = +ir.pattern.minute;
739
- const cadence = hourCadence(ir, minute) ?? hourRangeCadence(ir, minute);
777
+ function renderClockTimes(schedule, plan, opts) {
778
+ if (schedule.shapes.minute === "single") {
779
+ const minute = +schedule.pattern.minute;
780
+ const cadence = hourCadence(schedule, minute) ?? hourRangeCadence(schedule, minute);
740
781
  if (cadence !== null) {
741
782
  return cadence;
742
783
  }
@@ -763,48 +804,75 @@ var renderers = {
763
804
  singleMinute: renderMinutePast,
764
805
  standaloneSeconds: renderSeconds
765
806
  };
766
- function qualifier(ir, months2) {
767
- const { date, month, weekday } = ir.pattern;
768
- if (date !== "*" && weekday !== "*") {
769
- const datePart = quartzDate(date) || dateClauseBare(ir);
770
- const weekdayPart = quartzWeekday(weekday) || weekdayQualifier(ir);
771
- return datePart + " oder " + weekdayPart + monthScope(ir, months2);
807
+ function isDayUnion(schedule) {
808
+ return schedule.pattern.date !== "*" && schedule.pattern.weekday !== "*";
809
+ }
810
+ function dayUnionMonthLead(schedule, months2) {
811
+ return schedule.pattern.month === "*" ? "" : monthClause(schedule, months2) + " ";
812
+ }
813
+ function dayUnionDate(schedule) {
814
+ return quartzDate(schedule.pattern.date) || oddEvenDay(schedule.pattern.date) || dateClauseBare(schedule);
815
+ }
816
+ function dayUnionWeekday(schedule) {
817
+ const weekday = schedule.pattern.weekday;
818
+ const quartz = quartzWeekday(weekday);
819
+ if (quartz) {
820
+ return quartz;
821
+ }
822
+ const segments = segmentsOf(schedule, "weekday");
823
+ if (segments.length === 1 && segments[0].kind === "range" && segments[0].bounds[0] === "1" && segments[0].bounds[1] === "5") {
824
+ return "an einem Wochentag (Mo\u2013Fr)";
825
+ }
826
+ return weekdayQualifier(schedule);
827
+ }
828
+ function dateStepCadence(schedule) {
829
+ const date = schedule.pattern.date;
830
+ if (!isOpenStep(date)) {
831
+ return null;
832
+ }
833
+ const [start, step] = date.split("/");
834
+ return (start === "*" || start === "1") && +step === 2 ? "jeden zweiten Tag des Monats" : null;
835
+ }
836
+ function qualifier(schedule, months2) {
837
+ const { date, month, weekday } = schedule.pattern;
838
+ if (isDayUnion(schedule)) {
839
+ return dayUnionDate(schedule) + " oder " + dayUnionWeekday(schedule);
772
840
  }
773
841
  if (weekday !== "*") {
774
- return (quartzWeekday(weekday) || weekdayQualifier(ir)) + monthScope(ir, months2);
842
+ return (quartzWeekday(weekday) || weekdayQualifier(schedule)) + monthScope(schedule, months2);
775
843
  }
776
844
  if (date !== "*") {
777
- const quartz = quartzDate(date);
778
- return quartz ? quartz + monthScope(ir, months2) : datePhrase(ir, months2);
845
+ const quartz = quartzDate(date) || dateStepCadence(schedule);
846
+ return quartz ? quartz + monthScope(schedule, months2) : datePhrase(schedule, months2);
779
847
  }
780
848
  if (month !== "*") {
781
- return monthClause(ir, months2);
849
+ return monthClause(schedule, months2);
782
850
  }
783
851
  return "";
784
852
  }
785
853
  var LEADING_PLANS = /* @__PURE__ */ new Set(["clockTimes"]);
786
- function leadsQualifier(ir) {
787
- return LEADING_PLANS.has(ir.plan.kind) || isComposeMinuteZero(ir);
854
+ function leadsQualifier(schedule) {
855
+ return LEADING_PLANS.has(schedule.plan.kind) || isComposeMinuteZero(schedule);
788
856
  }
789
- function isComposeMinuteZero(ir) {
790
- return ir.plan.kind === "composeSeconds" && composeMinuteZero(ir, ir.plan);
857
+ function isComposeMinuteZero(schedule) {
858
+ return schedule.plan.kind === "composeSeconds" && composeMinuteZero(schedule, schedule.plan);
791
859
  }
792
- function needsDailyFrame(ir) {
793
- if (hourCadenceApplies(ir)) {
860
+ function needsDailyFrame(schedule) {
861
+ if (hourCadenceApplies(schedule)) {
794
862
  return false;
795
863
  }
796
- if (ir.plan.kind === "clockTimes" || isComposeMinuteZero(ir)) {
864
+ if (schedule.plan.kind === "clockTimes" || isComposeMinuteZero(schedule)) {
797
865
  return true;
798
866
  }
799
- if (ir.plan.kind !== "hourStep") {
867
+ if (schedule.plan.kind !== "hourStep") {
800
868
  return false;
801
869
  }
802
- const segment = stepSegment(ir, "hour");
803
- return !cleanStep(segment, 24) && !openOffsetCleanStride(ir, segment);
870
+ const segment = stepSegment(schedule, "hour");
871
+ return !cleanStep(segment, 24) && !openOffsetCleanStride(schedule, segment);
804
872
  }
805
- function render(ir, plan, opts) {
873
+ function render(schedule, plan, opts) {
806
874
  return renderers[plan.kind](
807
- ir,
875
+ schedule,
808
876
  plan,
809
877
  opts
810
878
  );
@@ -821,8 +889,8 @@ function normalizeOptions(options) {
821
889
  years: !!options.years
822
890
  };
823
891
  }
824
- function applyYear(description, ir) {
825
- const year = ir.pattern.year;
892
+ function applyYear(description, schedule) {
893
+ const year = schedule.pattern.year;
826
894
  if (year === "*") {
827
895
  return description;
828
896
  }
@@ -835,16 +903,19 @@ function applyYear(description, ir) {
835
903
  }
836
904
  return description + " im Jahr " + year;
837
905
  }
838
- function describe(ir, opts) {
839
- const core = render(ir, ir.plan, opts);
840
- const qual = qualifier(ir, opts.style.months);
906
+ function describe(schedule, opts) {
907
+ const core = render(schedule, schedule.plan, opts);
908
+ const qual = qualifier(schedule, opts.style.months);
841
909
  let base = core;
842
910
  if (qual) {
843
- base = leadsQualifier(ir) ? qual + " " + core : core + " " + qual;
844
- } else if (needsDailyFrame(ir)) {
911
+ base = leadsQualifier(schedule) ? qual + " " + core : core + " " + qual;
912
+ } else if (needsDailyFrame(schedule)) {
845
913
  base = "t\xE4glich " + core;
846
914
  }
847
- return applyYear(base, ir);
915
+ if (isDayUnion(schedule)) {
916
+ base = dayUnionMonthLead(schedule, opts.style.months) + base;
917
+ }
918
+ return applyYear(base, schedule);
848
919
  }
849
920
  var de2 = {
850
921
  describe,