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