cronli5 0.1.2 → 0.1.5

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.
package/dist/lang/en.cjs CHANGED
@@ -24,6 +24,26 @@ __export(index_exports, {
24
24
  });
25
25
  module.exports = __toCommonJS(index_exports);
26
26
 
27
+ // src/core/util.ts
28
+ function arithmeticStep(values) {
29
+ if (values.length < 5) {
30
+ return null;
31
+ }
32
+ const interval = values[1] - values[0];
33
+ if (interval < 2) {
34
+ return null;
35
+ }
36
+ for (let i = 2; i < values.length; i += 1) {
37
+ if (values[i] - values[i - 1] !== interval) {
38
+ return null;
39
+ }
40
+ }
41
+ return { start: values[0], interval, last: values[values.length - 1] };
42
+ }
43
+
44
+ // src/core/specs.ts
45
+ var maxClockTimes = 6;
46
+
27
47
  // src/core/format.ts
28
48
  function pad(n) {
29
49
  n = "" + n;
@@ -131,20 +151,6 @@ var weekdayNames = [
131
151
  ["Friday", "Fri"],
132
152
  ["Saturday", "Sat"]
133
153
  ];
134
- var monthAbbreviations = {
135
- JAN: monthNames[1],
136
- FEB: monthNames[2],
137
- MAR: monthNames[3],
138
- APR: monthNames[4],
139
- MAY: monthNames[5],
140
- JUN: monthNames[6],
141
- JUL: monthNames[7],
142
- AUG: monthNames[8],
143
- SEP: monthNames[9],
144
- OCT: monthNames[10],
145
- NOV: monthNames[11],
146
- DEC: monthNames[12]
147
- };
148
154
  var weekdayAbbreviations = {
149
155
  SUN: weekdayNames[0],
150
156
  MON: weekdayNames[1],
@@ -193,10 +199,50 @@ function renderSecondsWithinMinute(ir, plan, opts) {
193
199
  }
194
200
  return secondsLeadClause(ir, opts) + ", " + minuteWord + " " + minuteUnit + " past the hour, every hour" + trailingQualifier(ir, opts);
195
201
  }
202
+ function composeHourCadence(ir, plan, opts) {
203
+ const clockRest = plan.rest.kind === "clockTimes" || plan.rest.kind === "compactClockTimes";
204
+ return clockRest && ir.shapes.minute === "single" ? hourCadence(ir, +ir.pattern.minute, opts) : null;
205
+ }
196
206
  function renderComposeSeconds(ir, plan, opts) {
207
+ const cadence = composeHourCadence(ir, plan, opts);
208
+ if (cadence !== null) {
209
+ return cadence;
210
+ }
211
+ if (plan.rest.kind === "clockTimes" && (ir.shapes.second === "wildcard" || ir.shapes.second === "step")) {
212
+ const minute = plan.rest.times[0].minute;
213
+ if (+minute === 0) {
214
+ return secondsLeadClause(ir, opts) + " for one minute at " + durationHours(ir, plan.rest, opts);
215
+ }
216
+ return secondsLeadClause(ir, opts) + " of " + clockTimesOf(ir, plan.rest, opts);
217
+ }
218
+ if (ir.shapes.second === "wildcard" && plan.rest.kind === "minuteFrequency" && plan.rest.hours.kind === "none" && ir.pattern.minute === "*/2") {
219
+ return "every second of every other minute" + trailingQualifier(ir, opts);
220
+ }
197
221
  return secondsLeadClause(ir, opts) + ", " + render(ir, plan.rest, opts);
198
222
  }
223
+ function durationHours(ir, plan, opts) {
224
+ const hours = plan.times.map(function clock(time) {
225
+ return getTime({ hour: time.hour, minute: 0 }, opts);
226
+ });
227
+ const trail = dayQualifier(ir, leadingWords, opts);
228
+ return joinList(hours, opts) + (trail && ", " + trail);
229
+ }
230
+ function clockTimesOf(ir, plan, opts) {
231
+ const times = plan.times.map(function clock(time) {
232
+ return getTime({
233
+ hour: time.hour,
234
+ minute: time.minute,
235
+ second: time.second,
236
+ explicit: true
237
+ }, opts);
238
+ });
239
+ const trail = dayQualifier(ir, leadingWords, opts);
240
+ return joinList(times, opts) + (trail && ", " + trail);
241
+ }
199
242
  function secondsLeadClause(ir, opts) {
243
+ return secondsClause(ir, "minute", opts);
244
+ }
245
+ function secondsClause(ir, anchor, opts) {
200
246
  const secondField = ir.pattern.second;
201
247
  const shape = ir.shapes.second;
202
248
  if (secondField === "*") {
@@ -206,22 +252,27 @@ function secondsLeadClause(ir, opts) {
206
252
  return stepCycle60(
207
253
  ir.analyses.segments.second[0],
208
254
  "second",
209
- "minute",
255
+ anchor,
210
256
  opts
211
257
  );
212
258
  }
213
259
  if (shape === "range") {
214
260
  const bounds = secondField.split("-");
215
261
  const num = seriesNumber(bounds, opts);
216
- return "every second from " + num(bounds[0]) + through(opts) + num(bounds[1]) + " past the minute";
262
+ return "every second from " + num(bounds[0]) + through(opts) + num(bounds[1]) + " past the " + anchor;
217
263
  }
218
264
  if (shape === "single") {
219
- return "at " + getNumber(secondField, opts) + " " + pluralize(secondField, "second") + " past the minute";
265
+ return "at " + getNumber(secondField, opts) + " " + pluralize(secondField, "second") + " past the " + anchor;
220
266
  }
221
- return listPastThe(
267
+ return strideFromSegments(
268
+ ir.analyses.segments.second,
269
+ "second",
270
+ anchor,
271
+ opts
272
+ ) ?? listPastThe(
222
273
  segmentWords(ir.analyses.segments.second, opts),
223
274
  "second",
224
- "minute",
275
+ anchor,
225
276
  opts
226
277
  );
227
278
  }
@@ -236,12 +287,11 @@ function renderRangeOfMinutes(ir, plan, opts) {
236
287
  return minuteRangeLead(ir.pattern.minute, opts) + trailingQualifier(ir, opts);
237
288
  }
238
289
  function renderMultipleMinutes(ir, plan, opts) {
239
- return listPastThe(
240
- segmentWords(ir.analyses.segments.minute, opts),
241
- "minute",
242
- "hour",
290
+ const stride = strideFromSegments(ir.analyses.segments.minute, "minute", "hour", opts);
291
+ return (stride ?? listPastThe(segmentWords(
292
+ ir.analyses.segments.minute,
243
293
  opts
244
- ) + trailingQualifier(ir, opts);
294
+ ), "minute", "hour", opts)) + trailingQualifier(ir, opts);
245
295
  }
246
296
  function renderMinuteFrequency(ir, plan, opts) {
247
297
  let phrase = stepCycle60(
@@ -260,6 +310,9 @@ function renderMinuteFrequency(ir, plan, opts) {
260
310
  return phrase + trailingQualifier(ir, opts);
261
311
  }
262
312
  function renderMinuteSpanInHour(ir, plan, opts) {
313
+ if (ir.pattern.minute === "*") {
314
+ return "every minute of the " + getTime({ hour: plan.hour, minute: 0 }, opts) + " hour" + trailingQualifier(ir, opts);
315
+ }
263
316
  return "every minute from " + getTime({ hour: plan.hour, minute: plan.span[0] }, opts) + through(opts) + getTime({ hour: plan.hour, minute: plan.span[1] }, opts) + trailingQualifier(ir, opts);
264
317
  }
265
318
  function renderMinutesAcrossHours(ir, plan, opts) {
@@ -268,8 +321,9 @@ function renderMinutesAcrossHours(ir, plan, opts) {
268
321
  }
269
322
  const times = hourTimesFromPlan(ir, plan.times, true, opts);
270
323
  const lead = plan.form === "range" ? minuteRangeLead(ir.pattern.minute, opts) : (
271
- // The 'list' form is a minute list, which has segments.
272
- listPastThe(
324
+ // The 'list' form is a minute list, which has segments; an offset/uneven
325
+ // step enumerated to that list reads as a stride.
326
+ strideFromSegments(ir.analyses.segments.minute, "minute", "hour", opts) ?? listPastThe(
273
327
  segmentWords(ir.analyses.segments.minute, opts),
274
328
  "minute",
275
329
  "hour",
@@ -296,7 +350,13 @@ function renderMinuteSpanAcrossHourStep(ir, plan, opts) {
296
350
  if (plan.form === "wildcard") {
297
351
  return "every minute " + everyNthHour(segment, opts) + trailingQualifier(ir, opts);
298
352
  }
299
- return minuteRangeLead(ir.pattern.minute, opts) + ", " + stepHours(segment, opts) + trailingQualifier(ir, opts);
353
+ const lead = plan.form === "list" ? strideFromSegments(ir.analyses.segments.minute, "minute", "hour", opts) ?? listPastThe(
354
+ segmentWords(ir.analyses.segments.minute, opts),
355
+ "minute",
356
+ "hour",
357
+ opts
358
+ ) : minuteRangeLead(ir.pattern.minute, opts);
359
+ return lead + ", " + stepHours(segment, opts) + trailingQualifier(ir, opts);
300
360
  }
301
361
  function minuteRangeLead(minuteField, opts) {
302
362
  const bounds = minuteField.split("-");
@@ -320,7 +380,12 @@ function rangeMinuteLead(ir, opts) {
320
380
  if (ir.pattern.minute === "0") {
321
381
  return "every hour";
322
382
  }
323
- return listPastThe(
383
+ return strideFromSegments(
384
+ ir.analyses.segments.minute,
385
+ "minute",
386
+ "hour",
387
+ opts
388
+ ) ?? listPastThe(
324
389
  segmentWords(ir.analyses.segments.minute, opts),
325
390
  "minute",
326
391
  "hour",
@@ -337,6 +402,12 @@ function hourWindow(window, opts) {
337
402
  return "from " + getTime({ hour: window.from, minute: 0 }, opts) + through(opts) + getTime({ hour: window.to, minute: window.last }, opts);
338
403
  }
339
404
  function renderClockTimes(ir, plan, opts) {
405
+ if (ir.shapes.minute === "single") {
406
+ const cadence = hourCadence(ir, +ir.pattern.minute, opts);
407
+ if (cadence !== null) {
408
+ return cadence;
409
+ }
410
+ }
340
411
  const plain = mixedTwelve(plan.times);
341
412
  const times = plan.times.map(function clock(time) {
342
413
  return getTime({
@@ -350,6 +421,10 @@ function renderClockTimes(ir, plan, opts) {
350
421
  }
351
422
  function renderCompactClockTimes(ir, plan, opts) {
352
423
  if (plan.fold) {
424
+ const cadence = hourCadence(ir, +plan.minute, opts);
425
+ if (cadence !== null) {
426
+ return cadence;
427
+ }
353
428
  const hasRange = ir.analyses.segments.hour.some(function range(segment) {
354
429
  return segment.kind === "range";
355
430
  });
@@ -360,13 +435,14 @@ function renderCompactClockTimes(ir, plan, opts) {
360
435
  return interpretDayQualifier(ir, opts) + "at " + hourSegmentTimes(ir, fold, true, opts);
361
436
  }
362
437
  const phrase = (
363
- // The non-fold branch is a minute list, which has segments.
364
- listPastThe(
438
+ // The non-fold branch is a minute list, which has segments. An
439
+ // offset/uneven step enumerated to that list reads as a stride.
440
+ (strideFromSegments(ir.analyses.segments.minute, "minute", "hour", opts) ?? listPastThe(
365
441
  segmentWords(ir.analyses.segments.minute, opts),
366
442
  "minute",
367
443
  "hour",
368
444
  opts
369
- ) + ", at " + hourSegmentTimes(ir, { minute: 0, second: null }, true, opts) + trailingQualifier(ir, opts)
445
+ )) + ", at " + hourSegmentTimes(ir, { minute: 0, second: null }, true, opts) + trailingQualifier(ir, opts)
370
446
  );
371
447
  return ir.analyses.clockSecond ? secondsLeadClause(ir, opts) + ", " + phrase : phrase;
372
448
  }
@@ -414,24 +490,50 @@ var renderers = {
414
490
  singleMinute: renderSingleMinute,
415
491
  standaloneSeconds: renderStandaloneSeconds
416
492
  };
493
+ function renderStride(stride, opts) {
494
+ const { interval, start, last, cycle, unit, anchor } = stride;
495
+ const cadence = "every " + getNumber(interval, opts) + " " + unit + "s";
496
+ const tiles = cycle % interval === 0;
497
+ if (start === 0 && tiles) {
498
+ return cadence;
499
+ }
500
+ if (start < interval && tiles) {
501
+ return cadence + " from " + getNumber(start, opts) + " " + pluralize(start, unit) + " past the " + anchor;
502
+ }
503
+ const num = seriesNumber([start, last], opts);
504
+ return cadence + " from " + num(start) + through(opts) + num(last) + " " + pluralize(last, unit) + " past the " + anchor;
505
+ }
506
+ function singleValues(segments) {
507
+ const values = [];
508
+ for (const segment of segments) {
509
+ if (segment.kind !== "single") {
510
+ return null;
511
+ }
512
+ values.push(+segment.value);
513
+ }
514
+ return values;
515
+ }
516
+ function strideFromSegments(segments, unit, anchor, opts) {
517
+ const values = singleValues(segments);
518
+ const step = values && arithmeticStep(values);
519
+ return step ? renderStride({ ...step, cycle: 60, unit, anchor }, opts) : null;
520
+ }
417
521
  function stepCycle60(segment, unit, anchor, opts) {
418
522
  if (segment.startToken.indexOf("-") !== -1) {
419
523
  return listPastThe(numberWords(segment.fires, opts), unit, anchor, opts);
420
524
  }
421
525
  const start = segment.startToken === "*" ? 0 : +segment.startToken;
422
- const interval = segment.interval;
423
- if (start !== 0) {
424
- if (segment.fires.length <= 3) {
425
- return listPastThe(
426
- numberWords(segment.fires, opts),
427
- unit,
428
- anchor,
429
- opts
430
- );
431
- }
432
- return "every " + getNumber(interval, opts) + " " + unit + "s from " + getNumber(start, opts) + " " + pluralize(start, unit) + " past the " + anchor;
526
+ if (start !== 0 && segment.fires.length <= 3) {
527
+ return listPastThe(numberWords(segment.fires, opts), unit, anchor, opts);
433
528
  }
434
- return "every " + getNumber(interval, opts) + " " + unit + "s";
529
+ return renderStride({
530
+ interval: segment.interval,
531
+ start,
532
+ last: segment.fires[segment.fires.length - 1],
533
+ cycle: 60,
534
+ unit,
535
+ anchor
536
+ }, opts);
435
537
  }
436
538
  function stepHours(segment, opts) {
437
539
  if (segment.startToken.indexOf("-") !== -1) {
@@ -447,6 +549,68 @@ function stepHours(segment, opts) {
447
549
  }
448
550
  return "every " + getNumber(interval, opts) + " hours from " + getTime({ hour: start, minute: 0 }, opts);
449
551
  }
552
+ function hourStrideCadence(stride, opts) {
553
+ const { start, interval, last } = stride;
554
+ const cadence = "every " + getNumber(interval, opts) + " hours";
555
+ const tiles = 24 % interval === 0;
556
+ if (start === 0 && tiles) {
557
+ return cadence;
558
+ }
559
+ if (start < interval && tiles) {
560
+ return cadence + " from " + getTime({ hour: start, minute: 0 }, opts);
561
+ }
562
+ return cadence + " from " + getTime({ hour: start, minute: 0 }, opts) + through(opts) + getTime({ hour: last, minute: 0 }, opts);
563
+ }
564
+ function hourStride(ir) {
565
+ const segments = ir.analyses.segments.hour;
566
+ if (segments.length === 1 && segments[0].kind === "step") {
567
+ const segment = segments[0];
568
+ const start = segment.startToken === "*" ? 0 : +segment.startToken.split("-")[0];
569
+ return { interval: segment.interval, last: segment.fires[segment.fires.length - 1], start };
570
+ }
571
+ const values = singleValues(segments);
572
+ const step = values && arithmeticStep(values);
573
+ return step || null;
574
+ }
575
+ function subMinuteSecond(ir) {
576
+ return ir.pattern.second === "*" || ir.shapes.second === "step";
577
+ }
578
+ function hourCadenceLead(ir, minute, opts) {
579
+ if (minute === 0) {
580
+ if (subMinuteSecond(ir)) {
581
+ return secondsClause(ir, "minute", opts) + " for one minute";
582
+ }
583
+ return secondsClause(ir, "hour", opts);
584
+ }
585
+ const minutePhrase = getNumber(minute, opts) + " " + pluralize(minute, "minute") + " past the hour";
586
+ if (ir.pattern.second === "0") {
587
+ return minutePhrase;
588
+ }
589
+ return secondsClause(ir, "minute", opts) + ", " + minutePhrase;
590
+ }
591
+ function hourCadence(ir, minute, opts) {
592
+ const stride = hourStride(ir);
593
+ if (!stride) {
594
+ return null;
595
+ }
596
+ const fires = (stride.last - stride.start) / stride.interval + 1;
597
+ if (ir.pattern.second === "0" && fires <= maxClockTimes) {
598
+ return null;
599
+ }
600
+ const confinement = minute === 0 && subMinuteSecond(ir) && cleanStrideSegment(ir);
601
+ if (confinement) {
602
+ return secondsClause(ir, "minute", opts) + " for one minute " + everyNthHour(confinement, opts) + trailingQualifier(ir, opts);
603
+ }
604
+ return hourCadenceLead(ir, minute, opts) + ", " + hourStrideCadence(stride, opts) + trailingQualifier(ir, opts);
605
+ }
606
+ function cleanStrideSegment(ir) {
607
+ const segments = ir.analyses.segments.hour;
608
+ const segment = segments.length === 1 && segments[0];
609
+ if (!segment || segment.kind !== "step" || segment.startToken.indexOf("-") !== -1 || !(segment.interval in stepOrdinals)) {
610
+ return null;
611
+ }
612
+ return segment;
613
+ }
450
614
  function seriesNumber(values, opts) {
451
615
  const anyBig = values.some(function big(v) {
452
616
  return +v > 10;
@@ -758,7 +922,7 @@ function stepYears(yearField, opts) {
758
922
  return phrase;
759
923
  }
760
924
  function getTime(time, opts) {
761
- const { hour, minute, plain } = time;
925
+ const { hour, minute, plain, explicit } = time;
762
926
  const second = typeof time.second === "number" && time.second > 0 ? time.second : 0;
763
927
  if (!opts.ampm) {
764
928
  return clockDigits({
@@ -767,12 +931,12 @@ function getTime(time, opts) {
767
931
  second
768
932
  }, { pad: true, sep: opts.style.sep });
769
933
  }
770
- return twelveHourTime({ hour, minute, second, plain }, opts);
934
+ return twelveHourTime({ hour, minute, second, plain, explicit }, opts);
771
935
  }
772
936
  function twelveHourTime(time, opts) {
773
- const { hour, minute, second, plain } = time;
937
+ const { hour, minute, second, plain, explicit } = time;
774
938
  const style = opts.style;
775
- if (!plain && +minute === 0 && !second) {
939
+ if (!plain && !explicit && +minute === 0 && !second) {
776
940
  if (+hour === 0) {
777
941
  return style.midnight;
778
942
  }
@@ -782,7 +946,7 @@ function twelveHourTime(time, opts) {
782
946
  }
783
947
  const digits = clockDigits(
784
948
  { hour: hour % 12 || 12, minute, second },
785
- { lean: true, sep: style.sep }
949
+ { lean: !explicit, sep: style.sep }
786
950
  );
787
951
  return digits + (style.closeUp ? "" : " ") + (hour < 12 ? style.am : style.pm);
788
952
  }
@@ -805,7 +969,7 @@ function getOrdinal(n) {
805
969
  return n + suffix;
806
970
  }
807
971
  function getMonth(m, opts) {
808
- const month = monthNames[m] || monthAbbreviations[m];
972
+ const month = monthNames[+m];
809
973
  return month && month[opts.short ? 1 : 0];
810
974
  }
811
975
  function getWeekday(d, opts) {
@@ -818,7 +982,9 @@ var en = {
818
982
  fallback: "an unrecognizable cron pattern",
819
983
  options: normalizeOptions,
820
984
  reboot: "at system startup",
821
- sentence: (description) => "Runs " + description + "."
985
+ // A description ending in an abbreviation already carries its period
986
+ // ("…9 a.m."), so closing the sentence must not double it.
987
+ sentence: (description) => "Runs " + description + (description.endsWith(".") ? "" : ".")
822
988
  };
823
989
  var index_default = en;
824
990
  module.exports = module.exports.default;