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/cronli5.cjs CHANGED
@@ -95,41 +95,6 @@ function isNonNegativeInteger(value) {
95
95
  const digits = /^\d+$/;
96
96
  return digits.test(value);
97
97
  }
98
- function arithmeticStep(values) {
99
- if (values.length < 5) {
100
- return null;
101
- }
102
- const interval = values[1] - values[0];
103
- if (interval < 2) {
104
- return null;
105
- }
106
- for (let i = 2; i < values.length; i += 1) {
107
- if (values[i] - values[i - 1] !== interval) {
108
- return null;
109
- }
110
- }
111
- return { start: values[0], interval, last: values[values.length - 1] };
112
- }
113
- function weekdayDisplayKey(value) {
114
- return value === 0 ? 7 : value;
115
- }
116
- function orderWeekdaysForDisplay(segments) {
117
- const flattened = segments.flatMap(function flat(segment) {
118
- return segment.kind === "step" ? segment.fires.map(function single(value) {
119
- return { kind: "single", value: "" + value };
120
- }) : [segment];
121
- });
122
- function key(segment) {
123
- return segment.kind === "range" ? weekdayDisplayKey(+segment.bounds[0]) : weekdayDisplayKey(+segment.value);
124
- }
125
- return flattened.map(function index(segment, position) {
126
- return [segment, position];
127
- }).sort(function byDisplayKey(a, b) {
128
- return key(a[0]) - key(b[0]) || a[1] - b[1];
129
- }).map(function unwrap(pair) {
130
- return pair[0];
131
- });
132
- }
133
98
  function toFieldNumber(token, numberMap) {
134
99
  return isNonNegativeInteger(token) ? +token : numberMap[token.toUpperCase()];
135
100
  }
@@ -262,7 +227,7 @@ function normalizeField(value, field, spec) {
262
227
  const cycle = timeFieldCycle[field];
263
228
  const segments = stringValue.split(",").map(function canonical(segment) {
264
229
  return canonicalizeTokens(collapseFullSpanRange(
265
- enumerateNonUniformStep(
230
+ enumerateIfNonUniform(
266
231
  collapseFullSpanStep(
267
232
  collapseDegenerateRange(
268
233
  collapseOnceStep(collapseUnitStep(segment, spec), spec),
@@ -326,7 +291,7 @@ function collapseOnceStep(segment, spec) {
326
291
  }
327
292
  return start === "*" ? "" + spec.min : start;
328
293
  }
329
- function enumerateNonUniformStep(segment, spec, cycle) {
294
+ function enumerateIfNonUniform(segment, spec, cycle) {
330
295
  const parts = segment.split("/");
331
296
  if (typeof cycle !== "number" || parts.length !== 2 || includes(parts[0], "-")) {
332
297
  return segment;
@@ -465,6 +430,93 @@ function expandMacro(cronString) {
465
430
  throw new Error("`cronli5` does not recognize the macro `" + trimmed + "`.");
466
431
  }
467
432
 
433
+ // src/core/weekday.ts
434
+ function weekdayDisplayKey(value) {
435
+ return value === 0 ? 7 : value;
436
+ }
437
+ function orderWeekdaysForDisplay(segments) {
438
+ const flattened = segments.flatMap(function flat(segment) {
439
+ return segment.kind === "step" ? segment.fires.map(function single(value) {
440
+ return { kind: "single", value: "" + value };
441
+ }) : [segment];
442
+ });
443
+ function key(segment) {
444
+ return segment.kind === "range" ? weekdayDisplayKey(+segment.bounds[0]) : weekdayDisplayKey(+segment.value);
445
+ }
446
+ return flattened.map(function index(segment, position) {
447
+ return [segment, position];
448
+ }).sort(function byDisplayKey(a, b) {
449
+ return key(a[0]) - key(b[0]) || a[1] - b[1];
450
+ }).map(function unwrap(pair) {
451
+ return pair[0];
452
+ });
453
+ }
454
+
455
+ // src/core/cadence.ts
456
+ function arithmeticStep(values) {
457
+ if (values.length < 5) {
458
+ return null;
459
+ }
460
+ const interval = values[1] - values[0];
461
+ if (interval < 2) {
462
+ return null;
463
+ }
464
+ for (let i = 2; i < values.length; i += 1) {
465
+ if (values[i] - values[i - 1] !== interval) {
466
+ return null;
467
+ }
468
+ }
469
+ return { start: values[0], interval, last: values[values.length - 1] };
470
+ }
471
+ function segmentsOf(schedule, field) {
472
+ return schedule.analyses.segments[field] ?? [];
473
+ }
474
+ function stepSegment(schedule, field) {
475
+ return segmentsOf(schedule, field)[0];
476
+ }
477
+ function singleValues(segments) {
478
+ const values = [];
479
+ for (const segment of segments) {
480
+ if (segment.kind !== "single") {
481
+ return null;
482
+ }
483
+ values.push(+segment.value);
484
+ }
485
+ return values;
486
+ }
487
+ function offsetCleanStride(stride) {
488
+ return stride.start < stride.interval && 24 % stride.interval === 0;
489
+ }
490
+ function renderStride(spec, parts) {
491
+ const { start, interval, cycle } = spec;
492
+ const tiles = cycle % interval === 0;
493
+ if (start === 0 && tiles) {
494
+ return parts.bare();
495
+ }
496
+ if (start < interval && tiles) {
497
+ return parts.offset();
498
+ }
499
+ return parts.bounded();
500
+ }
501
+ function hourListStride(values) {
502
+ if (values.length < 2) {
503
+ return null;
504
+ }
505
+ const interval = values[1] - values[0];
506
+ if (interval < 2) {
507
+ return null;
508
+ }
509
+ for (let i = 2; i < values.length; i += 1) {
510
+ if (values[i] - values[i - 1] !== interval) {
511
+ return null;
512
+ }
513
+ }
514
+ if (values[0] !== 0 && values.length < 5) {
515
+ return null;
516
+ }
517
+ return { interval, last: values[values.length - 1], start: values[0] };
518
+ }
519
+
468
520
  // src/core/shapes.ts
469
521
  function isSingleValue(field) {
470
522
  return field !== "*" && !includes(field, ",") && !includes(field, "-") && !includes(field, "/");
@@ -481,6 +533,9 @@ function isDiscreteList(field) {
481
533
  function isDiscreteHours(hourField) {
482
534
  return hourField !== "*" && !isPlainRange(hourField) && !isPlainStep(hourField);
483
535
  }
536
+ function isOpenStep(field) {
537
+ return field.indexOf("/") !== -1 && field.indexOf("-") === -1 && field.indexOf(",") === -1;
538
+ }
484
539
 
485
540
  // src/core/analyze.ts
486
541
  function getOccurrences(start, interval, max) {
@@ -616,11 +671,11 @@ function analyze(pattern) {
616
671
  minuteSpan: minuteSpan(pattern.minute),
617
672
  segments
618
673
  };
619
- const content = { analyses, pattern, shapes };
620
- return { ...content, plan: selectStrategy(content) };
674
+ const facts = { analyses, pattern, shapes };
675
+ return { ...facts, plan: selectPlan(facts) };
621
676
  }
622
- function selectStrategy(content) {
623
- const { analyses, pattern, shapes } = content;
677
+ function selectPlan(facts) {
678
+ const { analyses, pattern, shapes } = facts;
624
679
  if (pattern.second !== "0") {
625
680
  const seconds = planSeconds(pattern, shapes, analyses);
626
681
  if (seconds) {
@@ -964,72 +1019,134 @@ function normalizeOptions(options) {
964
1019
  years: !!options.years
965
1020
  };
966
1021
  }
967
- function describe(ir, opts) {
968
- const body = confinement(ir, opts) ?? render(ir, ir.plan, opts);
969
- const lead = isDayUnion(ir, opts) ? dayUnionMonthLead(ir, opts) : "";
970
- return applyYear(lead + body, ir, opts);
1022
+ function describe(schedule, opts) {
1023
+ const dense = denseCadence(schedule, opts);
1024
+ if (dense !== null) {
1025
+ return applyYear(dense, schedule, opts);
1026
+ }
1027
+ const body = confinement(schedule, opts) ?? render(schedule, schedule.plan, opts);
1028
+ const lead = isDayUnion(schedule, opts) ? dayUnionMonthLead(schedule, opts) : "";
1029
+ return applyYear(lead + body, schedule, opts);
971
1030
  }
972
- function render(ir, plan, opts) {
1031
+ function render(schedule, plan, opts) {
973
1032
  const renderer = renderers[plan.kind];
974
- return renderer(ir, plan, opts);
1033
+ return renderer(schedule, plan, opts);
975
1034
  }
976
- function renderEverySecond(ir, plan, opts) {
977
- return "every second" + trailingQualifier(ir, opts);
1035
+ function isCadenceShape(shape) {
1036
+ return shape === "step" || shape === "range" || shape === "list";
978
1037
  }
979
- function renderStandaloneSeconds(ir, plan, opts) {
980
- return secondsLeadClause(ir, opts) + trailingQualifier(ir, opts);
1038
+ function isDenseCadence(schedule, opts) {
1039
+ if (!opts.style.untilWindow || opts.short || schedule.plan.kind !== "composeSeconds" || schedule.plan.rest.kind === "clockTimes" || isDayUnion(schedule, opts)) {
1040
+ return false;
1041
+ }
1042
+ const { shapes } = schedule;
1043
+ return isCadenceShape(shapes.second) && isCadenceShape(shapes.minute) && isCadenceShape(shapes.hour);
981
1044
  }
982
- function renderSecondPastMinute(ir, plan, opts) {
983
- const secondField = ir.pattern.second;
984
- return getNumber(secondField, opts) + " " + pluralize(secondField, "second") + " past the minute, every minute" + trailingQualifier(ir, opts);
1045
+ function denseHourFragment(schedule, opts) {
1046
+ const stride = hourStride(schedule);
1047
+ if (stride) {
1048
+ return hourStrideCadence(stride, opts);
1049
+ }
1050
+ if (schedule.shapes.hour === "range") {
1051
+ const segment = segmentsOf(schedule, "hour").find(function range(part) {
1052
+ return part.kind === "range";
1053
+ });
1054
+ return rangeWindow({
1055
+ continuous: false,
1056
+ from: +segment.bounds[0],
1057
+ throughMinute: 0,
1058
+ to: +segment.bounds[1]
1059
+ }, opts);
1060
+ }
1061
+ return "during the " + hourSegmentTimes(schedule, { minute: 0, second: null }, false, opts) + " hours";
985
1062
  }
986
- function renderSecondsWithinMinute(ir, plan, opts) {
987
- const minuteField = ir.pattern.minute;
1063
+ function denseMinuteFragment(schedule, opts) {
1064
+ if (schedule.shapes.minute === "step") {
1065
+ return stepCycle60(stepSegment(schedule, "minute"), "minute", "hour", opts);
1066
+ }
1067
+ if (schedule.shapes.minute === "range") {
1068
+ return minuteRangeLead(schedule.pattern.minute, opts);
1069
+ }
1070
+ return strideFromSegments(
1071
+ segmentsOf(schedule, "minute"),
1072
+ "minute",
1073
+ "hour",
1074
+ opts
1075
+ ) ?? listPastThe(
1076
+ segmentWords(segmentsOf(schedule, "minute"), opts),
1077
+ "minute",
1078
+ "hour",
1079
+ opts
1080
+ );
1081
+ }
1082
+ function denseCadence(schedule, opts) {
1083
+ if (!isDenseCadence(schedule, opts)) {
1084
+ return null;
1085
+ }
1086
+ const hour = denseHourFragment(schedule, opts);
1087
+ const minute = denseMinuteFragment(schedule, opts);
1088
+ const second = secondsClause(schedule, "minute", opts);
1089
+ const nested = hour + ", " + minute + ", and within each of those minutes, " + second;
1090
+ const anchor = trailingQualifier(schedule, opts).trim();
1091
+ return anchor ? anchor + ", " + nested : nested;
1092
+ }
1093
+ function renderEverySecond(schedule, plan, opts) {
1094
+ return "every second" + trailingQualifier(schedule, opts);
1095
+ }
1096
+ function renderStandaloneSeconds(schedule, plan, opts) {
1097
+ return secondsLeadClause(schedule, opts) + trailingQualifier(schedule, opts);
1098
+ }
1099
+ function renderSecondPastMinute(schedule, plan, opts) {
1100
+ const secondField = schedule.pattern.second;
1101
+ return getNumber(secondField, opts) + " " + pluralize(secondField, "second") + " past the minute, every minute" + trailingQualifier(schedule, opts);
1102
+ }
1103
+ function renderSecondsWithinMinute(schedule, plan, opts) {
1104
+ const minuteField = schedule.pattern.minute;
988
1105
  const minuteWord = getNumber(minuteField, opts);
989
1106
  const minuteUnit = pluralize(minuteField, "minute");
990
1107
  if (plan.singleSecond) {
991
- const secondField = ir.pattern.second;
992
- return minuteWord + " " + minuteUnit + " and " + getNumber(secondField, opts) + " " + pluralize(secondField, "second") + " past the hour, every hour" + trailingQualifier(ir, opts);
1108
+ const secondField = schedule.pattern.second;
1109
+ return minuteWord + " " + minuteUnit + " and " + getNumber(secondField, opts) + " " + pluralize(secondField, "second") + " past the hour, every hour" + trailingQualifier(schedule, opts);
993
1110
  }
994
- return secondsLeadClause(ir, opts) + ", " + minuteWord + " " + minuteUnit + " past the hour, every hour" + trailingQualifier(ir, opts);
1111
+ return secondsLeadClause(schedule, opts) + ", " + minuteWord + " " + minuteUnit + " past the hour, every hour" + trailingQualifier(schedule, opts);
995
1112
  }
996
- function composeHourCadence(ir, plan, opts) {
1113
+ function composeHourCadence(schedule, plan, opts) {
997
1114
  const clockRest = plan.rest.kind === "clockTimes" || plan.rest.kind === "compactClockTimes";
998
- if (!clockRest || ir.shapes.minute !== "single") {
1115
+ if (!clockRest || schedule.shapes.minute !== "single") {
999
1116
  return null;
1000
1117
  }
1001
- const minute = +ir.pattern.minute;
1002
- return hourCadence(ir, minute, opts) ?? hourRangeCadence(ir, minute, opts);
1118
+ const minute = +schedule.pattern.minute;
1119
+ return hourCadence(schedule, minute, opts) ?? hourRangeCadence(schedule, minute, opts);
1003
1120
  }
1004
- function clockTimesConfinement(ir, rest, opts) {
1005
- if (+rest.times[0].minute === 0 && ir.shapes.minute === "single") {
1006
- return secondsLeadClause(ir, opts) + " for one minute at " + durationHours(ir, rest, opts);
1121
+ function clockTimesConfinement(schedule, rest, opts) {
1122
+ if (+rest.times[0].minute === 0 && schedule.shapes.minute === "single") {
1123
+ return secondsLeadClause(schedule, opts) + " for one minute at " + durationHours(schedule, rest, opts);
1007
1124
  }
1008
- return secondsLeadClause(ir, opts) + " of " + clockTimesOf(ir, rest, opts);
1125
+ return secondsLeadClause(schedule, opts) + " of " + clockTimesOf(schedule, rest, opts);
1009
1126
  }
1010
- function renderComposeSeconds(ir, plan, opts) {
1011
- const cadence = composeHourCadence(ir, plan, opts);
1127
+ function renderComposeSeconds(schedule, plan, opts) {
1128
+ const cadence = composeHourCadence(schedule, plan, opts);
1012
1129
  if (cadence !== null) {
1013
1130
  return cadence;
1014
1131
  }
1015
- if (plan.rest.kind === "clockTimes" && (ir.shapes.second === "wildcard" || ir.shapes.second === "step")) {
1016
- return clockTimesConfinement(ir, plan.rest, opts);
1132
+ if (plan.rest.kind === "clockTimes" && (schedule.shapes.second === "wildcard" || schedule.shapes.second === "step")) {
1133
+ return clockTimesConfinement(schedule, plan.rest, opts);
1017
1134
  }
1018
- if (ir.shapes.second === "wildcard" && plan.rest.kind === "minuteFrequency" && plan.rest.hours.kind === "none" && ir.pattern.minute === "*/2") {
1019
- return "every second of every other minute" + trailingQualifier(ir, opts);
1135
+ if (schedule.shapes.second === "wildcard" && plan.rest.kind === "minuteFrequency" && plan.rest.hours.kind === "none" && schedule.pattern.minute === "*/2") {
1136
+ return "every second of every other minute" + trailingQualifier(schedule, opts);
1020
1137
  }
1021
- const restOwnsLead = plan.rest.kind === "compactClockTimes" && ir.analyses.clockSecond;
1022
- const lead = restOwnsLead ? "" : secondsLeadClause(ir, opts) + ", ";
1023
- return lead + render(ir, plan.rest, opts);
1138
+ const restOwnsLead = plan.rest.kind === "compactClockTimes" && schedule.analyses.clockSecond;
1139
+ const lead = restOwnsLead ? "" : secondsLeadClause(schedule, opts) + ", ";
1140
+ return lead + render(schedule, plan.rest, opts);
1024
1141
  }
1025
- function durationHours(ir, plan, opts) {
1142
+ function durationHours(schedule, plan, opts) {
1026
1143
  const hours = plan.times.map(function clock(time) {
1027
1144
  return getTime({ hour: time.hour, minute: 0 }, opts);
1028
1145
  });
1029
- const trail = dayQualifier(ir, leadingWords, opts);
1146
+ const trail = dayQualifier(schedule, leadingWords, opts);
1030
1147
  return joinList(hours, opts) + (trail && ", " + trail);
1031
1148
  }
1032
- function clockTimesOf(ir, plan, opts) {
1149
+ function clockTimesOf(schedule, plan, opts) {
1033
1150
  const times = plan.times.map(function clock(time) {
1034
1151
  return getTime({
1035
1152
  hour: time.hour,
@@ -1038,21 +1155,21 @@ function clockTimesOf(ir, plan, opts) {
1038
1155
  explicit: true
1039
1156
  }, opts);
1040
1157
  });
1041
- const trail = dayQualifier(ir, leadingWords, opts);
1158
+ const trail = dayQualifier(schedule, leadingWords, opts);
1042
1159
  return joinList(times, opts) + (trail && ", " + trail);
1043
1160
  }
1044
- function secondsLeadClause(ir, opts) {
1045
- return secondsClause(ir, "minute", opts);
1161
+ function secondsLeadClause(schedule, opts) {
1162
+ return secondsClause(schedule, "minute", opts);
1046
1163
  }
1047
- function secondsClause(ir, anchor, opts) {
1048
- const secondField = ir.pattern.second;
1049
- const shape = ir.shapes.second;
1164
+ function secondsClause(schedule, anchor, opts) {
1165
+ const secondField = schedule.pattern.second;
1166
+ const shape = schedule.shapes.second;
1050
1167
  if (secondField === "*") {
1051
1168
  return "every second";
1052
1169
  }
1053
1170
  if (shape === "step") {
1054
1171
  return stepCycle60(
1055
- ir.analyses.segments.second[0],
1172
+ stepSegment(schedule, "second"),
1056
1173
  "second",
1057
1174
  anchor,
1058
1175
  opts
@@ -1067,86 +1184,96 @@ function secondsClause(ir, anchor, opts) {
1067
1184
  return "at " + getNumber(secondField, opts) + " " + pluralize(secondField, "second") + " past the " + anchor;
1068
1185
  }
1069
1186
  return strideFromSegments(
1070
- ir.analyses.segments.second,
1187
+ segmentsOf(schedule, "second"),
1071
1188
  "second",
1072
1189
  anchor,
1073
1190
  opts
1074
1191
  ) ?? listPastThe(
1075
- segmentWords(ir.analyses.segments.second, opts),
1192
+ segmentWords(segmentsOf(schedule, "second"), opts),
1076
1193
  "second",
1077
1194
  anchor,
1078
1195
  opts
1079
1196
  );
1080
1197
  }
1081
- function renderEveryMinute(ir, plan, opts) {
1082
- return "every minute" + trailingQualifier(ir, opts);
1198
+ function renderEveryMinute(schedule, plan, opts) {
1199
+ return "every minute" + trailingQualifier(schedule, opts);
1083
1200
  }
1084
- function renderSingleMinute(ir, plan, opts) {
1085
- const minuteField = ir.pattern.minute;
1086
- return getNumber(minuteField, opts) + " " + pluralize(minuteField, "minute") + " past the hour, every hour" + trailingQualifier(ir, opts);
1201
+ function renderSingleMinute(schedule, plan, opts) {
1202
+ const minuteField = schedule.pattern.minute;
1203
+ return getNumber(minuteField, opts) + " " + pluralize(minuteField, "minute") + " past the hour, every hour" + trailingQualifier(schedule, opts);
1087
1204
  }
1088
- function renderRangeOfMinutes(ir, plan, opts) {
1089
- return minuteRangeLead(ir.pattern.minute, opts) + trailingQualifier(ir, opts);
1205
+ function renderRangeOfMinutes(schedule, plan, opts) {
1206
+ return minuteRangeLead(schedule.pattern.minute, opts) + trailingQualifier(schedule, opts);
1090
1207
  }
1091
- function renderMultipleMinutes(ir, plan, opts) {
1092
- const stride = strideFromSegments(ir.analyses.segments.minute, "minute", "hour", opts);
1208
+ function renderMultipleMinutes(schedule, plan, opts) {
1209
+ const stride = strideFromSegments(segmentsOf(schedule, "minute"), "minute", "hour", opts);
1093
1210
  return (stride ?? listPastThe(segmentWords(
1094
- ir.analyses.segments.minute,
1211
+ segmentsOf(schedule, "minute"),
1095
1212
  opts
1096
- ), "minute", "hour", opts)) + trailingQualifier(ir, opts);
1213
+ ), "minute", "hour", opts)) + trailingQualifier(schedule, opts);
1097
1214
  }
1098
- function renderMinuteFrequency(ir, plan, opts) {
1215
+ function renderMinuteFrequency(schedule, plan, opts) {
1099
1216
  let phrase = stepCycle60(
1100
- ir.analyses.segments.minute[0],
1217
+ stepSegment(schedule, "minute"),
1101
1218
  "minute",
1102
1219
  "hour",
1103
1220
  opts
1104
1221
  );
1105
1222
  if (plan.hours.kind === "during") {
1106
- const cadence = unevenHourCadence(ir, opts);
1107
- phrase += cadence ? ", " + cadence : " during the " + hourTimesFromPlan(ir, plan.hours.times, false, opts) + " hours";
1223
+ const cadence = unevenHourCadence(schedule, opts);
1224
+ phrase += cadence ? ", " + cadence : " during the " + hourTimesFromPlan(schedule, plan.hours.times, false, opts) + " hours";
1108
1225
  } else if (plan.hours.kind === "window") {
1109
- phrase += " " + hourWindow(plan.hours, opts);
1226
+ phrase += " " + rangeWindow({
1227
+ continuous: false,
1228
+ from: plan.hours.from,
1229
+ throughMinute: plan.hours.last,
1230
+ to: plan.hours.to
1231
+ }, opts);
1110
1232
  } else if (plan.hours.kind === "step") {
1111
- phrase += " " + everyNthHour(ir.analyses.segments.hour[0], opts);
1233
+ phrase += " " + everyNthHour(stepSegment(schedule, "hour"), opts);
1112
1234
  }
1113
- return phrase + trailingQualifier(ir, opts);
1235
+ return phrase + trailingQualifier(schedule, opts);
1114
1236
  }
1115
- function renderMinuteSpanInHour(ir, plan, opts) {
1116
- if (ir.pattern.minute === "*") {
1117
- return "every minute of the " + getTime({ hour: plan.hour, minute: 0 }, opts) + " hour" + trailingQualifier(ir, opts);
1237
+ function renderMinuteSpanInHour(schedule, plan, opts) {
1238
+ if (schedule.pattern.minute === "*") {
1239
+ return "every minute of the " + getTime({ hour: plan.hour, minute: 0 }, opts) + " hour" + trailingQualifier(schedule, opts);
1118
1240
  }
1119
- return "every minute from " + getTime({ hour: plan.hour, minute: plan.span[0] }, opts) + through(opts) + getTime({ hour: plan.hour, minute: plan.span[1] }, opts) + trailingQualifier(ir, opts);
1241
+ return "every minute from " + getTime({ hour: plan.hour, minute: plan.span[0] }, opts) + through(opts) + getTime({ hour: plan.hour, minute: plan.span[1] }, opts) + trailingQualifier(schedule, opts);
1120
1242
  }
1121
- function renderMinutesAcrossHours(ir, plan, opts) {
1122
- const cadence = unevenHourCadence(ir, opts);
1243
+ function renderMinutesAcrossHours(schedule, plan, opts) {
1244
+ const cadence = unevenHourCadence(schedule, opts);
1123
1245
  if (plan.form === "wildcard") {
1124
1246
  if (cadence !== null) {
1125
- return "every minute, " + cadence + trailingQualifier(ir, opts);
1247
+ return "every minute, " + cadence + trailingQualifier(schedule, opts);
1126
1248
  }
1127
- return "every minute during the " + hourTimesFromPlan(ir, plan.times, false, opts) + " hours" + trailingQualifier(ir, opts);
1249
+ return "every minute during the " + hourTimesFromPlan(schedule, plan.times, false, opts) + " hours" + trailingQualifier(schedule, opts);
1128
1250
  }
1129
1251
  if (plan.form === "range") {
1130
- const lead2 = minuteRangeLead(ir.pattern.minute, opts);
1252
+ const lead2 = minuteRangeLead(schedule.pattern.minute, opts);
1131
1253
  if (cadence !== null) {
1132
- return lead2 + ", " + cadence + trailingQualifier(ir, opts);
1254
+ return lead2 + ", " + cadence + trailingQualifier(schedule, opts);
1133
1255
  }
1134
1256
  if (singleHourFire(plan.times)) {
1135
- return lead2 + ", at " + hourTimesFromPlan(ir, plan.times, true, opts) + trailingQualifier(ir, opts);
1257
+ return lead2 + ", at " + hourTimesFromPlan(schedule, plan.times, true, opts) + trailingQualifier(schedule, opts);
1136
1258
  }
1137
- return lead2 + " during the " + hourTimesFromPlan(ir, plan.times, false, opts) + " hours" + trailingQualifier(ir, opts);
1259
+ return lead2 + " during the " + hourTimesFromPlan(schedule, plan.times, false, opts) + " hours" + trailingQualifier(schedule, opts);
1138
1260
  }
1139
- const lead = strideFromSegments(ir.analyses.segments.minute, "minute", "hour", opts) ?? listPastThe(
1140
- segmentWords(ir.analyses.segments.minute, opts),
1261
+ const lead = strideFromSegments(
1262
+ segmentsOf(schedule, "minute"),
1263
+ "minute",
1264
+ "hour",
1265
+ opts
1266
+ ) ?? listPastThe(
1267
+ segmentWords(segmentsOf(schedule, "minute"), opts),
1141
1268
  "minute",
1142
1269
  "hour",
1143
1270
  opts
1144
1271
  );
1145
1272
  if (cadence !== null) {
1146
- return lead + ", " + cadence + trailingQualifier(ir, opts);
1273
+ return lead + ", " + cadence + trailingQualifier(schedule, opts);
1147
1274
  }
1148
- const times = hourTimesFromPlan(ir, plan.times, true, opts);
1149
- return lead + ", at " + times + trailingQualifier(ir, opts);
1275
+ const times = hourTimesFromPlan(schedule, plan.times, true, opts);
1276
+ return lead + ", at " + times + trailingQualifier(schedule, opts);
1150
1277
  }
1151
1278
  var stepOrdinals = {
1152
1279
  2: "other",
@@ -1161,79 +1288,91 @@ function everyNthHour(segment, opts) {
1161
1288
  const start = segment.startToken === "*" ? 0 : +segment.startToken;
1162
1289
  return start === 0 ? base : base + " starting at " + getTime({ hour: start, minute: 0 }, opts);
1163
1290
  }
1164
- function renderMinuteSpanAcrossHourStep(ir, plan, opts) {
1165
- const segment = ir.analyses.segments.hour[0];
1291
+ function renderMinuteSpanAcrossHourStep(schedule, plan, opts) {
1292
+ const segment = stepSegment(schedule, "hour");
1166
1293
  if (plan.form === "wildcard") {
1167
- return "every minute " + everyNthHour(segment, opts) + trailingQualifier(ir, opts);
1294
+ return "every minute " + everyNthHour(segment, opts) + trailingQualifier(schedule, opts);
1168
1295
  }
1169
- const lead = plan.form === "list" ? strideFromSegments(ir.analyses.segments.minute, "minute", "hour", opts) ?? listPastThe(
1170
- segmentWords(ir.analyses.segments.minute, opts),
1296
+ const lead = plan.form === "list" ? strideFromSegments(
1297
+ segmentsOf(schedule, "minute"),
1298
+ "minute",
1299
+ "hour",
1300
+ opts
1301
+ ) ?? listPastThe(
1302
+ segmentWords(segmentsOf(schedule, "minute"), opts),
1171
1303
  "minute",
1172
1304
  "hour",
1173
1305
  opts
1174
- ) : minuteRangeLead(ir.pattern.minute, opts);
1175
- const cadence = unevenHourCadence(ir, opts);
1176
- return lead + ", " + (cadence ?? stepHours(segment, opts)) + trailingQualifier(ir, opts);
1306
+ ) : minuteRangeLead(schedule.pattern.minute, opts);
1307
+ const cadence = unevenHourCadence(schedule, opts);
1308
+ return lead + ", " + (cadence ?? stepHours(segment, opts)) + trailingQualifier(schedule, opts);
1177
1309
  }
1178
1310
  function minuteRangeLead(minuteField, opts) {
1179
1311
  const bounds = minuteField.split("-");
1180
1312
  const num = seriesNumber();
1181
1313
  return "every minute from " + num(bounds[0]) + through(opts) + num(bounds[1]) + " past the hour";
1182
1314
  }
1183
- function renderEveryHour(ir, plan, opts) {
1184
- return "every hour" + trailingQualifier(ir, opts);
1315
+ function renderEveryHour(schedule, plan, opts) {
1316
+ return "every hour" + trailingQualifier(schedule, opts);
1185
1317
  }
1186
- function renderHourRange(ir, plan, opts) {
1318
+ function renderHourRange(schedule, plan, opts) {
1187
1319
  const window = hourWindow(boundedWindow(plan), opts);
1188
1320
  if (plan.minuteForm === "wildcard") {
1189
- return "every minute " + window + trailingQualifier(ir, opts);
1321
+ return "every minute " + window + trailingQualifier(schedule, opts);
1190
1322
  }
1191
1323
  if (plan.minuteForm === "range") {
1192
- return minuteRangeLead(ir.pattern.minute, opts) + ", " + window + trailingQualifier(ir, opts);
1324
+ return minuteRangeLead(schedule.pattern.minute, opts) + ", " + window + trailingQualifier(schedule, opts);
1193
1325
  }
1194
- return rangeMinuteLead(ir, opts) + " " + window + trailingQualifier(ir, opts);
1326
+ return rangeMinuteLead(schedule, opts) + " " + window + trailingQualifier(schedule, opts);
1195
1327
  }
1196
- function rangeMinuteLead(ir, opts) {
1197
- if (ir.pattern.minute === "0") {
1328
+ function rangeMinuteLead(schedule, opts) {
1329
+ if (schedule.pattern.minute === "0") {
1198
1330
  return "every hour";
1199
1331
  }
1200
1332
  return strideFromSegments(
1201
- ir.analyses.segments.minute,
1333
+ segmentsOf(schedule, "minute"),
1202
1334
  "minute",
1203
1335
  "hour",
1204
1336
  opts
1205
1337
  ) ?? listPastThe(
1206
- segmentWords(ir.analyses.segments.minute, opts),
1338
+ segmentWords(segmentsOf(schedule, "minute"), opts),
1207
1339
  "minute",
1208
1340
  "hour",
1209
1341
  opts
1210
1342
  );
1211
1343
  }
1212
- function renderHourStep(ir, plan, opts) {
1213
- const cadence = unevenHourCadence(ir, opts);
1344
+ function renderHourStep(schedule, plan, opts) {
1345
+ const cadence = unevenHourCadence(schedule, opts);
1214
1346
  if (cadence !== null) {
1215
- return cadence + trailingQualifier(ir, opts);
1347
+ return cadence + trailingQualifier(schedule, opts);
1216
1348
  }
1217
- return stepHours(ir.analyses.segments.hour[0], opts) + trailingQualifier(ir, opts);
1349
+ return stepHours(stepSegment(schedule, "hour"), opts) + trailingQualifier(schedule, opts);
1218
1350
  }
1219
1351
  function boundedWindow(plan) {
1220
- const last = plan.minuteForm === "wildcard" ? plan.boundMinute ?? 0 : 0;
1221
- return { from: plan.from, last, to: plan.to };
1352
+ const continuous = plan.minuteForm === "wildcard";
1353
+ const closeMinute = continuous ? plan.boundMinute ?? 0 : 0;
1354
+ return { from: plan.from, closeMinute, to: plan.to, continuous };
1222
1355
  }
1223
- function rangeWindow(from, to, throughMinute, opts) {
1356
+ function rangeWindow(window, opts) {
1357
+ const { from, to, throughMinute, continuous } = window;
1224
1358
  const open = "from " + getTime({ hour: from, minute: 0 }, opts);
1225
1359
  if (opts.style.untilWindow && !opts.short && from !== to) {
1226
- return open + " until " + getTime({ hour: (to + 1) % 24, minute: 0 }, opts);
1360
+ return continuous ? open + " until " + getTime({ hour: (to + 1) % 24, minute: 0 }, opts) : open + through(opts) + getTime({ hour: to, minute: 0 }, opts);
1227
1361
  }
1228
1362
  return open + through(opts) + getTime({ hour: to, minute: throughMinute }, opts);
1229
1363
  }
1230
1364
  function hourWindow(window, opts) {
1231
- return rangeWindow(window.from, window.to, window.last, opts);
1365
+ return rangeWindow({
1366
+ continuous: window.continuous,
1367
+ from: window.from,
1368
+ throughMinute: window.closeMinute,
1369
+ to: window.to
1370
+ }, opts);
1232
1371
  }
1233
- function renderClockTimes(ir, plan, opts) {
1234
- if (ir.shapes.minute === "single") {
1235
- const minute = +ir.pattern.minute;
1236
- const cadence = hourCadence(ir, minute, opts) ?? hourRangeCadence(ir, minute, opts);
1372
+ function renderClockTimes(schedule, plan, opts) {
1373
+ if (schedule.shapes.minute === "single") {
1374
+ const minute = +schedule.pattern.minute;
1375
+ const cadence = hourCadence(schedule, minute, opts) ?? hourRangeCadence(schedule, minute, opts);
1237
1376
  if (cadence !== null) {
1238
1377
  return cadence;
1239
1378
  }
@@ -1247,93 +1386,94 @@ function renderClockTimes(ir, plan, opts) {
1247
1386
  plain
1248
1387
  }, opts);
1249
1388
  });
1250
- return interpretDayQualifier(ir, opts) + "at " + joinList(times, opts) + dayUnionTrail(ir, opts);
1389
+ return interpretDayQualifier(schedule, opts) + "at " + joinList(times, opts) + dayUnionTrail(schedule, opts);
1251
1390
  }
1252
- function dayUnionTrail(ir, opts) {
1253
- return isDayUnion(ir, opts) ? dayUnionCondition(ir, opts) : "";
1391
+ function dayUnionTrail(schedule, opts) {
1392
+ return isDayUnion(schedule, opts) ? dayUnionCondition(schedule, opts) : "";
1254
1393
  }
1255
- function renderCompactClockTimes(ir, plan, opts) {
1394
+ function renderCompactClockTimes(schedule, plan, opts) {
1256
1395
  if (plan.fold) {
1257
- const cadence2 = hourCadence(ir, +plan.minute, opts) ?? hourRangeCadence(ir, +plan.minute, opts);
1396
+ const cadence2 = hourCadence(schedule, +plan.minute, opts) ?? hourRangeCadence(schedule, +plan.minute, opts);
1258
1397
  if (cadence2 !== null) {
1259
1398
  return cadence2;
1260
1399
  }
1261
- const hasRange = ir.analyses.segments.hour.some(function range(segment) {
1400
+ const hasRange = segmentsOf(schedule, "hour").some(function range(segment) {
1262
1401
  return segment.kind === "range";
1263
1402
  });
1264
- if (hasRange && !ir.analyses.clockSecond) {
1265
- return foldedHourWindows(ir, plan, opts) + trailingQualifier(ir, opts);
1403
+ if (hasRange && !schedule.analyses.clockSecond) {
1404
+ return foldedHourWindows(schedule, plan, opts) + trailingQualifier(schedule, opts);
1266
1405
  }
1267
- const fold = { minute: plan.minute, second: ir.analyses.clockSecond };
1268
- return interpretDayQualifier(ir, opts) + "at " + hourSegmentTimes(ir, fold, true, opts) + dayUnionTrail(ir, opts);
1406
+ const fold = { minute: plan.minute, second: schedule.analyses.clockSecond };
1407
+ return interpretDayQualifier(schedule, opts) + "at " + hourSegmentTimes(schedule, fold, true, opts) + dayUnionTrail(schedule, opts);
1269
1408
  }
1270
1409
  const minuteLead = (
1271
1410
  // The non-fold branch is a minute list, which has segments. An
1272
1411
  // offset/uneven step enumerated to that list reads as a stride.
1273
- strideFromSegments(ir.analyses.segments.minute, "minute", "hour", opts) ?? listPastThe(
1274
- segmentWords(ir.analyses.segments.minute, opts),
1412
+ strideFromSegments(
1413
+ segmentsOf(schedule, "minute"),
1414
+ "minute",
1415
+ "hour",
1416
+ opts
1417
+ ) ?? listPastThe(
1418
+ segmentWords(segmentsOf(schedule, "minute"), opts),
1275
1419
  "minute",
1276
1420
  "hour",
1277
1421
  opts
1278
1422
  )
1279
1423
  );
1280
- const cadence = unevenHourCadence(ir, opts);
1281
- const phrase = cadence ? minuteLead + ", " + cadence + trailingQualifier(ir, opts) : minuteLead + ", at " + hourSegmentTimes(ir, { minute: 0, second: null }, true, opts) + trailingQualifier(ir, opts);
1282
- return ir.analyses.clockSecond ? secondsLeadClause(ir, opts) + ", " + phrase : phrase;
1424
+ const cadence = unevenHourCadence(schedule, opts);
1425
+ const phrase = cadence ? minuteLead + ", " + cadence + trailingQualifier(schedule, opts) : minuteLead + ", at " + hourSegmentTimes(schedule, { minute: 0, second: null }, true, opts) + trailingQualifier(schedule, opts);
1426
+ return schedule.analyses.clockSecond ? secondsLeadClause(schedule, opts) + ", " + phrase : phrase;
1283
1427
  }
1284
- function foldedHourWindows(ir, plan, opts) {
1428
+ function foldedHourWindows(schedule, plan, opts) {
1285
1429
  const minute = plan.minute;
1286
1430
  const windows = [];
1287
- const outliers = collectHourOutliers(ir);
1288
- const times = outliers.hours.map(function time(hour) {
1431
+ const times = collectHourOutliers(schedule).map(function time(hour) {
1289
1432
  return getTime({ hour, minute }, opts);
1290
1433
  });
1291
- ir.analyses.segments.hour.forEach(function classify(segment) {
1434
+ segmentsOf(schedule, "hour").forEach(function classify(segment) {
1292
1435
  if (segment.kind === "range") {
1293
- windows.push(rangeWindow(
1294
- +segment.bounds[0],
1295
- +segment.bounds[1],
1296
- minute,
1297
- opts
1298
- ));
1436
+ windows.push(rangeWindow({
1437
+ continuous: false,
1438
+ from: +segment.bounds[0],
1439
+ throughMinute: minute,
1440
+ to: +segment.bounds[1]
1441
+ }, opts));
1299
1442
  }
1300
1443
  });
1301
- const phrase = rangeMinuteLead(ir, opts) + " " + joinList(windows, opts);
1302
- return phrase + outlierTail(times, outliers.pureStrays, opts);
1444
+ const phrase = rangeMinuteLead(schedule, opts) + " " + joinList(windows, opts);
1445
+ return phrase + outlierTail(times, opts);
1303
1446
  }
1304
- function collectHourOutliers(ir) {
1447
+ function collectHourOutliers(schedule) {
1305
1448
  const hours = [];
1306
- let pureStrays = true;
1307
- ir.analyses.segments.hour.forEach(function classify(segment) {
1449
+ segmentsOf(schedule, "hour").forEach(function classify(segment) {
1308
1450
  if (segment.kind === "step") {
1309
1451
  hours.push(...segment.fires);
1310
- pureStrays = false;
1311
1452
  } else if (segment.kind !== "range") {
1312
1453
  hours.push(+segment.value);
1313
1454
  }
1314
1455
  });
1315
- return { hours, pureStrays };
1456
+ return hours;
1316
1457
  }
1317
- function outlierTail(times, pureStrays, opts) {
1458
+ function outlierTail(times, opts) {
1318
1459
  if (!times.length) {
1319
1460
  return "";
1320
1461
  }
1321
- const connector = pureStrays && opts.style.untilWindow && !opts.short ? " plus " : " and at ";
1322
- return connector + joinList(times, opts);
1462
+ return " and at " + joinList(times, opts);
1323
1463
  }
1324
1464
  function isCadenceField(token) {
1325
1465
  return token === "*" || token.startsWith("*/") && token.indexOf("-") === -1;
1326
1466
  }
1327
- function leadingCadence(ir, opts) {
1328
- const { second, minute } = ir.pattern;
1467
+ function leadingCadence(schedule, opts) {
1468
+ const { second, minute } = schedule.pattern;
1329
1469
  if (isCadenceField(second)) {
1330
- return { secondLead: true, text: secondsClause(ir, "minute", opts) };
1470
+ return { secondLead: true, text: secondsClause(schedule, "minute", opts) };
1331
1471
  }
1332
1472
  if (second === "0" && isCadenceField(minute)) {
1333
1473
  const text = minute === "*" ? "every minute" : (
1334
1474
  // A clean minute step's first segment is a step segment.
1335
1475
  stepCycle60(
1336
- ir.analyses.segments.minute[0],
1476
+ stepSegment(schedule, "minute"),
1337
1477
  "minute",
1338
1478
  "hour",
1339
1479
  opts
@@ -1343,19 +1483,19 @@ function leadingCadence(ir, opts) {
1343
1483
  }
1344
1484
  return null;
1345
1485
  }
1346
- function minuteConfinement(ir, opts) {
1347
- const minute = ir.pattern.minute;
1486
+ function minuteConfinement(schedule, opts) {
1487
+ const minute = schedule.pattern.minute;
1348
1488
  if (minute === "*") {
1349
1489
  return "";
1350
1490
  }
1351
1491
  if (isCadenceField(minute)) {
1352
1492
  return " of every other minute";
1353
1493
  }
1354
- const segments = ir.analyses.segments.minute;
1355
- if (ir.shapes.minute === "single") {
1494
+ const segments = segmentsOf(schedule, "minute");
1495
+ if (schedule.shapes.minute === "single") {
1356
1496
  return " during minute :" + pad(minute);
1357
1497
  }
1358
- if (ir.shapes.minute === "range") {
1498
+ if (schedule.shapes.minute === "range") {
1359
1499
  const bounds = minute.split("-");
1360
1500
  return " during minutes :" + pad(bounds[0]) + through(opts) + ":" + pad(bounds[1]);
1361
1501
  }
@@ -1364,59 +1504,61 @@ function minuteConfinement(ir, opts) {
1364
1504
  });
1365
1505
  return " during minutes " + joinList(values, opts);
1366
1506
  }
1367
- function hourConfinement(ir, opts) {
1368
- const hour = ir.pattern.hour;
1507
+ function hourConfinement(schedule, opts) {
1508
+ const hour = schedule.pattern.hour;
1369
1509
  if (hour === "*") {
1370
- const minutePinned = ir.pattern.minute !== "*" && !isCadenceField(ir.pattern.minute);
1510
+ const minutePinned = schedule.pattern.minute !== "*" && !isCadenceField(schedule.pattern.minute);
1371
1511
  return minutePinned ? " of every hour" : "";
1372
1512
  }
1373
1513
  if (isCadenceField(hour)) {
1374
1514
  return hour === "*/2" ? " of every other hour" : "";
1375
1515
  }
1376
- if (ir.shapes.hour === "single") {
1516
+ if (schedule.shapes.hour === "single") {
1377
1517
  const h = +hour;
1378
- if (ir.shapes.minute === "step") {
1518
+ if (schedule.shapes.minute === "step") {
1379
1519
  return " from " + getTime({ hour: h, minute: 0 }, opts) + " until " + getTime({ hour: (h + 1) % 24, minute: 0 }, opts);
1380
1520
  }
1381
- if (ir.pattern.minute !== "*" && !isCadenceField(ir.pattern.minute)) {
1521
+ if (schedule.pattern.minute !== "*" && !isCadenceField(schedule.pattern.minute)) {
1382
1522
  return " at " + getTime({ hour: h, minute: 0 }, opts);
1383
1523
  }
1384
1524
  return " of the " + getTime({ hour: h, minute: 0 }, opts) + " hour";
1385
1525
  }
1386
- if (ir.shapes.hour === "range") {
1526
+ if (schedule.shapes.hour === "range") {
1387
1527
  const bounds = hour.split("-");
1388
- return " " + rangeWindow(+bounds[0], +bounds[1], 0, opts);
1528
+ return " " + rangeWindow({
1529
+ continuous: schedule.pattern.minute === "*",
1530
+ from: +bounds[0],
1531
+ throughMinute: 0,
1532
+ to: +bounds[1]
1533
+ }, opts);
1389
1534
  }
1390
- return " during the " + hourSegmentTimes(ir, { minute: 0, second: null }, false, opts) + " hours";
1535
+ return " during the " + hourSegmentTimes(schedule, { minute: 0, second: null }, false, opts) + " hours";
1391
1536
  }
1392
- function isContiguousHourRange(ir) {
1393
- return ir.shapes.hour === "range";
1394
- }
1395
- function confinableHour(ir) {
1396
- if (ir.shapes.hour !== "step") {
1537
+ function confinableHour(schedule) {
1538
+ if (schedule.shapes.hour !== "step") {
1397
1539
  return true;
1398
1540
  }
1399
- const segment = ir.analyses.segments.hour[0];
1400
- return ir.pattern.hour === "*/2" || segment.startToken.indexOf("-") !== -1;
1541
+ const segment = stepSegment(schedule, "hour");
1542
+ return schedule.pattern.hour === "*/2" || segment.startToken.indexOf("-") !== -1;
1401
1543
  }
1402
- function isMinuteStride(ir) {
1403
- if (ir.shapes.minute !== "list") {
1544
+ function isMinuteStride(schedule) {
1545
+ if (schedule.shapes.minute !== "list") {
1404
1546
  return false;
1405
1547
  }
1406
- const values = singleValues(ir.analyses.segments.minute);
1548
+ const values = singleValues(segmentsOf(schedule, "minute"));
1407
1549
  return values !== null && arithmeticStep(values) !== null;
1408
1550
  }
1409
- function confinementEligible(ir, lead) {
1410
- const { minute, hour } = ir.pattern;
1551
+ function confinementEligible(schedule, lead) {
1552
+ const { minute, hour } = schedule.pattern;
1411
1553
  const minuteStep = isCadenceField(minute) && minute !== "*";
1412
- if (!confinableHour(ir)) {
1554
+ if (!confinableHour(schedule)) {
1413
1555
  return false;
1414
1556
  }
1415
1557
  if (lead.secondLead) {
1416
1558
  if (minuteStep) {
1417
- return minute === "*/2" && !isContiguousHourRange(ir);
1559
+ return minute === "*/2" && schedule.shapes.hour !== "range";
1418
1560
  }
1419
- if (isMinuteStride(ir) || ir.shapes.minute === "list" && ir.shapes.hour === "list") {
1561
+ if (isMinuteStride(schedule) || schedule.shapes.minute === "list" && schedule.shapes.hour === "list") {
1420
1562
  return false;
1421
1563
  }
1422
1564
  return true;
@@ -1424,21 +1566,21 @@ function confinementEligible(ir, lead) {
1424
1566
  if (hour === "*/2") {
1425
1567
  return true;
1426
1568
  }
1427
- return ir.shapes.hour === "single" && minute === "*/2";
1569
+ return schedule.shapes.hour === "single" && minute === "*/2";
1428
1570
  }
1429
- function confinement(ir, opts) {
1571
+ function confinement(schedule, opts) {
1430
1572
  if (!opts.style.untilWindow || opts.short) {
1431
1573
  return null;
1432
1574
  }
1433
- if (ir.pattern.minute === "*" && ir.pattern.hour === "*") {
1575
+ if (schedule.pattern.minute === "*" && schedule.pattern.hour === "*") {
1434
1576
  return null;
1435
1577
  }
1436
- const lead = leadingCadence(ir, opts);
1437
- if (!lead || !confinementEligible(ir, lead)) {
1578
+ const lead = leadingCadence(schedule, opts);
1579
+ if (!lead || !confinementEligible(schedule, lead)) {
1438
1580
  return null;
1439
1581
  }
1440
- const minutePart = lead.secondLead ? minuteConfinement(ir, opts) : "";
1441
- return lead.text + minutePart + hourConfinement(ir, opts) + trailingQualifier(ir, opts);
1582
+ const minutePart = lead.secondLead ? minuteConfinement(schedule, opts) : "";
1583
+ return lead.text + minutePart + hourConfinement(schedule, opts) + trailingQualifier(schedule, opts);
1442
1584
  }
1443
1585
  var renderers = {
1444
1586
  clockTimes: renderClockTimes,
@@ -1460,33 +1602,25 @@ var renderers = {
1460
1602
  singleMinute: renderSingleMinute,
1461
1603
  standaloneSeconds: renderStandaloneSeconds
1462
1604
  };
1463
- function renderStride(stride, opts) {
1605
+ function renderStride2(stride, opts) {
1464
1606
  const { interval, start, last, cycle, unit, anchor } = stride;
1465
1607
  const cadence = "every " + getNumber(interval, opts) + " " + unit + "s";
1466
- const tiles = cycle % interval === 0;
1467
- if (start === 0 && tiles) {
1468
- return cadence;
1469
- }
1470
- if (start < interval && tiles) {
1471
- return cadence + " from " + getNumber(start, opts) + " " + pluralize(start, unit) + " past the " + anchor;
1472
- }
1473
- const num = seriesNumber();
1474
- return cadence + " from " + num(start) + through(opts) + num(last) + " " + pluralize(last, unit) + " past the " + anchor;
1475
- }
1476
- function singleValues(segments) {
1477
- const values = [];
1478
- for (const segment of segments) {
1479
- if (segment.kind !== "single") {
1480
- return null;
1608
+ return renderStride({ start, interval, cycle }, {
1609
+ bare: () => cadence,
1610
+ // A clean wrap from a non-zero offset: name the start, no endpoint.
1611
+ offset: () => cadence + " from " + getNumber(start, opts) + " " + pluralize(start, unit) + " past the " + anchor,
1612
+ // A bounded, non-wrapping set: pin both endpoints. Each bound is a value,
1613
+ // so it reads as a digit, matching the range idiom ("from 0 through 30").
1614
+ bounded: () => {
1615
+ const num = seriesNumber();
1616
+ return cadence + " from " + num(start) + through(opts) + num(last) + " " + pluralize(last, unit) + " past the " + anchor;
1481
1617
  }
1482
- values.push(+segment.value);
1483
- }
1484
- return values;
1618
+ });
1485
1619
  }
1486
1620
  function strideFromSegments(segments, unit, anchor, opts) {
1487
1621
  const values = singleValues(segments);
1488
1622
  const step = values && arithmeticStep(values);
1489
- return step ? renderStride({ ...step, cycle: 60, unit, anchor }, opts) : null;
1623
+ return step ? renderStride2({ ...step, cycle: 60, unit, anchor }, opts) : null;
1490
1624
  }
1491
1625
  function stepCycle60(segment, unit, anchor, opts) {
1492
1626
  if (segment.startToken.indexOf("-") !== -1) {
@@ -1496,7 +1630,7 @@ function stepCycle60(segment, unit, anchor, opts) {
1496
1630
  if (start !== 0 && segment.fires.length <= 3) {
1497
1631
  return listPastThe(numberWords(segment.fires, opts), unit, anchor, opts);
1498
1632
  }
1499
- return renderStride({
1633
+ return renderStride2({
1500
1634
  interval: segment.interval,
1501
1635
  start,
1502
1636
  last: segment.fires[segment.fires.length - 1],
@@ -1522,45 +1656,21 @@ function stepHours(segment, opts) {
1522
1656
  function hourStrideCadence(stride, opts) {
1523
1657
  const { start, interval, last } = stride;
1524
1658
  const cadence = "every " + getNumber(interval, opts) + " hours";
1525
- const tiles = 24 % interval === 0;
1526
- if (start === 0 && tiles) {
1527
- return cadence;
1528
- }
1529
- if (start < interval && tiles) {
1530
- return cadence + " from " + getTime({ hour: start, minute: 0 }, opts);
1531
- }
1532
- return cadence + " from " + getTime({ hour: start, minute: 0 }, opts) + through(opts) + getTime({ hour: last, minute: 0 }, opts);
1533
- }
1534
- function offsetCleanStride(stride) {
1535
- return stride.start < stride.interval && 24 % stride.interval === 0;
1659
+ return renderStride({ start, interval, cycle: 24 }, {
1660
+ bare: () => cadence,
1661
+ offset: () => cadence + " from " + getTime({ hour: start, minute: 0 }, opts),
1662
+ bounded: () => cadence + " from " + getTime({ hour: start, minute: 0 }, opts) + through(opts) + getTime({ hour: last, minute: 0 }, opts)
1663
+ });
1536
1664
  }
1537
- function unevenHourCadence(ir, opts) {
1538
- const stride = hourStride(ir);
1665
+ function unevenHourCadence(schedule, opts) {
1666
+ const stride = hourStride(schedule);
1539
1667
  if (!stride || offsetCleanStride(stride)) {
1540
1668
  return null;
1541
1669
  }
1542
1670
  return hourStrideCadence(stride, opts);
1543
1671
  }
1544
- function hourListStride(values) {
1545
- if (values.length < 2) {
1546
- return null;
1547
- }
1548
- const interval = values[1] - values[0];
1549
- if (interval < 2) {
1550
- return null;
1551
- }
1552
- for (let i = 2; i < values.length; i += 1) {
1553
- if (values[i] - values[i - 1] !== interval) {
1554
- return null;
1555
- }
1556
- }
1557
- if (values[0] !== 0 && values.length < 5) {
1558
- return null;
1559
- }
1560
- return { interval, last: values[values.length - 1], start: values[0] };
1561
- }
1562
- function hourStride(ir) {
1563
- const segments = ir.analyses.segments.hour;
1672
+ function hourStride(schedule) {
1673
+ const segments = segmentsOf(schedule, "hour");
1564
1674
  if (segments.length === 1 && segments[0].kind === "step") {
1565
1675
  const segment = segments[0];
1566
1676
  if (segment.fires.length < 2) {
@@ -1572,83 +1682,83 @@ function hourStride(ir) {
1572
1682
  const values = singleValues(segments);
1573
1683
  return values && hourListStride(values);
1574
1684
  }
1575
- function subMinuteSecond(ir) {
1576
- return ir.pattern.second === "*" || ir.shapes.second === "step";
1685
+ function subMinuteSecond(schedule) {
1686
+ return schedule.pattern.second === "*" || schedule.shapes.second === "step";
1577
1687
  }
1578
- function hourCadenceLead(ir, minute, opts) {
1688
+ function hourCadenceLead(schedule, minute, opts) {
1579
1689
  if (minute === 0) {
1580
- if (subMinuteSecond(ir)) {
1581
- return secondsClause(ir, "minute", opts) + " for one minute";
1690
+ if (subMinuteSecond(schedule)) {
1691
+ return secondsClause(schedule, "minute", opts) + " for one minute";
1582
1692
  }
1583
- return secondsClause(ir, "hour", opts);
1693
+ return secondsClause(schedule, "hour", opts);
1584
1694
  }
1585
1695
  const minutePhrase = getNumber(minute, opts) + " " + pluralize(minute, "minute") + " past the hour";
1586
- if (ir.pattern.second === "0") {
1696
+ if (schedule.pattern.second === "0") {
1587
1697
  return minutePhrase;
1588
1698
  }
1589
- return secondsClause(ir, "minute", opts) + ", " + minutePhrase;
1699
+ return secondsClause(schedule, "minute", opts) + ", " + minutePhrase;
1590
1700
  }
1591
- function hourCadence(ir, minute, opts) {
1592
- const stride = hourStride(ir);
1701
+ function hourCadence(schedule, minute, opts) {
1702
+ const stride = hourStride(schedule);
1593
1703
  if (!stride) {
1594
1704
  return null;
1595
1705
  }
1596
1706
  const fires = (stride.last - stride.start) / stride.interval + 1;
1597
- if (ir.pattern.second === "0" && fires <= maxClockTimes && offsetCleanStride(stride)) {
1707
+ if (schedule.pattern.second === "0" && fires <= maxClockTimes && offsetCleanStride(stride)) {
1598
1708
  return null;
1599
1709
  }
1600
- const minuteZeroStride = minute === 0 && subMinuteSecond(ir) && cleanStrideSegment(ir);
1710
+ const minuteZeroStride = minute === 0 && subMinuteSecond(schedule) && cleanStrideSegment(schedule);
1601
1711
  if (minuteZeroStride) {
1602
- return secondsClause(ir, "minute", opts) + " for one minute " + everyNthHour(minuteZeroStride, opts) + trailingQualifier(ir, opts);
1712
+ return secondsClause(schedule, "minute", opts) + " for one minute " + everyNthHour(minuteZeroStride, opts) + trailingQualifier(schedule, opts);
1603
1713
  }
1604
- if (minute === 0 && ir.pattern.second === "0") {
1605
- return hourStrideCadence(stride, opts) + trailingQualifier(ir, opts);
1714
+ if (minute === 0 && schedule.pattern.second === "0") {
1715
+ return hourStrideCadence(stride, opts) + trailingQualifier(schedule, opts);
1606
1716
  }
1607
- return hourCadenceLead(ir, minute, opts) + ", " + hourStrideCadence(stride, opts) + trailingQualifier(ir, opts);
1717
+ return hourCadenceLead(schedule, minute, opts) + ", " + hourStrideCadence(stride, opts) + trailingQualifier(schedule, opts);
1608
1718
  }
1609
- function cleanStrideSegment(ir) {
1610
- const segments = ir.analyses.segments.hour;
1719
+ function cleanStrideSegment(schedule) {
1720
+ const segments = segmentsOf(schedule, "hour");
1611
1721
  const segment = segments.length === 1 && segments[0];
1612
1722
  if (!segment || segment.kind !== "step" || segment.startToken.indexOf("-") !== -1 || !(segment.interval in stepOrdinals)) {
1613
1723
  return null;
1614
1724
  }
1615
1725
  return segment;
1616
1726
  }
1617
- function hasHourWindow(ir) {
1618
- return ir.analyses.segments.hour.some(function range(segment) {
1727
+ function hasHourWindow(schedule) {
1728
+ return segmentsOf(schedule, "hour").some(function range(segment) {
1619
1729
  return segment.kind === "range";
1620
1730
  });
1621
1731
  }
1622
- function hourRangeWindowTail(ir, opts) {
1732
+ function hourRangeWindowTail(schedule, opts) {
1623
1733
  const windows = [];
1624
- const outliers = collectHourOutliers(ir);
1625
- ir.analyses.segments.hour.forEach(function classify(segment) {
1734
+ const outlierHours = collectHourOutliers(schedule);
1735
+ segmentsOf(schedule, "hour").forEach(function classify(segment) {
1626
1736
  if (segment.kind === "range") {
1627
- windows.push(rangeWindow(
1628
- +segment.bounds[0],
1629
- +segment.bounds[1],
1630
- 0,
1631
- opts
1632
- ));
1737
+ windows.push(rangeWindow({
1738
+ continuous: false,
1739
+ from: +segment.bounds[0],
1740
+ throughMinute: 0,
1741
+ to: +segment.bounds[1]
1742
+ }, opts));
1633
1743
  }
1634
1744
  });
1635
1745
  const phrase = "every hour " + joinList(windows, opts);
1636
- const times = outliers.hours.map(function time(hour) {
1746
+ const times = outlierHours.map(function time(hour) {
1637
1747
  return getTime({ hour, minute: 0 }, opts);
1638
1748
  });
1639
- return phrase + outlierTail(times, outliers.pureStrays, opts);
1749
+ return phrase + outlierTail(times, opts);
1640
1750
  }
1641
- function hourRangeCadence(ir, minute, opts) {
1642
- if (minute !== 0 || !hasHourWindow(ir)) {
1751
+ function hourRangeCadence(schedule, minute, opts) {
1752
+ if (minute !== 0 || !hasHourWindow(schedule)) {
1643
1753
  return null;
1644
1754
  }
1645
- if (ir.pattern.second === "0") {
1755
+ if (schedule.pattern.second === "0") {
1646
1756
  return null;
1647
1757
  }
1648
- if (subMinuteSecond(ir)) {
1649
- return secondsClause(ir, "minute", opts) + " for one minute during the " + hourSegmentTimes(ir, { minute: 0, second: null }, false, opts) + " hours" + trailingQualifier(ir, opts);
1758
+ if (subMinuteSecond(schedule)) {
1759
+ return secondsClause(schedule, "minute", opts) + " for one minute during the " + hourSegmentTimes(schedule, { minute: 0, second: null }, false, opts) + " hours" + trailingQualifier(schedule, opts);
1650
1760
  }
1651
- return hourCadenceLead(ir, minute, opts) + ", " + hourRangeWindowTail(ir, opts) + trailingQualifier(ir, opts);
1761
+ return hourCadenceLead(schedule, minute, opts) + ", " + hourRangeWindowTail(schedule, opts) + trailingQualifier(schedule, opts);
1652
1762
  }
1653
1763
  function seriesNumber() {
1654
1764
  return function format(n) {
@@ -1707,11 +1817,11 @@ function hourTimes(hours, opts) {
1707
1817
  function singleHourFire(times) {
1708
1818
  return times.kind === "fires" && times.fires.length === 1;
1709
1819
  }
1710
- function hourTimesFromPlan(ir, times, atContext, opts) {
1820
+ function hourTimesFromPlan(schedule, times, atContext, opts) {
1711
1821
  if (times.kind === "fires") {
1712
1822
  return hourTimes(times.fires, opts);
1713
1823
  }
1714
- return hourSegmentTimes(ir, { minute: 0, second: null }, atContext, opts);
1824
+ return hourSegmentTimes(schedule, { minute: 0, second: null }, atContext, opts);
1715
1825
  }
1716
1826
  function segmentHours(segment) {
1717
1827
  if (segment.kind === "range") {
@@ -1719,9 +1829,9 @@ function segmentHours(segment) {
1719
1829
  }
1720
1830
  return segment.kind === "step" ? segment.fires : [segment.value];
1721
1831
  }
1722
- function hourSegmentTimes(ir, fold, atContext, opts) {
1832
+ function hourSegmentTimes(schedule, fold, atContext, opts) {
1723
1833
  const { minute, second } = fold;
1724
- const segments = ir.analyses.segments.hour;
1834
+ const segments = segmentsOf(schedule, "hour");
1725
1835
  const plain = mixedTwelve(segments.flatMap(function entries(segment) {
1726
1836
  return segmentHours(segment).map(function entry(hour) {
1727
1837
  return { hour: +hour, minute, second };
@@ -1784,85 +1894,85 @@ var leadingWords = {
1784
1894
  stepDate: "",
1785
1895
  weekday: "every "
1786
1896
  };
1787
- function trailingQualifier(ir, opts) {
1788
- if (isDayUnion(ir, opts)) {
1789
- return dayUnionCondition(ir, opts);
1897
+ function trailingQualifier(schedule, opts) {
1898
+ if (isDayUnion(schedule, opts)) {
1899
+ return dayUnionCondition(schedule, opts);
1790
1900
  }
1791
- const phrase = dayQualifier(ir, trailingWords, opts);
1901
+ const phrase = dayQualifier(schedule, trailingWords, opts);
1792
1902
  return phrase && " " + phrase;
1793
1903
  }
1794
- function interpretDayQualifier(ir, opts) {
1795
- if (isDayUnion(ir, opts)) {
1904
+ function interpretDayQualifier(schedule, opts) {
1905
+ if (isDayUnion(schedule, opts)) {
1796
1906
  return "";
1797
1907
  }
1798
- return dayQualifier(ir, leadingWords, opts) + " ";
1908
+ return dayQualifier(schedule, leadingWords, opts) + " ";
1799
1909
  }
1800
- function dayQualifier(ir, words, opts) {
1801
- const pattern = ir.pattern;
1910
+ function dayQualifier(schedule, words, opts) {
1911
+ const pattern = schedule.pattern;
1802
1912
  if (pattern.date !== "*" && pattern.weekday !== "*") {
1803
- return dateOrWeekday(ir, opts);
1913
+ return dateOrWeekday(schedule, opts);
1804
1914
  }
1805
1915
  if (pattern.date !== "*") {
1806
- return datePhrase(ir, words, opts);
1916
+ return datePhrase(schedule, words, opts);
1807
1917
  }
1808
1918
  if (pattern.weekday !== "*") {
1809
1919
  const quartzWeekday = quartzWeekdayPhrase(pattern.weekday, opts);
1810
1920
  if (quartzWeekday) {
1811
- return monthScopeForRecurrence(quartzWeekday, ir, opts);
1921
+ return monthScopeForRecurrence(quartzWeekday, schedule, opts);
1812
1922
  }
1813
- const weekdays = words.weekday + weekdayPhrase(ir, words.recurringWeekday, opts);
1814
- return weekdays + monthScope(ir, opts);
1923
+ const weekdays = words.weekday + weekdayPhrase(schedule, words.recurringWeekday, opts);
1924
+ return weekdays + monthScope(schedule, opts);
1815
1925
  }
1816
1926
  if (pattern.month !== "*") {
1817
- return words.month + monthName(ir, opts);
1927
+ return words.month + monthName(schedule, opts);
1818
1928
  }
1819
1929
  return words.all;
1820
1930
  }
1821
- function datePhrase(ir, words, opts) {
1822
- const pattern = ir.pattern;
1931
+ function datePhrase(schedule, words, opts) {
1932
+ const pattern = schedule.pattern;
1823
1933
  const quartzDate = quartzDatePhrase(pattern.date, opts);
1824
1934
  if (quartzDate) {
1825
- return monthScopeForRecurrence(quartzDate, ir, opts);
1935
+ return monthScopeForRecurrence(quartzDate, schedule, opts);
1826
1936
  }
1827
1937
  if (isOpenStep(pattern.date)) {
1828
1938
  return monthScopeForRecurrence(
1829
1939
  words.stepDate + stepDates(pattern.date),
1830
- ir,
1940
+ schedule,
1831
1941
  opts
1832
1942
  );
1833
1943
  }
1834
- if (pattern.month !== "*" && !monthFoldsIntoDate(ir)) {
1835
- return "on the " + dateOrdinals(ir, opts) + monthScope(ir, opts);
1944
+ if (pattern.month !== "*" && !monthFoldsIntoDate(schedule)) {
1945
+ return "on the " + dateOrdinals(schedule, opts) + monthScope(schedule, opts);
1836
1946
  }
1837
1947
  if (pattern.month !== "*") {
1838
- return "on " + monthDatePhrase(ir, opts);
1948
+ return "on " + monthDatePhrase(schedule, opts);
1839
1949
  }
1840
- return "on the " + dateOrdinals(ir, opts);
1950
+ return "on the " + dateOrdinals(schedule, opts);
1841
1951
  }
1842
- function monthFoldsIntoDate(ir) {
1843
- return !oddEvenMonth(ir.pattern.month) && // Reached only with a restricted month, which has segments.
1844
- ir.analyses.segments.month.every(function flat(segment) {
1952
+ function monthFoldsIntoDate(schedule) {
1953
+ return !oddEvenMonth(schedule.pattern.month) && // Reached only with a restricted month, which has segments.
1954
+ segmentsOf(schedule, "month").every(function flat(segment) {
1845
1955
  return segment.kind !== "range";
1846
1956
  });
1847
1957
  }
1848
- function isDayUnion(ir, opts) {
1849
- return ir.pattern.date !== "*" && ir.pattern.weekday !== "*" && !!opts.style.untilWindow && !opts.short;
1958
+ function isDayUnion(schedule, opts) {
1959
+ return schedule.pattern.date !== "*" && schedule.pattern.weekday !== "*" && !!opts.style.untilWindow && !opts.short;
1850
1960
  }
1851
- function dayUnionCondition(ir, opts) {
1961
+ function dayUnionCondition(schedule, opts) {
1852
1962
  const pieces = [
1853
- ...dayUnionDatePieces(ir, opts),
1854
- ...dayUnionWeekdayPieces(ir, opts)
1963
+ ...dayUnionDatePieces(schedule, opts),
1964
+ ...dayUnionWeekdayPieces(schedule, opts)
1855
1965
  ];
1856
1966
  return " whenever the day is " + joinOr(pieces, opts);
1857
1967
  }
1858
- function dayUnionMonthLead(ir, opts) {
1859
- if (ir.pattern.month === "*") {
1968
+ function dayUnionMonthLead(schedule, opts) {
1969
+ if (schedule.pattern.month === "*") {
1860
1970
  return "";
1861
1971
  }
1862
- return "in " + monthName(ir, opts) + " ";
1972
+ return "in " + monthName(schedule, opts) + " ";
1863
1973
  }
1864
- function dayUnionDatePieces(ir, opts) {
1865
- const dateField = ir.pattern.date;
1974
+ function dayUnionDatePieces(schedule, opts) {
1975
+ const dateField = schedule.pattern.date;
1866
1976
  const quartz = quartzDatePhrase(dateField, opts);
1867
1977
  if (quartz) {
1868
1978
  return [quartz.replace(/^on /, "")];
@@ -1872,7 +1982,7 @@ function dayUnionDatePieces(ir, opts) {
1872
1982
  return [oddEven];
1873
1983
  }
1874
1984
  const pieces = [];
1875
- ir.analyses.segments.date.forEach(function expand(segment) {
1985
+ segmentsOf(schedule, "date").forEach(function expand(segment) {
1876
1986
  if (segment.kind === "range") {
1877
1987
  pieces.push("from the " + getOrdinal(segment.bounds[0]) + through(opts) + "the " + getOrdinal(segment.bounds[1]));
1878
1988
  } else if (segment.kind === "step") {
@@ -1885,14 +1995,14 @@ function dayUnionDatePieces(ir, opts) {
1885
1995
  });
1886
1996
  return pieces;
1887
1997
  }
1888
- function dayUnionWeekdayPieces(ir, opts) {
1889
- const weekdayField = ir.pattern.weekday;
1998
+ function dayUnionWeekdayPieces(schedule, opts) {
1999
+ const weekdayField = schedule.pattern.weekday;
1890
2000
  const quartz = quartzWeekdayPhrase(weekdayField, opts);
1891
2001
  if (quartz) {
1892
2002
  return [quartz.replace(/^on /, "")];
1893
2003
  }
1894
2004
  const pieces = [];
1895
- ir.analyses.segments.weekday.forEach(function expand(segment) {
2005
+ segmentsOf(schedule, "weekday").forEach(function expand(segment) {
1896
2006
  if (segment.kind === "range" && segment.bounds[0] === "1" && segment.bounds[1] === "5") {
1897
2007
  pieces.push("a weekday");
1898
2008
  } else if (segment.kind === "range") {
@@ -1920,16 +2030,16 @@ function oddEvenDay(dateField) {
1920
2030
  }
1921
2031
  return start === "2" ? "an even-numbered day" : null;
1922
2032
  }
1923
- function dateOrWeekday(ir, opts) {
1924
- const pattern = ir.pattern;
1925
- const weekdayPart = quartzWeekdayPhrase(pattern.weekday, opts) || "on " + weekdayPhrase(ir, false, opts);
1926
- if (pattern.month !== "*" && monthFoldsIntoDate(ir) && !quartzDatePhrase(pattern.date, opts) && !isOpenStep(pattern.date)) {
1927
- return "on " + monthDatePhrase(ir, opts) + " or " + weekdayPart + " in " + monthName(ir, opts);
2033
+ function dateOrWeekday(schedule, opts) {
2034
+ const pattern = schedule.pattern;
2035
+ const weekdayPart = quartzWeekdayPhrase(pattern.weekday, opts) || "on " + weekdayPhrase(schedule, false, opts);
2036
+ if (pattern.month !== "*" && monthFoldsIntoDate(schedule) && !quartzDatePhrase(pattern.date, opts) && !isOpenStep(pattern.date)) {
2037
+ return "on " + monthDatePhrase(schedule, opts) + " or " + weekdayPart + " in " + monthName(schedule, opts);
1928
2038
  }
1929
- return datePart(ir, opts) + " or " + weekdayPart + orMonthScope(ir, opts);
2039
+ return datePart(schedule, opts) + " or " + weekdayPart + orMonthScope(schedule, opts);
1930
2040
  }
1931
- function datePart(ir, opts) {
1932
- const pattern = ir.pattern;
2041
+ function datePart(schedule, opts) {
2042
+ const pattern = schedule.pattern;
1933
2043
  const quartzDate = quartzDatePhrase(pattern.date, opts);
1934
2044
  if (quartzDate) {
1935
2045
  return quartzDate;
@@ -1937,13 +2047,13 @@ function datePart(ir, opts) {
1937
2047
  if (isOpenStep(pattern.date)) {
1938
2048
  return stepDates(pattern.date);
1939
2049
  }
1940
- return "on the " + dateOrdinals(ir, opts);
2050
+ return "on the " + dateOrdinals(schedule, opts);
1941
2051
  }
1942
- function orMonthScope(ir, opts) {
1943
- if (ir.pattern.month === "*") {
2052
+ function orMonthScope(schedule, opts) {
2053
+ if (schedule.pattern.month === "*") {
1944
2054
  return "";
1945
2055
  }
1946
- return ", in " + monthName(ir, opts);
2056
+ return ", in " + monthName(schedule, opts);
1947
2057
  }
1948
2058
  function quartzDatePhrase(dateField, opts) {
1949
2059
  if (dateField === "L") {
@@ -1970,39 +2080,39 @@ function quartzWeekdayPhrase(weekdayField, opts) {
1970
2080
  return "on the last " + getWeekday(weekdayField.slice(0, -1), opts) + " of the month";
1971
2081
  }
1972
2082
  }
1973
- function monthDatePhrase(ir, opts) {
1974
- const month = monthName(ir, opts);
2083
+ function monthDatePhrase(schedule, opts) {
2084
+ const month = monthName(schedule, opts);
1975
2085
  const days = renderSegments(
1976
- ir.analyses.segments.date,
2086
+ segmentsOf(schedule, "date"),
1977
2087
  opts.style.ordinals ? getOrdinal : cardinalDay,
1978
2088
  opts
1979
2089
  );
1980
- if (opts.style.dayFirst && ir.shapes.date === "single" && ir.shapes.month !== "single") {
1981
- return "the " + getOrdinal(ir.pattern.date) + " of " + month;
2090
+ if (opts.style.dayFirst && schedule.shapes.date === "single" && schedule.shapes.month !== "single") {
2091
+ return "the " + getOrdinal(schedule.pattern.date) + " of " + month;
1982
2092
  }
1983
2093
  return opts.style.dayFirst ? days + " " + month : month + " " + days;
1984
2094
  }
1985
2095
  function cardinalDay(value) {
1986
2096
  return "" + value;
1987
2097
  }
1988
- function monthScope(ir, opts) {
1989
- if (ir.pattern.month === "*") {
2098
+ function monthScope(schedule, opts) {
2099
+ if (schedule.pattern.month === "*") {
1990
2100
  return "";
1991
2101
  }
1992
- return " in " + monthName(ir, opts);
2102
+ return " in " + monthName(schedule, opts);
1993
2103
  }
1994
- function monthScopeForRecurrence(phrase, ir, opts) {
1995
- if (ir.pattern.month === "*") {
2104
+ function monthScopeForRecurrence(phrase, schedule, opts) {
2105
+ if (schedule.pattern.month === "*") {
1996
2106
  return phrase;
1997
2107
  }
1998
2108
  const carriesRecurrence = phrase.indexOf(" of the month") !== -1;
1999
- if (carriesRecurrence && ir.shapes.month === "range") {
2000
- return phrase.replace(" of the month", " of each month") + " from " + monthName(ir, opts);
2109
+ if (carriesRecurrence && schedule.shapes.month === "range") {
2110
+ return phrase.replace(" of the month", " of each month") + " from " + monthName(schedule, opts);
2001
2111
  }
2002
- if (carriesRecurrence && (ir.shapes.month === "single" || ir.shapes.month === "step")) {
2003
- return phrase.replace(" of the month", "") + " in " + monthName(ir, opts);
2112
+ if (carriesRecurrence && (schedule.shapes.month === "single" || schedule.shapes.month === "step")) {
2113
+ return phrase.replace(" of the month", "") + " in " + monthName(schedule, opts);
2004
2114
  }
2005
- return phrase + " in " + monthName(ir, opts);
2115
+ return phrase + " in " + monthName(schedule, opts);
2006
2116
  }
2007
2117
  function stepDates(dateField) {
2008
2118
  const parts = dateField.split("/");
@@ -2015,15 +2125,15 @@ function stepDates(dateField) {
2015
2125
  }
2016
2126
  return phrase;
2017
2127
  }
2018
- function dateOrdinals(ir, opts) {
2019
- return renderSegments(ir.analyses.segments.date, getOrdinal, opts);
2128
+ function dateOrdinals(schedule, opts) {
2129
+ return renderSegments(segmentsOf(schedule, "date"), getOrdinal, opts);
2020
2130
  }
2021
- function monthName(ir, opts) {
2022
- const oddEven = oddEvenMonth(ir.pattern.month);
2131
+ function monthName(schedule, opts) {
2132
+ const oddEven = oddEvenMonth(schedule.pattern.month);
2023
2133
  if (oddEven) {
2024
2134
  return oddEven;
2025
2135
  }
2026
- return renderSegments(ir.analyses.segments.month, function name(value) {
2136
+ return renderSegments(segmentsOf(schedule, "month"), function name(value) {
2027
2137
  return getMonth(value, opts);
2028
2138
  }, opts);
2029
2139
  }
@@ -2040,8 +2150,8 @@ function oddEvenMonth(monthField) {
2040
2150
  }
2041
2151
  return start === "2" ? "every even-numbered month" : null;
2042
2152
  }
2043
- function weekdayPhrase(ir, recurring, opts) {
2044
- const segments = orderWeekdaysForDisplay(ir.analyses.segments.weekday);
2153
+ function weekdayPhrase(schedule, recurring, opts) {
2154
+ const segments = orderWeekdaysForDisplay(segmentsOf(schedule, "weekday"));
2045
2155
  const hasRange = segments.some(function range(segment) {
2046
2156
  return segment.kind === "range";
2047
2157
  });
@@ -2069,11 +2179,8 @@ function renderSegments(segments, word, opts) {
2069
2179
  });
2070
2180
  return joinList(pieces, opts);
2071
2181
  }
2072
- function isOpenStep(field) {
2073
- return field.indexOf("/") !== -1 && field.indexOf("-") === -1 && field.indexOf(",") === -1;
2074
- }
2075
- function applyYear(description, ir, opts) {
2076
- const yearField = ir.pattern.year;
2182
+ function applyYear(description, schedule, opts) {
2183
+ const yearField = schedule.pattern.year;
2077
2184
  if (yearField === "*") {
2078
2185
  return description;
2079
2186
  }
@@ -2081,7 +2188,7 @@ function applyYear(description, ir, opts) {
2081
2188
  return description + ", " + stepYears(yearField, opts);
2082
2189
  }
2083
2190
  const label = yearLabel(yearField, opts);
2084
- if (yearField.indexOf("-") === -1 && yearField.indexOf(",") === -1 && ir.pattern.date !== "*" && description.indexOf(" at ") !== -1) {
2191
+ if (yearField.indexOf("-") === -1 && yearField.indexOf(",") === -1 && schedule.pattern.date !== "*" && description.indexOf(" at ") !== -1) {
2085
2192
  const yearGlue = opts.style.dayFirst ? " " : ", ";
2086
2193
  return description.replace(" at ", yearGlue + label + " at ");
2087
2194
  }
@@ -2204,9 +2311,9 @@ function interpretCronPattern(cronPattern, lang, opts) {
2204
2311
  if (typeof cronPattern === "string" && cronPattern.trim().toLowerCase() === "@reboot") {
2205
2312
  return lang.reboot;
2206
2313
  }
2207
- const ir = analyze(prepare(cronPattern, opts));
2208
- const plan = lang.strategy ? lang.strategy(ir, ir.plan) : ir.plan;
2209
- return lang.describe({ ...ir, plan }, opts);
2314
+ const schedule = analyze(prepare(cronPattern, opts));
2315
+ const plan = lang.plan ? lang.plan(schedule, schedule.plan) : schedule.plan;
2316
+ return lang.describe({ ...schedule, plan }, opts);
2210
2317
  }
2211
2318
  var cronli5_default = cronli5;
2212
2319
  /**