cronli5 0.1.4 → 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 +25 -0
- package/cronli5.min.js +2 -2
- package/dist/cronli5.cjs +180 -36
- package/dist/cronli5.js +180 -36
- package/dist/lang/de.cjs +172 -8
- package/dist/lang/de.js +172 -8
- package/dist/lang/en.cjs +175 -29
- package/dist/lang/en.js +175 -29
- package/dist/lang/es.cjs +180 -25
- package/dist/lang/es.js +180 -25
- package/dist/lang/fi.cjs +188 -40
- package/dist/lang/fi.js +188 -40
- package/dist/lang/zh.cjs +165 -19
- package/dist/lang/zh.js +165 -19
- package/package.json +2 -1
- package/src/core/analyze.ts +7 -0
- package/src/core/ir.ts +1 -1
- package/src/core/util.ts +31 -1
- package/src/lang/de/index.ts +360 -16
- package/src/lang/en/index.ts +333 -33
- package/src/lang/es/index.ts +373 -40
- package/src/lang/fi/index.ts +404 -72
- package/src/lang/zh/index.ts +327 -35
- package/types/core/ir.d.ts +1 -1
- package/types/core/util.d.ts +6 -1
package/dist/lang/de.js
CHANGED
|
@@ -14,12 +14,28 @@ var weekdayNumbers = {
|
|
|
14
14
|
FRI: 5,
|
|
15
15
|
SAT: 6
|
|
16
16
|
};
|
|
17
|
+
var maxClockTimes = 6;
|
|
17
18
|
|
|
18
19
|
// src/core/util.ts
|
|
19
20
|
function isNonNegativeInteger(value) {
|
|
20
21
|
const digits = /^\d+$/;
|
|
21
22
|
return digits.test(value);
|
|
22
23
|
}
|
|
24
|
+
function arithmeticStep(values) {
|
|
25
|
+
if (values.length < 5) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
const interval = values[1] - values[0];
|
|
29
|
+
if (interval < 2) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
for (let i = 2; i < values.length; i += 1) {
|
|
33
|
+
if (values[i] - values[i - 1] !== interval) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return { start: values[0], interval, last: values[values.length - 1] };
|
|
38
|
+
}
|
|
23
39
|
function toFieldNumber(token, numberMap) {
|
|
24
40
|
return isNonNegativeInteger(token) ? +token : numberMap[token.toUpperCase()];
|
|
25
41
|
}
|
|
@@ -86,6 +102,49 @@ function stepSegment(segments) {
|
|
|
86
102
|
function cleanStep(segment, cycle) {
|
|
87
103
|
return (segment.startToken === "*" || +segment.startToken === 0) && cycle % segment.interval === 0;
|
|
88
104
|
}
|
|
105
|
+
function renderStride(stride) {
|
|
106
|
+
const { interval, start, last, cycle, unit, anchor } = stride;
|
|
107
|
+
const cadence = everyN(interval, unit);
|
|
108
|
+
const tiles = cycle % interval === 0;
|
|
109
|
+
if (start === 0 && tiles) {
|
|
110
|
+
return cadence;
|
|
111
|
+
}
|
|
112
|
+
const tail = anchor ? " " + anchor : "";
|
|
113
|
+
if (start < interval && tiles) {
|
|
114
|
+
return cadence + " ab " + unit.singular + " " + start + tail;
|
|
115
|
+
}
|
|
116
|
+
return cadence + " von " + unit.singular + " " + start + " bis " + last + tail;
|
|
117
|
+
}
|
|
118
|
+
function stepClause(segment, unit, anchor) {
|
|
119
|
+
const start = segment.startToken === "*" ? 0 : +segment.startToken;
|
|
120
|
+
const short = start !== 0 && segment.fires.length <= 3;
|
|
121
|
+
if (segment.startToken.indexOf("-") !== -1 || short) {
|
|
122
|
+
return "in den " + unit.plural + " " + joinList(segment.fires.map(String)) + " " + anchor;
|
|
123
|
+
}
|
|
124
|
+
return renderStride({
|
|
125
|
+
interval: segment.interval,
|
|
126
|
+
start,
|
|
127
|
+
last: segment.fires[segment.fires.length - 1],
|
|
128
|
+
cycle: 60,
|
|
129
|
+
unit,
|
|
130
|
+
anchor
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
function singleValues(segments) {
|
|
134
|
+
const values = [];
|
|
135
|
+
for (const segment of segments) {
|
|
136
|
+
if (segment.kind !== "single") {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
values.push(+segment.value);
|
|
140
|
+
}
|
|
141
|
+
return values;
|
|
142
|
+
}
|
|
143
|
+
function strideFromSegments(segments, unit, anchor) {
|
|
144
|
+
const values = singleValues(segments);
|
|
145
|
+
const step = values && arithmeticStep(values);
|
|
146
|
+
return step ? renderStride({ ...step, cycle: 60, unit, anchor }) : null;
|
|
147
|
+
}
|
|
89
148
|
var weekdayNames = [
|
|
90
149
|
"sonntags",
|
|
91
150
|
"montags",
|
|
@@ -152,6 +211,13 @@ function everyNthHour(segment) {
|
|
|
152
211
|
const start = segment.startToken === "*" ? 0 : +segment.startToken;
|
|
153
212
|
return start === 0 ? base : base + " ab " + start + " Uhr";
|
|
154
213
|
}
|
|
214
|
+
function confinedHourStride(segment) {
|
|
215
|
+
if (segment.startToken.indexOf("-") !== -1) {
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
const start = segment.startToken === "*" ? 0 : +segment.startToken;
|
|
219
|
+
return 24 % segment.interval === 0 && start < segment.interval;
|
|
220
|
+
}
|
|
155
221
|
function weekdayNoun(token) {
|
|
156
222
|
return weekdayNouns[toFieldNumber(token, weekdayNumbers)];
|
|
157
223
|
}
|
|
@@ -255,14 +321,17 @@ function countedPhrase(ir, field, singular, plural) {
|
|
|
255
321
|
return "in den " + plural + " " + joinList(fieldValues(ir, field));
|
|
256
322
|
}
|
|
257
323
|
function secondsLead(ir) {
|
|
324
|
+
return secondsClause(ir, "jeder Minute");
|
|
325
|
+
}
|
|
326
|
+
function secondsClause(ir, anchor) {
|
|
258
327
|
if (ir.pattern.second === "*") {
|
|
259
328
|
return "jede Sekunde";
|
|
260
329
|
}
|
|
261
330
|
const segments = ir.analyses.segments.second;
|
|
262
|
-
if (ir.shapes.second === "step"
|
|
263
|
-
return
|
|
331
|
+
if (ir.shapes.second === "step") {
|
|
332
|
+
return stepClause(stepSegment(segments), UNITS.second, anchor);
|
|
264
333
|
}
|
|
265
|
-
return countedPhrase(ir, "second", "Sekunde", "Sekunden") + "
|
|
334
|
+
return strideFromSegments(segments, UNITS.second, anchor) ?? countedPhrase(ir, "second", "Sekunde", "Sekunden") + " " + anchor;
|
|
266
335
|
}
|
|
267
336
|
function spanTime(hour, minute, sep) {
|
|
268
337
|
return hour + sep + pad(minute);
|
|
@@ -331,8 +400,15 @@ function renderEveryHour() {
|
|
|
331
400
|
function renderSeconds(ir) {
|
|
332
401
|
return secondsLead(ir);
|
|
333
402
|
}
|
|
403
|
+
function minutePastClause(ir) {
|
|
404
|
+
return strideFromSegments(
|
|
405
|
+
fieldSegments(ir, "minute"),
|
|
406
|
+
UNITS.minute,
|
|
407
|
+
"jeder Stunde"
|
|
408
|
+
) ?? countedPhrase(ir, "minute", "Minute", "Minuten") + " jeder Stunde";
|
|
409
|
+
}
|
|
334
410
|
function renderMinutePast(ir) {
|
|
335
|
-
return
|
|
411
|
+
return minutePastClause(ir);
|
|
336
412
|
}
|
|
337
413
|
function renderSecondsWithinMinute(ir, plan) {
|
|
338
414
|
if (plan.singleSecond) {
|
|
@@ -357,9 +433,21 @@ function renderMinuteSpanInHour(ir, plan, opts) {
|
|
|
357
433
|
return "jede Minute von " + spanTime(plan.hour, plan.span[0], sep) + " bis " + spanTime(plan.hour, plan.span[1], sep) + " Uhr";
|
|
358
434
|
}
|
|
359
435
|
function renderComposeSeconds(ir, plan, opts) {
|
|
436
|
+
if ((plan.rest.kind === "clockTimes" || plan.rest.kind === "compactClockTimes") && ir.shapes.minute === "single") {
|
|
437
|
+
const cadence = hourCadence(ir, +ir.pattern.minute);
|
|
438
|
+
if (cadence !== null) {
|
|
439
|
+
return cadence;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
360
442
|
if (composeMinuteZero(ir, plan)) {
|
|
361
443
|
return secondsLead(ir) + " " + clockMinuteGenitive(plan.rest.times, opts.style.sep);
|
|
362
444
|
}
|
|
445
|
+
if (plan.rest.kind === "minuteFrequency" && ir.shapes.second === "wildcard" && ir.shapes.hour === "wildcard") {
|
|
446
|
+
const minuteStep = stepSegment(ir.analyses.segments.minute);
|
|
447
|
+
if (minuteStep.startToken === "*" && minuteStep.interval === 2) {
|
|
448
|
+
return secondsLead(ir) + " jeder zweiten Minute";
|
|
449
|
+
}
|
|
450
|
+
}
|
|
363
451
|
return secondsLead(ir) + ", " + render(ir, plan.rest, opts);
|
|
364
452
|
}
|
|
365
453
|
function composeMinuteZero(ir, plan) {
|
|
@@ -377,29 +465,35 @@ function renderMinutesAcrossHours(ir, plan, opts) {
|
|
|
377
465
|
return "jede Minute " + duringHours(ir, plan.times, sep);
|
|
378
466
|
}
|
|
379
467
|
const hours = plan.times.kind === "fires" ? atHours(plan.times.fires) : joinList(hourSegmentParts(ir, 0, 0, sep));
|
|
380
|
-
return countedPhrase(ir, "minute", "Minute", "Minuten") + ", " + hours;
|
|
468
|
+
return (strideFromSegments(fieldSegments(ir, "minute"), UNITS.minute, "") ?? countedPhrase(ir, "minute", "Minute", "Minuten")) + ", " + hours;
|
|
381
469
|
}
|
|
382
470
|
function renderMinuteSpanAcrossHourStep(ir, plan) {
|
|
383
471
|
if (plan.form === "wildcard") {
|
|
384
472
|
return "jede Minute " + everyNthHour(stepSegment(ir.analyses.segments.hour));
|
|
385
473
|
}
|
|
386
|
-
|
|
474
|
+
const segment = stepSegment(ir.analyses.segments.hour);
|
|
475
|
+
const hours = confinedHourStride(segment) ? everyNthHour(segment) : atHours(segment.fires);
|
|
476
|
+
return (strideFromSegments(fieldSegments(ir, "minute"), UNITS.minute, "") ?? countedPhrase(ir, "minute", "Minute", "Minuten")) + ", " + hours;
|
|
387
477
|
}
|
|
388
478
|
function renderCompactClockTimes(ir, plan, opts) {
|
|
389
479
|
const sep = opts.style.sep;
|
|
390
480
|
if (plan.fold) {
|
|
481
|
+
const cadence = hourCadence(ir, plan.minute);
|
|
482
|
+
if (cadence !== null) {
|
|
483
|
+
return cadence;
|
|
484
|
+
}
|
|
391
485
|
const hourly = fieldSegments(ir, "hour").some((segment) => segment.kind === "range");
|
|
392
486
|
return (hourly ? "st\xFCndlich " : "t\xE4glich ") + joinList(hourSegmentParts(ir, plan.minute, ir.analyses.clockSecond, sep));
|
|
393
487
|
}
|
|
394
488
|
const hours = fieldSegments(ir, "hour").some((segment) => segment.kind === "range") ? joinList(hourSegmentParts(ir, 0, 0, sep)) : atHours(hourFires(ir));
|
|
395
489
|
const lead = ir.analyses.clockSecond ? countedPhrase(ir, "second", "Sekunde", "Sekunden") + ", " : "";
|
|
396
|
-
return lead + countedPhrase(ir, "minute", "Minute", "Minuten") + ", " + hours;
|
|
490
|
+
return lead + (strideFromSegments(fieldSegments(ir, "minute"), UNITS.minute, "") ?? countedPhrase(ir, "minute", "Minute", "Minuten")) + ", " + hours;
|
|
397
491
|
}
|
|
398
492
|
function renderMinuteFrequency(ir, plan, opts) {
|
|
399
493
|
const segment = stepSegment(ir.analyses.segments.minute);
|
|
400
494
|
const sep = opts.style.sep;
|
|
401
495
|
const clean = cleanStep(segment, 60);
|
|
402
|
-
const base =
|
|
496
|
+
const base = stepClause(segment, UNITS.minute, "jeder Stunde");
|
|
403
497
|
if (plan.hours.kind === "window") {
|
|
404
498
|
const window = hourWindow(
|
|
405
499
|
plan.hours.from,
|
|
@@ -421,6 +515,67 @@ function hourStepPhrase(ir) {
|
|
|
421
515
|
const segment = stepSegment(ir.analyses.segments.hour);
|
|
422
516
|
return cleanStep(segment, 24) ? everyN(segment.interval, UNITS.hour) : atHours(segment.fires);
|
|
423
517
|
}
|
|
518
|
+
function hourStrideCadence(stride) {
|
|
519
|
+
const { start, interval, last } = stride;
|
|
520
|
+
const cadence = everyN(interval, UNITS.hour);
|
|
521
|
+
const tiles = 24 % interval === 0;
|
|
522
|
+
if (start === 0 && tiles) {
|
|
523
|
+
return cadence;
|
|
524
|
+
}
|
|
525
|
+
if (start < interval && tiles) {
|
|
526
|
+
return cadence + " ab " + start + " Uhr";
|
|
527
|
+
}
|
|
528
|
+
return cadence + " von " + start + " bis " + last + " Uhr";
|
|
529
|
+
}
|
|
530
|
+
function hourStride(ir) {
|
|
531
|
+
const segments = fieldSegments(ir, "hour");
|
|
532
|
+
if (!segments) {
|
|
533
|
+
return null;
|
|
534
|
+
}
|
|
535
|
+
if (segments.length === 1 && segments[0].kind === "step") {
|
|
536
|
+
const segment = segments[0];
|
|
537
|
+
const start = segment.startToken === "*" ? 0 : +segment.startToken.split("-")[0];
|
|
538
|
+
return { interval: segment.interval, last: segment.fires[segment.fires.length - 1], start };
|
|
539
|
+
}
|
|
540
|
+
const values = singleValues(segments);
|
|
541
|
+
const step = values && arithmeticStep(values);
|
|
542
|
+
return step || null;
|
|
543
|
+
}
|
|
544
|
+
function subMinuteSecond(ir) {
|
|
545
|
+
return ir.pattern.second === "*" || ir.shapes.second === "step";
|
|
546
|
+
}
|
|
547
|
+
function hourCadenceLead(ir, minute) {
|
|
548
|
+
if (minute === 0) {
|
|
549
|
+
if (subMinuteSecond(ir)) {
|
|
550
|
+
return secondsClause(ir, "jeder Minute") + " f\xFCr eine Minute";
|
|
551
|
+
}
|
|
552
|
+
return secondsClause(ir, "jeder Stunde");
|
|
553
|
+
}
|
|
554
|
+
const minutePhrase = "in Minute " + minute;
|
|
555
|
+
if (ir.pattern.second === "0") {
|
|
556
|
+
return minutePhrase;
|
|
557
|
+
}
|
|
558
|
+
return secondsClause(ir, "jeder Minute") + ", " + minutePhrase;
|
|
559
|
+
}
|
|
560
|
+
function hourCadence(ir, minute) {
|
|
561
|
+
const stride = hourStride(ir);
|
|
562
|
+
if (!stride) {
|
|
563
|
+
return null;
|
|
564
|
+
}
|
|
565
|
+
const fires = (stride.last - stride.start) / stride.interval + 1;
|
|
566
|
+
if (ir.pattern.second === "0" && fires <= maxClockTimes) {
|
|
567
|
+
return null;
|
|
568
|
+
}
|
|
569
|
+
const segment = fieldSegments(ir, "hour")[0];
|
|
570
|
+
const confined = minute === 0 && subMinuteSecond(ir) && fieldSegments(ir, "hour").length === 1 && segment.kind === "step" && confinedHourStride(segment);
|
|
571
|
+
if (confined) {
|
|
572
|
+
return secondsClause(ir, "jeder Minute") + " f\xFCr eine Minute " + everyNthHour(segment);
|
|
573
|
+
}
|
|
574
|
+
return hourCadenceLead(ir, minute) + ", " + hourStrideCadence(stride);
|
|
575
|
+
}
|
|
576
|
+
function hourCadenceApplies(ir) {
|
|
577
|
+
return ir.shapes.minute === "single" && hourCadence(ir, +ir.pattern.minute) !== null;
|
|
578
|
+
}
|
|
424
579
|
function renderHourRange(ir, plan, opts) {
|
|
425
580
|
const window = hourWindow(
|
|
426
581
|
plan.from,
|
|
@@ -437,6 +592,12 @@ function renderHourRange(ir, plan, opts) {
|
|
|
437
592
|
return countedPhrase(ir, "minute", "Minute", "Minuten") + " jeder Stunde, " + window;
|
|
438
593
|
}
|
|
439
594
|
function renderClockTimes(ir, plan, opts) {
|
|
595
|
+
if (ir.shapes.minute === "single") {
|
|
596
|
+
const cadence = hourCadence(ir, +ir.pattern.minute);
|
|
597
|
+
if (cadence !== null) {
|
|
598
|
+
return cadence;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
440
601
|
return "um " + timesPhrase(plan.times, opts.style.sep);
|
|
441
602
|
}
|
|
442
603
|
var renderers = {
|
|
@@ -486,6 +647,9 @@ function isComposeMinuteZero(ir) {
|
|
|
486
647
|
return ir.plan.kind === "composeSeconds" && composeMinuteZero(ir, ir.plan);
|
|
487
648
|
}
|
|
488
649
|
function needsDailyFrame(ir) {
|
|
650
|
+
if (hourCadenceApplies(ir)) {
|
|
651
|
+
return false;
|
|
652
|
+
}
|
|
489
653
|
if (ir.plan.kind === "clockTimes" || isComposeMinuteZero(ir)) {
|
|
490
654
|
return true;
|
|
491
655
|
}
|
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;
|
|
@@ -179,7 +199,15 @@ function renderSecondsWithinMinute(ir, plan, opts) {
|
|
|
179
199
|
}
|
|
180
200
|
return secondsLeadClause(ir, opts) + ", " + minuteWord + " " + minuteUnit + " past the hour, every hour" + trailingQualifier(ir, opts);
|
|
181
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
|
+
}
|
|
182
206
|
function renderComposeSeconds(ir, plan, opts) {
|
|
207
|
+
const cadence = composeHourCadence(ir, plan, opts);
|
|
208
|
+
if (cadence !== null) {
|
|
209
|
+
return cadence;
|
|
210
|
+
}
|
|
183
211
|
if (plan.rest.kind === "clockTimes" && (ir.shapes.second === "wildcard" || ir.shapes.second === "step")) {
|
|
184
212
|
const minute = plan.rest.times[0].minute;
|
|
185
213
|
if (+minute === 0) {
|
|
@@ -212,6 +240,9 @@ function clockTimesOf(ir, plan, opts) {
|
|
|
212
240
|
return joinList(times, opts) + (trail && ", " + trail);
|
|
213
241
|
}
|
|
214
242
|
function secondsLeadClause(ir, opts) {
|
|
243
|
+
return secondsClause(ir, "minute", opts);
|
|
244
|
+
}
|
|
245
|
+
function secondsClause(ir, anchor, opts) {
|
|
215
246
|
const secondField = ir.pattern.second;
|
|
216
247
|
const shape = ir.shapes.second;
|
|
217
248
|
if (secondField === "*") {
|
|
@@ -221,22 +252,27 @@ function secondsLeadClause(ir, opts) {
|
|
|
221
252
|
return stepCycle60(
|
|
222
253
|
ir.analyses.segments.second[0],
|
|
223
254
|
"second",
|
|
224
|
-
|
|
255
|
+
anchor,
|
|
225
256
|
opts
|
|
226
257
|
);
|
|
227
258
|
}
|
|
228
259
|
if (shape === "range") {
|
|
229
260
|
const bounds = secondField.split("-");
|
|
230
261
|
const num = seriesNumber(bounds, opts);
|
|
231
|
-
return "every second from " + num(bounds[0]) + through(opts) + num(bounds[1]) + " past the
|
|
262
|
+
return "every second from " + num(bounds[0]) + through(opts) + num(bounds[1]) + " past the " + anchor;
|
|
232
263
|
}
|
|
233
264
|
if (shape === "single") {
|
|
234
|
-
return "at " + getNumber(secondField, opts) + " " + pluralize(secondField, "second") + " past the
|
|
265
|
+
return "at " + getNumber(secondField, opts) + " " + pluralize(secondField, "second") + " past the " + anchor;
|
|
235
266
|
}
|
|
236
|
-
return
|
|
267
|
+
return strideFromSegments(
|
|
268
|
+
ir.analyses.segments.second,
|
|
269
|
+
"second",
|
|
270
|
+
anchor,
|
|
271
|
+
opts
|
|
272
|
+
) ?? listPastThe(
|
|
237
273
|
segmentWords(ir.analyses.segments.second, opts),
|
|
238
274
|
"second",
|
|
239
|
-
|
|
275
|
+
anchor,
|
|
240
276
|
opts
|
|
241
277
|
);
|
|
242
278
|
}
|
|
@@ -251,12 +287,11 @@ function renderRangeOfMinutes(ir, plan, opts) {
|
|
|
251
287
|
return minuteRangeLead(ir.pattern.minute, opts) + trailingQualifier(ir, opts);
|
|
252
288
|
}
|
|
253
289
|
function renderMultipleMinutes(ir, plan, opts) {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
"hour",
|
|
290
|
+
const stride = strideFromSegments(ir.analyses.segments.minute, "minute", "hour", opts);
|
|
291
|
+
return (stride ?? listPastThe(segmentWords(
|
|
292
|
+
ir.analyses.segments.minute,
|
|
258
293
|
opts
|
|
259
|
-
) + trailingQualifier(ir, opts);
|
|
294
|
+
), "minute", "hour", opts)) + trailingQualifier(ir, opts);
|
|
260
295
|
}
|
|
261
296
|
function renderMinuteFrequency(ir, plan, opts) {
|
|
262
297
|
let phrase = stepCycle60(
|
|
@@ -286,8 +321,9 @@ function renderMinutesAcrossHours(ir, plan, opts) {
|
|
|
286
321
|
}
|
|
287
322
|
const times = hourTimesFromPlan(ir, plan.times, true, opts);
|
|
288
323
|
const lead = plan.form === "range" ? minuteRangeLead(ir.pattern.minute, opts) : (
|
|
289
|
-
// The 'list' form is a minute list, which has segments
|
|
290
|
-
|
|
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(
|
|
291
327
|
segmentWords(ir.analyses.segments.minute, opts),
|
|
292
328
|
"minute",
|
|
293
329
|
"hour",
|
|
@@ -314,7 +350,13 @@ function renderMinuteSpanAcrossHourStep(ir, plan, opts) {
|
|
|
314
350
|
if (plan.form === "wildcard") {
|
|
315
351
|
return "every minute " + everyNthHour(segment, opts) + trailingQualifier(ir, opts);
|
|
316
352
|
}
|
|
317
|
-
|
|
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);
|
|
318
360
|
}
|
|
319
361
|
function minuteRangeLead(minuteField, opts) {
|
|
320
362
|
const bounds = minuteField.split("-");
|
|
@@ -338,7 +380,12 @@ function rangeMinuteLead(ir, opts) {
|
|
|
338
380
|
if (ir.pattern.minute === "0") {
|
|
339
381
|
return "every hour";
|
|
340
382
|
}
|
|
341
|
-
return
|
|
383
|
+
return strideFromSegments(
|
|
384
|
+
ir.analyses.segments.minute,
|
|
385
|
+
"minute",
|
|
386
|
+
"hour",
|
|
387
|
+
opts
|
|
388
|
+
) ?? listPastThe(
|
|
342
389
|
segmentWords(ir.analyses.segments.minute, opts),
|
|
343
390
|
"minute",
|
|
344
391
|
"hour",
|
|
@@ -355,6 +402,12 @@ function hourWindow(window, opts) {
|
|
|
355
402
|
return "from " + getTime({ hour: window.from, minute: 0 }, opts) + through(opts) + getTime({ hour: window.to, minute: window.last }, opts);
|
|
356
403
|
}
|
|
357
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
|
+
}
|
|
358
411
|
const plain = mixedTwelve(plan.times);
|
|
359
412
|
const times = plan.times.map(function clock(time) {
|
|
360
413
|
return getTime({
|
|
@@ -368,6 +421,10 @@ function renderClockTimes(ir, plan, opts) {
|
|
|
368
421
|
}
|
|
369
422
|
function renderCompactClockTimes(ir, plan, opts) {
|
|
370
423
|
if (plan.fold) {
|
|
424
|
+
const cadence = hourCadence(ir, +plan.minute, opts);
|
|
425
|
+
if (cadence !== null) {
|
|
426
|
+
return cadence;
|
|
427
|
+
}
|
|
371
428
|
const hasRange = ir.analyses.segments.hour.some(function range(segment) {
|
|
372
429
|
return segment.kind === "range";
|
|
373
430
|
});
|
|
@@ -378,13 +435,14 @@ function renderCompactClockTimes(ir, plan, opts) {
|
|
|
378
435
|
return interpretDayQualifier(ir, opts) + "at " + hourSegmentTimes(ir, fold, true, opts);
|
|
379
436
|
}
|
|
380
437
|
const phrase = (
|
|
381
|
-
// The non-fold branch is a minute list, which has segments.
|
|
382
|
-
|
|
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(
|
|
383
441
|
segmentWords(ir.analyses.segments.minute, opts),
|
|
384
442
|
"minute",
|
|
385
443
|
"hour",
|
|
386
444
|
opts
|
|
387
|
-
) + ", 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)
|
|
388
446
|
);
|
|
389
447
|
return ir.analyses.clockSecond ? secondsLeadClause(ir, opts) + ", " + phrase : phrase;
|
|
390
448
|
}
|
|
@@ -432,24 +490,50 @@ var renderers = {
|
|
|
432
490
|
singleMinute: renderSingleMinute,
|
|
433
491
|
standaloneSeconds: renderStandaloneSeconds
|
|
434
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
|
+
}
|
|
435
521
|
function stepCycle60(segment, unit, anchor, opts) {
|
|
436
522
|
if (segment.startToken.indexOf("-") !== -1) {
|
|
437
523
|
return listPastThe(numberWords(segment.fires, opts), unit, anchor, opts);
|
|
438
524
|
}
|
|
439
525
|
const start = segment.startToken === "*" ? 0 : +segment.startToken;
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
if (segment.fires.length <= 3) {
|
|
443
|
-
return listPastThe(
|
|
444
|
-
numberWords(segment.fires, opts),
|
|
445
|
-
unit,
|
|
446
|
-
anchor,
|
|
447
|
-
opts
|
|
448
|
-
);
|
|
449
|
-
}
|
|
450
|
-
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);
|
|
451
528
|
}
|
|
452
|
-
return
|
|
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);
|
|
453
537
|
}
|
|
454
538
|
function stepHours(segment, opts) {
|
|
455
539
|
if (segment.startToken.indexOf("-") !== -1) {
|
|
@@ -465,6 +549,68 @@ function stepHours(segment, opts) {
|
|
|
465
549
|
}
|
|
466
550
|
return "every " + getNumber(interval, opts) + " hours from " + getTime({ hour: start, minute: 0 }, opts);
|
|
467
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
|
+
}
|
|
468
614
|
function seriesNumber(values, opts) {
|
|
469
615
|
const anyBig = values.some(function big(v) {
|
|
470
616
|
return +v > 10;
|