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.cjs
CHANGED
|
@@ -95,6 +95,21 @@ function isNonNegativeInteger(value) {
|
|
|
95
95
|
const digits = /^\d+$/;
|
|
96
96
|
return digits.test(value);
|
|
97
97
|
}
|
|
98
|
+
function arithmeticStep(values) {
|
|
99
|
+
if (values.length < 5) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
const interval = values[1] - values[0];
|
|
103
|
+
if (interval < 2) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
for (let i = 2; i < values.length; i += 1) {
|
|
107
|
+
if (values[i] - values[i - 1] !== interval) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return { start: values[0], interval, last: values[values.length - 1] };
|
|
112
|
+
}
|
|
98
113
|
function toFieldNumber(token, numberMap) {
|
|
99
114
|
return isNonNegativeInteger(token) ? +token : numberMap[token.toUpperCase()];
|
|
100
115
|
}
|
|
@@ -226,14 +241,20 @@ function normalizeField(value, field, spec) {
|
|
|
226
241
|
}
|
|
227
242
|
const cycle = timeFieldCycle[field];
|
|
228
243
|
const segments = stringValue.split(",").map(function canonical(segment) {
|
|
229
|
-
return
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
244
|
+
return canonicalizeTokens(collapseFullSpanRange(
|
|
245
|
+
enumerateNonUniformStep(
|
|
246
|
+
collapseFullSpanStep(
|
|
247
|
+
collapseDegenerateRange(
|
|
248
|
+
collapseOnceStep(collapseUnitStep(segment, spec), spec),
|
|
249
|
+
spec
|
|
250
|
+
),
|
|
251
|
+
spec
|
|
252
|
+
),
|
|
253
|
+
spec,
|
|
254
|
+
cycle
|
|
233
255
|
),
|
|
234
|
-
spec
|
|
235
|
-
|
|
236
|
-
);
|
|
256
|
+
spec
|
|
257
|
+
), spec);
|
|
237
258
|
}).join(",").split(",");
|
|
238
259
|
if (segments.indexOf("*") !== -1) {
|
|
239
260
|
return "*";
|
|
@@ -242,6 +263,23 @@ function normalizeField(value, field, spec) {
|
|
|
242
263
|
return firstFire(a, spec) - firstFire(b, spec);
|
|
243
264
|
}).join(",");
|
|
244
265
|
}
|
|
266
|
+
function canonicalizeTokens(segment, spec) {
|
|
267
|
+
if (!spec.numbers) {
|
|
268
|
+
return segment;
|
|
269
|
+
}
|
|
270
|
+
const parts = segment.split("/");
|
|
271
|
+
const start = parts[0].split("-").map(function fold(token) {
|
|
272
|
+
return canonicalizeToken(token, spec);
|
|
273
|
+
}).join("-");
|
|
274
|
+
return parts.length === 2 ? start + "/" + parts[1] : start;
|
|
275
|
+
}
|
|
276
|
+
function canonicalizeToken(token, spec) {
|
|
277
|
+
if (token === "*") {
|
|
278
|
+
return token;
|
|
279
|
+
}
|
|
280
|
+
const number = toFieldNumber(token, spec.numbers);
|
|
281
|
+
return "" + (number > spec.top ? spec.min : number);
|
|
282
|
+
}
|
|
245
283
|
function collapseUnitStep(segment, spec) {
|
|
246
284
|
const parts = segment.split("/");
|
|
247
285
|
if (!spec.cyclic || parts.length !== 2 || +parts[1] !== 1) {
|
|
@@ -284,6 +322,35 @@ function enumerateNonUniformStep(segment, spec, cycle) {
|
|
|
284
322
|
}
|
|
285
323
|
return fires.join(",");
|
|
286
324
|
}
|
|
325
|
+
function collapseFullSpanStep(segment, spec) {
|
|
326
|
+
const parts = segment.split("/");
|
|
327
|
+
if (parts.length !== 2 || !includes(parts[0], "-")) {
|
|
328
|
+
return segment;
|
|
329
|
+
}
|
|
330
|
+
return collapseFullSpanRange(parts[0], spec) === "*" ? "*/" + parts[1] : segment;
|
|
331
|
+
}
|
|
332
|
+
function collapseFullSpanRange(segment, spec) {
|
|
333
|
+
if (typeof spec.top !== "number" || includes(segment, "/") || !includes(segment, "-")) {
|
|
334
|
+
return segment;
|
|
335
|
+
}
|
|
336
|
+
const bounds = segment.split("-");
|
|
337
|
+
const low = toFieldNumber(bounds[0], spec.numbers);
|
|
338
|
+
const high = toFieldNumber(bounds[1], spec.numbers);
|
|
339
|
+
if (low > high) {
|
|
340
|
+
return segment;
|
|
341
|
+
}
|
|
342
|
+
const top = spec.top;
|
|
343
|
+
const fired = {};
|
|
344
|
+
for (let value = low; value <= high; value += 1) {
|
|
345
|
+
fired[value > top ? spec.min : value] = true;
|
|
346
|
+
}
|
|
347
|
+
for (let value = spec.min; value <= top; value += 1) {
|
|
348
|
+
if (!fired[value]) {
|
|
349
|
+
return segment;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return "*";
|
|
353
|
+
}
|
|
287
354
|
function collapseDegenerateRange(segment, spec) {
|
|
288
355
|
const start = segment.split("/")[0];
|
|
289
356
|
if (!includes(start, "-")) {
|
|
@@ -573,7 +640,7 @@ function planStandaloneSeconds(pattern, shapes) {
|
|
|
573
640
|
}
|
|
574
641
|
return { kind: "standaloneSeconds" };
|
|
575
642
|
}
|
|
576
|
-
function planMinutes(pattern, shapes, analyses,
|
|
643
|
+
function planMinutes(pattern, shapes, analyses, subMinuteSecond2 = false) {
|
|
577
644
|
if (shapes.minute === "step") {
|
|
578
645
|
return {
|
|
579
646
|
hours: planFrequencyHours(pattern, shapes, analyses),
|
|
@@ -596,7 +663,7 @@ function planMinutes(pattern, shapes, analyses, subMinuteSecond = false) {
|
|
|
596
663
|
return underStep;
|
|
597
664
|
}
|
|
598
665
|
if (pattern.hour === "*") {
|
|
599
|
-
return planMinutesUnderOpenHour(pattern, shapes,
|
|
666
|
+
return planMinutesUnderOpenHour(pattern, shapes, subMinuteSecond2);
|
|
600
667
|
}
|
|
601
668
|
}
|
|
602
669
|
function cleanHourStride(hourField) {
|
|
@@ -618,6 +685,9 @@ function planMinuteUnderHourStep(pattern, shapes) {
|
|
|
618
685
|
if (shapes.minute === "range") {
|
|
619
686
|
return { form: "range", kind: "minuteSpanAcrossHourStep" };
|
|
620
687
|
}
|
|
688
|
+
if (shapes.minute === "list" && cleanHourStride(pattern.hour)) {
|
|
689
|
+
return { form: "list", kind: "minuteSpanAcrossHourStep" };
|
|
690
|
+
}
|
|
621
691
|
return null;
|
|
622
692
|
}
|
|
623
693
|
function planFrequencyHours(pattern, shapes, analyses) {
|
|
@@ -666,7 +736,7 @@ function planMinutesAcrossHours(pattern, shapes) {
|
|
|
666
736
|
}
|
|
667
737
|
return null;
|
|
668
738
|
}
|
|
669
|
-
function planMinutesUnderOpenHour(pattern, shapes,
|
|
739
|
+
function planMinutesUnderOpenHour(pattern, shapes, subMinuteSecond2) {
|
|
670
740
|
if (shapes.minute === "range") {
|
|
671
741
|
return { kind: "rangeOfMinutes" };
|
|
672
742
|
}
|
|
@@ -676,16 +746,16 @@ function planMinutesUnderOpenHour(pattern, shapes, subMinuteSecond) {
|
|
|
676
746
|
if (pattern.minute === "*") {
|
|
677
747
|
return { kind: "everyMinute" };
|
|
678
748
|
}
|
|
679
|
-
if (pattern.minute !== "0" ||
|
|
749
|
+
if (pattern.minute !== "0" || subMinuteSecond2) {
|
|
680
750
|
return { kind: "singleMinute" };
|
|
681
751
|
}
|
|
682
752
|
}
|
|
683
|
-
function planHours(pattern, shapes, analyses,
|
|
684
|
-
const absorbsMinuteZero =
|
|
753
|
+
function planHours(pattern, shapes, analyses, subMinuteSecond2 = false) {
|
|
754
|
+
const absorbsMinuteZero = subMinuteSecond2 && pattern.minute === "0";
|
|
685
755
|
if (shapes.hour === "range" && !absorbsMinuteZero) {
|
|
686
756
|
return planHourRange(pattern, shapes, analyses);
|
|
687
757
|
}
|
|
688
|
-
if (shapes.hour === "step" && pattern.minute === "0" && !
|
|
758
|
+
if (shapes.hour === "step" && pattern.minute === "0" && !subMinuteSecond2) {
|
|
689
759
|
return { kind: "hourStep" };
|
|
690
760
|
}
|
|
691
761
|
if (pattern.hour === "*" && !absorbsMinuteZero) {
|
|
@@ -852,20 +922,6 @@ var weekdayNames = [
|
|
|
852
922
|
["Friday", "Fri"],
|
|
853
923
|
["Saturday", "Sat"]
|
|
854
924
|
];
|
|
855
|
-
var monthAbbreviations = {
|
|
856
|
-
JAN: monthNames[1],
|
|
857
|
-
FEB: monthNames[2],
|
|
858
|
-
MAR: monthNames[3],
|
|
859
|
-
APR: monthNames[4],
|
|
860
|
-
MAY: monthNames[5],
|
|
861
|
-
JUN: monthNames[6],
|
|
862
|
-
JUL: monthNames[7],
|
|
863
|
-
AUG: monthNames[8],
|
|
864
|
-
SEP: monthNames[9],
|
|
865
|
-
OCT: monthNames[10],
|
|
866
|
-
NOV: monthNames[11],
|
|
867
|
-
DEC: monthNames[12]
|
|
868
|
-
};
|
|
869
925
|
var weekdayAbbreviations = {
|
|
870
926
|
SUN: weekdayNames[0],
|
|
871
927
|
MON: weekdayNames[1],
|
|
@@ -914,10 +970,50 @@ function renderSecondsWithinMinute(ir, plan, opts) {
|
|
|
914
970
|
}
|
|
915
971
|
return secondsLeadClause(ir, opts) + ", " + minuteWord + " " + minuteUnit + " past the hour, every hour" + trailingQualifier(ir, opts);
|
|
916
972
|
}
|
|
973
|
+
function composeHourCadence(ir, plan, opts) {
|
|
974
|
+
const clockRest = plan.rest.kind === "clockTimes" || plan.rest.kind === "compactClockTimes";
|
|
975
|
+
return clockRest && ir.shapes.minute === "single" ? hourCadence(ir, +ir.pattern.minute, opts) : null;
|
|
976
|
+
}
|
|
917
977
|
function renderComposeSeconds(ir, plan, opts) {
|
|
978
|
+
const cadence = composeHourCadence(ir, plan, opts);
|
|
979
|
+
if (cadence !== null) {
|
|
980
|
+
return cadence;
|
|
981
|
+
}
|
|
982
|
+
if (plan.rest.kind === "clockTimes" && (ir.shapes.second === "wildcard" || ir.shapes.second === "step")) {
|
|
983
|
+
const minute = plan.rest.times[0].minute;
|
|
984
|
+
if (+minute === 0) {
|
|
985
|
+
return secondsLeadClause(ir, opts) + " for one minute at " + durationHours(ir, plan.rest, opts);
|
|
986
|
+
}
|
|
987
|
+
return secondsLeadClause(ir, opts) + " of " + clockTimesOf(ir, plan.rest, opts);
|
|
988
|
+
}
|
|
989
|
+
if (ir.shapes.second === "wildcard" && plan.rest.kind === "minuteFrequency" && plan.rest.hours.kind === "none" && ir.pattern.minute === "*/2") {
|
|
990
|
+
return "every second of every other minute" + trailingQualifier(ir, opts);
|
|
991
|
+
}
|
|
918
992
|
return secondsLeadClause(ir, opts) + ", " + render(ir, plan.rest, opts);
|
|
919
993
|
}
|
|
994
|
+
function durationHours(ir, plan, opts) {
|
|
995
|
+
const hours = plan.times.map(function clock(time) {
|
|
996
|
+
return getTime({ hour: time.hour, minute: 0 }, opts);
|
|
997
|
+
});
|
|
998
|
+
const trail = dayQualifier(ir, leadingWords, opts);
|
|
999
|
+
return joinList(hours, opts) + (trail && ", " + trail);
|
|
1000
|
+
}
|
|
1001
|
+
function clockTimesOf(ir, plan, opts) {
|
|
1002
|
+
const times = plan.times.map(function clock(time) {
|
|
1003
|
+
return getTime({
|
|
1004
|
+
hour: time.hour,
|
|
1005
|
+
minute: time.minute,
|
|
1006
|
+
second: time.second,
|
|
1007
|
+
explicit: true
|
|
1008
|
+
}, opts);
|
|
1009
|
+
});
|
|
1010
|
+
const trail = dayQualifier(ir, leadingWords, opts);
|
|
1011
|
+
return joinList(times, opts) + (trail && ", " + trail);
|
|
1012
|
+
}
|
|
920
1013
|
function secondsLeadClause(ir, opts) {
|
|
1014
|
+
return secondsClause(ir, "minute", opts);
|
|
1015
|
+
}
|
|
1016
|
+
function secondsClause(ir, anchor, opts) {
|
|
921
1017
|
const secondField = ir.pattern.second;
|
|
922
1018
|
const shape = ir.shapes.second;
|
|
923
1019
|
if (secondField === "*") {
|
|
@@ -927,22 +1023,27 @@ function secondsLeadClause(ir, opts) {
|
|
|
927
1023
|
return stepCycle60(
|
|
928
1024
|
ir.analyses.segments.second[0],
|
|
929
1025
|
"second",
|
|
930
|
-
|
|
1026
|
+
anchor,
|
|
931
1027
|
opts
|
|
932
1028
|
);
|
|
933
1029
|
}
|
|
934
1030
|
if (shape === "range") {
|
|
935
1031
|
const bounds = secondField.split("-");
|
|
936
1032
|
const num = seriesNumber(bounds, opts);
|
|
937
|
-
return "every second from " + num(bounds[0]) + through(opts) + num(bounds[1]) + " past the
|
|
1033
|
+
return "every second from " + num(bounds[0]) + through(opts) + num(bounds[1]) + " past the " + anchor;
|
|
938
1034
|
}
|
|
939
1035
|
if (shape === "single") {
|
|
940
|
-
return "at " + getNumber(secondField, opts) + " " + pluralize(secondField, "second") + " past the
|
|
1036
|
+
return "at " + getNumber(secondField, opts) + " " + pluralize(secondField, "second") + " past the " + anchor;
|
|
941
1037
|
}
|
|
942
|
-
return
|
|
1038
|
+
return strideFromSegments(
|
|
1039
|
+
ir.analyses.segments.second,
|
|
1040
|
+
"second",
|
|
1041
|
+
anchor,
|
|
1042
|
+
opts
|
|
1043
|
+
) ?? listPastThe(
|
|
943
1044
|
segmentWords(ir.analyses.segments.second, opts),
|
|
944
1045
|
"second",
|
|
945
|
-
|
|
1046
|
+
anchor,
|
|
946
1047
|
opts
|
|
947
1048
|
);
|
|
948
1049
|
}
|
|
@@ -957,12 +1058,11 @@ function renderRangeOfMinutes(ir, plan, opts) {
|
|
|
957
1058
|
return minuteRangeLead(ir.pattern.minute, opts) + trailingQualifier(ir, opts);
|
|
958
1059
|
}
|
|
959
1060
|
function renderMultipleMinutes(ir, plan, opts) {
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
"hour",
|
|
1061
|
+
const stride = strideFromSegments(ir.analyses.segments.minute, "minute", "hour", opts);
|
|
1062
|
+
return (stride ?? listPastThe(segmentWords(
|
|
1063
|
+
ir.analyses.segments.minute,
|
|
964
1064
|
opts
|
|
965
|
-
) + trailingQualifier(ir, opts);
|
|
1065
|
+
), "minute", "hour", opts)) + trailingQualifier(ir, opts);
|
|
966
1066
|
}
|
|
967
1067
|
function renderMinuteFrequency(ir, plan, opts) {
|
|
968
1068
|
let phrase = stepCycle60(
|
|
@@ -981,6 +1081,9 @@ function renderMinuteFrequency(ir, plan, opts) {
|
|
|
981
1081
|
return phrase + trailingQualifier(ir, opts);
|
|
982
1082
|
}
|
|
983
1083
|
function renderMinuteSpanInHour(ir, plan, opts) {
|
|
1084
|
+
if (ir.pattern.minute === "*") {
|
|
1085
|
+
return "every minute of the " + getTime({ hour: plan.hour, minute: 0 }, opts) + " hour" + trailingQualifier(ir, opts);
|
|
1086
|
+
}
|
|
984
1087
|
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);
|
|
985
1088
|
}
|
|
986
1089
|
function renderMinutesAcrossHours(ir, plan, opts) {
|
|
@@ -989,8 +1092,9 @@ function renderMinutesAcrossHours(ir, plan, opts) {
|
|
|
989
1092
|
}
|
|
990
1093
|
const times = hourTimesFromPlan(ir, plan.times, true, opts);
|
|
991
1094
|
const lead = plan.form === "range" ? minuteRangeLead(ir.pattern.minute, opts) : (
|
|
992
|
-
// The 'list' form is a minute list, which has segments
|
|
993
|
-
|
|
1095
|
+
// The 'list' form is a minute list, which has segments; an offset/uneven
|
|
1096
|
+
// step enumerated to that list reads as a stride.
|
|
1097
|
+
strideFromSegments(ir.analyses.segments.minute, "minute", "hour", opts) ?? listPastThe(
|
|
994
1098
|
segmentWords(ir.analyses.segments.minute, opts),
|
|
995
1099
|
"minute",
|
|
996
1100
|
"hour",
|
|
@@ -1017,7 +1121,13 @@ function renderMinuteSpanAcrossHourStep(ir, plan, opts) {
|
|
|
1017
1121
|
if (plan.form === "wildcard") {
|
|
1018
1122
|
return "every minute " + everyNthHour(segment, opts) + trailingQualifier(ir, opts);
|
|
1019
1123
|
}
|
|
1020
|
-
|
|
1124
|
+
const lead = plan.form === "list" ? strideFromSegments(ir.analyses.segments.minute, "minute", "hour", opts) ?? listPastThe(
|
|
1125
|
+
segmentWords(ir.analyses.segments.minute, opts),
|
|
1126
|
+
"minute",
|
|
1127
|
+
"hour",
|
|
1128
|
+
opts
|
|
1129
|
+
) : minuteRangeLead(ir.pattern.minute, opts);
|
|
1130
|
+
return lead + ", " + stepHours(segment, opts) + trailingQualifier(ir, opts);
|
|
1021
1131
|
}
|
|
1022
1132
|
function minuteRangeLead(minuteField, opts) {
|
|
1023
1133
|
const bounds = minuteField.split("-");
|
|
@@ -1041,7 +1151,12 @@ function rangeMinuteLead(ir, opts) {
|
|
|
1041
1151
|
if (ir.pattern.minute === "0") {
|
|
1042
1152
|
return "every hour";
|
|
1043
1153
|
}
|
|
1044
|
-
return
|
|
1154
|
+
return strideFromSegments(
|
|
1155
|
+
ir.analyses.segments.minute,
|
|
1156
|
+
"minute",
|
|
1157
|
+
"hour",
|
|
1158
|
+
opts
|
|
1159
|
+
) ?? listPastThe(
|
|
1045
1160
|
segmentWords(ir.analyses.segments.minute, opts),
|
|
1046
1161
|
"minute",
|
|
1047
1162
|
"hour",
|
|
@@ -1058,6 +1173,12 @@ function hourWindow(window, opts) {
|
|
|
1058
1173
|
return "from " + getTime({ hour: window.from, minute: 0 }, opts) + through(opts) + getTime({ hour: window.to, minute: window.last }, opts);
|
|
1059
1174
|
}
|
|
1060
1175
|
function renderClockTimes(ir, plan, opts) {
|
|
1176
|
+
if (ir.shapes.minute === "single") {
|
|
1177
|
+
const cadence = hourCadence(ir, +ir.pattern.minute, opts);
|
|
1178
|
+
if (cadence !== null) {
|
|
1179
|
+
return cadence;
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1061
1182
|
const plain = mixedTwelve(plan.times);
|
|
1062
1183
|
const times = plan.times.map(function clock(time) {
|
|
1063
1184
|
return getTime({
|
|
@@ -1071,6 +1192,10 @@ function renderClockTimes(ir, plan, opts) {
|
|
|
1071
1192
|
}
|
|
1072
1193
|
function renderCompactClockTimes(ir, plan, opts) {
|
|
1073
1194
|
if (plan.fold) {
|
|
1195
|
+
const cadence = hourCadence(ir, +plan.minute, opts);
|
|
1196
|
+
if (cadence !== null) {
|
|
1197
|
+
return cadence;
|
|
1198
|
+
}
|
|
1074
1199
|
const hasRange = ir.analyses.segments.hour.some(function range(segment) {
|
|
1075
1200
|
return segment.kind === "range";
|
|
1076
1201
|
});
|
|
@@ -1081,13 +1206,14 @@ function renderCompactClockTimes(ir, plan, opts) {
|
|
|
1081
1206
|
return interpretDayQualifier(ir, opts) + "at " + hourSegmentTimes(ir, fold, true, opts);
|
|
1082
1207
|
}
|
|
1083
1208
|
const phrase = (
|
|
1084
|
-
// The non-fold branch is a minute list, which has segments.
|
|
1085
|
-
|
|
1209
|
+
// The non-fold branch is a minute list, which has segments. An
|
|
1210
|
+
// offset/uneven step enumerated to that list reads as a stride.
|
|
1211
|
+
(strideFromSegments(ir.analyses.segments.minute, "minute", "hour", opts) ?? listPastThe(
|
|
1086
1212
|
segmentWords(ir.analyses.segments.minute, opts),
|
|
1087
1213
|
"minute",
|
|
1088
1214
|
"hour",
|
|
1089
1215
|
opts
|
|
1090
|
-
) + ", at " + hourSegmentTimes(ir, { minute: 0, second: null }, true, opts) + trailingQualifier(ir, opts)
|
|
1216
|
+
)) + ", at " + hourSegmentTimes(ir, { minute: 0, second: null }, true, opts) + trailingQualifier(ir, opts)
|
|
1091
1217
|
);
|
|
1092
1218
|
return ir.analyses.clockSecond ? secondsLeadClause(ir, opts) + ", " + phrase : phrase;
|
|
1093
1219
|
}
|
|
@@ -1135,24 +1261,50 @@ var renderers = {
|
|
|
1135
1261
|
singleMinute: renderSingleMinute,
|
|
1136
1262
|
standaloneSeconds: renderStandaloneSeconds
|
|
1137
1263
|
};
|
|
1264
|
+
function renderStride(stride, opts) {
|
|
1265
|
+
const { interval, start, last, cycle, unit, anchor } = stride;
|
|
1266
|
+
const cadence = "every " + getNumber(interval, opts) + " " + unit + "s";
|
|
1267
|
+
const tiles = cycle % interval === 0;
|
|
1268
|
+
if (start === 0 && tiles) {
|
|
1269
|
+
return cadence;
|
|
1270
|
+
}
|
|
1271
|
+
if (start < interval && tiles) {
|
|
1272
|
+
return cadence + " from " + getNumber(start, opts) + " " + pluralize(start, unit) + " past the " + anchor;
|
|
1273
|
+
}
|
|
1274
|
+
const num = seriesNumber([start, last], opts);
|
|
1275
|
+
return cadence + " from " + num(start) + through(opts) + num(last) + " " + pluralize(last, unit) + " past the " + anchor;
|
|
1276
|
+
}
|
|
1277
|
+
function singleValues(segments) {
|
|
1278
|
+
const values = [];
|
|
1279
|
+
for (const segment of segments) {
|
|
1280
|
+
if (segment.kind !== "single") {
|
|
1281
|
+
return null;
|
|
1282
|
+
}
|
|
1283
|
+
values.push(+segment.value);
|
|
1284
|
+
}
|
|
1285
|
+
return values;
|
|
1286
|
+
}
|
|
1287
|
+
function strideFromSegments(segments, unit, anchor, opts) {
|
|
1288
|
+
const values = singleValues(segments);
|
|
1289
|
+
const step = values && arithmeticStep(values);
|
|
1290
|
+
return step ? renderStride({ ...step, cycle: 60, unit, anchor }, opts) : null;
|
|
1291
|
+
}
|
|
1138
1292
|
function stepCycle60(segment, unit, anchor, opts) {
|
|
1139
1293
|
if (segment.startToken.indexOf("-") !== -1) {
|
|
1140
1294
|
return listPastThe(numberWords(segment.fires, opts), unit, anchor, opts);
|
|
1141
1295
|
}
|
|
1142
1296
|
const start = segment.startToken === "*" ? 0 : +segment.startToken;
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
if (segment.fires.length <= 3) {
|
|
1146
|
-
return listPastThe(
|
|
1147
|
-
numberWords(segment.fires, opts),
|
|
1148
|
-
unit,
|
|
1149
|
-
anchor,
|
|
1150
|
-
opts
|
|
1151
|
-
);
|
|
1152
|
-
}
|
|
1153
|
-
return "every " + getNumber(interval, opts) + " " + unit + "s from " + getNumber(start, opts) + " " + pluralize(start, unit) + " past the " + anchor;
|
|
1297
|
+
if (start !== 0 && segment.fires.length <= 3) {
|
|
1298
|
+
return listPastThe(numberWords(segment.fires, opts), unit, anchor, opts);
|
|
1154
1299
|
}
|
|
1155
|
-
return
|
|
1300
|
+
return renderStride({
|
|
1301
|
+
interval: segment.interval,
|
|
1302
|
+
start,
|
|
1303
|
+
last: segment.fires[segment.fires.length - 1],
|
|
1304
|
+
cycle: 60,
|
|
1305
|
+
unit,
|
|
1306
|
+
anchor
|
|
1307
|
+
}, opts);
|
|
1156
1308
|
}
|
|
1157
1309
|
function stepHours(segment, opts) {
|
|
1158
1310
|
if (segment.startToken.indexOf("-") !== -1) {
|
|
@@ -1168,6 +1320,68 @@ function stepHours(segment, opts) {
|
|
|
1168
1320
|
}
|
|
1169
1321
|
return "every " + getNumber(interval, opts) + " hours from " + getTime({ hour: start, minute: 0 }, opts);
|
|
1170
1322
|
}
|
|
1323
|
+
function hourStrideCadence(stride, opts) {
|
|
1324
|
+
const { start, interval, last } = stride;
|
|
1325
|
+
const cadence = "every " + getNumber(interval, opts) + " hours";
|
|
1326
|
+
const tiles = 24 % interval === 0;
|
|
1327
|
+
if (start === 0 && tiles) {
|
|
1328
|
+
return cadence;
|
|
1329
|
+
}
|
|
1330
|
+
if (start < interval && tiles) {
|
|
1331
|
+
return cadence + " from " + getTime({ hour: start, minute: 0 }, opts);
|
|
1332
|
+
}
|
|
1333
|
+
return cadence + " from " + getTime({ hour: start, minute: 0 }, opts) + through(opts) + getTime({ hour: last, minute: 0 }, opts);
|
|
1334
|
+
}
|
|
1335
|
+
function hourStride(ir) {
|
|
1336
|
+
const segments = ir.analyses.segments.hour;
|
|
1337
|
+
if (segments.length === 1 && segments[0].kind === "step") {
|
|
1338
|
+
const segment = segments[0];
|
|
1339
|
+
const start = segment.startToken === "*" ? 0 : +segment.startToken.split("-")[0];
|
|
1340
|
+
return { interval: segment.interval, last: segment.fires[segment.fires.length - 1], start };
|
|
1341
|
+
}
|
|
1342
|
+
const values = singleValues(segments);
|
|
1343
|
+
const step = values && arithmeticStep(values);
|
|
1344
|
+
return step || null;
|
|
1345
|
+
}
|
|
1346
|
+
function subMinuteSecond(ir) {
|
|
1347
|
+
return ir.pattern.second === "*" || ir.shapes.second === "step";
|
|
1348
|
+
}
|
|
1349
|
+
function hourCadenceLead(ir, minute, opts) {
|
|
1350
|
+
if (minute === 0) {
|
|
1351
|
+
if (subMinuteSecond(ir)) {
|
|
1352
|
+
return secondsClause(ir, "minute", opts) + " for one minute";
|
|
1353
|
+
}
|
|
1354
|
+
return secondsClause(ir, "hour", opts);
|
|
1355
|
+
}
|
|
1356
|
+
const minutePhrase = getNumber(minute, opts) + " " + pluralize(minute, "minute") + " past the hour";
|
|
1357
|
+
if (ir.pattern.second === "0") {
|
|
1358
|
+
return minutePhrase;
|
|
1359
|
+
}
|
|
1360
|
+
return secondsClause(ir, "minute", opts) + ", " + minutePhrase;
|
|
1361
|
+
}
|
|
1362
|
+
function hourCadence(ir, minute, opts) {
|
|
1363
|
+
const stride = hourStride(ir);
|
|
1364
|
+
if (!stride) {
|
|
1365
|
+
return null;
|
|
1366
|
+
}
|
|
1367
|
+
const fires = (stride.last - stride.start) / stride.interval + 1;
|
|
1368
|
+
if (ir.pattern.second === "0" && fires <= maxClockTimes) {
|
|
1369
|
+
return null;
|
|
1370
|
+
}
|
|
1371
|
+
const confinement = minute === 0 && subMinuteSecond(ir) && cleanStrideSegment(ir);
|
|
1372
|
+
if (confinement) {
|
|
1373
|
+
return secondsClause(ir, "minute", opts) + " for one minute " + everyNthHour(confinement, opts) + trailingQualifier(ir, opts);
|
|
1374
|
+
}
|
|
1375
|
+
return hourCadenceLead(ir, minute, opts) + ", " + hourStrideCadence(stride, opts) + trailingQualifier(ir, opts);
|
|
1376
|
+
}
|
|
1377
|
+
function cleanStrideSegment(ir) {
|
|
1378
|
+
const segments = ir.analyses.segments.hour;
|
|
1379
|
+
const segment = segments.length === 1 && segments[0];
|
|
1380
|
+
if (!segment || segment.kind !== "step" || segment.startToken.indexOf("-") !== -1 || !(segment.interval in stepOrdinals)) {
|
|
1381
|
+
return null;
|
|
1382
|
+
}
|
|
1383
|
+
return segment;
|
|
1384
|
+
}
|
|
1171
1385
|
function seriesNumber(values, opts) {
|
|
1172
1386
|
const anyBig = values.some(function big(v) {
|
|
1173
1387
|
return +v > 10;
|
|
@@ -1479,7 +1693,7 @@ function stepYears(yearField, opts) {
|
|
|
1479
1693
|
return phrase;
|
|
1480
1694
|
}
|
|
1481
1695
|
function getTime(time, opts) {
|
|
1482
|
-
const { hour, minute, plain } = time;
|
|
1696
|
+
const { hour, minute, plain, explicit } = time;
|
|
1483
1697
|
const second = typeof time.second === "number" && time.second > 0 ? time.second : 0;
|
|
1484
1698
|
if (!opts.ampm) {
|
|
1485
1699
|
return clockDigits({
|
|
@@ -1488,12 +1702,12 @@ function getTime(time, opts) {
|
|
|
1488
1702
|
second
|
|
1489
1703
|
}, { pad: true, sep: opts.style.sep });
|
|
1490
1704
|
}
|
|
1491
|
-
return twelveHourTime({ hour, minute, second, plain }, opts);
|
|
1705
|
+
return twelveHourTime({ hour, minute, second, plain, explicit }, opts);
|
|
1492
1706
|
}
|
|
1493
1707
|
function twelveHourTime(time, opts) {
|
|
1494
|
-
const { hour, minute, second, plain } = time;
|
|
1708
|
+
const { hour, minute, second, plain, explicit } = time;
|
|
1495
1709
|
const style = opts.style;
|
|
1496
|
-
if (!plain && +minute === 0 && !second) {
|
|
1710
|
+
if (!plain && !explicit && +minute === 0 && !second) {
|
|
1497
1711
|
if (+hour === 0) {
|
|
1498
1712
|
return style.midnight;
|
|
1499
1713
|
}
|
|
@@ -1503,7 +1717,7 @@ function twelveHourTime(time, opts) {
|
|
|
1503
1717
|
}
|
|
1504
1718
|
const digits = clockDigits(
|
|
1505
1719
|
{ hour: hour % 12 || 12, minute, second },
|
|
1506
|
-
{ lean:
|
|
1720
|
+
{ lean: !explicit, sep: style.sep }
|
|
1507
1721
|
);
|
|
1508
1722
|
return digits + (style.closeUp ? "" : " ") + (hour < 12 ? style.am : style.pm);
|
|
1509
1723
|
}
|
|
@@ -1526,7 +1740,7 @@ function getOrdinal(n) {
|
|
|
1526
1740
|
return n + suffix;
|
|
1527
1741
|
}
|
|
1528
1742
|
function getMonth(m, opts) {
|
|
1529
|
-
const month = monthNames[m]
|
|
1743
|
+
const month = monthNames[+m];
|
|
1530
1744
|
return month && month[opts.short ? 1 : 0];
|
|
1531
1745
|
}
|
|
1532
1746
|
function getWeekday(d, opts) {
|
|
@@ -1539,7 +1753,9 @@ var en = {
|
|
|
1539
1753
|
fallback: "an unrecognizable cron pattern",
|
|
1540
1754
|
options: normalizeOptions,
|
|
1541
1755
|
reboot: "at system startup",
|
|
1542
|
-
|
|
1756
|
+
// A description ending in an abbreviation already carries its period
|
|
1757
|
+
// ("…9 a.m."), so closing the sentence must not double it.
|
|
1758
|
+
sentence: (description) => "Runs " + description + (description.endsWith(".") ? "" : ".")
|
|
1543
1759
|
};
|
|
1544
1760
|
var en_default = en;
|
|
1545
1761
|
|