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/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,6 +32,56 @@ function arithmeticStep(values) {
36
32
  }
37
33
  return { start: values[0], interval, last: values[values.length - 1] };
38
34
  }
35
+ function segmentsOf(schedule, field) {
36
+ return schedule.analyses.segments[field] ?? [];
37
+ }
38
+ function stepSegment(schedule, field) {
39
+ return segmentsOf(schedule, field)[0];
40
+ }
41
+ function singleValues(segments) {
42
+ const values = [];
43
+ for (const segment of segments) {
44
+ if (segment.kind !== "single") {
45
+ return null;
46
+ }
47
+ values.push(+segment.value);
48
+ }
49
+ return values;
50
+ }
51
+ function offsetCleanStride(stride) {
52
+ return stride.start < stride.interval && 24 % stride.interval === 0;
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
+ }
65
+ function hourListStride(values) {
66
+ if (values.length < 2) {
67
+ return null;
68
+ }
69
+ const interval = values[1] - values[0];
70
+ if (interval < 2) {
71
+ return null;
72
+ }
73
+ for (let i = 2; i < values.length; i += 1) {
74
+ if (values[i] - values[i - 1] !== interval) {
75
+ return null;
76
+ }
77
+ }
78
+ if (values[0] !== 0 && values.length < 5) {
79
+ return null;
80
+ }
81
+ return { interval, last: values[values.length - 1], start: values[0] };
82
+ }
83
+
84
+ // src/core/weekday.ts
39
85
  function weekdayDisplayKey(value) {
40
86
  return value === 0 ? 7 : value;
41
87
  }
@@ -56,10 +102,21 @@ function orderWeekdaysForDisplay(segments) {
56
102
  return pair[0];
57
103
  });
58
104
  }
105
+
106
+ // src/core/util.ts
107
+ function isNonNegativeInteger(value) {
108
+ const digits = /^\d+$/;
109
+ return digits.test(value);
110
+ }
59
111
  function toFieldNumber(token, numberMap) {
60
112
  return isNonNegativeInteger(token) ? +token : numberMap[token.toUpperCase()];
61
113
  }
62
114
 
115
+ // src/core/shapes.ts
116
+ function isOpenStep(field) {
117
+ return field.indexOf("/") !== -1 && field.indexOf("-") === -1 && field.indexOf(",") === -1;
118
+ }
119
+
63
120
  // src/lang/de/dialects.ts
64
121
  var months = [
65
122
  null,
@@ -119,24 +176,18 @@ function everyN(interval, unit) {
119
176
  function withAnchor(clause, anchor) {
120
177
  return anchor ? clause + " " + anchor : clause;
121
178
  }
122
- function stepSegment(segments) {
123
- return segments[0];
124
- }
125
179
  function cleanStep(segment, cycle) {
126
180
  return (segment.startToken === "*" || +segment.startToken === 0) && cycle % segment.interval === 0;
127
181
  }
128
- function renderStride(stride) {
182
+ function renderStride2(stride) {
129
183
  const { interval, start, last, cycle, unit, anchor } = stride;
130
184
  const cadence = everyN(interval, unit);
131
- const tiles = cycle % interval === 0;
132
- if (start === 0 && tiles) {
133
- return cadence;
134
- }
135
185
  const tail = anchor ? " " + anchor : "";
136
- if (start < interval && tiles) {
137
- return cadence + " ab " + unit.singular + " " + start + tail;
138
- }
139
- 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
+ });
140
191
  }
141
192
  function stepClause(segment, unit, anchor) {
142
193
  const start = segment.startToken === "*" ? 0 : +segment.startToken;
@@ -147,7 +198,7 @@ function stepClause(segment, unit, anchor) {
147
198
  anchor
148
199
  );
149
200
  }
150
- return renderStride({
201
+ return renderStride2({
151
202
  interval: segment.interval,
152
203
  start,
153
204
  last: segment.fires[segment.fires.length - 1],
@@ -156,20 +207,10 @@ function stepClause(segment, unit, anchor) {
156
207
  anchor
157
208
  });
158
209
  }
159
- function singleValues(segments) {
160
- const values = [];
161
- for (const segment of segments) {
162
- if (segment.kind !== "single") {
163
- return null;
164
- }
165
- values.push(+segment.value);
166
- }
167
- return values;
168
- }
169
210
  function strideFromSegments(segments, unit, anchor) {
170
211
  const values = singleValues(segments);
171
212
  const step = values && arithmeticStep(values);
172
- return step ? renderStride({ ...step, cycle: 60, unit, anchor }) : null;
213
+ return step ? renderStride2({ ...step, cycle: 60, unit, anchor }) : null;
173
214
  }
174
215
  var weekdayNames = [
175
216
  "sonntags",
@@ -180,9 +221,6 @@ var weekdayNames = [
180
221
  "freitags",
181
222
  "samstags"
182
223
  ];
183
- function fieldSegments(ir, field) {
184
- return ir.analyses.segments[field];
185
- }
186
224
  function flattenSteps(segments) {
187
225
  return segments.flatMap(function flat(segment) {
188
226
  return segment.kind === "step" ? segment.fires.map(function single(value) {
@@ -205,8 +243,8 @@ function weekdayName(token) {
205
243
  function weekdayRange(bounds) {
206
244
  return weekdayName(bounds[0]) + " bis " + weekdayName(bounds[1]);
207
245
  }
208
- function weekdayQualifier(ir) {
209
- const segments = orderWeekdaysForDisplay(fieldSegments(ir, "weekday"));
246
+ function weekdayQualifier(schedule) {
247
+ const segments = orderWeekdaysForDisplay(segmentsOf(schedule, "weekday"));
210
248
  if (segments.length === 1 && segments[0].kind === "range") {
211
249
  return weekdayRange(segments[0].bounds);
212
250
  }
@@ -269,26 +307,39 @@ function quartzDate(field) {
269
307
  }
270
308
  return null;
271
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
+ }
272
323
  function monthName(token, months2) {
273
324
  return months2[+token];
274
325
  }
275
326
  function monthRange(bounds, months2) {
276
327
  return "von " + monthName(bounds[0], months2) + " bis " + monthName(bounds[1], months2);
277
328
  }
278
- function monthNamesList(ir, months2) {
279
- return joinList(flattenSteps(fieldSegments(ir, "month")).map(function name(segment) {
329
+ function monthNamesList(schedule, months2) {
330
+ return joinList(flattenSteps(segmentsOf(schedule, "month")).map(function name(segment) {
280
331
  return segment.kind === "range" ? monthRange(segment.bounds, months2) : monthName(segment.value, months2);
281
332
  }));
282
333
  }
283
- function monthClause(ir, months2) {
284
- const segments = flattenSteps(fieldSegments(ir, "month"));
334
+ function monthClause(schedule, months2) {
335
+ const segments = flattenSteps(segmentsOf(schedule, "month"));
285
336
  if (segments.length === 1 && segments[0].kind === "range") {
286
337
  return monthRange(segments[0].bounds, months2);
287
338
  }
288
- return "im " + monthNamesList(ir, months2);
339
+ return "im " + monthNamesList(schedule, months2);
289
340
  }
290
- function monthScope(ir, months2) {
291
- return ir.pattern.month === "*" ? "" : " " + monthClause(ir, months2);
341
+ function monthScope(schedule, months2) {
342
+ return schedule.pattern.month === "*" ? "" : " " + monthClause(schedule, months2);
292
343
  }
293
344
  function ordinalDay(value) {
294
345
  return value + ".";
@@ -296,8 +347,8 @@ function ordinalDay(value) {
296
347
  function dateRange(bounds) {
297
348
  return "vom " + ordinalDay(bounds[0]) + " bis zum " + ordinalDay(bounds[1]);
298
349
  }
299
- function dateClauseBare(ir) {
300
- const segments = flattenSteps(fieldSegments(ir, "date"));
350
+ function dateClauseBare(schedule) {
351
+ const segments = flattenSteps(segmentsOf(schedule, "date"));
301
352
  if (segments.length === 1 && segments[0].kind === "range") {
302
353
  return dateRange(segments[0].bounds);
303
354
  }
@@ -310,13 +361,13 @@ function dateClauseBare(ir) {
310
361
  return segment.kind === "range" ? dateRange(segment.bounds) : "am " + ordinalDay(segment.value);
311
362
  }));
312
363
  }
313
- function datePhrase(ir, months2) {
314
- const clause = dateClauseBare(ir);
315
- if (ir.pattern.month === "*") {
364
+ function datePhrase(schedule, months2) {
365
+ const clause = dateClauseBare(schedule);
366
+ if (schedule.pattern.month === "*") {
316
367
  return clause;
317
368
  }
318
- const monthRanged = flattenSteps(fieldSegments(ir, "month")).some((segment) => segment.kind === "range");
319
- 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);
320
371
  }
321
372
  function bareTime(time, sep) {
322
373
  if (time.second) {
@@ -335,32 +386,35 @@ function timesPhrase(times, sep) {
335
386
  function hourWindow(from, to, last, sep) {
336
387
  return "von " + bareTime({ hour: from, minute: 0 }, sep) + " bis " + bareTime({ hour: to, minute: last }, sep) + " Uhr";
337
388
  }
338
- function fieldValues(ir, field) {
339
- return flattenSteps(fieldSegments(ir, field)).map(function value(segment) {
389
+ function fieldValues(schedule, field) {
390
+ return flattenSteps(segmentsOf(schedule, field)).map(function value(segment) {
340
391
  return segment.kind === "range" ? segment.bounds[0] + " bis " + segment.bounds[1] : String(segment.value);
341
392
  });
342
393
  }
343
- function countedPhrase(ir, field, singular, plural) {
344
- if (ir.shapes[field] === "single") {
345
- 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];
346
397
  }
347
- return "in den " + plural + " " + joinList(fieldValues(ir, field));
398
+ return "in den " + plural + " " + joinList(fieldValues(schedule, field));
348
399
  }
349
- function minuteAnchor(ir) {
350
- return ir.pattern.minute === "*" ? "jeder Minute" : "";
400
+ function minuteAnchor(schedule) {
401
+ return schedule.pattern.minute === "*" ? "jeder Minute" : "";
351
402
  }
352
- function secondsLead(ir) {
353
- return secondsClause(ir, minuteAnchor(ir));
403
+ function secondsLead(schedule) {
404
+ return secondsClause(schedule, minuteAnchor(schedule));
354
405
  }
355
- function secondsClause(ir, anchor) {
356
- if (ir.pattern.second === "*") {
406
+ function secondsClause(schedule, anchor) {
407
+ if (schedule.pattern.second === "*") {
357
408
  return "jede Sekunde";
358
409
  }
359
- const segments = ir.analyses.segments.second;
360
- if (ir.shapes.second === "step") {
361
- return stepClause(stepSegment(segments), 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);
362
413
  }
363
- 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
+ );
364
418
  }
365
419
  function spanTime(hour, minute, sep) {
366
420
  return hour + sep + pad(minute);
@@ -368,8 +422,8 @@ function spanTime(hour, minute, sep) {
368
422
  function atHours(hours) {
369
423
  return "um " + joinList(hours.map(String)) + " Uhr";
370
424
  }
371
- function hourFires(ir) {
372
- return flattenSteps(fieldSegments(ir, "hour")).map(function fire(segment) {
425
+ function hourFires(schedule) {
426
+ return flattenSteps(segmentsOf(schedule, "hour")).map(function fire(segment) {
373
427
  return segment.kind === "range" ? +segment.bounds[0] : +segment.value;
374
428
  });
375
429
  }
@@ -379,8 +433,8 @@ function partTime(hour, minute, second, sep) {
379
433
  }
380
434
  return minute === 0 ? String(hour) : hour + sep + pad(minute);
381
435
  }
382
- function hourSegmentParts(ir, minute, second, sep) {
383
- return fieldSegments(ir, "hour").map(function part(segment) {
436
+ function hourSegmentParts(schedule, minute, second, sep) {
437
+ return segmentsOf(schedule, "hour").map(function part(segment) {
384
438
  if (segment.kind === "range") {
385
439
  return "von " + partTime(+segment.bounds[0], minute, second, sep) + " bis " + partTime(+segment.bounds[1], minute, second, sep) + " Uhr";
386
440
  }
@@ -392,13 +446,13 @@ function hourSegmentParts(ir, minute, second, sep) {
392
446
  return "um " + partTime(+segment.value, minute, second, sep) + " Uhr";
393
447
  });
394
448
  }
395
- function duringWindows(ir, times, sep) {
449
+ function duringWindows(schedule, times, sep) {
396
450
  if (times.kind === "fires") {
397
451
  return times.fires.map(function each(hour) {
398
452
  return hourWindow(hour, hour, 59, sep);
399
453
  });
400
454
  }
401
- return fieldSegments(ir, "hour").flatMap(function part(segment) {
455
+ return segmentsOf(schedule, "hour").flatMap(function part(segment) {
402
456
  if (segment.kind === "range") {
403
457
  return [hourWindow(+segment.bounds[0], +segment.bounds[1], 59, sep)];
404
458
  }
@@ -410,8 +464,8 @@ function duringWindows(ir, times, sep) {
410
464
  return [hourWindow(+segment.value, +segment.value, 59, sep)];
411
465
  });
412
466
  }
413
- function duringHours(ir, times, sep) {
414
- const windows = duringWindows(ir, times, sep);
467
+ function duringHours(schedule, times, sep) {
468
+ const windows = duringWindows(schedule, times, sep);
415
469
  if (windows.length <= 3 || times.kind !== "fires") {
416
470
  return joinList(windows);
417
471
  }
@@ -426,24 +480,24 @@ function renderEveryMinute() {
426
480
  function renderEveryHour() {
427
481
  return everyUnit(UNITS.hour);
428
482
  }
429
- function renderSeconds(ir) {
430
- return secondsLead(ir);
483
+ function renderSeconds(schedule) {
484
+ return secondsLead(schedule);
431
485
  }
432
- function minutePastClause(ir) {
486
+ function minutePastClause(schedule) {
433
487
  return strideFromSegments(
434
- fieldSegments(ir, "minute"),
488
+ segmentsOf(schedule, "minute"),
435
489
  UNITS.minute,
436
490
  "jeder Stunde"
437
- ) ?? countedPhrase(ir, "minute", "Minute", "Minuten") + " jeder Stunde";
491
+ ) ?? countedPhrase(schedule, "minute", "Minute", "Minuten") + " jeder Stunde";
438
492
  }
439
- function renderMinutePast(ir) {
440
- return minutePastClause(ir);
493
+ function renderMinutePast(schedule) {
494
+ return minutePastClause(schedule);
441
495
  }
442
- function renderSecondsWithinMinute(ir, plan) {
496
+ function renderSecondsWithinMinute(schedule, plan) {
443
497
  if (plan.singleSecond) {
444
- 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";
445
499
  }
446
- return secondsLead(ir) + ", in Minute " + ir.pattern.minute + " jeder Stunde";
500
+ return secondsLead(schedule) + ", in Minute " + schedule.pattern.minute + " jeder Stunde";
447
501
  }
448
502
  function wholeHour(hour) {
449
503
  if (hour === 0) {
@@ -454,39 +508,39 @@ function wholeHour(hour) {
454
508
  }
455
509
  return "der " + hour + "-Uhr-Stunde";
456
510
  }
457
- function renderMinuteSpanInHour(ir, plan, opts) {
458
- if (ir.pattern.minute === "*") {
511
+ function renderMinuteSpanInHour(schedule, plan, opts) {
512
+ if (schedule.pattern.minute === "*") {
459
513
  return "jede Minute " + wholeHour(plan.hour);
460
514
  }
461
515
  const sep = opts.style.sep;
462
516
  return "jede Minute von " + spanTime(plan.hour, plan.span[0], sep) + " bis " + spanTime(plan.hour, plan.span[1], sep) + " Uhr";
463
517
  }
464
- function isEveryOtherMinuteSeconds(ir, plan) {
465
- 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") {
466
520
  return false;
467
521
  }
468
- const minuteStep = stepSegment(ir.analyses.segments.minute);
522
+ const minuteStep = stepSegment(schedule, "minute");
469
523
  return minuteStep.startToken === "*" && minuteStep.interval === 2;
470
524
  }
471
- function renderComposeSeconds(ir, plan, opts) {
472
- if ((plan.rest.kind === "clockTimes" || plan.rest.kind === "compactClockTimes") && ir.shapes.minute === "single") {
473
- const minute = +ir.pattern.minute;
474
- 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);
475
529
  if (cadence !== null) {
476
530
  return cadence;
477
531
  }
478
532
  }
479
- if (composeMinuteZero(ir, plan)) {
480
- 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);
481
535
  }
482
- if (isEveryOtherMinuteSeconds(ir, plan)) {
483
- return secondsLead(ir) + " jeder zweiten Minute";
536
+ if (isEveryOtherMinuteSeconds(schedule, plan)) {
537
+ return secondsLead(schedule) + " jeder zweiten Minute";
484
538
  }
485
- const restOwnsLead = plan.rest.kind === "compactClockTimes" && ir.analyses.clockSecond;
486
- const lead = restOwnsLead ? "" : secondsLead(ir) + ", ";
487
- 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);
488
542
  }
489
- function composeMinuteZero(ir, plan) {
543
+ function composeMinuteZero(schedule, plan) {
490
544
  return plan.rest.kind === "clockTimes" && plan.rest.times.every((time) => +time.minute === 0);
491
545
  }
492
546
  function clockMinuteGenitive(times, sep) {
@@ -495,44 +549,49 @@ function clockMinuteGenitive(times, sep) {
495
549
  });
496
550
  return clocks.length === 1 ? "der Minute " + clocks[0] : "der Minuten " + joinList(clocks);
497
551
  }
498
- function renderMinutesAcrossHours(ir, plan, opts) {
552
+ function renderMinutesAcrossHours(schedule, plan, opts) {
499
553
  const sep = opts.style.sep;
500
- const cadence = unevenHourCadence(ir);
554
+ const cadence = unevenHourCadence(schedule);
501
555
  if (plan.form === "wildcard") {
502
- 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);
503
557
  }
504
- const minuteLead = strideFromSegments(fieldSegments(ir, "minute"), UNITS.minute, "") ?? countedPhrase(ir, "minute", "Minute", "Minuten");
558
+ const minuteLead = strideFromSegments(segmentsOf(schedule, "minute"), UNITS.minute, "") ?? countedPhrase(schedule, "minute", "Minute", "Minuten");
505
559
  if (cadence !== null) {
506
560
  return minuteLead + ", " + cadence;
507
561
  }
508
- 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));
509
563
  return minuteLead + ", " + hours;
510
564
  }
511
- function renderMinuteSpanAcrossHourStep(ir, plan) {
512
- const cadence = unevenHourCadence(ir);
565
+ function renderMinuteSpanAcrossHourStep(schedule, plan) {
566
+ const cadence = unevenHourCadence(schedule);
513
567
  if (plan.form === "wildcard") {
514
- return "jede Minute " + everyNthHour(stepSegment(ir.analyses.segments.hour));
568
+ return "jede Minute " + everyNthHour(stepSegment(schedule, "hour"));
515
569
  }
516
- const segment = stepSegment(ir.analyses.segments.hour);
570
+ const segment = stepSegment(schedule, "hour");
517
571
  const hours = cadence ?? (confinedHourStride(segment) ? everyNthHour(segment) : atHours(segment.fires));
518
- return (strideFromSegments(fieldSegments(ir, "minute"), UNITS.minute, "") ?? countedPhrase(ir, "minute", "Minute", "Minuten")) + ", " + hours;
572
+ return (strideFromSegments(segmentsOf(schedule, "minute"), UNITS.minute, "") ?? countedPhrase(schedule, "minute", "Minute", "Minuten")) + ", " + hours;
519
573
  }
520
- function renderCompactClockTimes(ir, plan, opts) {
574
+ function renderCompactClockTimes(schedule, plan, opts) {
521
575
  const sep = opts.style.sep;
522
576
  if (plan.fold) {
523
- const cadence = hourCadence(ir, plan.minute) ?? hourRangeCadence(ir, plan.minute);
577
+ const cadence = hourCadence(schedule, plan.minute) ?? hourRangeCadence(schedule, plan.minute);
524
578
  if (cadence !== null) {
525
579
  return cadence;
526
580
  }
527
- const hourly = fieldSegments(ir, "hour").some((segment) => segment.kind === "range");
528
- 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
+ ));
529
588
  }
530
- const hours = unevenHourCadence(ir) ?? (fieldSegments(ir, "hour").some((segment) => segment.kind === "range") ? joinList(hourSegmentParts(ir, 0, 0, sep)) : atHours(hourFires(ir)));
531
- const lead = ir.analyses.clockSecond ? countedPhrase(ir, "second", "Sekunde", "Sekunden") + ", " : "";
532
- return lead + (strideFromSegments(fieldSegments(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;
533
592
  }
534
- function renderMinuteFrequency(ir, plan, opts) {
535
- const segment = stepSegment(ir.analyses.segments.minute);
593
+ function renderMinuteFrequency(schedule, plan, opts) {
594
+ const segment = stepSegment(schedule, "minute");
536
595
  const sep = opts.style.sep;
537
596
  const clean = cleanStep(segment, 60);
538
597
  if (plan.hours.kind === "window") {
@@ -552,68 +611,44 @@ function renderMinuteFrequency(ir, plan, opts) {
552
611
  }
553
612
  const base = stepClause(segment, UNITS.minute, "jeder Stunde");
554
613
  if (plan.hours.kind === "during") {
555
- const cadence = unevenHourCadence(ir);
556
- 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);
557
616
  }
558
617
  if (plan.hours.kind === "step") {
559
- return base + " " + everyNthHour(stepSegment(ir.analyses.segments.hour));
618
+ return base + " " + everyNthHour(stepSegment(schedule, "hour"));
560
619
  }
561
620
  return base;
562
621
  }
563
- function hourStepPhrase(ir) {
564
- const cadence = unevenHourCadence(ir);
622
+ function hourStepPhrase(schedule) {
623
+ const cadence = unevenHourCadence(schedule);
565
624
  if (cadence !== null) {
566
625
  return cadence;
567
626
  }
568
- const segment = stepSegment(ir.analyses.segments.hour);
627
+ const segment = stepSegment(schedule, "hour");
569
628
  if (cleanStep(segment, 24)) {
570
629
  return everyN(segment.interval, UNITS.hour);
571
630
  }
572
- const stride = openOffsetCleanStride(ir, segment);
631
+ const stride = openOffsetCleanStride(schedule, segment);
573
632
  return stride ? hourStrideCadence(stride) : atHours(segment.fires);
574
633
  }
575
- function openOffsetCleanStride(ir, segment) {
634
+ function openOffsetCleanStride(schedule, segment) {
576
635
  if (segment.startToken.indexOf("-") !== -1) {
577
636
  return null;
578
637
  }
579
- const stride = hourStride(ir);
638
+ const stride = hourStride(schedule);
580
639
  return stride && offsetCleanStride(stride) ? stride : null;
581
640
  }
582
641
  function hourStrideCadence(stride) {
583
642
  const { start, interval, last } = stride;
584
643
  const cadence = everyN(interval, UNITS.hour);
585
- const tiles = 24 % interval === 0;
586
- if (start === 0 && tiles) {
587
- return cadence;
588
- }
589
- if (start < interval && tiles) {
590
- return cadence + " ab " + start + " Uhr";
591
- }
592
- return cadence + " von " + start + " bis " + last + " Uhr";
593
- }
594
- function hourListStride(values) {
595
- if (values.length < 2) {
596
- return null;
597
- }
598
- const interval = values[1] - values[0];
599
- if (interval < 2) {
600
- return null;
601
- }
602
- for (let i = 2; i < values.length; i += 1) {
603
- if (values[i] - values[i - 1] !== interval) {
604
- return null;
605
- }
606
- }
607
- if (values[0] !== 0 && values.length < 5) {
608
- return null;
609
- }
610
- return { interval, last: values[values.length - 1], start: values[0] };
611
- }
612
- function offsetCleanStride(stride) {
613
- return stride.start < stride.interval && 24 % stride.interval === 0;
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 = fieldSegments(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 = fieldSegments(ir, "hour")[0];
664
- const confined = minute === 0 && subMinuteSecond(ir) && fieldSegments(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 = fieldSegments(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
- fieldSegments(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.analyses.segments.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,