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/CHANGELOG.md +89 -0
- package/cli.js +9 -0
- package/cronli5.min.js +2 -2
- package/dist/cronli5.cjs +280 -64
- package/dist/cronli5.js +280 -64
- package/dist/lang/de.cjs +227 -42
- package/dist/lang/de.js +227 -42
- package/dist/lang/en.cjs +216 -50
- package/dist/lang/en.js +216 -50
- package/dist/lang/es.cjs +259 -54
- package/dist/lang/es.js +259 -54
- package/dist/lang/fi.cjs +230 -69
- package/dist/lang/fi.js +230 -69
- package/dist/lang/zh.cjs +190 -19
- package/dist/lang/zh.js +190 -19
- package/package.json +3 -1
- package/src/core/analyze.ts +7 -0
- package/src/core/ir.ts +1 -1
- package/src/core/normalize.ts +94 -4
- package/src/core/util.ts +31 -1
- package/src/lang/de/index.ts +449 -46
- package/src/lang/en/index.ts +433 -63
- package/src/lang/es/index.ts +505 -63
- package/src/lang/fi/index.ts +455 -89
- package/src/lang/zh/index.ts +393 -30
- package/types/core/ir.d.ts +1 -1
- package/types/core/util.d.ts +6 -1
package/dist/cronli5.js
CHANGED
|
@@ -69,6 +69,21 @@ function isNonNegativeInteger(value) {
|
|
|
69
69
|
const digits = /^\d+$/;
|
|
70
70
|
return digits.test(value);
|
|
71
71
|
}
|
|
72
|
+
function arithmeticStep(values) {
|
|
73
|
+
if (values.length < 5) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const interval = values[1] - values[0];
|
|
77
|
+
if (interval < 2) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
for (let i = 2; i < values.length; i += 1) {
|
|
81
|
+
if (values[i] - values[i - 1] !== interval) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return { start: values[0], interval, last: values[values.length - 1] };
|
|
86
|
+
}
|
|
72
87
|
function toFieldNumber(token, numberMap) {
|
|
73
88
|
return isNonNegativeInteger(token) ? +token : numberMap[token.toUpperCase()];
|
|
74
89
|
}
|
|
@@ -200,14 +215,20 @@ function normalizeField(value, field, spec) {
|
|
|
200
215
|
}
|
|
201
216
|
const cycle = timeFieldCycle[field];
|
|
202
217
|
const segments = stringValue.split(",").map(function canonical(segment) {
|
|
203
|
-
return
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
218
|
+
return canonicalizeTokens(collapseFullSpanRange(
|
|
219
|
+
enumerateNonUniformStep(
|
|
220
|
+
collapseFullSpanStep(
|
|
221
|
+
collapseDegenerateRange(
|
|
222
|
+
collapseOnceStep(collapseUnitStep(segment, spec), spec),
|
|
223
|
+
spec
|
|
224
|
+
),
|
|
225
|
+
spec
|
|
226
|
+
),
|
|
227
|
+
spec,
|
|
228
|
+
cycle
|
|
207
229
|
),
|
|
208
|
-
spec
|
|
209
|
-
|
|
210
|
-
);
|
|
230
|
+
spec
|
|
231
|
+
), spec);
|
|
211
232
|
}).join(",").split(",");
|
|
212
233
|
if (segments.indexOf("*") !== -1) {
|
|
213
234
|
return "*";
|
|
@@ -216,6 +237,23 @@ function normalizeField(value, field, spec) {
|
|
|
216
237
|
return firstFire(a, spec) - firstFire(b, spec);
|
|
217
238
|
}).join(",");
|
|
218
239
|
}
|
|
240
|
+
function canonicalizeTokens(segment, spec) {
|
|
241
|
+
if (!spec.numbers) {
|
|
242
|
+
return segment;
|
|
243
|
+
}
|
|
244
|
+
const parts = segment.split("/");
|
|
245
|
+
const start = parts[0].split("-").map(function fold(token) {
|
|
246
|
+
return canonicalizeToken(token, spec);
|
|
247
|
+
}).join("-");
|
|
248
|
+
return parts.length === 2 ? start + "/" + parts[1] : start;
|
|
249
|
+
}
|
|
250
|
+
function canonicalizeToken(token, spec) {
|
|
251
|
+
if (token === "*") {
|
|
252
|
+
return token;
|
|
253
|
+
}
|
|
254
|
+
const number = toFieldNumber(token, spec.numbers);
|
|
255
|
+
return "" + (number > spec.top ? spec.min : number);
|
|
256
|
+
}
|
|
219
257
|
function collapseUnitStep(segment, spec) {
|
|
220
258
|
const parts = segment.split("/");
|
|
221
259
|
if (!spec.cyclic || parts.length !== 2 || +parts[1] !== 1) {
|
|
@@ -258,6 +296,35 @@ function enumerateNonUniformStep(segment, spec, cycle) {
|
|
|
258
296
|
}
|
|
259
297
|
return fires.join(",");
|
|
260
298
|
}
|
|
299
|
+
function collapseFullSpanStep(segment, spec) {
|
|
300
|
+
const parts = segment.split("/");
|
|
301
|
+
if (parts.length !== 2 || !includes(parts[0], "-")) {
|
|
302
|
+
return segment;
|
|
303
|
+
}
|
|
304
|
+
return collapseFullSpanRange(parts[0], spec) === "*" ? "*/" + parts[1] : segment;
|
|
305
|
+
}
|
|
306
|
+
function collapseFullSpanRange(segment, spec) {
|
|
307
|
+
if (typeof spec.top !== "number" || includes(segment, "/") || !includes(segment, "-")) {
|
|
308
|
+
return segment;
|
|
309
|
+
}
|
|
310
|
+
const bounds = segment.split("-");
|
|
311
|
+
const low = toFieldNumber(bounds[0], spec.numbers);
|
|
312
|
+
const high = toFieldNumber(bounds[1], spec.numbers);
|
|
313
|
+
if (low > high) {
|
|
314
|
+
return segment;
|
|
315
|
+
}
|
|
316
|
+
const top = spec.top;
|
|
317
|
+
const fired = {};
|
|
318
|
+
for (let value = low; value <= high; value += 1) {
|
|
319
|
+
fired[value > top ? spec.min : value] = true;
|
|
320
|
+
}
|
|
321
|
+
for (let value = spec.min; value <= top; value += 1) {
|
|
322
|
+
if (!fired[value]) {
|
|
323
|
+
return segment;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return "*";
|
|
327
|
+
}
|
|
261
328
|
function collapseDegenerateRange(segment, spec) {
|
|
262
329
|
const start = segment.split("/")[0];
|
|
263
330
|
if (!includes(start, "-")) {
|
|
@@ -547,7 +614,7 @@ function planStandaloneSeconds(pattern, shapes) {
|
|
|
547
614
|
}
|
|
548
615
|
return { kind: "standaloneSeconds" };
|
|
549
616
|
}
|
|
550
|
-
function planMinutes(pattern, shapes, analyses,
|
|
617
|
+
function planMinutes(pattern, shapes, analyses, subMinuteSecond2 = false) {
|
|
551
618
|
if (shapes.minute === "step") {
|
|
552
619
|
return {
|
|
553
620
|
hours: planFrequencyHours(pattern, shapes, analyses),
|
|
@@ -570,7 +637,7 @@ function planMinutes(pattern, shapes, analyses, subMinuteSecond = false) {
|
|
|
570
637
|
return underStep;
|
|
571
638
|
}
|
|
572
639
|
if (pattern.hour === "*") {
|
|
573
|
-
return planMinutesUnderOpenHour(pattern, shapes,
|
|
640
|
+
return planMinutesUnderOpenHour(pattern, shapes, subMinuteSecond2);
|
|
574
641
|
}
|
|
575
642
|
}
|
|
576
643
|
function cleanHourStride(hourField) {
|
|
@@ -592,6 +659,9 @@ function planMinuteUnderHourStep(pattern, shapes) {
|
|
|
592
659
|
if (shapes.minute === "range") {
|
|
593
660
|
return { form: "range", kind: "minuteSpanAcrossHourStep" };
|
|
594
661
|
}
|
|
662
|
+
if (shapes.minute === "list" && cleanHourStride(pattern.hour)) {
|
|
663
|
+
return { form: "list", kind: "minuteSpanAcrossHourStep" };
|
|
664
|
+
}
|
|
595
665
|
return null;
|
|
596
666
|
}
|
|
597
667
|
function planFrequencyHours(pattern, shapes, analyses) {
|
|
@@ -640,7 +710,7 @@ function planMinutesAcrossHours(pattern, shapes) {
|
|
|
640
710
|
}
|
|
641
711
|
return null;
|
|
642
712
|
}
|
|
643
|
-
function planMinutesUnderOpenHour(pattern, shapes,
|
|
713
|
+
function planMinutesUnderOpenHour(pattern, shapes, subMinuteSecond2) {
|
|
644
714
|
if (shapes.minute === "range") {
|
|
645
715
|
return { kind: "rangeOfMinutes" };
|
|
646
716
|
}
|
|
@@ -650,16 +720,16 @@ function planMinutesUnderOpenHour(pattern, shapes, subMinuteSecond) {
|
|
|
650
720
|
if (pattern.minute === "*") {
|
|
651
721
|
return { kind: "everyMinute" };
|
|
652
722
|
}
|
|
653
|
-
if (pattern.minute !== "0" ||
|
|
723
|
+
if (pattern.minute !== "0" || subMinuteSecond2) {
|
|
654
724
|
return { kind: "singleMinute" };
|
|
655
725
|
}
|
|
656
726
|
}
|
|
657
|
-
function planHours(pattern, shapes, analyses,
|
|
658
|
-
const absorbsMinuteZero =
|
|
727
|
+
function planHours(pattern, shapes, analyses, subMinuteSecond2 = false) {
|
|
728
|
+
const absorbsMinuteZero = subMinuteSecond2 && pattern.minute === "0";
|
|
659
729
|
if (shapes.hour === "range" && !absorbsMinuteZero) {
|
|
660
730
|
return planHourRange(pattern, shapes, analyses);
|
|
661
731
|
}
|
|
662
|
-
if (shapes.hour === "step" && pattern.minute === "0" && !
|
|
732
|
+
if (shapes.hour === "step" && pattern.minute === "0" && !subMinuteSecond2) {
|
|
663
733
|
return { kind: "hourStep" };
|
|
664
734
|
}
|
|
665
735
|
if (pattern.hour === "*" && !absorbsMinuteZero) {
|
|
@@ -826,20 +896,6 @@ var weekdayNames = [
|
|
|
826
896
|
["Friday", "Fri"],
|
|
827
897
|
["Saturday", "Sat"]
|
|
828
898
|
];
|
|
829
|
-
var monthAbbreviations = {
|
|
830
|
-
JAN: monthNames[1],
|
|
831
|
-
FEB: monthNames[2],
|
|
832
|
-
MAR: monthNames[3],
|
|
833
|
-
APR: monthNames[4],
|
|
834
|
-
MAY: monthNames[5],
|
|
835
|
-
JUN: monthNames[6],
|
|
836
|
-
JUL: monthNames[7],
|
|
837
|
-
AUG: monthNames[8],
|
|
838
|
-
SEP: monthNames[9],
|
|
839
|
-
OCT: monthNames[10],
|
|
840
|
-
NOV: monthNames[11],
|
|
841
|
-
DEC: monthNames[12]
|
|
842
|
-
};
|
|
843
899
|
var weekdayAbbreviations = {
|
|
844
900
|
SUN: weekdayNames[0],
|
|
845
901
|
MON: weekdayNames[1],
|
|
@@ -888,10 +944,50 @@ function renderSecondsWithinMinute(ir, plan, opts) {
|
|
|
888
944
|
}
|
|
889
945
|
return secondsLeadClause(ir, opts) + ", " + minuteWord + " " + minuteUnit + " past the hour, every hour" + trailingQualifier(ir, opts);
|
|
890
946
|
}
|
|
947
|
+
function composeHourCadence(ir, plan, opts) {
|
|
948
|
+
const clockRest = plan.rest.kind === "clockTimes" || plan.rest.kind === "compactClockTimes";
|
|
949
|
+
return clockRest && ir.shapes.minute === "single" ? hourCadence(ir, +ir.pattern.minute, opts) : null;
|
|
950
|
+
}
|
|
891
951
|
function renderComposeSeconds(ir, plan, opts) {
|
|
952
|
+
const cadence = composeHourCadence(ir, plan, opts);
|
|
953
|
+
if (cadence !== null) {
|
|
954
|
+
return cadence;
|
|
955
|
+
}
|
|
956
|
+
if (plan.rest.kind === "clockTimes" && (ir.shapes.second === "wildcard" || ir.shapes.second === "step")) {
|
|
957
|
+
const minute = plan.rest.times[0].minute;
|
|
958
|
+
if (+minute === 0) {
|
|
959
|
+
return secondsLeadClause(ir, opts) + " for one minute at " + durationHours(ir, plan.rest, opts);
|
|
960
|
+
}
|
|
961
|
+
return secondsLeadClause(ir, opts) + " of " + clockTimesOf(ir, plan.rest, opts);
|
|
962
|
+
}
|
|
963
|
+
if (ir.shapes.second === "wildcard" && plan.rest.kind === "minuteFrequency" && plan.rest.hours.kind === "none" && ir.pattern.minute === "*/2") {
|
|
964
|
+
return "every second of every other minute" + trailingQualifier(ir, opts);
|
|
965
|
+
}
|
|
892
966
|
return secondsLeadClause(ir, opts) + ", " + render(ir, plan.rest, opts);
|
|
893
967
|
}
|
|
968
|
+
function durationHours(ir, plan, opts) {
|
|
969
|
+
const hours = plan.times.map(function clock(time) {
|
|
970
|
+
return getTime({ hour: time.hour, minute: 0 }, opts);
|
|
971
|
+
});
|
|
972
|
+
const trail = dayQualifier(ir, leadingWords, opts);
|
|
973
|
+
return joinList(hours, opts) + (trail && ", " + trail);
|
|
974
|
+
}
|
|
975
|
+
function clockTimesOf(ir, plan, opts) {
|
|
976
|
+
const times = plan.times.map(function clock(time) {
|
|
977
|
+
return getTime({
|
|
978
|
+
hour: time.hour,
|
|
979
|
+
minute: time.minute,
|
|
980
|
+
second: time.second,
|
|
981
|
+
explicit: true
|
|
982
|
+
}, opts);
|
|
983
|
+
});
|
|
984
|
+
const trail = dayQualifier(ir, leadingWords, opts);
|
|
985
|
+
return joinList(times, opts) + (trail && ", " + trail);
|
|
986
|
+
}
|
|
894
987
|
function secondsLeadClause(ir, opts) {
|
|
988
|
+
return secondsClause(ir, "minute", opts);
|
|
989
|
+
}
|
|
990
|
+
function secondsClause(ir, anchor, opts) {
|
|
895
991
|
const secondField = ir.pattern.second;
|
|
896
992
|
const shape = ir.shapes.second;
|
|
897
993
|
if (secondField === "*") {
|
|
@@ -901,22 +997,27 @@ function secondsLeadClause(ir, opts) {
|
|
|
901
997
|
return stepCycle60(
|
|
902
998
|
ir.analyses.segments.second[0],
|
|
903
999
|
"second",
|
|
904
|
-
|
|
1000
|
+
anchor,
|
|
905
1001
|
opts
|
|
906
1002
|
);
|
|
907
1003
|
}
|
|
908
1004
|
if (shape === "range") {
|
|
909
1005
|
const bounds = secondField.split("-");
|
|
910
1006
|
const num = seriesNumber(bounds, opts);
|
|
911
|
-
return "every second from " + num(bounds[0]) + through(opts) + num(bounds[1]) + " past the
|
|
1007
|
+
return "every second from " + num(bounds[0]) + through(opts) + num(bounds[1]) + " past the " + anchor;
|
|
912
1008
|
}
|
|
913
1009
|
if (shape === "single") {
|
|
914
|
-
return "at " + getNumber(secondField, opts) + " " + pluralize(secondField, "second") + " past the
|
|
1010
|
+
return "at " + getNumber(secondField, opts) + " " + pluralize(secondField, "second") + " past the " + anchor;
|
|
915
1011
|
}
|
|
916
|
-
return
|
|
1012
|
+
return strideFromSegments(
|
|
1013
|
+
ir.analyses.segments.second,
|
|
1014
|
+
"second",
|
|
1015
|
+
anchor,
|
|
1016
|
+
opts
|
|
1017
|
+
) ?? listPastThe(
|
|
917
1018
|
segmentWords(ir.analyses.segments.second, opts),
|
|
918
1019
|
"second",
|
|
919
|
-
|
|
1020
|
+
anchor,
|
|
920
1021
|
opts
|
|
921
1022
|
);
|
|
922
1023
|
}
|
|
@@ -931,12 +1032,11 @@ function renderRangeOfMinutes(ir, plan, opts) {
|
|
|
931
1032
|
return minuteRangeLead(ir.pattern.minute, opts) + trailingQualifier(ir, opts);
|
|
932
1033
|
}
|
|
933
1034
|
function renderMultipleMinutes(ir, plan, opts) {
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
"hour",
|
|
1035
|
+
const stride = strideFromSegments(ir.analyses.segments.minute, "minute", "hour", opts);
|
|
1036
|
+
return (stride ?? listPastThe(segmentWords(
|
|
1037
|
+
ir.analyses.segments.minute,
|
|
938
1038
|
opts
|
|
939
|
-
) + trailingQualifier(ir, opts);
|
|
1039
|
+
), "minute", "hour", opts)) + trailingQualifier(ir, opts);
|
|
940
1040
|
}
|
|
941
1041
|
function renderMinuteFrequency(ir, plan, opts) {
|
|
942
1042
|
let phrase = stepCycle60(
|
|
@@ -955,6 +1055,9 @@ function renderMinuteFrequency(ir, plan, opts) {
|
|
|
955
1055
|
return phrase + trailingQualifier(ir, opts);
|
|
956
1056
|
}
|
|
957
1057
|
function renderMinuteSpanInHour(ir, plan, opts) {
|
|
1058
|
+
if (ir.pattern.minute === "*") {
|
|
1059
|
+
return "every minute of the " + getTime({ hour: plan.hour, minute: 0 }, opts) + " hour" + trailingQualifier(ir, opts);
|
|
1060
|
+
}
|
|
958
1061
|
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);
|
|
959
1062
|
}
|
|
960
1063
|
function renderMinutesAcrossHours(ir, plan, opts) {
|
|
@@ -963,8 +1066,9 @@ function renderMinutesAcrossHours(ir, plan, opts) {
|
|
|
963
1066
|
}
|
|
964
1067
|
const times = hourTimesFromPlan(ir, plan.times, true, opts);
|
|
965
1068
|
const lead = plan.form === "range" ? minuteRangeLead(ir.pattern.minute, opts) : (
|
|
966
|
-
// The 'list' form is a minute list, which has segments
|
|
967
|
-
|
|
1069
|
+
// The 'list' form is a minute list, which has segments; an offset/uneven
|
|
1070
|
+
// step enumerated to that list reads as a stride.
|
|
1071
|
+
strideFromSegments(ir.analyses.segments.minute, "minute", "hour", opts) ?? listPastThe(
|
|
968
1072
|
segmentWords(ir.analyses.segments.minute, opts),
|
|
969
1073
|
"minute",
|
|
970
1074
|
"hour",
|
|
@@ -991,7 +1095,13 @@ function renderMinuteSpanAcrossHourStep(ir, plan, opts) {
|
|
|
991
1095
|
if (plan.form === "wildcard") {
|
|
992
1096
|
return "every minute " + everyNthHour(segment, opts) + trailingQualifier(ir, opts);
|
|
993
1097
|
}
|
|
994
|
-
|
|
1098
|
+
const lead = plan.form === "list" ? strideFromSegments(ir.analyses.segments.minute, "minute", "hour", opts) ?? listPastThe(
|
|
1099
|
+
segmentWords(ir.analyses.segments.minute, opts),
|
|
1100
|
+
"minute",
|
|
1101
|
+
"hour",
|
|
1102
|
+
opts
|
|
1103
|
+
) : minuteRangeLead(ir.pattern.minute, opts);
|
|
1104
|
+
return lead + ", " + stepHours(segment, opts) + trailingQualifier(ir, opts);
|
|
995
1105
|
}
|
|
996
1106
|
function minuteRangeLead(minuteField, opts) {
|
|
997
1107
|
const bounds = minuteField.split("-");
|
|
@@ -1015,7 +1125,12 @@ function rangeMinuteLead(ir, opts) {
|
|
|
1015
1125
|
if (ir.pattern.minute === "0") {
|
|
1016
1126
|
return "every hour";
|
|
1017
1127
|
}
|
|
1018
|
-
return
|
|
1128
|
+
return strideFromSegments(
|
|
1129
|
+
ir.analyses.segments.minute,
|
|
1130
|
+
"minute",
|
|
1131
|
+
"hour",
|
|
1132
|
+
opts
|
|
1133
|
+
) ?? listPastThe(
|
|
1019
1134
|
segmentWords(ir.analyses.segments.minute, opts),
|
|
1020
1135
|
"minute",
|
|
1021
1136
|
"hour",
|
|
@@ -1032,6 +1147,12 @@ function hourWindow(window, opts) {
|
|
|
1032
1147
|
return "from " + getTime({ hour: window.from, minute: 0 }, opts) + through(opts) + getTime({ hour: window.to, minute: window.last }, opts);
|
|
1033
1148
|
}
|
|
1034
1149
|
function renderClockTimes(ir, plan, opts) {
|
|
1150
|
+
if (ir.shapes.minute === "single") {
|
|
1151
|
+
const cadence = hourCadence(ir, +ir.pattern.minute, opts);
|
|
1152
|
+
if (cadence !== null) {
|
|
1153
|
+
return cadence;
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1035
1156
|
const plain = mixedTwelve(plan.times);
|
|
1036
1157
|
const times = plan.times.map(function clock(time) {
|
|
1037
1158
|
return getTime({
|
|
@@ -1045,6 +1166,10 @@ function renderClockTimes(ir, plan, opts) {
|
|
|
1045
1166
|
}
|
|
1046
1167
|
function renderCompactClockTimes(ir, plan, opts) {
|
|
1047
1168
|
if (plan.fold) {
|
|
1169
|
+
const cadence = hourCadence(ir, +plan.minute, opts);
|
|
1170
|
+
if (cadence !== null) {
|
|
1171
|
+
return cadence;
|
|
1172
|
+
}
|
|
1048
1173
|
const hasRange = ir.analyses.segments.hour.some(function range(segment) {
|
|
1049
1174
|
return segment.kind === "range";
|
|
1050
1175
|
});
|
|
@@ -1055,13 +1180,14 @@ function renderCompactClockTimes(ir, plan, opts) {
|
|
|
1055
1180
|
return interpretDayQualifier(ir, opts) + "at " + hourSegmentTimes(ir, fold, true, opts);
|
|
1056
1181
|
}
|
|
1057
1182
|
const phrase = (
|
|
1058
|
-
// The non-fold branch is a minute list, which has segments.
|
|
1059
|
-
|
|
1183
|
+
// The non-fold branch is a minute list, which has segments. An
|
|
1184
|
+
// offset/uneven step enumerated to that list reads as a stride.
|
|
1185
|
+
(strideFromSegments(ir.analyses.segments.minute, "minute", "hour", opts) ?? listPastThe(
|
|
1060
1186
|
segmentWords(ir.analyses.segments.minute, opts),
|
|
1061
1187
|
"minute",
|
|
1062
1188
|
"hour",
|
|
1063
1189
|
opts
|
|
1064
|
-
) + ", at " + hourSegmentTimes(ir, { minute: 0, second: null }, true, opts) + trailingQualifier(ir, opts)
|
|
1190
|
+
)) + ", at " + hourSegmentTimes(ir, { minute: 0, second: null }, true, opts) + trailingQualifier(ir, opts)
|
|
1065
1191
|
);
|
|
1066
1192
|
return ir.analyses.clockSecond ? secondsLeadClause(ir, opts) + ", " + phrase : phrase;
|
|
1067
1193
|
}
|
|
@@ -1109,24 +1235,50 @@ var renderers = {
|
|
|
1109
1235
|
singleMinute: renderSingleMinute,
|
|
1110
1236
|
standaloneSeconds: renderStandaloneSeconds
|
|
1111
1237
|
};
|
|
1238
|
+
function renderStride(stride, opts) {
|
|
1239
|
+
const { interval, start, last, cycle, unit, anchor } = stride;
|
|
1240
|
+
const cadence = "every " + getNumber(interval, opts) + " " + unit + "s";
|
|
1241
|
+
const tiles = cycle % interval === 0;
|
|
1242
|
+
if (start === 0 && tiles) {
|
|
1243
|
+
return cadence;
|
|
1244
|
+
}
|
|
1245
|
+
if (start < interval && tiles) {
|
|
1246
|
+
return cadence + " from " + getNumber(start, opts) + " " + pluralize(start, unit) + " past the " + anchor;
|
|
1247
|
+
}
|
|
1248
|
+
const num = seriesNumber([start, last], opts);
|
|
1249
|
+
return cadence + " from " + num(start) + through(opts) + num(last) + " " + pluralize(last, unit) + " past the " + anchor;
|
|
1250
|
+
}
|
|
1251
|
+
function singleValues(segments) {
|
|
1252
|
+
const values = [];
|
|
1253
|
+
for (const segment of segments) {
|
|
1254
|
+
if (segment.kind !== "single") {
|
|
1255
|
+
return null;
|
|
1256
|
+
}
|
|
1257
|
+
values.push(+segment.value);
|
|
1258
|
+
}
|
|
1259
|
+
return values;
|
|
1260
|
+
}
|
|
1261
|
+
function strideFromSegments(segments, unit, anchor, opts) {
|
|
1262
|
+
const values = singleValues(segments);
|
|
1263
|
+
const step = values && arithmeticStep(values);
|
|
1264
|
+
return step ? renderStride({ ...step, cycle: 60, unit, anchor }, opts) : null;
|
|
1265
|
+
}
|
|
1112
1266
|
function stepCycle60(segment, unit, anchor, opts) {
|
|
1113
1267
|
if (segment.startToken.indexOf("-") !== -1) {
|
|
1114
1268
|
return listPastThe(numberWords(segment.fires, opts), unit, anchor, opts);
|
|
1115
1269
|
}
|
|
1116
1270
|
const start = segment.startToken === "*" ? 0 : +segment.startToken;
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
if (segment.fires.length <= 3) {
|
|
1120
|
-
return listPastThe(
|
|
1121
|
-
numberWords(segment.fires, opts),
|
|
1122
|
-
unit,
|
|
1123
|
-
anchor,
|
|
1124
|
-
opts
|
|
1125
|
-
);
|
|
1126
|
-
}
|
|
1127
|
-
return "every " + getNumber(interval, opts) + " " + unit + "s from " + getNumber(start, opts) + " " + pluralize(start, unit) + " past the " + anchor;
|
|
1271
|
+
if (start !== 0 && segment.fires.length <= 3) {
|
|
1272
|
+
return listPastThe(numberWords(segment.fires, opts), unit, anchor, opts);
|
|
1128
1273
|
}
|
|
1129
|
-
return
|
|
1274
|
+
return renderStride({
|
|
1275
|
+
interval: segment.interval,
|
|
1276
|
+
start,
|
|
1277
|
+
last: segment.fires[segment.fires.length - 1],
|
|
1278
|
+
cycle: 60,
|
|
1279
|
+
unit,
|
|
1280
|
+
anchor
|
|
1281
|
+
}, opts);
|
|
1130
1282
|
}
|
|
1131
1283
|
function stepHours(segment, opts) {
|
|
1132
1284
|
if (segment.startToken.indexOf("-") !== -1) {
|
|
@@ -1142,6 +1294,68 @@ function stepHours(segment, opts) {
|
|
|
1142
1294
|
}
|
|
1143
1295
|
return "every " + getNumber(interval, opts) + " hours from " + getTime({ hour: start, minute: 0 }, opts);
|
|
1144
1296
|
}
|
|
1297
|
+
function hourStrideCadence(stride, opts) {
|
|
1298
|
+
const { start, interval, last } = stride;
|
|
1299
|
+
const cadence = "every " + getNumber(interval, opts) + " hours";
|
|
1300
|
+
const tiles = 24 % interval === 0;
|
|
1301
|
+
if (start === 0 && tiles) {
|
|
1302
|
+
return cadence;
|
|
1303
|
+
}
|
|
1304
|
+
if (start < interval && tiles) {
|
|
1305
|
+
return cadence + " from " + getTime({ hour: start, minute: 0 }, opts);
|
|
1306
|
+
}
|
|
1307
|
+
return cadence + " from " + getTime({ hour: start, minute: 0 }, opts) + through(opts) + getTime({ hour: last, minute: 0 }, opts);
|
|
1308
|
+
}
|
|
1309
|
+
function hourStride(ir) {
|
|
1310
|
+
const segments = ir.analyses.segments.hour;
|
|
1311
|
+
if (segments.length === 1 && segments[0].kind === "step") {
|
|
1312
|
+
const segment = segments[0];
|
|
1313
|
+
const start = segment.startToken === "*" ? 0 : +segment.startToken.split("-")[0];
|
|
1314
|
+
return { interval: segment.interval, last: segment.fires[segment.fires.length - 1], start };
|
|
1315
|
+
}
|
|
1316
|
+
const values = singleValues(segments);
|
|
1317
|
+
const step = values && arithmeticStep(values);
|
|
1318
|
+
return step || null;
|
|
1319
|
+
}
|
|
1320
|
+
function subMinuteSecond(ir) {
|
|
1321
|
+
return ir.pattern.second === "*" || ir.shapes.second === "step";
|
|
1322
|
+
}
|
|
1323
|
+
function hourCadenceLead(ir, minute, opts) {
|
|
1324
|
+
if (minute === 0) {
|
|
1325
|
+
if (subMinuteSecond(ir)) {
|
|
1326
|
+
return secondsClause(ir, "minute", opts) + " for one minute";
|
|
1327
|
+
}
|
|
1328
|
+
return secondsClause(ir, "hour", opts);
|
|
1329
|
+
}
|
|
1330
|
+
const minutePhrase = getNumber(minute, opts) + " " + pluralize(minute, "minute") + " past the hour";
|
|
1331
|
+
if (ir.pattern.second === "0") {
|
|
1332
|
+
return minutePhrase;
|
|
1333
|
+
}
|
|
1334
|
+
return secondsClause(ir, "minute", opts) + ", " + minutePhrase;
|
|
1335
|
+
}
|
|
1336
|
+
function hourCadence(ir, minute, opts) {
|
|
1337
|
+
const stride = hourStride(ir);
|
|
1338
|
+
if (!stride) {
|
|
1339
|
+
return null;
|
|
1340
|
+
}
|
|
1341
|
+
const fires = (stride.last - stride.start) / stride.interval + 1;
|
|
1342
|
+
if (ir.pattern.second === "0" && fires <= maxClockTimes) {
|
|
1343
|
+
return null;
|
|
1344
|
+
}
|
|
1345
|
+
const confinement = minute === 0 && subMinuteSecond(ir) && cleanStrideSegment(ir);
|
|
1346
|
+
if (confinement) {
|
|
1347
|
+
return secondsClause(ir, "minute", opts) + " for one minute " + everyNthHour(confinement, opts) + trailingQualifier(ir, opts);
|
|
1348
|
+
}
|
|
1349
|
+
return hourCadenceLead(ir, minute, opts) + ", " + hourStrideCadence(stride, opts) + trailingQualifier(ir, opts);
|
|
1350
|
+
}
|
|
1351
|
+
function cleanStrideSegment(ir) {
|
|
1352
|
+
const segments = ir.analyses.segments.hour;
|
|
1353
|
+
const segment = segments.length === 1 && segments[0];
|
|
1354
|
+
if (!segment || segment.kind !== "step" || segment.startToken.indexOf("-") !== -1 || !(segment.interval in stepOrdinals)) {
|
|
1355
|
+
return null;
|
|
1356
|
+
}
|
|
1357
|
+
return segment;
|
|
1358
|
+
}
|
|
1145
1359
|
function seriesNumber(values, opts) {
|
|
1146
1360
|
const anyBig = values.some(function big(v) {
|
|
1147
1361
|
return +v > 10;
|
|
@@ -1453,7 +1667,7 @@ function stepYears(yearField, opts) {
|
|
|
1453
1667
|
return phrase;
|
|
1454
1668
|
}
|
|
1455
1669
|
function getTime(time, opts) {
|
|
1456
|
-
const { hour, minute, plain } = time;
|
|
1670
|
+
const { hour, minute, plain, explicit } = time;
|
|
1457
1671
|
const second = typeof time.second === "number" && time.second > 0 ? time.second : 0;
|
|
1458
1672
|
if (!opts.ampm) {
|
|
1459
1673
|
return clockDigits({
|
|
@@ -1462,12 +1676,12 @@ function getTime(time, opts) {
|
|
|
1462
1676
|
second
|
|
1463
1677
|
}, { pad: true, sep: opts.style.sep });
|
|
1464
1678
|
}
|
|
1465
|
-
return twelveHourTime({ hour, minute, second, plain }, opts);
|
|
1679
|
+
return twelveHourTime({ hour, minute, second, plain, explicit }, opts);
|
|
1466
1680
|
}
|
|
1467
1681
|
function twelveHourTime(time, opts) {
|
|
1468
|
-
const { hour, minute, second, plain } = time;
|
|
1682
|
+
const { hour, minute, second, plain, explicit } = time;
|
|
1469
1683
|
const style = opts.style;
|
|
1470
|
-
if (!plain && +minute === 0 && !second) {
|
|
1684
|
+
if (!plain && !explicit && +minute === 0 && !second) {
|
|
1471
1685
|
if (+hour === 0) {
|
|
1472
1686
|
return style.midnight;
|
|
1473
1687
|
}
|
|
@@ -1477,7 +1691,7 @@ function twelveHourTime(time, opts) {
|
|
|
1477
1691
|
}
|
|
1478
1692
|
const digits = clockDigits(
|
|
1479
1693
|
{ hour: hour % 12 || 12, minute, second },
|
|
1480
|
-
{ lean:
|
|
1694
|
+
{ lean: !explicit, sep: style.sep }
|
|
1481
1695
|
);
|
|
1482
1696
|
return digits + (style.closeUp ? "" : " ") + (hour < 12 ? style.am : style.pm);
|
|
1483
1697
|
}
|
|
@@ -1500,7 +1714,7 @@ function getOrdinal(n) {
|
|
|
1500
1714
|
return n + suffix;
|
|
1501
1715
|
}
|
|
1502
1716
|
function getMonth(m, opts) {
|
|
1503
|
-
const month = monthNames[m]
|
|
1717
|
+
const month = monthNames[+m];
|
|
1504
1718
|
return month && month[opts.short ? 1 : 0];
|
|
1505
1719
|
}
|
|
1506
1720
|
function getWeekday(d, opts) {
|
|
@@ -1513,7 +1727,9 @@ var en = {
|
|
|
1513
1727
|
fallback: "an unrecognizable cron pattern",
|
|
1514
1728
|
options: normalizeOptions,
|
|
1515
1729
|
reboot: "at system startup",
|
|
1516
|
-
|
|
1730
|
+
// A description ending in an abbreviation already carries its period
|
|
1731
|
+
// ("…9 a.m."), so closing the sentence must not double it.
|
|
1732
|
+
sentence: (description) => "Runs " + description + (description.endsWith(".") ? "" : ".")
|
|
1517
1733
|
};
|
|
1518
1734
|
var en_default = en;
|
|
1519
1735
|
|