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.cjs CHANGED
@@ -57,6 +57,16 @@ function isNonNegativeInteger(value) {
57
57
  const digits = /^\d+$/;
58
58
  return digits.test(value);
59
59
  }
60
+ function toFieldNumber(token, numberMap) {
61
+ return isNonNegativeInteger(token) ? +token : numberMap[token.toUpperCase()];
62
+ }
63
+
64
+ // src/core/shapes.ts
65
+ function isOpenStep(field) {
66
+ return field.indexOf("/") !== -1 && field.indexOf("-") === -1 && field.indexOf(",") === -1;
67
+ }
68
+
69
+ // src/core/cadence.ts
60
70
  function arithmeticStep(values) {
61
71
  if (values.length < 5) {
62
72
  return null;
@@ -72,34 +82,11 @@ function arithmeticStep(values) {
72
82
  }
73
83
  return { start: values[0], interval, last: values[values.length - 1] };
74
84
  }
75
- function weekdayDisplayKey(value) {
76
- return value === 0 ? 7 : value;
85
+ function segmentsOf(schedule, field) {
86
+ return schedule.analyses.segments[field] ?? [];
77
87
  }
78
- function orderWeekdaysForDisplay(segments) {
79
- const flattened = segments.flatMap(function flat(segment) {
80
- return segment.kind === "step" ? segment.fires.map(function single(value) {
81
- return { kind: "single", value: "" + value };
82
- }) : [segment];
83
- });
84
- function key(segment) {
85
- return segment.kind === "range" ? weekdayDisplayKey(+segment.bounds[0]) : weekdayDisplayKey(+segment.value);
86
- }
87
- return flattened.map(function index(segment, position) {
88
- return [segment, position];
89
- }).sort(function byDisplayKey(a, b) {
90
- return key(a[0]) - key(b[0]) || a[1] - b[1];
91
- }).map(function unwrap(pair) {
92
- return pair[0];
93
- });
94
- }
95
- function toFieldNumber(token, numberMap) {
96
- return isNonNegativeInteger(token) ? +token : numberMap[token.toUpperCase()];
97
- }
98
- function segmentsOf(ir, field) {
99
- return ir.analyses.segments[field] ?? [];
100
- }
101
- function stepSegment(ir, field) {
102
- return segmentsOf(ir, field)[0];
88
+ function stepSegment(schedule, field) {
89
+ return segmentsOf(schedule, field)[0];
103
90
  }
104
91
  function singleValues(segments) {
105
92
  const values = [];
@@ -114,6 +101,17 @@ function singleValues(segments) {
114
101
  function offsetCleanStride(stride) {
115
102
  return stride.start < stride.interval && 24 % stride.interval === 0;
116
103
  }
104
+ function renderStride(spec, parts) {
105
+ const { start, interval, cycle } = spec;
106
+ const tiles = cycle % interval === 0;
107
+ if (start === 0 && tiles) {
108
+ return parts.bare();
109
+ }
110
+ if (start < interval && tiles) {
111
+ return parts.offset();
112
+ }
113
+ return parts.bounded();
114
+ }
117
115
  function hourListStride(values) {
118
116
  if (values.length < 2) {
119
117
  return null;
@@ -133,9 +131,26 @@ function hourListStride(values) {
133
131
  return { interval, last: values[values.length - 1], start: values[0] };
134
132
  }
135
133
 
136
- // src/core/shapes.ts
137
- function isOpenStep(field) {
138
- return field.indexOf("/") !== -1 && field.indexOf("-") === -1 && field.indexOf(",") === -1;
134
+ // src/core/weekday.ts
135
+ function weekdayDisplayKey(value) {
136
+ return value === 0 ? 7 : value;
137
+ }
138
+ function orderWeekdaysForDisplay(segments) {
139
+ const flattened = segments.flatMap(function flat(segment) {
140
+ return segment.kind === "step" ? segment.fires.map(function single(value) {
141
+ return { kind: "single", value: "" + value };
142
+ }) : [segment];
143
+ });
144
+ function key(segment) {
145
+ return segment.kind === "range" ? weekdayDisplayKey(+segment.bounds[0]) : weekdayDisplayKey(+segment.value);
146
+ }
147
+ return flattened.map(function index(segment, position) {
148
+ return [segment, position];
149
+ }).sort(function byDisplayKey(a, b) {
150
+ return key(a[0]) - key(b[0]) || a[1] - b[1];
151
+ }).map(function unwrap(pair) {
152
+ return pair[0];
153
+ });
139
154
  }
140
155
 
141
156
  // src/lang/es/dialects.ts
@@ -215,107 +230,107 @@ function normalizeOptions(options) {
215
230
  years: !!options.years
216
231
  };
217
232
  }
218
- function describe(ir, opts) {
219
- return applyYear(render(ir, ir.plan, opts), ir, opts);
233
+ function describe(schedule, opts) {
234
+ return applyYear(render(schedule, schedule.plan, opts), schedule, opts);
220
235
  }
221
- function render(ir, plan, opts) {
222
- const phrase = renderers[plan.kind](ir, plan, opts);
223
- if (!isDateWeekdayUnion(ir)) {
236
+ function render(schedule, plan, opts) {
237
+ const phrase = renderers[plan.kind](schedule, plan, opts);
238
+ if (!isDateWeekdayUnion(schedule)) {
224
239
  return phrase;
225
240
  }
226
- const lead = unionMonthLeadFull(ir);
227
- return (lead ? lead + " " : "") + phrase + unionYaseaSuffix(ir, opts);
241
+ const lead = unionMonthLeadFull(schedule);
242
+ return (lead ? lead + " " : "") + phrase + unionYaseaSuffix(schedule, opts);
228
243
  }
229
- function renderEverySecond(ir, plan, opts) {
230
- return "cada segundo" + trailingQualifier(ir, opts);
244
+ function renderEverySecond(schedule, plan, opts) {
245
+ return "cada segundo" + trailingQualifier(schedule, opts);
231
246
  }
232
- function renderStandaloneSeconds(ir, plan, opts) {
233
- return secondsLeadClause(ir, opts) + trailingQualifier(ir, opts);
247
+ function renderStandaloneSeconds(schedule, plan, opts) {
248
+ return secondsLeadClause(schedule, opts) + trailingQualifier(schedule, opts);
234
249
  }
235
- function renderSecondPastMinute(ir, plan, opts) {
236
- return "en el segundo " + ir.pattern.second + " de cada minuto" + trailingQualifier(ir, opts);
250
+ function renderSecondPastMinute(schedule, plan, opts) {
251
+ return "en el segundo " + schedule.pattern.second + " de cada minuto" + trailingQualifier(schedule, opts);
237
252
  }
238
- function renderSecondsWithinMinute(ir, plan, opts) {
239
- const minuteField = ir.pattern.minute;
253
+ function renderSecondsWithinMinute(schedule, plan, opts) {
254
+ const minuteField = schedule.pattern.minute;
240
255
  if (plan.singleSecond) {
241
- return "en el minuto " + minuteField + " y el segundo " + ir.pattern.second + " de cada hora" + trailingQualifier(ir, opts);
256
+ return "en el minuto " + minuteField + " y el segundo " + schedule.pattern.second + " de cada hora" + trailingQualifier(schedule, opts);
242
257
  }
243
- return secondsLeadClause(ir, opts) + ", en el minuto " + minuteField + " de cada hora" + trailingQualifier(ir, opts);
258
+ return secondsLeadClause(schedule, opts) + ", en el minuto " + minuteField + " de cada hora" + trailingQualifier(schedule, opts);
244
259
  }
245
- function secondsListAtClock(ir, rest, opts) {
260
+ function secondsListAtClock(schedule, rest, opts) {
246
261
  const clockPhrases = rest.times.map(function clock(time) {
247
262
  return atTime(timePhrase(time.hour, time.minute, null, opts));
248
263
  });
249
264
  const grouped = groupClockTimesByArticle(clockPhrases);
250
265
  const clockList = grouped.startsWith("a ") ? grouped.slice(2) : grouped;
251
- const stride = strideFromSegments(segmentsOf(ir, "second"), "segundo", "", opts);
252
- const secondsPhrase = stride ?? "en los segundos " + joinList(segmentWords(segmentsOf(ir, "second")));
253
- const dayFrame = trailingQualifier(ir, opts);
266
+ const stride = strideFromSegments(segmentsOf(schedule, "second"), "segundo", "", opts);
267
+ const secondsPhrase = stride ?? "en los segundos " + joinList(segmentWords(segmentsOf(schedule, "second")));
268
+ const dayFrame = trailingQualifier(schedule, opts);
254
269
  return (dayFrame ? dayFrame.trimStart() + ", " : "") + secondsPhrase + " de " + clockList;
255
270
  }
256
- function composeHourCadence(ir, plan, opts) {
271
+ function composeHourCadence(schedule, plan, opts) {
257
272
  const clockRest = plan.rest.kind === "clockTimes" || plan.rest.kind === "compactClockTimes";
258
- if (!clockRest || ir.shapes.minute !== "single") {
273
+ if (!clockRest || schedule.shapes.minute !== "single") {
259
274
  return null;
260
275
  }
261
- const minute = +ir.pattern.minute;
262
- return hourCadence(ir, minute, opts) ?? hourRangeCadence(ir, minute, opts);
276
+ const minute = +schedule.pattern.minute;
277
+ return hourCadence(schedule, minute, opts) ?? hourRangeCadence(schedule, minute, opts);
263
278
  }
264
- function isPinnedMinuteSeconds(ir, plan) {
265
- return plan.rest.kind === "clockTimes" && (ir.shapes.second === "wildcard" || ir.shapes.second === "step");
279
+ function isPinnedMinuteSeconds(schedule, plan) {
280
+ return plan.rest.kind === "clockTimes" && (schedule.shapes.second === "wildcard" || schedule.shapes.second === "step");
266
281
  }
267
- function renderComposeSeconds(ir, plan, opts) {
268
- const hourCad = composeHourCadence(ir, plan, opts);
282
+ function renderComposeSeconds(schedule, plan, opts) {
283
+ const hourCad = composeHourCadence(schedule, plan, opts);
269
284
  if (hourCad !== null) {
270
285
  return hourCad;
271
286
  }
272
- if (isPinnedMinuteSeconds(ir, plan)) {
273
- return pinnedMinuteSeconds(ir, plan.rest, opts);
287
+ if (isPinnedMinuteSeconds(schedule, plan)) {
288
+ return pinnedMinuteSeconds(schedule, plan.rest, opts);
274
289
  }
275
- if (plan.rest.kind === "clockTimes" && ir.shapes.second === "list") {
276
- return secondsListAtClock(ir, plan.rest, opts);
290
+ if (plan.rest.kind === "clockTimes" && schedule.shapes.second === "list") {
291
+ return secondsListAtClock(schedule, plan.rest, opts);
277
292
  }
278
- if (plan.rest.kind === "hourRange" && ir.shapes.second === "step" && ir.pattern.weekday !== "*") {
293
+ if (plan.rest.kind === "hourRange" && schedule.shapes.second === "step" && schedule.pattern.weekday !== "*") {
279
294
  const restNode = plan.rest;
280
295
  const window = hourWindow(boundedWindow(restNode), opts);
281
- const dayFrame = weekdayQualifier(ir) + monthScope(ir);
282
- const cadence = "cada " + numero(stepSegment(ir, "second").interval, opts) + " segundos del minuto " + ir.pattern.minute;
296
+ const dayFrame = weekdayQualifier(schedule) + monthScope(schedule);
297
+ const cadence = "cada " + numero(stepSegment(schedule, "second").interval, opts) + " segundos del minuto " + schedule.pattern.minute;
283
298
  return dayFrame + ", " + window + ", " + cadence;
284
299
  }
285
- if (isEveryOtherMinuteSeconds(ir, plan)) {
286
- return secondsLeadClause(ir, opts) + " de " + render(ir, plan.rest, opts);
300
+ if (isEveryOtherMinuteSeconds(schedule, plan)) {
301
+ return secondsLeadClause(schedule, opts) + " de " + render(schedule, plan.rest, opts);
287
302
  }
288
- const restOwnsLead = plan.rest.kind === "compactClockTimes" && ir.analyses.clockSecond;
289
- const lead = restOwnsLead ? "" : secondsLeadClause(ir, opts) + ", ";
290
- return lead + render(ir, plan.rest, opts);
303
+ const restOwnsLead = plan.rest.kind === "compactClockTimes" && schedule.analyses.clockSecond;
304
+ const lead = restOwnsLead ? "" : secondsLeadClause(schedule, opts) + ", ";
305
+ return lead + render(schedule, plan.rest, opts);
291
306
  }
292
- function isEveryOtherMinuteSeconds(ir, plan) {
293
- if (plan.rest.kind !== "minuteFrequency" || ir.shapes.second !== "wildcard" || ir.shapes.hour !== "wildcard") {
307
+ function isEveryOtherMinuteSeconds(schedule, plan) {
308
+ if (plan.rest.kind !== "minuteFrequency" || schedule.shapes.second !== "wildcard" || schedule.shapes.hour !== "wildcard") {
294
309
  return false;
295
310
  }
296
- const minuteStep = stepSegment(ir, "minute");
311
+ const minuteStep = stepSegment(schedule, "minute");
297
312
  return minuteStep.startToken === "*" && minuteStep.interval === 2;
298
313
  }
299
- function pinnedMinuteSeconds(ir, rest, opts) {
300
- const dayTrail = leadingQualifier(ir, opts).trimEnd();
314
+ function pinnedMinuteSeconds(schedule, rest, opts) {
315
+ const dayTrail = leadingQualifier(schedule, opts).trimEnd();
301
316
  const trail = dayTrail ? ", " + dayTrail : "";
302
- if (+rest.times[0].minute === 0 && ir.shapes.minute === "single") {
303
- return secondsLeadClause(ir, opts) + " durante un minuto " + durationHourList(rest.times, opts) + trail;
317
+ if (+rest.times[0].minute === 0 && schedule.shapes.minute === "single") {
318
+ return secondsLeadClause(schedule, opts) + " durante un minuto " + durationHourList(rest.times, opts) + trail;
304
319
  }
305
- return secondsLeadClause(ir, opts) + " de " + explicitClockList(rest.times, opts) + trail;
320
+ return secondsLeadClause(schedule, opts) + " de " + explicitClockList(rest.times, opts) + trail;
306
321
  }
307
- function secondsLeadClause(ir, opts) {
308
- return secondsClause(ir, "minuto", opts);
322
+ function secondsLeadClause(schedule, opts) {
323
+ return secondsClause(schedule, "minuto", opts);
309
324
  }
310
- function secondsClause(ir, anchor, opts) {
311
- const secondField = ir.pattern.second;
312
- const shape = ir.shapes.second;
325
+ function secondsClause(schedule, anchor, opts) {
326
+ const secondField = schedule.pattern.second;
327
+ const shape = schedule.shapes.second;
313
328
  if (secondField === "*") {
314
329
  return "cada segundo";
315
330
  }
316
331
  if (shape === "step") {
317
332
  return stepCycle60(
318
- stepSegment(ir, "second"),
333
+ stepSegment(schedule, "second"),
319
334
  "segundo",
320
335
  anchor,
321
336
  opts
@@ -329,31 +344,31 @@ function secondsClause(ir, anchor, opts) {
329
344
  return "en el segundo " + secondField + " de cada " + anchor;
330
345
  }
331
346
  return strideFromSegments(
332
- segmentsOf(ir, "second"),
347
+ segmentsOf(schedule, "second"),
333
348
  "segundo",
334
349
  anchor,
335
350
  opts
336
- ) ?? "en los segundos " + joinList(segmentWords(segmentsOf(ir, "second"))) + " de cada " + anchor;
351
+ ) ?? "en los segundos " + joinList(segmentWords(segmentsOf(schedule, "second"))) + " de cada " + anchor;
337
352
  }
338
- function renderEveryMinute(ir, plan, opts) {
339
- return "cada minuto" + trailingQualifier(ir, opts);
353
+ function renderEveryMinute(schedule, plan, opts) {
354
+ return "cada minuto" + trailingQualifier(schedule, opts);
340
355
  }
341
- function renderSingleMinute(ir, plan, opts) {
342
- return "en el minuto " + ir.pattern.minute + " de cada hora" + trailingQualifier(ir, opts);
356
+ function renderSingleMinute(schedule, plan, opts) {
357
+ return "en el minuto " + schedule.pattern.minute + " de cada hora" + trailingQualifier(schedule, opts);
343
358
  }
344
- function renderRangeOfMinutes(ir, plan, opts) {
345
- return minuteRangeLead(ir.pattern.minute) + " de cada hora" + trailingQualifier(ir, opts);
359
+ function renderRangeOfMinutes(schedule, plan, opts) {
360
+ return minuteRangeLead(schedule.pattern.minute) + " de cada hora" + trailingQualifier(schedule, opts);
346
361
  }
347
- function renderMultipleMinutes(ir, plan, opts) {
348
- return minutesList(ir, opts) + trailingQualifier(ir, opts);
362
+ function renderMultipleMinutes(schedule, plan, opts) {
363
+ return minutesList(schedule, opts) + trailingQualifier(schedule, opts);
349
364
  }
350
- function minutesList(ir, opts) {
365
+ function minutesList(schedule, opts) {
351
366
  return strideFromSegments(
352
- segmentsOf(ir, "minute"),
367
+ segmentsOf(schedule, "minute"),
353
368
  "minuto",
354
369
  "hora",
355
370
  opts
356
- ) ?? "en los minutos " + joinList(segmentWords(segmentsOf(ir, "minute"))) + " de cada hora";
371
+ ) ?? "en los minutos " + joinList(segmentWords(segmentsOf(schedule, "minute"))) + " de cada hora";
357
372
  }
358
373
  function minuteRangeLead(minuteField) {
359
374
  const bounds = minuteField.split("-");
@@ -408,86 +423,86 @@ function spanHours(hours) {
408
423
  return (hour === 1 ? "la " : "las ") + hour;
409
424
  }));
410
425
  }
411
- function renderMinuteFrequency(ir, plan, opts) {
426
+ function renderMinuteFrequency(schedule, plan, opts) {
412
427
  let phrase = stepCycle60(
413
- stepSegment(ir, "minute"),
428
+ stepSegment(schedule, "minute"),
414
429
  "minuto",
415
430
  "hora",
416
431
  opts
417
432
  );
418
433
  if (plan.hours.kind === "during") {
419
- const cadence = unevenHourCadence(ir, opts);
434
+ const cadence = unevenHourCadence(schedule, opts);
420
435
  if (cadence) {
421
436
  phrase += ", " + cadence;
422
437
  } else {
423
- phrase += singleHourStep(ir.analyses.segments.hour) ? ", " + stepHourSpan(stepSegment(ir, "hour"), opts) : " " + hourSpanFromTimes(ir, plan.hours.times, opts);
438
+ phrase += singleHourStep(schedule.analyses.segments.hour) ? ", " + stepHourSpan(stepSegment(schedule, "hour"), opts) : " " + hourSpanFromTimes(schedule, plan.hours.times, opts);
424
439
  }
425
440
  } else if (plan.hours.kind === "window") {
426
441
  phrase += " " + hourWindow(plan.hours, opts);
427
442
  } else if (plan.hours.kind === "step") {
428
- phrase += ", " + stepHourSpan(stepSegment(ir, "hour"), opts);
443
+ phrase += ", " + stepHourSpan(stepSegment(schedule, "hour"), opts);
429
444
  }
430
- return phrase + trailingQualifier(ir, opts);
445
+ return phrase + trailingQualifier(schedule, opts);
431
446
  }
432
- function renderMinuteSpanInHour(ir, plan, opts) {
433
- if (ir.pattern.minute === "*") {
434
- return "cada minuto de la hora " + fromTime(timePhrase(plan.hour, 0, null, opts)) + trailingQualifier(ir, opts);
447
+ function renderMinuteSpanInHour(schedule, plan, opts) {
448
+ if (schedule.pattern.minute === "*") {
449
+ return "cada minuto de la hora " + fromTime(timePhrase(plan.hour, 0, null, opts)) + trailingQualifier(schedule, opts);
435
450
  }
436
451
  return "cada minuto " + timeRange(
437
452
  { hour: plan.hour, minute: plan.span[0] },
438
453
  { hour: plan.hour, minute: plan.span[1] },
439
454
  opts
440
- ) + trailingQualifier(ir, opts);
455
+ ) + trailingQualifier(schedule, opts);
441
456
  }
442
- function renderMinutesAcrossHours(ir, plan, opts) {
443
- const cadence = unevenHourCadence(ir, opts);
457
+ function renderMinutesAcrossHours(schedule, plan, opts) {
458
+ const cadence = unevenHourCadence(schedule, opts);
444
459
  if (plan.form === "wildcard") {
445
460
  if (cadence !== null) {
446
- return "cada minuto, " + cadence + trailingQualifier(ir, opts);
461
+ return "cada minuto, " + cadence + trailingQualifier(schedule, opts);
447
462
  }
448
- if (singleHourStep(ir.analyses.segments.hour)) {
449
- return "cada minuto, " + stepHourSpan(stepSegment(ir, "hour"), opts) + trailingQualifier(ir, opts);
463
+ if (singleHourStep(schedule.analyses.segments.hour)) {
464
+ return "cada minuto, " + stepHourSpan(stepSegment(schedule, "hour"), opts) + trailingQualifier(schedule, opts);
450
465
  }
451
- return "cada minuto " + hourSpanFromTimes(ir, plan.times, opts) + trailingQualifier(ir, opts);
466
+ return "cada minuto " + hourSpanFromTimes(schedule, plan.times, opts) + trailingQualifier(schedule, opts);
452
467
  }
453
- const lead = plan.form === "range" ? minuteRangeLead(ir.pattern.minute) : minutesList(ir, opts);
468
+ const lead = plan.form === "range" ? minuteRangeLead(schedule.pattern.minute) : minutesList(schedule, opts);
454
469
  if (cadence !== null) {
455
- return lead + ", " + cadence + trailingQualifier(ir, opts);
470
+ return lead + ", " + cadence + trailingQualifier(schedule, opts);
456
471
  }
457
- return lead + ", " + atHourTimes(ir, plan.times, opts) + trailingQualifier(ir, opts);
472
+ return lead + ", " + atHourTimes(schedule, plan.times, opts) + trailingQualifier(schedule, opts);
458
473
  }
459
- function renderMinuteSpanAcrossHourStep(ir, plan, opts) {
460
- const segment = stepSegment(ir, "hour");
461
- const cadence = unevenHourCadence(ir, opts);
474
+ function renderMinuteSpanAcrossHourStep(schedule, plan, opts) {
475
+ const segment = stepSegment(schedule, "hour");
476
+ const cadence = unevenHourCadence(schedule, opts);
462
477
  if (plan.form === "wildcard") {
463
- return "cada minuto, " + stepHourSpan(segment, opts) + trailingQualifier(ir, opts);
478
+ return "cada minuto, " + stepHourSpan(segment, opts) + trailingQualifier(schedule, opts);
464
479
  }
465
- const lead = plan.form === "list" ? minutesList(ir, opts) : minuteRangeLead(ir.pattern.minute);
466
- return lead + ", " + (cadence ?? stepHours(segment, opts)) + trailingQualifier(ir, opts);
480
+ const lead = plan.form === "list" ? minutesList(schedule, opts) : minuteRangeLead(schedule.pattern.minute);
481
+ return lead + ", " + (cadence ?? stepHours(segment, opts)) + trailingQualifier(schedule, opts);
467
482
  }
468
- function renderEveryHour(ir, plan, opts) {
469
- return "cada hora" + trailingQualifier(ir, opts);
483
+ function renderEveryHour(schedule, plan, opts) {
484
+ return "cada hora" + trailingQualifier(schedule, opts);
470
485
  }
471
- function renderHourRange(ir, plan, opts) {
486
+ function renderHourRange(schedule, plan, opts) {
472
487
  const window = hourWindow(boundedWindow(plan), opts);
473
488
  if (plan.minuteForm === "wildcard") {
474
- return "cada minuto " + window + trailingQualifier(ir, opts);
489
+ return "cada minuto " + window + trailingQualifier(schedule, opts);
475
490
  }
476
491
  if (plan.minuteForm === "range") {
477
- return minuteRangeLead(ir.pattern.minute) + ", " + window + trailingQualifier(ir, opts);
492
+ return minuteRangeLead(schedule.pattern.minute) + ", " + window + trailingQualifier(schedule, opts);
478
493
  }
479
- if (ir.pattern.minute === "0") {
480
- return "cada hora " + window + trailingQualifier(ir, opts);
494
+ if (schedule.pattern.minute === "0") {
495
+ return "cada hora " + window + trailingQualifier(schedule, opts);
481
496
  }
482
- const lead = ir.shapes.minute === "single" ? "en el minuto " + ir.pattern.minute + " de cada hora" : minutesList(ir, opts);
483
- return lead + ", " + window + trailingQualifier(ir, opts);
497
+ const lead = schedule.shapes.minute === "single" ? "en el minuto " + schedule.pattern.minute + " de cada hora" : minutesList(schedule, opts);
498
+ return lead + ", " + window + trailingQualifier(schedule, opts);
484
499
  }
485
- function renderHourStep(ir, plan, opts) {
486
- const cadence = unevenHourCadence(ir, opts);
500
+ function renderHourStep(schedule, plan, opts) {
501
+ const cadence = unevenHourCadence(schedule, opts);
487
502
  if (cadence !== null) {
488
- return cadence + trailingQualifier(ir, opts);
503
+ return cadence + trailingQualifier(schedule, opts);
489
504
  }
490
- return stepHours(stepSegment(ir, "hour"), opts) + trailingQualifier(ir, opts);
505
+ return stepHours(stepSegment(schedule, "hour"), opts) + trailingQualifier(schedule, opts);
491
506
  }
492
507
  function boundedWindow(plan) {
493
508
  const last = plan.minuteForm === "wildcard" ? plan.boundMinute ?? 0 : 0;
@@ -500,42 +515,46 @@ function hourWindow(window, opts) {
500
515
  opts
501
516
  );
502
517
  }
503
- function isDateWeekdayUnion(ir) {
504
- return ir.pattern.date !== "*" && ir.pattern.weekday !== "*";
518
+ function isDateWeekdayUnion(schedule) {
519
+ return schedule.pattern.date !== "*" && schedule.pattern.weekday !== "*";
505
520
  }
506
- function unionMonthLeadFull(ir) {
507
- if (ir.pattern.month === "*") {
521
+ function unionMonthLeadFull(schedule) {
522
+ if (schedule.pattern.month === "*") {
508
523
  return "";
509
524
  }
510
- const lead = monthPhrase(ir, monthRanged(ir) ? "de " : "en ");
511
- const segments = flattenSteps(segmentsOf(ir, "month"));
512
- const isEnumeration = !monthRanged(ir) && segments.length >= 2;
525
+ const lead = monthPhrase(schedule, monthRanged(schedule) ? "de " : "en ");
526
+ const segments = flattenSteps(segmentsOf(schedule, "month"));
527
+ const isEnumeration = !monthRanged(schedule) && segments.length >= 2;
513
528
  return isEnumeration ? lead + "," : lead;
514
529
  }
515
- function domArm(ir, opts) {
516
- const date = ir.pattern.date;
530
+ function domArm(schedule, opts) {
531
+ const date = schedule.pattern.date;
517
532
  const quartz = quartzDatePhrase(date);
518
533
  if (quartz) {
519
534
  return quartz;
520
535
  }
536
+ const parity = parityDayPredicate(date);
537
+ if (parity) {
538
+ return parity;
539
+ }
521
540
  if (isOpenStep(date)) {
522
541
  return stepDates(date, opts);
523
542
  }
524
- const segments = segmentsOf(ir, "date");
543
+ const segments = segmentsOf(schedule, "date");
525
544
  if (segments.length === 1 && segments[0].kind === "range") {
526
545
  return "del " + segments[0].bounds[0] + " al " + segments[0].bounds[1] + " del mes";
527
546
  }
528
547
  if (segments.length === 1 && segments[0].kind === "single") {
529
- return ir.pattern.month === "*" ? "el " + segments[0].value + " de cada mes" : "el d\xEDa " + segments[0].value;
548
+ return schedule.pattern.month === "*" ? "el " + segments[0].value + " de cada mes" : "el d\xEDa " + segments[0].value;
530
549
  }
531
550
  return "los d\xEDas " + joinList(segmentWords(segments)) + " del mes";
532
551
  }
533
- function dowArm(ir) {
534
- const quartz = quartzWeekdayPhrase(ir.pattern.weekday);
552
+ function dowArm(schedule) {
553
+ const quartz = quartzWeekdayPhrase(schedule.pattern.weekday);
535
554
  if (quartz) {
536
555
  return quartz;
537
556
  }
538
- const segments = orderWeekdaysForDisplay(segmentsOf(ir, "weekday"));
557
+ const segments = orderWeekdaysForDisplay(segmentsOf(schedule, "weekday"));
539
558
  const allSingles = segments.every(function single(segment) {
540
559
  return segment.kind === "single";
541
560
  });
@@ -548,19 +567,19 @@ function dowArm(ir) {
548
567
  }));
549
568
  }
550
569
  if (segments.length === 1) {
551
- return weekdayRange(segments[0]);
570
+ return "cualquier d\xEDa " + weekdayRange(segments[0]);
552
571
  }
553
572
  return joinList(segments.map(function name(segment) {
554
573
  return segment.kind === "range" ? weekdayRange(segment) : "los " + pluralWeekday(segment.value);
555
574
  }));
556
575
  }
557
- function unionYaseaSuffix(ir, opts) {
558
- return ", ya sea " + domArm(ir, opts) + " o " + dowArm(ir);
576
+ function unionYaseaSuffix(schedule, opts) {
577
+ return ", ya sea " + domArm(schedule, opts) + " o " + dowArm(schedule);
559
578
  }
560
- function renderClockTimes(ir, plan, opts) {
561
- if (ir.shapes.minute === "single") {
562
- const minute = +ir.pattern.minute;
563
- const cadence = hourCadence(ir, minute, opts) ?? hourRangeCadence(ir, minute, opts);
579
+ function renderClockTimes(schedule, plan, opts) {
580
+ if (schedule.shapes.minute === "single") {
581
+ const minute = +schedule.pattern.minute;
582
+ const cadence = hourCadence(schedule, minute, opts) ?? hourRangeCadence(schedule, minute, opts);
564
583
  if (cadence !== null) {
565
584
  return cadence;
566
585
  }
@@ -568,7 +587,7 @@ function renderClockTimes(ir, plan, opts) {
568
587
  const phrases = plan.times.map(function clock(time) {
569
588
  return atTime(timePhrase(time.hour, time.minute, time.second, opts));
570
589
  });
571
- return leadingQualifier(ir, opts) + groupClockTimes(phrases);
590
+ return leadingQualifier(schedule, opts) + groupClockTimes(phrases);
572
591
  }
573
592
  function explicitClockList(times, opts) {
574
593
  const phrases = times.map(function clock(time) {
@@ -744,23 +763,33 @@ function groupClockTimesByArticle(phrases) {
744
763
  const connector = doubleY ? ", " : " y ";
745
764
  return laPart + connector + lasPart;
746
765
  }
747
- function renderCompactClockTimes(ir, plan, opts) {
766
+ function renderCompactClockTimes(schedule, plan, opts) {
748
767
  if (plan.fold) {
749
- const cadence2 = hourCadence(ir, plan.minute, opts) ?? hourRangeCadence(ir, plan.minute, opts);
768
+ const cadence2 = hourCadence(schedule, plan.minute, opts) ?? hourRangeCadence(schedule, plan.minute, opts);
750
769
  if (cadence2 !== null) {
751
770
  return cadence2;
752
771
  }
753
- const ranged = segmentsOf(ir, "hour").some(function range(segment) {
772
+ const ranged = segmentsOf(schedule, "hour").some(function range(segment) {
754
773
  return segment.kind === "range";
755
774
  });
756
- if (ranged && !ir.analyses.clockSecond) {
757
- return "cada hora " + hourSegmentTimes(ir, plan.minute, ir.analyses.clockSecond, opts) + trailingQualifier(ir, opts);
775
+ if (ranged && !schedule.analyses.clockSecond) {
776
+ return "cada hora " + hourSegmentTimes(
777
+ schedule,
778
+ plan.minute,
779
+ schedule.analyses.clockSecond,
780
+ opts
781
+ ) + trailingQualifier(schedule, opts);
758
782
  }
759
- return leadingQualifier(ir, opts) + hourSegmentTimes(ir, plan.minute, ir.analyses.clockSecond, opts);
783
+ return leadingQualifier(schedule, opts) + hourSegmentTimes(
784
+ schedule,
785
+ plan.minute,
786
+ schedule.analyses.clockSecond,
787
+ opts
788
+ );
760
789
  }
761
- const cadence = unevenHourCadence(ir, opts);
762
- const phrase = cadence ? minutesList(ir, opts) + ", " + cadence + trailingQualifier(ir, opts) : minutesList(ir, opts) + ", " + hourContextTimes(ir, opts) + trailingQualifier(ir, opts);
763
- return ir.analyses.clockSecond ? secondsLeadClause(ir, opts) + ", " + phrase : phrase;
790
+ const cadence = unevenHourCadence(schedule, opts);
791
+ const phrase = cadence ? minutesList(schedule, opts) + ", " + cadence + trailingQualifier(schedule, opts) : minutesList(schedule, opts) + ", " + hourContextTimes(schedule, opts) + trailingQualifier(schedule, opts);
792
+ return schedule.analyses.clockSecond ? secondsLeadClause(schedule, opts) + ", " + phrase : phrase;
764
793
  }
765
794
  var renderers = {
766
795
  clockTimes: renderClockTimes,
@@ -782,18 +811,15 @@ var renderers = {
782
811
  singleMinute: renderSingleMinute,
783
812
  standaloneSeconds: renderStandaloneSeconds
784
813
  };
785
- function renderStride(stride, opts) {
814
+ function renderStride2(stride, opts) {
786
815
  const { interval, start, last, cycle, unit, anchor } = stride;
787
816
  const cadence = "cada " + numero(interval, opts) + " " + unit + "s";
788
- const tiles = cycle % interval === 0;
789
- if (start === 0 && tiles) {
790
- return cadence;
791
- }
792
817
  const tail = anchor ? " de cada " + anchor : "";
793
- if (start < interval && tiles) {
794
- return cadence + " a partir del " + unit + " " + start + tail;
795
- }
796
- return cadence + " del " + unit + " " + start + " al " + last + tail;
818
+ return renderStride({ start, interval, cycle }, {
819
+ bare: () => cadence,
820
+ offset: () => cadence + " a partir del " + unit + " " + start + tail,
821
+ bounded: () => cadence + " del " + unit + " " + start + " al " + last + tail
822
+ });
797
823
  }
798
824
  function stepCycle60(segment, unit, anchor, opts) {
799
825
  if (segment.startToken.indexOf("-") !== -1) {
@@ -803,7 +829,7 @@ function stepCycle60(segment, unit, anchor, opts) {
803
829
  if (start !== 0 && segment.fires.length <= 3) {
804
830
  return "en los " + unit + "s " + joinList(wordList(segment.fires)) + " de cada " + anchor;
805
831
  }
806
- return renderStride({
832
+ return renderStride2({
807
833
  interval: segment.interval,
808
834
  start,
809
835
  last: segment.fires[segment.fires.length - 1],
@@ -815,7 +841,7 @@ function stepCycle60(segment, unit, anchor, opts) {
815
841
  function strideFromSegments(segments, unit, anchor, opts) {
816
842
  const values = singleValues(segments);
817
843
  const step = values && arithmeticStep(values);
818
- return step ? renderStride({ ...step, cycle: 60, unit, anchor }, opts) : null;
844
+ return step ? renderStride2({ ...step, cycle: 60, unit, anchor }, opts) : null;
819
845
  }
820
846
  function stepHours(segment, opts) {
821
847
  if (segment.startToken.indexOf("-") !== -1) {
@@ -834,24 +860,21 @@ function stepHours(segment, opts) {
834
860
  function hourStrideCadence(stride, opts) {
835
861
  const { start, interval, last } = stride;
836
862
  const cadence = "cada " + numero(interval, opts) + " horas";
837
- const tiles = 24 % interval === 0;
838
- if (start === 0 && tiles) {
839
- return cadence;
840
- }
841
- if (start < interval && tiles) {
842
- return cadence + " a partir de " + timePhrase(start, 0, null, opts);
843
- }
844
- return cadence + " de " + timePhrase(start, 0, null, opts) + " a " + timePhrase(last, 0, null, opts);
863
+ return renderStride({ start, interval, cycle: 24 }, {
864
+ bare: () => cadence,
865
+ offset: () => cadence + " a partir de " + timePhrase(start, 0, null, opts),
866
+ bounded: () => cadence + " de " + timePhrase(start, 0, null, opts) + " a " + timePhrase(last, 0, null, opts)
867
+ });
845
868
  }
846
- function unevenHourCadence(ir, opts) {
847
- const stride = hourStride(ir);
869
+ function unevenHourCadence(schedule, opts) {
870
+ const stride = hourStride(schedule);
848
871
  if (!stride || offsetCleanStride(stride)) {
849
872
  return null;
850
873
  }
851
874
  return hourStrideCadence(stride, opts);
852
875
  }
853
- function hourStride(ir) {
854
- const segments = segmentsOf(ir, "hour");
876
+ function hourStride(schedule) {
877
+ const segments = segmentsOf(schedule, "hour");
855
878
  if (segments.length === 1 && segments[0].kind === "step") {
856
879
  const segment = segments[0];
857
880
  if (segment.fires.length < 2) {
@@ -863,64 +886,64 @@ function hourStride(ir) {
863
886
  const values = singleValues(segments);
864
887
  return values && hourListStride(values);
865
888
  }
866
- function subMinuteSecond(ir) {
867
- return ir.pattern.second === "*" || ir.shapes.second === "step";
889
+ function subMinuteSecond(schedule) {
890
+ return schedule.pattern.second === "*" || schedule.shapes.second === "step";
868
891
  }
869
- function hourCadenceLead(ir, minute, opts) {
892
+ function hourCadenceLead(schedule, minute, opts) {
870
893
  if (minute === 0) {
871
- if (subMinuteSecond(ir)) {
872
- return secondsClause(ir, "minuto", opts) + " durante un minuto";
894
+ if (subMinuteSecond(schedule)) {
895
+ return secondsClause(schedule, "minuto", opts) + " durante un minuto";
873
896
  }
874
- return secondsClause(ir, "hora", opts);
897
+ return secondsClause(schedule, "hora", opts);
875
898
  }
876
899
  const minutePhrase = "en el minuto " + minute;
877
- if (ir.pattern.second === "0") {
900
+ if (schedule.pattern.second === "0") {
878
901
  return minutePhrase;
879
902
  }
880
- return secondsClause(ir, "minuto", opts) + ", " + minutePhrase;
903
+ return secondsClause(schedule, "minuto", opts) + ", " + minutePhrase;
881
904
  }
882
- function hourCadence(ir, minute, opts) {
883
- const stride = hourStride(ir);
905
+ function hourCadence(schedule, minute, opts) {
906
+ const stride = hourStride(schedule);
884
907
  if (!stride) {
885
908
  return null;
886
909
  }
887
910
  const fires = (stride.last - stride.start) / stride.interval + 1;
888
- if (ir.pattern.second === "0" && fires <= maxClockTimes && offsetCleanStride(stride)) {
911
+ if (schedule.pattern.second === "0" && fires <= maxClockTimes && offsetCleanStride(stride)) {
889
912
  return null;
890
913
  }
891
- const confinement = minute === 0 && subMinuteSecond(ir) && cleanStrideSegment(ir);
914
+ const confinement = minute === 0 && subMinuteSecond(schedule) && cleanStrideSegment(schedule);
892
915
  if (confinement) {
893
- return secondsClause(ir, "minuto", opts) + " durante un minuto, " + stepHourSpan(confinement, opts) + trailingQualifier(ir, opts);
916
+ return secondsClause(schedule, "minuto", opts) + " durante un minuto, " + stepHourSpan(confinement, opts) + trailingQualifier(schedule, opts);
894
917
  }
895
- if (minute === 0 && ir.pattern.second === "0") {
896
- return hourStrideCadence(stride, opts) + trailingQualifier(ir, opts);
918
+ if (minute === 0 && schedule.pattern.second === "0") {
919
+ return hourStrideCadence(stride, opts) + trailingQualifier(schedule, opts);
897
920
  }
898
- return hourCadenceLead(ir, minute, opts) + ", " + hourStrideCadence(stride, opts) + trailingQualifier(ir, opts);
921
+ return hourCadenceLead(schedule, minute, opts) + ", " + hourStrideCadence(stride, opts) + trailingQualifier(schedule, opts);
899
922
  }
900
- function cleanStrideSegment(ir) {
901
- const segments = segmentsOf(ir, "hour");
923
+ function cleanStrideSegment(schedule) {
924
+ const segments = segmentsOf(schedule, "hour");
902
925
  const segment = segments.length === 1 && segments[0];
903
926
  if (!segment || segment.kind !== "step" || segment.startToken.indexOf("-") !== -1) {
904
927
  return null;
905
928
  }
906
929
  return segment;
907
930
  }
908
- function hasHourWindow(ir) {
909
- return segmentsOf(ir, "hour").some(function range(segment) {
931
+ function hasHourWindow(schedule) {
932
+ return segmentsOf(schedule, "hour").some(function range(segment) {
910
933
  return segment.kind === "range";
911
934
  });
912
935
  }
913
- function hourRangeCadence(ir, minute, opts) {
914
- if (minute !== 0 || !hasHourWindow(ir) || ir.pattern.second === "0") {
936
+ function hourRangeCadence(schedule, minute, opts) {
937
+ if (minute !== 0 || !hasHourWindow(schedule) || schedule.pattern.second === "0") {
915
938
  return null;
916
939
  }
917
- if (subMinuteSecond(ir)) {
918
- return secondsClause(ir, "minuto", opts) + " durante un minuto, durante las horas " + hourSegmentTimes(ir, 0, null, opts) + trailingQualifier(ir, opts);
940
+ if (subMinuteSecond(schedule)) {
941
+ return secondsClause(schedule, "minuto", opts) + " durante un minuto, durante las horas " + hourSegmentTimes(schedule, 0, null, opts) + trailingQualifier(schedule, opts);
919
942
  }
920
- return hourCadenceLead(ir, minute, opts) + ", " + hourSegmentTimes(ir, 0, null, opts) + trailingQualifier(ir, opts);
943
+ return hourCadenceLead(schedule, minute, opts) + ", " + hourSegmentTimes(schedule, 0, null, opts) + trailingQualifier(schedule, opts);
921
944
  }
922
- function hourContextTimes(ir, opts) {
923
- const segments = segmentsOf(ir, "hour");
945
+ function hourContextTimes(schedule, opts) {
946
+ const segments = segmentsOf(schedule, "hour");
924
947
  const points = [];
925
948
  const hasRange = segments.some(function range(segment) {
926
949
  return segment.kind === "range";
@@ -966,25 +989,25 @@ function atTimes(hours, opts) {
966
989
  return atTime(timePhrase(hour, 0, null, opts));
967
990
  });
968
991
  }
969
- function atHourTimes(ir, times, opts) {
992
+ function atHourTimes(schedule, times, opts) {
970
993
  if (times.kind === "fires") {
971
994
  return groupClockTimesByArticle(atTimes(times.fires, opts));
972
995
  }
973
- return hourSegmentTimes(ir, 0, null, opts);
996
+ return hourSegmentTimes(schedule, 0, null, opts);
974
997
  }
975
- function hourSpanFromTimes(ir, times, opts) {
998
+ function hourSpanFromTimes(schedule, times, opts) {
976
999
  if (times.kind === "fires" && times.fires.length > 3) {
977
1000
  return "durante las horas " + hourSpanList(times.fires, opts);
978
1001
  }
979
- return hourWindowsFromTimes(ir, times, opts);
1002
+ return hourWindowsFromTimes(schedule, times, opts);
980
1003
  }
981
- function hourWindowsFromTimes(ir, times, opts) {
1004
+ function hourWindowsFromTimes(schedule, times, opts) {
982
1005
  if (times.kind === "fires") {
983
1006
  return joinList(times.fires.map(function window(hour) {
984
1007
  return hourAsWindow(hour, opts);
985
1008
  }));
986
1009
  }
987
- return joinList(segmentsOf(ir, "hour").map(function window(segment) {
1010
+ return joinList(segmentsOf(schedule, "hour").map(function window(segment) {
988
1011
  if (segment.kind === "range") {
989
1012
  return timeRange(
990
1013
  { hour: +segment.bounds[0], minute: 0 },
@@ -1000,10 +1023,10 @@ function hourWindowsFromTimes(ir, times, opts) {
1000
1023
  return hourAsWindow(+segment.value, opts);
1001
1024
  }));
1002
1025
  }
1003
- function hourSegmentTimes(ir, minute, second, opts) {
1026
+ function hourSegmentTimes(schedule, minute, second, opts) {
1004
1027
  const pieces = [];
1005
1028
  const fromRange = [];
1006
- segmentsOf(ir, "hour").forEach(function clock(segment) {
1029
+ segmentsOf(schedule, "hour").forEach(function clock(segment) {
1007
1030
  if (segment.kind === "step") {
1008
1031
  segment.fires.forEach(function each(hour) {
1009
1032
  pieces.push(atTime(timePhrase(hour, minute, second, opts)));
@@ -1115,47 +1138,47 @@ function dayPeriod(hour, opts) {
1115
1138
  }
1116
1139
  return "de la tarde";
1117
1140
  }
1118
- function leadingQualifier(ir, opts) {
1119
- const pattern = ir.pattern;
1141
+ function leadingQualifier(schedule, opts) {
1142
+ const pattern = schedule.pattern;
1120
1143
  if (pattern.date !== "*" && pattern.weekday !== "*") {
1121
1144
  return "";
1122
1145
  }
1123
1146
  if (pattern.date !== "*") {
1124
- return datePhrase(ir, opts) + " ";
1147
+ return datePhrase(schedule, opts) + " ";
1125
1148
  }
1126
1149
  if (pattern.weekday !== "*") {
1127
- return weekdayQualifier(ir) + monthScope(ir) + " ";
1150
+ return weekdayQualifier(schedule) + monthScope(schedule) + " ";
1128
1151
  }
1129
1152
  if (pattern.month !== "*") {
1130
- return "todos los d\xEDas " + monthPhrase(ir, "de ") + " ";
1153
+ return "todos los d\xEDas " + monthPhrase(schedule, "de ") + " ";
1131
1154
  }
1132
1155
  return "todos los d\xEDas ";
1133
1156
  }
1134
- function trailingQualifier(ir, opts) {
1135
- const pattern = ir.pattern;
1157
+ function trailingQualifier(schedule, opts) {
1158
+ const pattern = schedule.pattern;
1136
1159
  if (pattern.date !== "*" && pattern.weekday !== "*") {
1137
1160
  return "";
1138
1161
  }
1139
1162
  if (pattern.date !== "*") {
1140
- return " " + datePhrase(ir, opts);
1163
+ return " " + datePhrase(schedule, opts);
1141
1164
  }
1142
1165
  if (pattern.weekday !== "*") {
1143
- return " " + weekdayQualifier(ir) + monthScope(ir);
1166
+ return " " + weekdayQualifier(schedule) + monthScope(schedule);
1144
1167
  }
1145
1168
  if (pattern.month !== "*") {
1146
- return " " + monthPhrase(ir, "en ");
1169
+ return " " + monthPhrase(schedule, "en ");
1147
1170
  }
1148
1171
  return "";
1149
1172
  }
1150
- function datePhrase(ir, opts) {
1151
- const pattern = ir.pattern;
1173
+ function datePhrase(schedule, opts) {
1174
+ const pattern = schedule.pattern;
1152
1175
  if (quartzDatePhrase(pattern.date) || isOpenStep(pattern.date)) {
1153
- return dateClause(ir, "", opts) + monthScope(ir);
1176
+ return dateClause(schedule, "", opts) + monthScope(schedule);
1154
1177
  }
1155
- return dateClause(ir, dateMonthPart(ir), opts);
1178
+ return dateClause(schedule, dateMonthPart(schedule), opts);
1156
1179
  }
1157
- function dateClause(ir, monthPart, opts) {
1158
- const pattern = ir.pattern;
1180
+ function dateClause(schedule, monthPart, opts) {
1181
+ const pattern = schedule.pattern;
1159
1182
  const quartz = quartzDatePhrase(pattern.date);
1160
1183
  if (quartz) {
1161
1184
  return quartz;
@@ -1163,31 +1186,31 @@ function dateClause(ir, monthPart, opts) {
1163
1186
  if (isOpenStep(pattern.date)) {
1164
1187
  return stepDates(pattern.date, opts);
1165
1188
  }
1166
- const segments = segmentsOf(ir, "date");
1189
+ const segments = segmentsOf(schedule, "date");
1167
1190
  if (segments.length === 1 && segments[0].kind === "range") {
1168
- return "del " + segments[0].bounds[0] + " al " + segments[0].bounds[1] + monthPart + foldedYear(ir);
1191
+ return "del " + segments[0].bounds[0] + " al " + segments[0].bounds[1] + monthPart + foldedYear(schedule);
1169
1192
  }
1170
1193
  if (segments.length === 1 && segments[0].kind === "single") {
1171
- return "el " + segments[0].value + monthPart + foldedYear(ir);
1194
+ return "el " + segments[0].value + monthPart + foldedYear(schedule);
1172
1195
  }
1173
- return "los d\xEDas " + joinList(segmentWords(segments)) + monthPart + foldedYear(ir);
1196
+ return "los d\xEDas " + joinList(segmentWords(segments)) + monthPart + foldedYear(schedule);
1174
1197
  }
1175
- function monthRanged(ir) {
1176
- return ir.pattern.month !== "*" && segmentsOf(ir, "month").some(function range(segment) {
1198
+ function monthRanged(schedule) {
1199
+ return schedule.pattern.month !== "*" && segmentsOf(schedule, "month").some(function range(segment) {
1177
1200
  return segment.kind === "range";
1178
1201
  });
1179
1202
  }
1180
- function dateMonthPart(ir) {
1181
- if (ir.pattern.month === "*") {
1203
+ function dateMonthPart(schedule) {
1204
+ if (schedule.pattern.month === "*") {
1182
1205
  return " de cada mes";
1183
1206
  }
1184
- if (monthRanged(ir)) {
1185
- return " de cada mes, " + monthPhrase(ir, "de ");
1207
+ if (monthRanged(schedule)) {
1208
+ return " de cada mes, " + monthPhrase(schedule, "de ");
1186
1209
  }
1187
- return " " + monthPhrase(ir, "de ");
1210
+ return " " + monthPhrase(schedule, "de ");
1188
1211
  }
1189
- function foldedYear(ir) {
1190
- const yearField = ir.pattern.year;
1212
+ function foldedYear(schedule) {
1213
+ const yearField = schedule.pattern.year;
1191
1214
  if (yearField === "*" || yearField.indexOf("/") !== -1 || yearField.indexOf("-") !== -1 || yearField.indexOf(",") !== -1) {
1192
1215
  return "";
1193
1216
  }
@@ -1218,12 +1241,12 @@ function quartzWeekdayPhrase(weekdayField) {
1218
1241
  return "el \xFAltimo " + weekdayName(weekdayField.slice(0, -1)) + " del mes";
1219
1242
  }
1220
1243
  }
1221
- function weekdayQualifier(ir) {
1222
- const quartz = quartzWeekdayPhrase(ir.pattern.weekday);
1244
+ function weekdayQualifier(schedule) {
1245
+ const quartz = quartzWeekdayPhrase(schedule.pattern.weekday);
1223
1246
  if (quartz) {
1224
1247
  return quartz;
1225
1248
  }
1226
- const segments = orderWeekdaysForDisplay(segmentsOf(ir, "weekday"));
1249
+ const segments = orderWeekdaysForDisplay(segmentsOf(schedule, "weekday"));
1227
1250
  const allSingles = segments.every(function single(segment) {
1228
1251
  return segment.kind === "single";
1229
1252
  });
@@ -1249,8 +1272,8 @@ function flattenSteps(segments) {
1249
1272
  }) : [segment];
1250
1273
  });
1251
1274
  }
1252
- function monthPhrase(ir, lead) {
1253
- const segments = flattenSteps(segmentsOf(ir, "month"));
1275
+ function monthPhrase(schedule, lead) {
1276
+ const segments = flattenSteps(segmentsOf(schedule, "month"));
1254
1277
  const ranged = segments.some(function range(segment) {
1255
1278
  return segment.kind === "range";
1256
1279
  });
@@ -1266,11 +1289,26 @@ function monthPhrase(ir, lead) {
1266
1289
  return lead + monthName(segment.value);
1267
1290
  }));
1268
1291
  }
1269
- function monthScope(ir) {
1270
- if (ir.pattern.month === "*") {
1292
+ function monthScope(schedule) {
1293
+ if (schedule.pattern.month === "*") {
1271
1294
  return "";
1272
1295
  }
1273
- return (monthRanged(ir) ? ", " : " ") + monthPhrase(ir, "de ");
1296
+ return (monthRanged(schedule) ? ", " : " ") + monthPhrase(schedule, "de ");
1297
+ }
1298
+ function parityDayPredicate(dateField) {
1299
+ if (!isOpenStep(dateField)) {
1300
+ return;
1301
+ }
1302
+ const [start, step] = dateField.split("/");
1303
+ if (+step !== 2) {
1304
+ return;
1305
+ }
1306
+ if (start === "*" || start === "1") {
1307
+ return "un d\xEDa impar del mes";
1308
+ }
1309
+ if (start === "2") {
1310
+ return "un d\xEDa par del mes";
1311
+ }
1274
1312
  }
1275
1313
  function stepDates(dateField, opts) {
1276
1314
  const parts = dateField.split("/");
@@ -1280,15 +1318,15 @@ function stepDates(dateField, opts) {
1280
1318
  }
1281
1319
  return phrase;
1282
1320
  }
1283
- function applyYear(description, ir, opts) {
1284
- const yearField = ir.pattern.year;
1321
+ function applyYear(description, schedule, opts) {
1322
+ const yearField = schedule.pattern.year;
1285
1323
  if (yearField === "*") {
1286
1324
  return description;
1287
1325
  }
1288
1326
  if (yearField.indexOf("/") !== -1) {
1289
1327
  return description + " " + stepYears(yearField, opts);
1290
1328
  }
1291
- if (foldedYear(ir) && ir.pattern.date !== "*") {
1329
+ if (foldedYear(schedule) && schedule.pattern.date !== "*") {
1292
1330
  return description;
1293
1331
  }
1294
1332
  if (yearField.indexOf(",") !== -1) {