cronli5 0.2.1 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/CHANGELOG.md +109 -0
  2. package/README.md +4 -4
  3. package/cronli5.min.js +2 -2
  4. package/dist/cronli5.cjs +471 -383
  5. package/dist/cronli5.js +471 -383
  6. package/dist/lang/de.cjs +286 -215
  7. package/dist/lang/de.js +286 -215
  8. package/dist/lang/en.cjs +413 -327
  9. package/dist/lang/en.js +413 -327
  10. package/dist/lang/es.cjs +303 -265
  11. package/dist/lang/es.js +303 -265
  12. package/dist/lang/fi.cjs +311 -266
  13. package/dist/lang/fi.js +311 -266
  14. package/dist/lang/zh.cjs +320 -240
  15. package/dist/lang/zh.js +320 -240
  16. package/package.json +6 -6
  17. package/src/core/analyze.ts +12 -12
  18. package/src/core/cadence.ts +164 -0
  19. package/src/core/index.ts +3 -1
  20. package/src/core/normalize.ts +3 -3
  21. package/src/core/parse.ts +1 -1
  22. package/src/core/{ir.ts → schedule.ts} +17 -18
  23. package/src/core/specs.ts +1 -1
  24. package/src/core/util.ts +3 -165
  25. package/src/core/validate.ts +1 -1
  26. package/src/core/weekday.ts +54 -0
  27. package/src/cronli5.ts +5 -5
  28. package/src/lang/de/index.ts +329 -219
  29. package/src/lang/en/dialects.ts +1 -1
  30. package/src/lang/en/index.ts +521 -372
  31. package/src/lang/es/index.ts +338 -286
  32. package/src/lang/es/notes.md +1 -1
  33. package/src/lang/fi/dialects.ts +1 -1
  34. package/src/lang/fi/index.ts +365 -299
  35. package/src/lang/fi/notes.md +23 -8
  36. package/src/lang/fi/status.json +1 -1
  37. package/src/lang/zh/index.ts +386 -245
  38. package/src/types.ts +6 -6
  39. package/types/core/analyze.d.ts +3 -3
  40. package/types/core/cadence.d.ts +33 -0
  41. package/types/core/index.d.ts +3 -1
  42. package/types/core/normalize.d.ts +1 -1
  43. package/types/core/parse.d.ts +1 -1
  44. package/types/core/{ir.d.ts → schedule.d.ts} +11 -16
  45. package/types/core/specs.d.ts +1 -1
  46. package/types/core/util.d.ts +1 -30
  47. package/types/core/weekday.d.ts +10 -0
  48. package/types/lang/de/index.d.ts +1 -1
  49. package/types/lang/en/dialects.d.ts +1 -1
  50. package/types/lang/en/index.d.ts +1 -1
  51. package/types/lang/es/index.d.ts +1 -1
  52. package/types/lang/fi/dialects.d.ts +1 -1
  53. package/types/lang/fi/index.d.ts +1 -1
  54. package/types/lang/zh/index.d.ts +1 -1
  55. package/types/types.d.ts +5 -5
package/dist/lang/es.js CHANGED
@@ -31,6 +31,16 @@ function isNonNegativeInteger(value) {
31
31
  const digits = /^\d+$/;
32
32
  return digits.test(value);
33
33
  }
34
+ function toFieldNumber(token, numberMap) {
35
+ return isNonNegativeInteger(token) ? +token : numberMap[token.toUpperCase()];
36
+ }
37
+
38
+ // src/core/shapes.ts
39
+ function isOpenStep(field) {
40
+ return field.indexOf("/") !== -1 && field.indexOf("-") === -1 && field.indexOf(",") === -1;
41
+ }
42
+
43
+ // src/core/cadence.ts
34
44
  function arithmeticStep(values) {
35
45
  if (values.length < 5) {
36
46
  return null;
@@ -46,34 +56,11 @@ function arithmeticStep(values) {
46
56
  }
47
57
  return { start: values[0], interval, last: values[values.length - 1] };
48
58
  }
49
- function weekdayDisplayKey(value) {
50
- return value === 0 ? 7 : value;
59
+ function segmentsOf(schedule, field) {
60
+ return schedule.analyses.segments[field] ?? [];
51
61
  }
52
- function orderWeekdaysForDisplay(segments) {
53
- const flattened = segments.flatMap(function flat(segment) {
54
- return segment.kind === "step" ? segment.fires.map(function single(value) {
55
- return { kind: "single", value: "" + value };
56
- }) : [segment];
57
- });
58
- function key(segment) {
59
- return segment.kind === "range" ? weekdayDisplayKey(+segment.bounds[0]) : weekdayDisplayKey(+segment.value);
60
- }
61
- return flattened.map(function index(segment, position) {
62
- return [segment, position];
63
- }).sort(function byDisplayKey(a, b) {
64
- return key(a[0]) - key(b[0]) || a[1] - b[1];
65
- }).map(function unwrap(pair) {
66
- return pair[0];
67
- });
68
- }
69
- function toFieldNumber(token, numberMap) {
70
- return isNonNegativeInteger(token) ? +token : numberMap[token.toUpperCase()];
71
- }
72
- function segmentsOf(ir, field) {
73
- return ir.analyses.segments[field] ?? [];
74
- }
75
- function stepSegment(ir, field) {
76
- return segmentsOf(ir, field)[0];
62
+ function stepSegment(schedule, field) {
63
+ return segmentsOf(schedule, field)[0];
77
64
  }
78
65
  function singleValues(segments) {
79
66
  const values = [];
@@ -88,6 +75,17 @@ function singleValues(segments) {
88
75
  function offsetCleanStride(stride) {
89
76
  return stride.start < stride.interval && 24 % stride.interval === 0;
90
77
  }
78
+ function renderStride(spec, parts) {
79
+ const { start, interval, cycle } = spec;
80
+ const tiles = cycle % interval === 0;
81
+ if (start === 0 && tiles) {
82
+ return parts.bare();
83
+ }
84
+ if (start < interval && tiles) {
85
+ return parts.offset();
86
+ }
87
+ return parts.bounded();
88
+ }
91
89
  function hourListStride(values) {
92
90
  if (values.length < 2) {
93
91
  return null;
@@ -107,9 +105,26 @@ function hourListStride(values) {
107
105
  return { interval, last: values[values.length - 1], start: values[0] };
108
106
  }
109
107
 
110
- // src/core/shapes.ts
111
- function isOpenStep(field) {
112
- return field.indexOf("/") !== -1 && field.indexOf("-") === -1 && field.indexOf(",") === -1;
108
+ // src/core/weekday.ts
109
+ function weekdayDisplayKey(value) {
110
+ return value === 0 ? 7 : value;
111
+ }
112
+ function orderWeekdaysForDisplay(segments) {
113
+ const flattened = segments.flatMap(function flat(segment) {
114
+ return segment.kind === "step" ? segment.fires.map(function single(value) {
115
+ return { kind: "single", value: "" + value };
116
+ }) : [segment];
117
+ });
118
+ function key(segment) {
119
+ return segment.kind === "range" ? weekdayDisplayKey(+segment.bounds[0]) : weekdayDisplayKey(+segment.value);
120
+ }
121
+ return flattened.map(function index(segment, position) {
122
+ return [segment, position];
123
+ }).sort(function byDisplayKey(a, b) {
124
+ return key(a[0]) - key(b[0]) || a[1] - b[1];
125
+ }).map(function unwrap(pair) {
126
+ return pair[0];
127
+ });
113
128
  }
114
129
 
115
130
  // src/lang/es/dialects.ts
@@ -189,107 +204,107 @@ function normalizeOptions(options) {
189
204
  years: !!options.years
190
205
  };
191
206
  }
192
- function describe(ir, opts) {
193
- return applyYear(render(ir, ir.plan, opts), ir, opts);
207
+ function describe(schedule, opts) {
208
+ return applyYear(render(schedule, schedule.plan, opts), schedule, opts);
194
209
  }
195
- function render(ir, plan, opts) {
196
- const phrase = renderers[plan.kind](ir, plan, opts);
197
- if (!isDateWeekdayUnion(ir)) {
210
+ function render(schedule, plan, opts) {
211
+ const phrase = renderers[plan.kind](schedule, plan, opts);
212
+ if (!isDateWeekdayUnion(schedule)) {
198
213
  return phrase;
199
214
  }
200
- const lead = unionMonthLeadFull(ir);
201
- return (lead ? lead + " " : "") + phrase + unionYaseaSuffix(ir, opts);
215
+ const lead = unionMonthLeadFull(schedule);
216
+ return (lead ? lead + " " : "") + phrase + unionYaseaSuffix(schedule, opts);
202
217
  }
203
- function renderEverySecond(ir, plan, opts) {
204
- return "cada segundo" + trailingQualifier(ir, opts);
218
+ function renderEverySecond(schedule, plan, opts) {
219
+ return "cada segundo" + trailingQualifier(schedule, opts);
205
220
  }
206
- function renderStandaloneSeconds(ir, plan, opts) {
207
- return secondsLeadClause(ir, opts) + trailingQualifier(ir, opts);
221
+ function renderStandaloneSeconds(schedule, plan, opts) {
222
+ return secondsLeadClause(schedule, opts) + trailingQualifier(schedule, opts);
208
223
  }
209
- function renderSecondPastMinute(ir, plan, opts) {
210
- return "en el segundo " + ir.pattern.second + " de cada minuto" + trailingQualifier(ir, opts);
224
+ function renderSecondPastMinute(schedule, plan, opts) {
225
+ return "en el segundo " + schedule.pattern.second + " de cada minuto" + trailingQualifier(schedule, opts);
211
226
  }
212
- function renderSecondsWithinMinute(ir, plan, opts) {
213
- const minuteField = ir.pattern.minute;
227
+ function renderSecondsWithinMinute(schedule, plan, opts) {
228
+ const minuteField = schedule.pattern.minute;
214
229
  if (plan.singleSecond) {
215
- return "en el minuto " + minuteField + " y el segundo " + ir.pattern.second + " de cada hora" + trailingQualifier(ir, opts);
230
+ return "en el minuto " + minuteField + " y el segundo " + schedule.pattern.second + " de cada hora" + trailingQualifier(schedule, opts);
216
231
  }
217
- return secondsLeadClause(ir, opts) + ", en el minuto " + minuteField + " de cada hora" + trailingQualifier(ir, opts);
232
+ return secondsLeadClause(schedule, opts) + ", en el minuto " + minuteField + " de cada hora" + trailingQualifier(schedule, opts);
218
233
  }
219
- function secondsListAtClock(ir, rest, opts) {
234
+ function secondsListAtClock(schedule, rest, opts) {
220
235
  const clockPhrases = rest.times.map(function clock(time) {
221
236
  return atTime(timePhrase(time.hour, time.minute, null, opts));
222
237
  });
223
238
  const grouped = groupClockTimesByArticle(clockPhrases);
224
239
  const clockList = grouped.startsWith("a ") ? grouped.slice(2) : grouped;
225
- const stride = strideFromSegments(segmentsOf(ir, "second"), "segundo", "", opts);
226
- const secondsPhrase = stride ?? "en los segundos " + joinList(segmentWords(segmentsOf(ir, "second")));
227
- const dayFrame = trailingQualifier(ir, opts);
240
+ const stride = strideFromSegments(segmentsOf(schedule, "second"), "segundo", "", opts);
241
+ const secondsPhrase = stride ?? "en los segundos " + joinList(segmentWords(segmentsOf(schedule, "second")));
242
+ const dayFrame = trailingQualifier(schedule, opts);
228
243
  return (dayFrame ? dayFrame.trimStart() + ", " : "") + secondsPhrase + " de " + clockList;
229
244
  }
230
- function composeHourCadence(ir, plan, opts) {
245
+ function composeHourCadence(schedule, plan, opts) {
231
246
  const clockRest = plan.rest.kind === "clockTimes" || plan.rest.kind === "compactClockTimes";
232
- if (!clockRest || ir.shapes.minute !== "single") {
247
+ if (!clockRest || schedule.shapes.minute !== "single") {
233
248
  return null;
234
249
  }
235
- const minute = +ir.pattern.minute;
236
- return hourCadence(ir, minute, opts) ?? hourRangeCadence(ir, minute, opts);
250
+ const minute = +schedule.pattern.minute;
251
+ return hourCadence(schedule, minute, opts) ?? hourRangeCadence(schedule, minute, opts);
237
252
  }
238
- function isPinnedMinuteSeconds(ir, plan) {
239
- return plan.rest.kind === "clockTimes" && (ir.shapes.second === "wildcard" || ir.shapes.second === "step");
253
+ function isPinnedMinuteSeconds(schedule, plan) {
254
+ return plan.rest.kind === "clockTimes" && (schedule.shapes.second === "wildcard" || schedule.shapes.second === "step");
240
255
  }
241
- function renderComposeSeconds(ir, plan, opts) {
242
- const hourCad = composeHourCadence(ir, plan, opts);
256
+ function renderComposeSeconds(schedule, plan, opts) {
257
+ const hourCad = composeHourCadence(schedule, plan, opts);
243
258
  if (hourCad !== null) {
244
259
  return hourCad;
245
260
  }
246
- if (isPinnedMinuteSeconds(ir, plan)) {
247
- return pinnedMinuteSeconds(ir, plan.rest, opts);
261
+ if (isPinnedMinuteSeconds(schedule, plan)) {
262
+ return pinnedMinuteSeconds(schedule, plan.rest, opts);
248
263
  }
249
- if (plan.rest.kind === "clockTimes" && ir.shapes.second === "list") {
250
- return secondsListAtClock(ir, plan.rest, opts);
264
+ if (plan.rest.kind === "clockTimes" && schedule.shapes.second === "list") {
265
+ return secondsListAtClock(schedule, plan.rest, opts);
251
266
  }
252
- if (plan.rest.kind === "hourRange" && ir.shapes.second === "step" && ir.pattern.weekday !== "*") {
267
+ if (plan.rest.kind === "hourRange" && schedule.shapes.second === "step" && schedule.pattern.weekday !== "*") {
253
268
  const restNode = plan.rest;
254
269
  const window = hourWindow(boundedWindow(restNode), opts);
255
- const dayFrame = weekdayQualifier(ir) + monthScope(ir);
256
- const cadence = "cada " + numero(stepSegment(ir, "second").interval, opts) + " segundos del minuto " + ir.pattern.minute;
270
+ const dayFrame = weekdayQualifier(schedule) + monthScope(schedule);
271
+ const cadence = "cada " + numero(stepSegment(schedule, "second").interval, opts) + " segundos del minuto " + schedule.pattern.minute;
257
272
  return dayFrame + ", " + window + ", " + cadence;
258
273
  }
259
- if (isEveryOtherMinuteSeconds(ir, plan)) {
260
- return secondsLeadClause(ir, opts) + " de " + render(ir, plan.rest, opts);
274
+ if (isEveryOtherMinuteSeconds(schedule, plan)) {
275
+ return secondsLeadClause(schedule, opts) + " de " + render(schedule, plan.rest, opts);
261
276
  }
262
- const restOwnsLead = plan.rest.kind === "compactClockTimes" && ir.analyses.clockSecond;
263
- const lead = restOwnsLead ? "" : secondsLeadClause(ir, opts) + ", ";
264
- return lead + render(ir, plan.rest, opts);
277
+ const restOwnsLead = plan.rest.kind === "compactClockTimes" && schedule.analyses.clockSecond;
278
+ const lead = restOwnsLead ? "" : secondsLeadClause(schedule, opts) + ", ";
279
+ return lead + render(schedule, plan.rest, opts);
265
280
  }
266
- function isEveryOtherMinuteSeconds(ir, plan) {
267
- if (plan.rest.kind !== "minuteFrequency" || ir.shapes.second !== "wildcard" || ir.shapes.hour !== "wildcard") {
281
+ function isEveryOtherMinuteSeconds(schedule, plan) {
282
+ if (plan.rest.kind !== "minuteFrequency" || schedule.shapes.second !== "wildcard" || schedule.shapes.hour !== "wildcard") {
268
283
  return false;
269
284
  }
270
- const minuteStep = stepSegment(ir, "minute");
285
+ const minuteStep = stepSegment(schedule, "minute");
271
286
  return minuteStep.startToken === "*" && minuteStep.interval === 2;
272
287
  }
273
- function pinnedMinuteSeconds(ir, rest, opts) {
274
- const dayTrail = leadingQualifier(ir, opts).trimEnd();
288
+ function pinnedMinuteSeconds(schedule, rest, opts) {
289
+ const dayTrail = leadingQualifier(schedule, opts).trimEnd();
275
290
  const trail = dayTrail ? ", " + dayTrail : "";
276
- if (+rest.times[0].minute === 0 && ir.shapes.minute === "single") {
277
- return secondsLeadClause(ir, opts) + " durante un minuto " + durationHourList(rest.times, opts) + trail;
291
+ if (+rest.times[0].minute === 0 && schedule.shapes.minute === "single") {
292
+ return secondsLeadClause(schedule, opts) + " durante un minuto " + durationHourList(rest.times, opts) + trail;
278
293
  }
279
- return secondsLeadClause(ir, opts) + " de " + explicitClockList(rest.times, opts) + trail;
294
+ return secondsLeadClause(schedule, opts) + " de " + explicitClockList(rest.times, opts) + trail;
280
295
  }
281
- function secondsLeadClause(ir, opts) {
282
- return secondsClause(ir, "minuto", opts);
296
+ function secondsLeadClause(schedule, opts) {
297
+ return secondsClause(schedule, "minuto", opts);
283
298
  }
284
- function secondsClause(ir, anchor, opts) {
285
- const secondField = ir.pattern.second;
286
- const shape = ir.shapes.second;
299
+ function secondsClause(schedule, anchor, opts) {
300
+ const secondField = schedule.pattern.second;
301
+ const shape = schedule.shapes.second;
287
302
  if (secondField === "*") {
288
303
  return "cada segundo";
289
304
  }
290
305
  if (shape === "step") {
291
306
  return stepCycle60(
292
- stepSegment(ir, "second"),
307
+ stepSegment(schedule, "second"),
293
308
  "segundo",
294
309
  anchor,
295
310
  opts
@@ -303,31 +318,31 @@ function secondsClause(ir, anchor, opts) {
303
318
  return "en el segundo " + secondField + " de cada " + anchor;
304
319
  }
305
320
  return strideFromSegments(
306
- segmentsOf(ir, "second"),
321
+ segmentsOf(schedule, "second"),
307
322
  "segundo",
308
323
  anchor,
309
324
  opts
310
- ) ?? "en los segundos " + joinList(segmentWords(segmentsOf(ir, "second"))) + " de cada " + anchor;
325
+ ) ?? "en los segundos " + joinList(segmentWords(segmentsOf(schedule, "second"))) + " de cada " + anchor;
311
326
  }
312
- function renderEveryMinute(ir, plan, opts) {
313
- return "cada minuto" + trailingQualifier(ir, opts);
327
+ function renderEveryMinute(schedule, plan, opts) {
328
+ return "cada minuto" + trailingQualifier(schedule, opts);
314
329
  }
315
- function renderSingleMinute(ir, plan, opts) {
316
- return "en el minuto " + ir.pattern.minute + " de cada hora" + trailingQualifier(ir, opts);
330
+ function renderSingleMinute(schedule, plan, opts) {
331
+ return "en el minuto " + schedule.pattern.minute + " de cada hora" + trailingQualifier(schedule, opts);
317
332
  }
318
- function renderRangeOfMinutes(ir, plan, opts) {
319
- return minuteRangeLead(ir.pattern.minute) + " de cada hora" + trailingQualifier(ir, opts);
333
+ function renderRangeOfMinutes(schedule, plan, opts) {
334
+ return minuteRangeLead(schedule.pattern.minute) + " de cada hora" + trailingQualifier(schedule, opts);
320
335
  }
321
- function renderMultipleMinutes(ir, plan, opts) {
322
- return minutesList(ir, opts) + trailingQualifier(ir, opts);
336
+ function renderMultipleMinutes(schedule, plan, opts) {
337
+ return minutesList(schedule, opts) + trailingQualifier(schedule, opts);
323
338
  }
324
- function minutesList(ir, opts) {
339
+ function minutesList(schedule, opts) {
325
340
  return strideFromSegments(
326
- segmentsOf(ir, "minute"),
341
+ segmentsOf(schedule, "minute"),
327
342
  "minuto",
328
343
  "hora",
329
344
  opts
330
- ) ?? "en los minutos " + joinList(segmentWords(segmentsOf(ir, "minute"))) + " de cada hora";
345
+ ) ?? "en los minutos " + joinList(segmentWords(segmentsOf(schedule, "minute"))) + " de cada hora";
331
346
  }
332
347
  function minuteRangeLead(minuteField) {
333
348
  const bounds = minuteField.split("-");
@@ -382,86 +397,86 @@ function spanHours(hours) {
382
397
  return (hour === 1 ? "la " : "las ") + hour;
383
398
  }));
384
399
  }
385
- function renderMinuteFrequency(ir, plan, opts) {
400
+ function renderMinuteFrequency(schedule, plan, opts) {
386
401
  let phrase = stepCycle60(
387
- stepSegment(ir, "minute"),
402
+ stepSegment(schedule, "minute"),
388
403
  "minuto",
389
404
  "hora",
390
405
  opts
391
406
  );
392
407
  if (plan.hours.kind === "during") {
393
- const cadence = unevenHourCadence(ir, opts);
408
+ const cadence = unevenHourCadence(schedule, opts);
394
409
  if (cadence) {
395
410
  phrase += ", " + cadence;
396
411
  } else {
397
- phrase += singleHourStep(ir.analyses.segments.hour) ? ", " + stepHourSpan(stepSegment(ir, "hour"), opts) : " " + hourSpanFromTimes(ir, plan.hours.times, opts);
412
+ phrase += singleHourStep(schedule.analyses.segments.hour) ? ", " + stepHourSpan(stepSegment(schedule, "hour"), opts) : " " + hourSpanFromTimes(schedule, plan.hours.times, opts);
398
413
  }
399
414
  } else if (plan.hours.kind === "window") {
400
415
  phrase += " " + hourWindow(plan.hours, opts);
401
416
  } else if (plan.hours.kind === "step") {
402
- phrase += ", " + stepHourSpan(stepSegment(ir, "hour"), opts);
417
+ phrase += ", " + stepHourSpan(stepSegment(schedule, "hour"), opts);
403
418
  }
404
- return phrase + trailingQualifier(ir, opts);
419
+ return phrase + trailingQualifier(schedule, opts);
405
420
  }
406
- function renderMinuteSpanInHour(ir, plan, opts) {
407
- if (ir.pattern.minute === "*") {
408
- return "cada minuto de la hora " + fromTime(timePhrase(plan.hour, 0, null, opts)) + trailingQualifier(ir, opts);
421
+ function renderMinuteSpanInHour(schedule, plan, opts) {
422
+ if (schedule.pattern.minute === "*") {
423
+ return "cada minuto de la hora " + fromTime(timePhrase(plan.hour, 0, null, opts)) + trailingQualifier(schedule, opts);
409
424
  }
410
425
  return "cada minuto " + timeRange(
411
426
  { hour: plan.hour, minute: plan.span[0] },
412
427
  { hour: plan.hour, minute: plan.span[1] },
413
428
  opts
414
- ) + trailingQualifier(ir, opts);
429
+ ) + trailingQualifier(schedule, opts);
415
430
  }
416
- function renderMinutesAcrossHours(ir, plan, opts) {
417
- const cadence = unevenHourCadence(ir, opts);
431
+ function renderMinutesAcrossHours(schedule, plan, opts) {
432
+ const cadence = unevenHourCadence(schedule, opts);
418
433
  if (plan.form === "wildcard") {
419
434
  if (cadence !== null) {
420
- return "cada minuto, " + cadence + trailingQualifier(ir, opts);
435
+ return "cada minuto, " + cadence + trailingQualifier(schedule, opts);
421
436
  }
422
- if (singleHourStep(ir.analyses.segments.hour)) {
423
- return "cada minuto, " + stepHourSpan(stepSegment(ir, "hour"), opts) + trailingQualifier(ir, opts);
437
+ if (singleHourStep(schedule.analyses.segments.hour)) {
438
+ return "cada minuto, " + stepHourSpan(stepSegment(schedule, "hour"), opts) + trailingQualifier(schedule, opts);
424
439
  }
425
- return "cada minuto " + hourSpanFromTimes(ir, plan.times, opts) + trailingQualifier(ir, opts);
440
+ return "cada minuto " + hourSpanFromTimes(schedule, plan.times, opts) + trailingQualifier(schedule, opts);
426
441
  }
427
- const lead = plan.form === "range" ? minuteRangeLead(ir.pattern.minute) : minutesList(ir, opts);
442
+ const lead = plan.form === "range" ? minuteRangeLead(schedule.pattern.minute) : minutesList(schedule, opts);
428
443
  if (cadence !== null) {
429
- return lead + ", " + cadence + trailingQualifier(ir, opts);
444
+ return lead + ", " + cadence + trailingQualifier(schedule, opts);
430
445
  }
431
- return lead + ", " + atHourTimes(ir, plan.times, opts) + trailingQualifier(ir, opts);
446
+ return lead + ", " + atHourTimes(schedule, plan.times, opts) + trailingQualifier(schedule, opts);
432
447
  }
433
- function renderMinuteSpanAcrossHourStep(ir, plan, opts) {
434
- const segment = stepSegment(ir, "hour");
435
- const cadence = unevenHourCadence(ir, opts);
448
+ function renderMinuteSpanAcrossHourStep(schedule, plan, opts) {
449
+ const segment = stepSegment(schedule, "hour");
450
+ const cadence = unevenHourCadence(schedule, opts);
436
451
  if (plan.form === "wildcard") {
437
- return "cada minuto, " + stepHourSpan(segment, opts) + trailingQualifier(ir, opts);
452
+ return "cada minuto, " + stepHourSpan(segment, opts) + trailingQualifier(schedule, opts);
438
453
  }
439
- const lead = plan.form === "list" ? minutesList(ir, opts) : minuteRangeLead(ir.pattern.minute);
440
- return lead + ", " + (cadence ?? stepHours(segment, opts)) + trailingQualifier(ir, opts);
454
+ const lead = plan.form === "list" ? minutesList(schedule, opts) : minuteRangeLead(schedule.pattern.minute);
455
+ return lead + ", " + (cadence ?? stepHours(segment, opts)) + trailingQualifier(schedule, opts);
441
456
  }
442
- function renderEveryHour(ir, plan, opts) {
443
- return "cada hora" + trailingQualifier(ir, opts);
457
+ function renderEveryHour(schedule, plan, opts) {
458
+ return "cada hora" + trailingQualifier(schedule, opts);
444
459
  }
445
- function renderHourRange(ir, plan, opts) {
460
+ function renderHourRange(schedule, plan, opts) {
446
461
  const window = hourWindow(boundedWindow(plan), opts);
447
462
  if (plan.minuteForm === "wildcard") {
448
- return "cada minuto " + window + trailingQualifier(ir, opts);
463
+ return "cada minuto " + window + trailingQualifier(schedule, opts);
449
464
  }
450
465
  if (plan.minuteForm === "range") {
451
- return minuteRangeLead(ir.pattern.minute) + ", " + window + trailingQualifier(ir, opts);
466
+ return minuteRangeLead(schedule.pattern.minute) + ", " + window + trailingQualifier(schedule, opts);
452
467
  }
453
- if (ir.pattern.minute === "0") {
454
- return "cada hora " + window + trailingQualifier(ir, opts);
468
+ if (schedule.pattern.minute === "0") {
469
+ return "cada hora " + window + trailingQualifier(schedule, opts);
455
470
  }
456
- const lead = ir.shapes.minute === "single" ? "en el minuto " + ir.pattern.minute + " de cada hora" : minutesList(ir, opts);
457
- return lead + ", " + window + trailingQualifier(ir, opts);
471
+ const lead = schedule.shapes.minute === "single" ? "en el minuto " + schedule.pattern.minute + " de cada hora" : minutesList(schedule, opts);
472
+ return lead + ", " + window + trailingQualifier(schedule, opts);
458
473
  }
459
- function renderHourStep(ir, plan, opts) {
460
- const cadence = unevenHourCadence(ir, opts);
474
+ function renderHourStep(schedule, plan, opts) {
475
+ const cadence = unevenHourCadence(schedule, opts);
461
476
  if (cadence !== null) {
462
- return cadence + trailingQualifier(ir, opts);
477
+ return cadence + trailingQualifier(schedule, opts);
463
478
  }
464
- return stepHours(stepSegment(ir, "hour"), opts) + trailingQualifier(ir, opts);
479
+ return stepHours(stepSegment(schedule, "hour"), opts) + trailingQualifier(schedule, opts);
465
480
  }
466
481
  function boundedWindow(plan) {
467
482
  const last = plan.minuteForm === "wildcard" ? plan.boundMinute ?? 0 : 0;
@@ -474,42 +489,46 @@ function hourWindow(window, opts) {
474
489
  opts
475
490
  );
476
491
  }
477
- function isDateWeekdayUnion(ir) {
478
- return ir.pattern.date !== "*" && ir.pattern.weekday !== "*";
492
+ function isDateWeekdayUnion(schedule) {
493
+ return schedule.pattern.date !== "*" && schedule.pattern.weekday !== "*";
479
494
  }
480
- function unionMonthLeadFull(ir) {
481
- if (ir.pattern.month === "*") {
495
+ function unionMonthLeadFull(schedule) {
496
+ if (schedule.pattern.month === "*") {
482
497
  return "";
483
498
  }
484
- const lead = monthPhrase(ir, monthRanged(ir) ? "de " : "en ");
485
- const segments = flattenSteps(segmentsOf(ir, "month"));
486
- const isEnumeration = !monthRanged(ir) && segments.length >= 2;
499
+ const lead = monthPhrase(schedule, monthRanged(schedule) ? "de " : "en ");
500
+ const segments = flattenSteps(segmentsOf(schedule, "month"));
501
+ const isEnumeration = !monthRanged(schedule) && segments.length >= 2;
487
502
  return isEnumeration ? lead + "," : lead;
488
503
  }
489
- function domArm(ir, opts) {
490
- const date = ir.pattern.date;
504
+ function domArm(schedule, opts) {
505
+ const date = schedule.pattern.date;
491
506
  const quartz = quartzDatePhrase(date);
492
507
  if (quartz) {
493
508
  return quartz;
494
509
  }
510
+ const parity = parityDayPredicate(date);
511
+ if (parity) {
512
+ return parity;
513
+ }
495
514
  if (isOpenStep(date)) {
496
515
  return stepDates(date, opts);
497
516
  }
498
- const segments = segmentsOf(ir, "date");
517
+ const segments = segmentsOf(schedule, "date");
499
518
  if (segments.length === 1 && segments[0].kind === "range") {
500
519
  return "del " + segments[0].bounds[0] + " al " + segments[0].bounds[1] + " del mes";
501
520
  }
502
521
  if (segments.length === 1 && segments[0].kind === "single") {
503
- return ir.pattern.month === "*" ? "el " + segments[0].value + " de cada mes" : "el d\xEDa " + segments[0].value;
522
+ return schedule.pattern.month === "*" ? "el " + segments[0].value + " de cada mes" : "el d\xEDa " + segments[0].value;
504
523
  }
505
524
  return "los d\xEDas " + joinList(segmentWords(segments)) + " del mes";
506
525
  }
507
- function dowArm(ir) {
508
- const quartz = quartzWeekdayPhrase(ir.pattern.weekday);
526
+ function dowArm(schedule) {
527
+ const quartz = quartzWeekdayPhrase(schedule.pattern.weekday);
509
528
  if (quartz) {
510
529
  return quartz;
511
530
  }
512
- const segments = orderWeekdaysForDisplay(segmentsOf(ir, "weekday"));
531
+ const segments = orderWeekdaysForDisplay(segmentsOf(schedule, "weekday"));
513
532
  const allSingles = segments.every(function single(segment) {
514
533
  return segment.kind === "single";
515
534
  });
@@ -522,19 +541,19 @@ function dowArm(ir) {
522
541
  }));
523
542
  }
524
543
  if (segments.length === 1) {
525
- return weekdayRange(segments[0]);
544
+ return "cualquier d\xEDa " + weekdayRange(segments[0]);
526
545
  }
527
546
  return joinList(segments.map(function name(segment) {
528
547
  return segment.kind === "range" ? weekdayRange(segment) : "los " + pluralWeekday(segment.value);
529
548
  }));
530
549
  }
531
- function unionYaseaSuffix(ir, opts) {
532
- return ", ya sea " + domArm(ir, opts) + " o " + dowArm(ir);
550
+ function unionYaseaSuffix(schedule, opts) {
551
+ return ", ya sea " + domArm(schedule, opts) + " o " + dowArm(schedule);
533
552
  }
534
- function renderClockTimes(ir, plan, opts) {
535
- if (ir.shapes.minute === "single") {
536
- const minute = +ir.pattern.minute;
537
- const cadence = hourCadence(ir, minute, opts) ?? hourRangeCadence(ir, minute, opts);
553
+ function renderClockTimes(schedule, plan, opts) {
554
+ if (schedule.shapes.minute === "single") {
555
+ const minute = +schedule.pattern.minute;
556
+ const cadence = hourCadence(schedule, minute, opts) ?? hourRangeCadence(schedule, minute, opts);
538
557
  if (cadence !== null) {
539
558
  return cadence;
540
559
  }
@@ -542,7 +561,7 @@ function renderClockTimes(ir, plan, opts) {
542
561
  const phrases = plan.times.map(function clock(time) {
543
562
  return atTime(timePhrase(time.hour, time.minute, time.second, opts));
544
563
  });
545
- return leadingQualifier(ir, opts) + groupClockTimes(phrases);
564
+ return leadingQualifier(schedule, opts) + groupClockTimes(phrases);
546
565
  }
547
566
  function explicitClockList(times, opts) {
548
567
  const phrases = times.map(function clock(time) {
@@ -718,23 +737,33 @@ function groupClockTimesByArticle(phrases) {
718
737
  const connector = doubleY ? ", " : " y ";
719
738
  return laPart + connector + lasPart;
720
739
  }
721
- function renderCompactClockTimes(ir, plan, opts) {
740
+ function renderCompactClockTimes(schedule, plan, opts) {
722
741
  if (plan.fold) {
723
- const cadence2 = hourCadence(ir, plan.minute, opts) ?? hourRangeCadence(ir, plan.minute, opts);
742
+ const cadence2 = hourCadence(schedule, plan.minute, opts) ?? hourRangeCadence(schedule, plan.minute, opts);
724
743
  if (cadence2 !== null) {
725
744
  return cadence2;
726
745
  }
727
- const ranged = segmentsOf(ir, "hour").some(function range(segment) {
746
+ const ranged = segmentsOf(schedule, "hour").some(function range(segment) {
728
747
  return segment.kind === "range";
729
748
  });
730
- if (ranged && !ir.analyses.clockSecond) {
731
- return "cada hora " + hourSegmentTimes(ir, plan.minute, ir.analyses.clockSecond, opts) + trailingQualifier(ir, opts);
749
+ if (ranged && !schedule.analyses.clockSecond) {
750
+ return "cada hora " + hourSegmentTimes(
751
+ schedule,
752
+ plan.minute,
753
+ schedule.analyses.clockSecond,
754
+ opts
755
+ ) + trailingQualifier(schedule, opts);
732
756
  }
733
- return leadingQualifier(ir, opts) + hourSegmentTimes(ir, plan.minute, ir.analyses.clockSecond, opts);
757
+ return leadingQualifier(schedule, opts) + hourSegmentTimes(
758
+ schedule,
759
+ plan.minute,
760
+ schedule.analyses.clockSecond,
761
+ opts
762
+ );
734
763
  }
735
- const cadence = unevenHourCadence(ir, opts);
736
- const phrase = cadence ? minutesList(ir, opts) + ", " + cadence + trailingQualifier(ir, opts) : minutesList(ir, opts) + ", " + hourContextTimes(ir, opts) + trailingQualifier(ir, opts);
737
- return ir.analyses.clockSecond ? secondsLeadClause(ir, opts) + ", " + phrase : phrase;
764
+ const cadence = unevenHourCadence(schedule, opts);
765
+ const phrase = cadence ? minutesList(schedule, opts) + ", " + cadence + trailingQualifier(schedule, opts) : minutesList(schedule, opts) + ", " + hourContextTimes(schedule, opts) + trailingQualifier(schedule, opts);
766
+ return schedule.analyses.clockSecond ? secondsLeadClause(schedule, opts) + ", " + phrase : phrase;
738
767
  }
739
768
  var renderers = {
740
769
  clockTimes: renderClockTimes,
@@ -756,18 +785,15 @@ var renderers = {
756
785
  singleMinute: renderSingleMinute,
757
786
  standaloneSeconds: renderStandaloneSeconds
758
787
  };
759
- function renderStride(stride, opts) {
788
+ function renderStride2(stride, opts) {
760
789
  const { interval, start, last, cycle, unit, anchor } = stride;
761
790
  const cadence = "cada " + numero(interval, opts) + " " + unit + "s";
762
- const tiles = cycle % interval === 0;
763
- if (start === 0 && tiles) {
764
- return cadence;
765
- }
766
791
  const tail = anchor ? " de cada " + anchor : "";
767
- if (start < interval && tiles) {
768
- return cadence + " a partir del " + unit + " " + start + tail;
769
- }
770
- return cadence + " del " + unit + " " + start + " al " + last + tail;
792
+ return renderStride({ start, interval, cycle }, {
793
+ bare: () => cadence,
794
+ offset: () => cadence + " a partir del " + unit + " " + start + tail,
795
+ bounded: () => cadence + " del " + unit + " " + start + " al " + last + tail
796
+ });
771
797
  }
772
798
  function stepCycle60(segment, unit, anchor, opts) {
773
799
  if (segment.startToken.indexOf("-") !== -1) {
@@ -777,7 +803,7 @@ function stepCycle60(segment, unit, anchor, opts) {
777
803
  if (start !== 0 && segment.fires.length <= 3) {
778
804
  return "en los " + unit + "s " + joinList(wordList(segment.fires)) + " de cada " + anchor;
779
805
  }
780
- return renderStride({
806
+ return renderStride2({
781
807
  interval: segment.interval,
782
808
  start,
783
809
  last: segment.fires[segment.fires.length - 1],
@@ -789,7 +815,7 @@ function stepCycle60(segment, unit, anchor, opts) {
789
815
  function strideFromSegments(segments, unit, anchor, opts) {
790
816
  const values = singleValues(segments);
791
817
  const step = values && arithmeticStep(values);
792
- return step ? renderStride({ ...step, cycle: 60, unit, anchor }, opts) : null;
818
+ return step ? renderStride2({ ...step, cycle: 60, unit, anchor }, opts) : null;
793
819
  }
794
820
  function stepHours(segment, opts) {
795
821
  if (segment.startToken.indexOf("-") !== -1) {
@@ -808,24 +834,21 @@ function stepHours(segment, opts) {
808
834
  function hourStrideCadence(stride, opts) {
809
835
  const { start, interval, last } = stride;
810
836
  const cadence = "cada " + numero(interval, opts) + " horas";
811
- const tiles = 24 % interval === 0;
812
- if (start === 0 && tiles) {
813
- return cadence;
814
- }
815
- if (start < interval && tiles) {
816
- return cadence + " a partir de " + timePhrase(start, 0, null, opts);
817
- }
818
- return cadence + " de " + timePhrase(start, 0, null, opts) + " a " + timePhrase(last, 0, null, opts);
837
+ return renderStride({ start, interval, cycle: 24 }, {
838
+ bare: () => cadence,
839
+ offset: () => cadence + " a partir de " + timePhrase(start, 0, null, opts),
840
+ bounded: () => cadence + " de " + timePhrase(start, 0, null, opts) + " a " + timePhrase(last, 0, null, opts)
841
+ });
819
842
  }
820
- function unevenHourCadence(ir, opts) {
821
- const stride = hourStride(ir);
843
+ function unevenHourCadence(schedule, opts) {
844
+ const stride = hourStride(schedule);
822
845
  if (!stride || offsetCleanStride(stride)) {
823
846
  return null;
824
847
  }
825
848
  return hourStrideCadence(stride, opts);
826
849
  }
827
- function hourStride(ir) {
828
- const segments = segmentsOf(ir, "hour");
850
+ function hourStride(schedule) {
851
+ const segments = segmentsOf(schedule, "hour");
829
852
  if (segments.length === 1 && segments[0].kind === "step") {
830
853
  const segment = segments[0];
831
854
  if (segment.fires.length < 2) {
@@ -837,64 +860,64 @@ function hourStride(ir) {
837
860
  const values = singleValues(segments);
838
861
  return values && hourListStride(values);
839
862
  }
840
- function subMinuteSecond(ir) {
841
- return ir.pattern.second === "*" || ir.shapes.second === "step";
863
+ function subMinuteSecond(schedule) {
864
+ return schedule.pattern.second === "*" || schedule.shapes.second === "step";
842
865
  }
843
- function hourCadenceLead(ir, minute, opts) {
866
+ function hourCadenceLead(schedule, minute, opts) {
844
867
  if (minute === 0) {
845
- if (subMinuteSecond(ir)) {
846
- return secondsClause(ir, "minuto", opts) + " durante un minuto";
868
+ if (subMinuteSecond(schedule)) {
869
+ return secondsClause(schedule, "minuto", opts) + " durante un minuto";
847
870
  }
848
- return secondsClause(ir, "hora", opts);
871
+ return secondsClause(schedule, "hora", opts);
849
872
  }
850
873
  const minutePhrase = "en el minuto " + minute;
851
- if (ir.pattern.second === "0") {
874
+ if (schedule.pattern.second === "0") {
852
875
  return minutePhrase;
853
876
  }
854
- return secondsClause(ir, "minuto", opts) + ", " + minutePhrase;
877
+ return secondsClause(schedule, "minuto", opts) + ", " + minutePhrase;
855
878
  }
856
- function hourCadence(ir, minute, opts) {
857
- const stride = hourStride(ir);
879
+ function hourCadence(schedule, minute, opts) {
880
+ const stride = hourStride(schedule);
858
881
  if (!stride) {
859
882
  return null;
860
883
  }
861
884
  const fires = (stride.last - stride.start) / stride.interval + 1;
862
- if (ir.pattern.second === "0" && fires <= maxClockTimes && offsetCleanStride(stride)) {
885
+ if (schedule.pattern.second === "0" && fires <= maxClockTimes && offsetCleanStride(stride)) {
863
886
  return null;
864
887
  }
865
- const confinement = minute === 0 && subMinuteSecond(ir) && cleanStrideSegment(ir);
888
+ const confinement = minute === 0 && subMinuteSecond(schedule) && cleanStrideSegment(schedule);
866
889
  if (confinement) {
867
- return secondsClause(ir, "minuto", opts) + " durante un minuto, " + stepHourSpan(confinement, opts) + trailingQualifier(ir, opts);
890
+ return secondsClause(schedule, "minuto", opts) + " durante un minuto, " + stepHourSpan(confinement, opts) + trailingQualifier(schedule, opts);
868
891
  }
869
- if (minute === 0 && ir.pattern.second === "0") {
870
- return hourStrideCadence(stride, opts) + trailingQualifier(ir, opts);
892
+ if (minute === 0 && schedule.pattern.second === "0") {
893
+ return hourStrideCadence(stride, opts) + trailingQualifier(schedule, opts);
871
894
  }
872
- return hourCadenceLead(ir, minute, opts) + ", " + hourStrideCadence(stride, opts) + trailingQualifier(ir, opts);
895
+ return hourCadenceLead(schedule, minute, opts) + ", " + hourStrideCadence(stride, opts) + trailingQualifier(schedule, opts);
873
896
  }
874
- function cleanStrideSegment(ir) {
875
- const segments = segmentsOf(ir, "hour");
897
+ function cleanStrideSegment(schedule) {
898
+ const segments = segmentsOf(schedule, "hour");
876
899
  const segment = segments.length === 1 && segments[0];
877
900
  if (!segment || segment.kind !== "step" || segment.startToken.indexOf("-") !== -1) {
878
901
  return null;
879
902
  }
880
903
  return segment;
881
904
  }
882
- function hasHourWindow(ir) {
883
- return segmentsOf(ir, "hour").some(function range(segment) {
905
+ function hasHourWindow(schedule) {
906
+ return segmentsOf(schedule, "hour").some(function range(segment) {
884
907
  return segment.kind === "range";
885
908
  });
886
909
  }
887
- function hourRangeCadence(ir, minute, opts) {
888
- if (minute !== 0 || !hasHourWindow(ir) || ir.pattern.second === "0") {
910
+ function hourRangeCadence(schedule, minute, opts) {
911
+ if (minute !== 0 || !hasHourWindow(schedule) || schedule.pattern.second === "0") {
889
912
  return null;
890
913
  }
891
- if (subMinuteSecond(ir)) {
892
- return secondsClause(ir, "minuto", opts) + " durante un minuto, durante las horas " + hourSegmentTimes(ir, 0, null, opts) + trailingQualifier(ir, opts);
914
+ if (subMinuteSecond(schedule)) {
915
+ return secondsClause(schedule, "minuto", opts) + " durante un minuto, durante las horas " + hourSegmentTimes(schedule, 0, null, opts) + trailingQualifier(schedule, opts);
893
916
  }
894
- return hourCadenceLead(ir, minute, opts) + ", " + hourSegmentTimes(ir, 0, null, opts) + trailingQualifier(ir, opts);
917
+ return hourCadenceLead(schedule, minute, opts) + ", " + hourSegmentTimes(schedule, 0, null, opts) + trailingQualifier(schedule, opts);
895
918
  }
896
- function hourContextTimes(ir, opts) {
897
- const segments = segmentsOf(ir, "hour");
919
+ function hourContextTimes(schedule, opts) {
920
+ const segments = segmentsOf(schedule, "hour");
898
921
  const points = [];
899
922
  const hasRange = segments.some(function range(segment) {
900
923
  return segment.kind === "range";
@@ -940,25 +963,25 @@ function atTimes(hours, opts) {
940
963
  return atTime(timePhrase(hour, 0, null, opts));
941
964
  });
942
965
  }
943
- function atHourTimes(ir, times, opts) {
966
+ function atHourTimes(schedule, times, opts) {
944
967
  if (times.kind === "fires") {
945
968
  return groupClockTimesByArticle(atTimes(times.fires, opts));
946
969
  }
947
- return hourSegmentTimes(ir, 0, null, opts);
970
+ return hourSegmentTimes(schedule, 0, null, opts);
948
971
  }
949
- function hourSpanFromTimes(ir, times, opts) {
972
+ function hourSpanFromTimes(schedule, times, opts) {
950
973
  if (times.kind === "fires" && times.fires.length > 3) {
951
974
  return "durante las horas " + hourSpanList(times.fires, opts);
952
975
  }
953
- return hourWindowsFromTimes(ir, times, opts);
976
+ return hourWindowsFromTimes(schedule, times, opts);
954
977
  }
955
- function hourWindowsFromTimes(ir, times, opts) {
978
+ function hourWindowsFromTimes(schedule, times, opts) {
956
979
  if (times.kind === "fires") {
957
980
  return joinList(times.fires.map(function window(hour) {
958
981
  return hourAsWindow(hour, opts);
959
982
  }));
960
983
  }
961
- return joinList(segmentsOf(ir, "hour").map(function window(segment) {
984
+ return joinList(segmentsOf(schedule, "hour").map(function window(segment) {
962
985
  if (segment.kind === "range") {
963
986
  return timeRange(
964
987
  { hour: +segment.bounds[0], minute: 0 },
@@ -974,10 +997,10 @@ function hourWindowsFromTimes(ir, times, opts) {
974
997
  return hourAsWindow(+segment.value, opts);
975
998
  }));
976
999
  }
977
- function hourSegmentTimes(ir, minute, second, opts) {
1000
+ function hourSegmentTimes(schedule, minute, second, opts) {
978
1001
  const pieces = [];
979
1002
  const fromRange = [];
980
- segmentsOf(ir, "hour").forEach(function clock(segment) {
1003
+ segmentsOf(schedule, "hour").forEach(function clock(segment) {
981
1004
  if (segment.kind === "step") {
982
1005
  segment.fires.forEach(function each(hour) {
983
1006
  pieces.push(atTime(timePhrase(hour, minute, second, opts)));
@@ -1089,47 +1112,47 @@ function dayPeriod(hour, opts) {
1089
1112
  }
1090
1113
  return "de la tarde";
1091
1114
  }
1092
- function leadingQualifier(ir, opts) {
1093
- const pattern = ir.pattern;
1115
+ function leadingQualifier(schedule, opts) {
1116
+ const pattern = schedule.pattern;
1094
1117
  if (pattern.date !== "*" && pattern.weekday !== "*") {
1095
1118
  return "";
1096
1119
  }
1097
1120
  if (pattern.date !== "*") {
1098
- return datePhrase(ir, opts) + " ";
1121
+ return datePhrase(schedule, opts) + " ";
1099
1122
  }
1100
1123
  if (pattern.weekday !== "*") {
1101
- return weekdayQualifier(ir) + monthScope(ir) + " ";
1124
+ return weekdayQualifier(schedule) + monthScope(schedule) + " ";
1102
1125
  }
1103
1126
  if (pattern.month !== "*") {
1104
- return "todos los d\xEDas " + monthPhrase(ir, "de ") + " ";
1127
+ return "todos los d\xEDas " + monthPhrase(schedule, "de ") + " ";
1105
1128
  }
1106
1129
  return "todos los d\xEDas ";
1107
1130
  }
1108
- function trailingQualifier(ir, opts) {
1109
- const pattern = ir.pattern;
1131
+ function trailingQualifier(schedule, opts) {
1132
+ const pattern = schedule.pattern;
1110
1133
  if (pattern.date !== "*" && pattern.weekday !== "*") {
1111
1134
  return "";
1112
1135
  }
1113
1136
  if (pattern.date !== "*") {
1114
- return " " + datePhrase(ir, opts);
1137
+ return " " + datePhrase(schedule, opts);
1115
1138
  }
1116
1139
  if (pattern.weekday !== "*") {
1117
- return " " + weekdayQualifier(ir) + monthScope(ir);
1140
+ return " " + weekdayQualifier(schedule) + monthScope(schedule);
1118
1141
  }
1119
1142
  if (pattern.month !== "*") {
1120
- return " " + monthPhrase(ir, "en ");
1143
+ return " " + monthPhrase(schedule, "en ");
1121
1144
  }
1122
1145
  return "";
1123
1146
  }
1124
- function datePhrase(ir, opts) {
1125
- const pattern = ir.pattern;
1147
+ function datePhrase(schedule, opts) {
1148
+ const pattern = schedule.pattern;
1126
1149
  if (quartzDatePhrase(pattern.date) || isOpenStep(pattern.date)) {
1127
- return dateClause(ir, "", opts) + monthScope(ir);
1150
+ return dateClause(schedule, "", opts) + monthScope(schedule);
1128
1151
  }
1129
- return dateClause(ir, dateMonthPart(ir), opts);
1152
+ return dateClause(schedule, dateMonthPart(schedule), opts);
1130
1153
  }
1131
- function dateClause(ir, monthPart, opts) {
1132
- const pattern = ir.pattern;
1154
+ function dateClause(schedule, monthPart, opts) {
1155
+ const pattern = schedule.pattern;
1133
1156
  const quartz = quartzDatePhrase(pattern.date);
1134
1157
  if (quartz) {
1135
1158
  return quartz;
@@ -1137,31 +1160,31 @@ function dateClause(ir, monthPart, opts) {
1137
1160
  if (isOpenStep(pattern.date)) {
1138
1161
  return stepDates(pattern.date, opts);
1139
1162
  }
1140
- const segments = segmentsOf(ir, "date");
1163
+ const segments = segmentsOf(schedule, "date");
1141
1164
  if (segments.length === 1 && segments[0].kind === "range") {
1142
- return "del " + segments[0].bounds[0] + " al " + segments[0].bounds[1] + monthPart + foldedYear(ir);
1165
+ return "del " + segments[0].bounds[0] + " al " + segments[0].bounds[1] + monthPart + foldedYear(schedule);
1143
1166
  }
1144
1167
  if (segments.length === 1 && segments[0].kind === "single") {
1145
- return "el " + segments[0].value + monthPart + foldedYear(ir);
1168
+ return "el " + segments[0].value + monthPart + foldedYear(schedule);
1146
1169
  }
1147
- return "los d\xEDas " + joinList(segmentWords(segments)) + monthPart + foldedYear(ir);
1170
+ return "los d\xEDas " + joinList(segmentWords(segments)) + monthPart + foldedYear(schedule);
1148
1171
  }
1149
- function monthRanged(ir) {
1150
- return ir.pattern.month !== "*" && segmentsOf(ir, "month").some(function range(segment) {
1172
+ function monthRanged(schedule) {
1173
+ return schedule.pattern.month !== "*" && segmentsOf(schedule, "month").some(function range(segment) {
1151
1174
  return segment.kind === "range";
1152
1175
  });
1153
1176
  }
1154
- function dateMonthPart(ir) {
1155
- if (ir.pattern.month === "*") {
1177
+ function dateMonthPart(schedule) {
1178
+ if (schedule.pattern.month === "*") {
1156
1179
  return " de cada mes";
1157
1180
  }
1158
- if (monthRanged(ir)) {
1159
- return " de cada mes, " + monthPhrase(ir, "de ");
1181
+ if (monthRanged(schedule)) {
1182
+ return " de cada mes, " + monthPhrase(schedule, "de ");
1160
1183
  }
1161
- return " " + monthPhrase(ir, "de ");
1184
+ return " " + monthPhrase(schedule, "de ");
1162
1185
  }
1163
- function foldedYear(ir) {
1164
- const yearField = ir.pattern.year;
1186
+ function foldedYear(schedule) {
1187
+ const yearField = schedule.pattern.year;
1165
1188
  if (yearField === "*" || yearField.indexOf("/") !== -1 || yearField.indexOf("-") !== -1 || yearField.indexOf(",") !== -1) {
1166
1189
  return "";
1167
1190
  }
@@ -1192,12 +1215,12 @@ function quartzWeekdayPhrase(weekdayField) {
1192
1215
  return "el \xFAltimo " + weekdayName(weekdayField.slice(0, -1)) + " del mes";
1193
1216
  }
1194
1217
  }
1195
- function weekdayQualifier(ir) {
1196
- const quartz = quartzWeekdayPhrase(ir.pattern.weekday);
1218
+ function weekdayQualifier(schedule) {
1219
+ const quartz = quartzWeekdayPhrase(schedule.pattern.weekday);
1197
1220
  if (quartz) {
1198
1221
  return quartz;
1199
1222
  }
1200
- const segments = orderWeekdaysForDisplay(segmentsOf(ir, "weekday"));
1223
+ const segments = orderWeekdaysForDisplay(segmentsOf(schedule, "weekday"));
1201
1224
  const allSingles = segments.every(function single(segment) {
1202
1225
  return segment.kind === "single";
1203
1226
  });
@@ -1223,8 +1246,8 @@ function flattenSteps(segments) {
1223
1246
  }) : [segment];
1224
1247
  });
1225
1248
  }
1226
- function monthPhrase(ir, lead) {
1227
- const segments = flattenSteps(segmentsOf(ir, "month"));
1249
+ function monthPhrase(schedule, lead) {
1250
+ const segments = flattenSteps(segmentsOf(schedule, "month"));
1228
1251
  const ranged = segments.some(function range(segment) {
1229
1252
  return segment.kind === "range";
1230
1253
  });
@@ -1240,11 +1263,26 @@ function monthPhrase(ir, lead) {
1240
1263
  return lead + monthName(segment.value);
1241
1264
  }));
1242
1265
  }
1243
- function monthScope(ir) {
1244
- if (ir.pattern.month === "*") {
1266
+ function monthScope(schedule) {
1267
+ if (schedule.pattern.month === "*") {
1245
1268
  return "";
1246
1269
  }
1247
- return (monthRanged(ir) ? ", " : " ") + monthPhrase(ir, "de ");
1270
+ return (monthRanged(schedule) ? ", " : " ") + monthPhrase(schedule, "de ");
1271
+ }
1272
+ function parityDayPredicate(dateField) {
1273
+ if (!isOpenStep(dateField)) {
1274
+ return;
1275
+ }
1276
+ const [start, step] = dateField.split("/");
1277
+ if (+step !== 2) {
1278
+ return;
1279
+ }
1280
+ if (start === "*" || start === "1") {
1281
+ return "un d\xEDa impar del mes";
1282
+ }
1283
+ if (start === "2") {
1284
+ return "un d\xEDa par del mes";
1285
+ }
1248
1286
  }
1249
1287
  function stepDates(dateField, opts) {
1250
1288
  const parts = dateField.split("/");
@@ -1254,15 +1292,15 @@ function stepDates(dateField, opts) {
1254
1292
  }
1255
1293
  return phrase;
1256
1294
  }
1257
- function applyYear(description, ir, opts) {
1258
- const yearField = ir.pattern.year;
1295
+ function applyYear(description, schedule, opts) {
1296
+ const yearField = schedule.pattern.year;
1259
1297
  if (yearField === "*") {
1260
1298
  return description;
1261
1299
  }
1262
1300
  if (yearField.indexOf("/") !== -1) {
1263
1301
  return description + " " + stepYears(yearField, opts);
1264
1302
  }
1265
- if (foldedYear(ir) && ir.pattern.date !== "*") {
1303
+ if (foldedYear(schedule) && schedule.pattern.date !== "*") {
1266
1304
  return description;
1267
1305
  }
1268
1306
  if (yearField.indexOf(",") !== -1) {