cronli5 0.1.7 → 0.2.1
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 +68 -0
- package/README.md +2 -2
- package/cronli5.min.js +2 -2
- package/dist/cronli5.cjs +451 -144
- package/dist/cronli5.js +451 -144
- package/dist/lang/de.cjs +65 -65
- package/dist/lang/de.js +65 -65
- package/dist/lang/en.cjs +450 -141
- package/dist/lang/en.js +450 -141
- package/dist/lang/es.cjs +71 -72
- package/dist/lang/es.js +71 -72
- package/dist/lang/fi.cjs +71 -66
- package/dist/lang/fi.js +71 -66
- package/dist/lang/zh.cjs +36 -36
- package/dist/lang/zh.js +36 -36
- package/package.json +1 -1
- package/src/core/analyze.ts +14 -13
- package/src/core/ir.ts +13 -8
- package/src/core/shapes.ts +8 -1
- package/src/core/util.ts +86 -3
- package/src/core/validate.ts +1 -1
- package/src/cronli5.ts +3 -3
- package/src/lang/de/index.ts +30 -99
- package/src/lang/en/dialects.ts +6 -2
- package/src/lang/en/index.ts +820 -212
- package/src/lang/es/index.ts +36 -120
- package/src/lang/fi/index.ts +33 -104
- package/src/lang/zh/index.ts +23 -48
- package/src/types.ts +2 -2
- package/types/core/analyze.d.ts +2 -2
- package/types/core/ir.d.ts +8 -7
- package/types/core/shapes.d.ts +2 -1
- package/types/core/util.d.ts +17 -2
- package/types/types.d.ts +1 -1
package/dist/lang/en.cjs
CHANGED
|
@@ -60,6 +60,48 @@ function orderWeekdaysForDisplay(segments) {
|
|
|
60
60
|
return pair[0];
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
|
+
function segmentsOf(ir, field) {
|
|
64
|
+
return ir.analyses.segments[field] ?? [];
|
|
65
|
+
}
|
|
66
|
+
function stepSegment(ir, field) {
|
|
67
|
+
return segmentsOf(ir, field)[0];
|
|
68
|
+
}
|
|
69
|
+
function singleValues(segments) {
|
|
70
|
+
const values = [];
|
|
71
|
+
for (const segment of segments) {
|
|
72
|
+
if (segment.kind !== "single") {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
values.push(+segment.value);
|
|
76
|
+
}
|
|
77
|
+
return values;
|
|
78
|
+
}
|
|
79
|
+
function offsetCleanStride(stride) {
|
|
80
|
+
return stride.start < stride.interval && 24 % stride.interval === 0;
|
|
81
|
+
}
|
|
82
|
+
function hourListStride(values) {
|
|
83
|
+
if (values.length < 2) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
const interval = values[1] - values[0];
|
|
87
|
+
if (interval < 2) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
for (let i = 2; i < values.length; i += 1) {
|
|
91
|
+
if (values[i] - values[i - 1] !== interval) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (values[0] !== 0 && values.length < 5) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
return { interval, last: values[values.length - 1], start: values[0] };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// src/core/shapes.ts
|
|
102
|
+
function isOpenStep(field) {
|
|
103
|
+
return field.indexOf("/") !== -1 && field.indexOf("-") === -1 && field.indexOf(",") === -1;
|
|
104
|
+
}
|
|
63
105
|
|
|
64
106
|
// src/core/specs.ts
|
|
65
107
|
var maxClockTimes = 6;
|
|
@@ -104,7 +146,8 @@ var dialects = {
|
|
|
104
146
|
pm: "p.m.",
|
|
105
147
|
sep: ":",
|
|
106
148
|
serialComma: true,
|
|
107
|
-
through: " through "
|
|
149
|
+
through: " through ",
|
|
150
|
+
untilWindow: true
|
|
108
151
|
},
|
|
109
152
|
house: {
|
|
110
153
|
am: "AM",
|
|
@@ -121,7 +164,7 @@ var dialects = {
|
|
|
121
164
|
};
|
|
122
165
|
function resolveDialect(dialect) {
|
|
123
166
|
if (typeof dialect === "object" && dialect !== null) {
|
|
124
|
-
return { ...dialects.us, ...dialect };
|
|
167
|
+
return { ...dialects.us, untilWindow: false, ...dialect };
|
|
125
168
|
}
|
|
126
169
|
const name = dialect === "uk" ? "gb" : dialect;
|
|
127
170
|
return dialects[name] || dialects.us;
|
|
@@ -193,7 +236,9 @@ function normalizeOptions(options) {
|
|
|
193
236
|
};
|
|
194
237
|
}
|
|
195
238
|
function describe(ir, opts) {
|
|
196
|
-
|
|
239
|
+
const body = confinement(ir, opts) ?? render(ir, ir.plan, opts);
|
|
240
|
+
const lead = isDayUnion(ir, opts) ? dayUnionMonthLead(ir, opts) : "";
|
|
241
|
+
return applyYear(lead + body, ir, opts);
|
|
197
242
|
}
|
|
198
243
|
function render(ir, plan, opts) {
|
|
199
244
|
const renderer = renderers[plan.kind];
|
|
@@ -278,7 +323,7 @@ function secondsClause(ir, anchor, opts) {
|
|
|
278
323
|
}
|
|
279
324
|
if (shape === "step") {
|
|
280
325
|
return stepCycle60(
|
|
281
|
-
ir
|
|
326
|
+
stepSegment(ir, "second"),
|
|
282
327
|
"second",
|
|
283
328
|
anchor,
|
|
284
329
|
opts
|
|
@@ -286,19 +331,19 @@ function secondsClause(ir, anchor, opts) {
|
|
|
286
331
|
}
|
|
287
332
|
if (shape === "range") {
|
|
288
333
|
const bounds = secondField.split("-");
|
|
289
|
-
const num = seriesNumber(
|
|
334
|
+
const num = seriesNumber();
|
|
290
335
|
return "every second from " + num(bounds[0]) + through(opts) + num(bounds[1]) + " past the " + anchor;
|
|
291
336
|
}
|
|
292
337
|
if (shape === "single") {
|
|
293
338
|
return "at " + getNumber(secondField, opts) + " " + pluralize(secondField, "second") + " past the " + anchor;
|
|
294
339
|
}
|
|
295
340
|
return strideFromSegments(
|
|
296
|
-
ir
|
|
341
|
+
segmentsOf(ir, "second"),
|
|
297
342
|
"second",
|
|
298
343
|
anchor,
|
|
299
344
|
opts
|
|
300
345
|
) ?? listPastThe(
|
|
301
|
-
segmentWords(ir
|
|
346
|
+
segmentWords(segmentsOf(ir, "second"), opts),
|
|
302
347
|
"second",
|
|
303
348
|
anchor,
|
|
304
349
|
opts
|
|
@@ -315,15 +360,15 @@ function renderRangeOfMinutes(ir, plan, opts) {
|
|
|
315
360
|
return minuteRangeLead(ir.pattern.minute, opts) + trailingQualifier(ir, opts);
|
|
316
361
|
}
|
|
317
362
|
function renderMultipleMinutes(ir, plan, opts) {
|
|
318
|
-
const stride = strideFromSegments(ir
|
|
363
|
+
const stride = strideFromSegments(segmentsOf(ir, "minute"), "minute", "hour", opts);
|
|
319
364
|
return (stride ?? listPastThe(segmentWords(
|
|
320
|
-
ir
|
|
365
|
+
segmentsOf(ir, "minute"),
|
|
321
366
|
opts
|
|
322
367
|
), "minute", "hour", opts)) + trailingQualifier(ir, opts);
|
|
323
368
|
}
|
|
324
369
|
function renderMinuteFrequency(ir, plan, opts) {
|
|
325
370
|
let phrase = stepCycle60(
|
|
326
|
-
ir
|
|
371
|
+
stepSegment(ir, "minute"),
|
|
327
372
|
"minute",
|
|
328
373
|
"hour",
|
|
329
374
|
opts
|
|
@@ -332,9 +377,14 @@ function renderMinuteFrequency(ir, plan, opts) {
|
|
|
332
377
|
const cadence = unevenHourCadence(ir, opts);
|
|
333
378
|
phrase += cadence ? ", " + cadence : " during the " + hourTimesFromPlan(ir, plan.hours.times, false, opts) + " hours";
|
|
334
379
|
} else if (plan.hours.kind === "window") {
|
|
335
|
-
phrase += " " +
|
|
380
|
+
phrase += " " + rangeWindow({
|
|
381
|
+
continuous: false,
|
|
382
|
+
from: plan.hours.from,
|
|
383
|
+
throughMinute: plan.hours.last,
|
|
384
|
+
to: plan.hours.to
|
|
385
|
+
}, opts);
|
|
336
386
|
} else if (plan.hours.kind === "step") {
|
|
337
|
-
phrase += " " + everyNthHour(ir
|
|
387
|
+
phrase += " " + everyNthHour(stepSegment(ir, "hour"), opts);
|
|
338
388
|
}
|
|
339
389
|
return phrase + trailingQualifier(ir, opts);
|
|
340
390
|
}
|
|
@@ -352,15 +402,21 @@ function renderMinutesAcrossHours(ir, plan, opts) {
|
|
|
352
402
|
}
|
|
353
403
|
return "every minute during the " + hourTimesFromPlan(ir, plan.times, false, opts) + " hours" + trailingQualifier(ir, opts);
|
|
354
404
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
"
|
|
362
|
-
|
|
363
|
-
)
|
|
405
|
+
if (plan.form === "range") {
|
|
406
|
+
const lead2 = minuteRangeLead(ir.pattern.minute, opts);
|
|
407
|
+
if (cadence !== null) {
|
|
408
|
+
return lead2 + ", " + cadence + trailingQualifier(ir, opts);
|
|
409
|
+
}
|
|
410
|
+
if (singleHourFire(plan.times)) {
|
|
411
|
+
return lead2 + ", at " + hourTimesFromPlan(ir, plan.times, true, opts) + trailingQualifier(ir, opts);
|
|
412
|
+
}
|
|
413
|
+
return lead2 + " during the " + hourTimesFromPlan(ir, plan.times, false, opts) + " hours" + trailingQualifier(ir, opts);
|
|
414
|
+
}
|
|
415
|
+
const lead = strideFromSegments(segmentsOf(ir, "minute"), "minute", "hour", opts) ?? listPastThe(
|
|
416
|
+
segmentWords(segmentsOf(ir, "minute"), opts),
|
|
417
|
+
"minute",
|
|
418
|
+
"hour",
|
|
419
|
+
opts
|
|
364
420
|
);
|
|
365
421
|
if (cadence !== null) {
|
|
366
422
|
return lead + ", " + cadence + trailingQualifier(ir, opts);
|
|
@@ -382,12 +438,12 @@ function everyNthHour(segment, opts) {
|
|
|
382
438
|
return start === 0 ? base : base + " starting at " + getTime({ hour: start, minute: 0 }, opts);
|
|
383
439
|
}
|
|
384
440
|
function renderMinuteSpanAcrossHourStep(ir, plan, opts) {
|
|
385
|
-
const segment = ir
|
|
441
|
+
const segment = stepSegment(ir, "hour");
|
|
386
442
|
if (plan.form === "wildcard") {
|
|
387
443
|
return "every minute " + everyNthHour(segment, opts) + trailingQualifier(ir, opts);
|
|
388
444
|
}
|
|
389
|
-
const lead = plan.form === "list" ? strideFromSegments(ir
|
|
390
|
-
segmentWords(ir
|
|
445
|
+
const lead = plan.form === "list" ? strideFromSegments(segmentsOf(ir, "minute"), "minute", "hour", opts) ?? listPastThe(
|
|
446
|
+
segmentWords(segmentsOf(ir, "minute"), opts),
|
|
391
447
|
"minute",
|
|
392
448
|
"hour",
|
|
393
449
|
opts
|
|
@@ -397,7 +453,7 @@ function renderMinuteSpanAcrossHourStep(ir, plan, opts) {
|
|
|
397
453
|
}
|
|
398
454
|
function minuteRangeLead(minuteField, opts) {
|
|
399
455
|
const bounds = minuteField.split("-");
|
|
400
|
-
const num = seriesNumber(
|
|
456
|
+
const num = seriesNumber();
|
|
401
457
|
return "every minute from " + num(bounds[0]) + through(opts) + num(bounds[1]) + " past the hour";
|
|
402
458
|
}
|
|
403
459
|
function renderEveryHour(ir, plan, opts) {
|
|
@@ -418,12 +474,12 @@ function rangeMinuteLead(ir, opts) {
|
|
|
418
474
|
return "every hour";
|
|
419
475
|
}
|
|
420
476
|
return strideFromSegments(
|
|
421
|
-
ir
|
|
477
|
+
segmentsOf(ir, "minute"),
|
|
422
478
|
"minute",
|
|
423
479
|
"hour",
|
|
424
480
|
opts
|
|
425
481
|
) ?? listPastThe(
|
|
426
|
-
segmentWords(ir
|
|
482
|
+
segmentWords(segmentsOf(ir, "minute"), opts),
|
|
427
483
|
"minute",
|
|
428
484
|
"hour",
|
|
429
485
|
opts
|
|
@@ -434,14 +490,28 @@ function renderHourStep(ir, plan, opts) {
|
|
|
434
490
|
if (cadence !== null) {
|
|
435
491
|
return cadence + trailingQualifier(ir, opts);
|
|
436
492
|
}
|
|
437
|
-
return stepHours(ir
|
|
493
|
+
return stepHours(stepSegment(ir, "hour"), opts) + trailingQualifier(ir, opts);
|
|
438
494
|
}
|
|
439
495
|
function boundedWindow(plan) {
|
|
440
|
-
const
|
|
441
|
-
|
|
496
|
+
const continuous = plan.minuteForm === "wildcard";
|
|
497
|
+
const closeMinute = continuous ? plan.boundMinute ?? 0 : 0;
|
|
498
|
+
return { from: plan.from, closeMinute, to: plan.to, continuous };
|
|
499
|
+
}
|
|
500
|
+
function rangeWindow(window, opts) {
|
|
501
|
+
const { from, to, throughMinute, continuous } = window;
|
|
502
|
+
const open = "from " + getTime({ hour: from, minute: 0 }, opts);
|
|
503
|
+
if (opts.style.untilWindow && !opts.short && from !== to) {
|
|
504
|
+
return continuous ? open + " until " + getTime({ hour: (to + 1) % 24, minute: 0 }, opts) : open + through(opts) + getTime({ hour: to, minute: 0 }, opts);
|
|
505
|
+
}
|
|
506
|
+
return open + through(opts) + getTime({ hour: to, minute: throughMinute }, opts);
|
|
442
507
|
}
|
|
443
508
|
function hourWindow(window, opts) {
|
|
444
|
-
return
|
|
509
|
+
return rangeWindow({
|
|
510
|
+
continuous: window.continuous,
|
|
511
|
+
from: window.from,
|
|
512
|
+
throughMinute: window.closeMinute,
|
|
513
|
+
to: window.to
|
|
514
|
+
}, opts);
|
|
445
515
|
}
|
|
446
516
|
function renderClockTimes(ir, plan, opts) {
|
|
447
517
|
if (ir.shapes.minute === "single") {
|
|
@@ -460,7 +530,10 @@ function renderClockTimes(ir, plan, opts) {
|
|
|
460
530
|
plain
|
|
461
531
|
}, opts);
|
|
462
532
|
});
|
|
463
|
-
return interpretDayQualifier(ir, opts) + "at " + joinList(times, opts);
|
|
533
|
+
return interpretDayQualifier(ir, opts) + "at " + joinList(times, opts) + dayUnionTrail(ir, opts);
|
|
534
|
+
}
|
|
535
|
+
function dayUnionTrail(ir, opts) {
|
|
536
|
+
return isDayUnion(ir, opts) ? dayUnionCondition(ir, opts) : "";
|
|
464
537
|
}
|
|
465
538
|
function renderCompactClockTimes(ir, plan, opts) {
|
|
466
539
|
if (plan.fold) {
|
|
@@ -468,20 +541,20 @@ function renderCompactClockTimes(ir, plan, opts) {
|
|
|
468
541
|
if (cadence2 !== null) {
|
|
469
542
|
return cadence2;
|
|
470
543
|
}
|
|
471
|
-
const hasRange = ir
|
|
544
|
+
const hasRange = segmentsOf(ir, "hour").some(function range(segment) {
|
|
472
545
|
return segment.kind === "range";
|
|
473
546
|
});
|
|
474
547
|
if (hasRange && !ir.analyses.clockSecond) {
|
|
475
548
|
return foldedHourWindows(ir, plan, opts) + trailingQualifier(ir, opts);
|
|
476
549
|
}
|
|
477
550
|
const fold = { minute: plan.minute, second: ir.analyses.clockSecond };
|
|
478
|
-
return interpretDayQualifier(ir, opts) + "at " + hourSegmentTimes(ir, fold, true, opts);
|
|
551
|
+
return interpretDayQualifier(ir, opts) + "at " + hourSegmentTimes(ir, fold, true, opts) + dayUnionTrail(ir, opts);
|
|
479
552
|
}
|
|
480
553
|
const minuteLead = (
|
|
481
554
|
// The non-fold branch is a minute list, which has segments. An
|
|
482
555
|
// offset/uneven step enumerated to that list reads as a stride.
|
|
483
|
-
strideFromSegments(ir
|
|
484
|
-
segmentWords(ir
|
|
556
|
+
strideFromSegments(segmentsOf(ir, "minute"), "minute", "hour", opts) ?? listPastThe(
|
|
557
|
+
segmentWords(segmentsOf(ir, "minute"), opts),
|
|
485
558
|
"minute",
|
|
486
559
|
"hour",
|
|
487
560
|
opts
|
|
@@ -494,26 +567,162 @@ function renderCompactClockTimes(ir, plan, opts) {
|
|
|
494
567
|
function foldedHourWindows(ir, plan, opts) {
|
|
495
568
|
const minute = plan.minute;
|
|
496
569
|
const windows = [];
|
|
497
|
-
const
|
|
498
|
-
|
|
570
|
+
const times = collectHourOutliers(ir).map(function time(hour) {
|
|
571
|
+
return getTime({ hour, minute }, opts);
|
|
572
|
+
});
|
|
573
|
+
segmentsOf(ir, "hour").forEach(function classify(segment) {
|
|
499
574
|
if (segment.kind === "range") {
|
|
500
|
-
windows.push(
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
} else {
|
|
507
|
-
singles.push(+segment.value);
|
|
575
|
+
windows.push(rangeWindow({
|
|
576
|
+
continuous: false,
|
|
577
|
+
from: +segment.bounds[0],
|
|
578
|
+
throughMinute: minute,
|
|
579
|
+
to: +segment.bounds[1]
|
|
580
|
+
}, opts));
|
|
508
581
|
}
|
|
509
582
|
});
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
583
|
+
const phrase = rangeMinuteLead(ir, opts) + " " + joinList(windows, opts);
|
|
584
|
+
return phrase + outlierTail(times, opts);
|
|
585
|
+
}
|
|
586
|
+
function collectHourOutliers(ir) {
|
|
587
|
+
const hours = [];
|
|
588
|
+
segmentsOf(ir, "hour").forEach(function classify(segment) {
|
|
589
|
+
if (segment.kind === "step") {
|
|
590
|
+
hours.push(...segment.fires);
|
|
591
|
+
} else if (segment.kind !== "range") {
|
|
592
|
+
hours.push(+segment.value);
|
|
593
|
+
}
|
|
594
|
+
});
|
|
595
|
+
return hours;
|
|
596
|
+
}
|
|
597
|
+
function outlierTail(times, opts) {
|
|
598
|
+
if (!times.length) {
|
|
599
|
+
return "";
|
|
515
600
|
}
|
|
516
|
-
return
|
|
601
|
+
return " and at " + joinList(times, opts);
|
|
602
|
+
}
|
|
603
|
+
function isCadenceField(token) {
|
|
604
|
+
return token === "*" || token.startsWith("*/") && token.indexOf("-") === -1;
|
|
605
|
+
}
|
|
606
|
+
function leadingCadence(ir, opts) {
|
|
607
|
+
const { second, minute } = ir.pattern;
|
|
608
|
+
if (isCadenceField(second)) {
|
|
609
|
+
return { secondLead: true, text: secondsClause(ir, "minute", opts) };
|
|
610
|
+
}
|
|
611
|
+
if (second === "0" && isCadenceField(minute)) {
|
|
612
|
+
const text = minute === "*" ? "every minute" : (
|
|
613
|
+
// A clean minute step's first segment is a step segment.
|
|
614
|
+
stepCycle60(
|
|
615
|
+
stepSegment(ir, "minute"),
|
|
616
|
+
"minute",
|
|
617
|
+
"hour",
|
|
618
|
+
opts
|
|
619
|
+
)
|
|
620
|
+
);
|
|
621
|
+
return { secondLead: false, text };
|
|
622
|
+
}
|
|
623
|
+
return null;
|
|
624
|
+
}
|
|
625
|
+
function minuteConfinement(ir, opts) {
|
|
626
|
+
const minute = ir.pattern.minute;
|
|
627
|
+
if (minute === "*") {
|
|
628
|
+
return "";
|
|
629
|
+
}
|
|
630
|
+
if (isCadenceField(minute)) {
|
|
631
|
+
return " of every other minute";
|
|
632
|
+
}
|
|
633
|
+
const segments = segmentsOf(ir, "minute");
|
|
634
|
+
if (ir.shapes.minute === "single") {
|
|
635
|
+
return " during minute :" + pad(minute);
|
|
636
|
+
}
|
|
637
|
+
if (ir.shapes.minute === "range") {
|
|
638
|
+
const bounds = minute.split("-");
|
|
639
|
+
return " during minutes :" + pad(bounds[0]) + through(opts) + ":" + pad(bounds[1]);
|
|
640
|
+
}
|
|
641
|
+
const values = segmentWords(segments, opts).map(function colon(word) {
|
|
642
|
+
return ":" + pad(word);
|
|
643
|
+
});
|
|
644
|
+
return " during minutes " + joinList(values, opts);
|
|
645
|
+
}
|
|
646
|
+
function hourConfinement(ir, opts) {
|
|
647
|
+
const hour = ir.pattern.hour;
|
|
648
|
+
if (hour === "*") {
|
|
649
|
+
const minutePinned = ir.pattern.minute !== "*" && !isCadenceField(ir.pattern.minute);
|
|
650
|
+
return minutePinned ? " of every hour" : "";
|
|
651
|
+
}
|
|
652
|
+
if (isCadenceField(hour)) {
|
|
653
|
+
return hour === "*/2" ? " of every other hour" : "";
|
|
654
|
+
}
|
|
655
|
+
if (ir.shapes.hour === "single") {
|
|
656
|
+
const h = +hour;
|
|
657
|
+
if (ir.shapes.minute === "step") {
|
|
658
|
+
return " from " + getTime({ hour: h, minute: 0 }, opts) + " until " + getTime({ hour: (h + 1) % 24, minute: 0 }, opts);
|
|
659
|
+
}
|
|
660
|
+
if (ir.pattern.minute !== "*" && !isCadenceField(ir.pattern.minute)) {
|
|
661
|
+
return " at " + getTime({ hour: h, minute: 0 }, opts);
|
|
662
|
+
}
|
|
663
|
+
return " of the " + getTime({ hour: h, minute: 0 }, opts) + " hour";
|
|
664
|
+
}
|
|
665
|
+
if (ir.shapes.hour === "range") {
|
|
666
|
+
const bounds = hour.split("-");
|
|
667
|
+
return " " + rangeWindow({
|
|
668
|
+
continuous: ir.pattern.minute === "*",
|
|
669
|
+
from: +bounds[0],
|
|
670
|
+
throughMinute: 0,
|
|
671
|
+
to: +bounds[1]
|
|
672
|
+
}, opts);
|
|
673
|
+
}
|
|
674
|
+
return " during the " + hourSegmentTimes(ir, { minute: 0, second: null }, false, opts) + " hours";
|
|
675
|
+
}
|
|
676
|
+
function isContiguousHourRange(ir) {
|
|
677
|
+
return ir.shapes.hour === "range";
|
|
678
|
+
}
|
|
679
|
+
function confinableHour(ir) {
|
|
680
|
+
if (ir.shapes.hour !== "step") {
|
|
681
|
+
return true;
|
|
682
|
+
}
|
|
683
|
+
const segment = stepSegment(ir, "hour");
|
|
684
|
+
return ir.pattern.hour === "*/2" || segment.startToken.indexOf("-") !== -1;
|
|
685
|
+
}
|
|
686
|
+
function isMinuteStride(ir) {
|
|
687
|
+
if (ir.shapes.minute !== "list") {
|
|
688
|
+
return false;
|
|
689
|
+
}
|
|
690
|
+
const values = singleValues(segmentsOf(ir, "minute"));
|
|
691
|
+
return values !== null && arithmeticStep(values) !== null;
|
|
692
|
+
}
|
|
693
|
+
function confinementEligible(ir, lead) {
|
|
694
|
+
const { minute, hour } = ir.pattern;
|
|
695
|
+
const minuteStep = isCadenceField(minute) && minute !== "*";
|
|
696
|
+
if (!confinableHour(ir)) {
|
|
697
|
+
return false;
|
|
698
|
+
}
|
|
699
|
+
if (lead.secondLead) {
|
|
700
|
+
if (minuteStep) {
|
|
701
|
+
return minute === "*/2" && !isContiguousHourRange(ir);
|
|
702
|
+
}
|
|
703
|
+
if (isMinuteStride(ir) || ir.shapes.minute === "list" && ir.shapes.hour === "list") {
|
|
704
|
+
return false;
|
|
705
|
+
}
|
|
706
|
+
return true;
|
|
707
|
+
}
|
|
708
|
+
if (hour === "*/2") {
|
|
709
|
+
return true;
|
|
710
|
+
}
|
|
711
|
+
return ir.shapes.hour === "single" && minute === "*/2";
|
|
712
|
+
}
|
|
713
|
+
function confinement(ir, opts) {
|
|
714
|
+
if (!opts.style.untilWindow || opts.short) {
|
|
715
|
+
return null;
|
|
716
|
+
}
|
|
717
|
+
if (ir.pattern.minute === "*" && ir.pattern.hour === "*") {
|
|
718
|
+
return null;
|
|
719
|
+
}
|
|
720
|
+
const lead = leadingCadence(ir, opts);
|
|
721
|
+
if (!lead || !confinementEligible(ir, lead)) {
|
|
722
|
+
return null;
|
|
723
|
+
}
|
|
724
|
+
const minutePart = lead.secondLead ? minuteConfinement(ir, opts) : "";
|
|
725
|
+
return lead.text + minutePart + hourConfinement(ir, opts) + trailingQualifier(ir, opts);
|
|
517
726
|
}
|
|
518
727
|
var renderers = {
|
|
519
728
|
clockTimes: renderClockTimes,
|
|
@@ -545,19 +754,9 @@ function renderStride(stride, opts) {
|
|
|
545
754
|
if (start < interval && tiles) {
|
|
546
755
|
return cadence + " from " + getNumber(start, opts) + " " + pluralize(start, unit) + " past the " + anchor;
|
|
547
756
|
}
|
|
548
|
-
const num = seriesNumber(
|
|
757
|
+
const num = seriesNumber();
|
|
549
758
|
return cadence + " from " + num(start) + through(opts) + num(last) + " " + pluralize(last, unit) + " past the " + anchor;
|
|
550
759
|
}
|
|
551
|
-
function singleValues(segments) {
|
|
552
|
-
const values = [];
|
|
553
|
-
for (const segment of segments) {
|
|
554
|
-
if (segment.kind !== "single") {
|
|
555
|
-
return null;
|
|
556
|
-
}
|
|
557
|
-
values.push(+segment.value);
|
|
558
|
-
}
|
|
559
|
-
return values;
|
|
560
|
-
}
|
|
561
760
|
function strideFromSegments(segments, unit, anchor, opts) {
|
|
562
761
|
const values = singleValues(segments);
|
|
563
762
|
const step = values && arithmeticStep(values);
|
|
@@ -606,9 +805,6 @@ function hourStrideCadence(stride, opts) {
|
|
|
606
805
|
}
|
|
607
806
|
return cadence + " from " + getTime({ hour: start, minute: 0 }, opts) + through(opts) + getTime({ hour: last, minute: 0 }, opts);
|
|
608
807
|
}
|
|
609
|
-
function offsetCleanStride(stride) {
|
|
610
|
-
return stride.start < stride.interval && 24 % stride.interval === 0;
|
|
611
|
-
}
|
|
612
808
|
function unevenHourCadence(ir, opts) {
|
|
613
809
|
const stride = hourStride(ir);
|
|
614
810
|
if (!stride || offsetCleanStride(stride)) {
|
|
@@ -616,26 +812,8 @@ function unevenHourCadence(ir, opts) {
|
|
|
616
812
|
}
|
|
617
813
|
return hourStrideCadence(stride, opts);
|
|
618
814
|
}
|
|
619
|
-
function hourListStride(values) {
|
|
620
|
-
if (values.length < 2) {
|
|
621
|
-
return null;
|
|
622
|
-
}
|
|
623
|
-
const interval = values[1] - values[0];
|
|
624
|
-
if (interval < 2) {
|
|
625
|
-
return null;
|
|
626
|
-
}
|
|
627
|
-
for (let i = 2; i < values.length; i += 1) {
|
|
628
|
-
if (values[i] - values[i - 1] !== interval) {
|
|
629
|
-
return null;
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
if (values[0] !== 0 && values.length < 5) {
|
|
633
|
-
return null;
|
|
634
|
-
}
|
|
635
|
-
return { interval, last: values[values.length - 1], start: values[0] };
|
|
636
|
-
}
|
|
637
815
|
function hourStride(ir) {
|
|
638
|
-
const segments = ir
|
|
816
|
+
const segments = segmentsOf(ir, "hour");
|
|
639
817
|
if (segments.length === 1 && segments[0].kind === "step") {
|
|
640
818
|
const segment = segments[0];
|
|
641
819
|
if (segment.fires.length < 2) {
|
|
@@ -672,9 +850,9 @@ function hourCadence(ir, minute, opts) {
|
|
|
672
850
|
if (ir.pattern.second === "0" && fires <= maxClockTimes && offsetCleanStride(stride)) {
|
|
673
851
|
return null;
|
|
674
852
|
}
|
|
675
|
-
const
|
|
676
|
-
if (
|
|
677
|
-
return secondsClause(ir, "minute", opts) + " for one minute " + everyNthHour(
|
|
853
|
+
const minuteZeroStride = minute === 0 && subMinuteSecond(ir) && cleanStrideSegment(ir);
|
|
854
|
+
if (minuteZeroStride) {
|
|
855
|
+
return secondsClause(ir, "minute", opts) + " for one minute " + everyNthHour(minuteZeroStride, opts) + trailingQualifier(ir, opts);
|
|
678
856
|
}
|
|
679
857
|
if (minute === 0 && ir.pattern.second === "0") {
|
|
680
858
|
return hourStrideCadence(stride, opts) + trailingQualifier(ir, opts);
|
|
@@ -682,7 +860,7 @@ function hourCadence(ir, minute, opts) {
|
|
|
682
860
|
return hourCadenceLead(ir, minute, opts) + ", " + hourStrideCadence(stride, opts) + trailingQualifier(ir, opts);
|
|
683
861
|
}
|
|
684
862
|
function cleanStrideSegment(ir) {
|
|
685
|
-
const segments = ir
|
|
863
|
+
const segments = segmentsOf(ir, "hour");
|
|
686
864
|
const segment = segments.length === 1 && segments[0];
|
|
687
865
|
if (!segment || segment.kind !== "step" || segment.startToken.indexOf("-") !== -1 || !(segment.interval in stepOrdinals)) {
|
|
688
866
|
return null;
|
|
@@ -690,32 +868,28 @@ function cleanStrideSegment(ir) {
|
|
|
690
868
|
return segment;
|
|
691
869
|
}
|
|
692
870
|
function hasHourWindow(ir) {
|
|
693
|
-
return ir
|
|
871
|
+
return segmentsOf(ir, "hour").some(function range(segment) {
|
|
694
872
|
return segment.kind === "range";
|
|
695
873
|
});
|
|
696
874
|
}
|
|
697
875
|
function hourRangeWindowTail(ir, opts) {
|
|
698
876
|
const windows = [];
|
|
699
|
-
const
|
|
700
|
-
ir
|
|
877
|
+
const outlierHours = collectHourOutliers(ir);
|
|
878
|
+
segmentsOf(ir, "hour").forEach(function classify(segment) {
|
|
701
879
|
if (segment.kind === "range") {
|
|
702
|
-
windows.push(
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
} else {
|
|
709
|
-
singles.push(+segment.value);
|
|
880
|
+
windows.push(rangeWindow({
|
|
881
|
+
continuous: false,
|
|
882
|
+
from: +segment.bounds[0],
|
|
883
|
+
throughMinute: 0,
|
|
884
|
+
to: +segment.bounds[1]
|
|
885
|
+
}, opts));
|
|
710
886
|
}
|
|
711
887
|
});
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
}
|
|
718
|
-
return phrase;
|
|
888
|
+
const phrase = "every hour " + joinList(windows, opts);
|
|
889
|
+
const times = outlierHours.map(function time(hour) {
|
|
890
|
+
return getTime({ hour, minute: 0 }, opts);
|
|
891
|
+
});
|
|
892
|
+
return phrase + outlierTail(times, opts);
|
|
719
893
|
}
|
|
720
894
|
function hourRangeCadence(ir, minute, opts) {
|
|
721
895
|
if (minute !== 0 || !hasHourWindow(ir)) {
|
|
@@ -729,25 +903,29 @@ function hourRangeCadence(ir, minute, opts) {
|
|
|
729
903
|
}
|
|
730
904
|
return hourCadenceLead(ir, minute, opts) + ", " + hourRangeWindowTail(ir, opts) + trailingQualifier(ir, opts);
|
|
731
905
|
}
|
|
732
|
-
function seriesNumber(
|
|
733
|
-
const anyBig = values.some(function big(v) {
|
|
734
|
-
return +v > 10;
|
|
735
|
-
});
|
|
906
|
+
function seriesNumber() {
|
|
736
907
|
return function format(n) {
|
|
737
|
-
return
|
|
908
|
+
return "" + n;
|
|
909
|
+
};
|
|
910
|
+
}
|
|
911
|
+
function listNumber(count, opts) {
|
|
912
|
+
return count > 1 ? function asNumeral(n) {
|
|
913
|
+
return "" + n;
|
|
914
|
+
} : function spelled(n) {
|
|
915
|
+
return getNumber(n, opts);
|
|
738
916
|
};
|
|
739
917
|
}
|
|
740
918
|
function numberWords(fires, opts) {
|
|
741
|
-
return fires.map(
|
|
919
|
+
return fires.map(listNumber(fires.length, opts));
|
|
742
920
|
}
|
|
743
921
|
function segmentWords(segments, opts) {
|
|
744
|
-
const
|
|
922
|
+
const count = segments.reduce(function tally(sum, segment) {
|
|
745
923
|
if (segment.kind === "range") {
|
|
746
|
-
return
|
|
924
|
+
return sum + 1;
|
|
747
925
|
}
|
|
748
|
-
return segment.kind === "step" ? segment.fires :
|
|
749
|
-
});
|
|
750
|
-
const num =
|
|
926
|
+
return sum + (segment.kind === "step" ? segment.fires.length : 1);
|
|
927
|
+
}, 0);
|
|
928
|
+
const num = listNumber(count, opts);
|
|
751
929
|
return segments.flatMap(function word(segment) {
|
|
752
930
|
if (segment.kind === "range") {
|
|
753
931
|
return [num(segment.bounds[0]) + through(opts) + num(segment.bounds[1])];
|
|
@@ -779,6 +957,9 @@ function hourTimes(hours, opts) {
|
|
|
779
957
|
});
|
|
780
958
|
return joinList(times, opts);
|
|
781
959
|
}
|
|
960
|
+
function singleHourFire(times) {
|
|
961
|
+
return times.kind === "fires" && times.fires.length === 1;
|
|
962
|
+
}
|
|
782
963
|
function hourTimesFromPlan(ir, times, atContext, opts) {
|
|
783
964
|
if (times.kind === "fires") {
|
|
784
965
|
return hourTimes(times.fires, opts);
|
|
@@ -793,7 +974,7 @@ function segmentHours(segment) {
|
|
|
793
974
|
}
|
|
794
975
|
function hourSegmentTimes(ir, fold, atContext, opts) {
|
|
795
976
|
const { minute, second } = fold;
|
|
796
|
-
const segments = ir
|
|
977
|
+
const segments = segmentsOf(ir, "hour");
|
|
797
978
|
const plain = mixedTwelve(segments.flatMap(function entries(segment) {
|
|
798
979
|
return segmentHours(segment).map(function entry(hour) {
|
|
799
980
|
return { hour: +hour, minute, second };
|
|
@@ -826,28 +1007,47 @@ function disambiguateTimes(pieces, segments, atContext) {
|
|
|
826
1007
|
return index === 0 ? piece : "at " + piece;
|
|
827
1008
|
});
|
|
828
1009
|
}
|
|
829
|
-
function
|
|
1010
|
+
function joinWith(items, conjunction, opts) {
|
|
830
1011
|
if (items.length <= 1) {
|
|
831
1012
|
return items.join("");
|
|
832
1013
|
}
|
|
833
1014
|
if (items.length === 2) {
|
|
834
|
-
return items[0] +
|
|
1015
|
+
return items[0] + conjunction + items[1];
|
|
835
1016
|
}
|
|
836
|
-
const
|
|
837
|
-
return items.slice(0, -1).join(", ") +
|
|
1017
|
+
const tail = opts.style.serialComma ? "," + conjunction : conjunction;
|
|
1018
|
+
return items.slice(0, -1).join(", ") + tail + items[items.length - 1];
|
|
1019
|
+
}
|
|
1020
|
+
function joinList(items, opts) {
|
|
1021
|
+
return joinWith(items, " and ", opts);
|
|
1022
|
+
}
|
|
1023
|
+
function joinOr(items, opts) {
|
|
1024
|
+
return joinWith(items, " or ", opts);
|
|
838
1025
|
}
|
|
839
|
-
var trailingWords = {
|
|
1026
|
+
var trailingWords = {
|
|
1027
|
+
all: "",
|
|
1028
|
+
month: "in ",
|
|
1029
|
+
recurringWeekday: true,
|
|
1030
|
+
stepDate: "on ",
|
|
1031
|
+
weekday: "on "
|
|
1032
|
+
};
|
|
840
1033
|
var leadingWords = {
|
|
841
1034
|
all: "every day",
|
|
842
1035
|
month: "every day in ",
|
|
1036
|
+
recurringWeekday: false,
|
|
843
1037
|
stepDate: "",
|
|
844
1038
|
weekday: "every "
|
|
845
1039
|
};
|
|
846
1040
|
function trailingQualifier(ir, opts) {
|
|
1041
|
+
if (isDayUnion(ir, opts)) {
|
|
1042
|
+
return dayUnionCondition(ir, opts);
|
|
1043
|
+
}
|
|
847
1044
|
const phrase = dayQualifier(ir, trailingWords, opts);
|
|
848
1045
|
return phrase && " " + phrase;
|
|
849
1046
|
}
|
|
850
1047
|
function interpretDayQualifier(ir, opts) {
|
|
1048
|
+
if (isDayUnion(ir, opts)) {
|
|
1049
|
+
return "";
|
|
1050
|
+
}
|
|
851
1051
|
return dayQualifier(ir, leadingWords, opts) + " ";
|
|
852
1052
|
}
|
|
853
1053
|
function dayQualifier(ir, words, opts) {
|
|
@@ -859,7 +1059,11 @@ function dayQualifier(ir, words, opts) {
|
|
|
859
1059
|
return datePhrase(ir, words, opts);
|
|
860
1060
|
}
|
|
861
1061
|
if (pattern.weekday !== "*") {
|
|
862
|
-
const
|
|
1062
|
+
const quartzWeekday = quartzWeekdayPhrase(pattern.weekday, opts);
|
|
1063
|
+
if (quartzWeekday) {
|
|
1064
|
+
return monthScopeForRecurrence(quartzWeekday, ir, opts);
|
|
1065
|
+
}
|
|
1066
|
+
const weekdays = words.weekday + weekdayPhrase(ir, words.recurringWeekday, opts);
|
|
863
1067
|
return weekdays + monthScope(ir, opts);
|
|
864
1068
|
}
|
|
865
1069
|
if (pattern.month !== "*") {
|
|
@@ -871,10 +1075,14 @@ function datePhrase(ir, words, opts) {
|
|
|
871
1075
|
const pattern = ir.pattern;
|
|
872
1076
|
const quartzDate = quartzDatePhrase(pattern.date, opts);
|
|
873
1077
|
if (quartzDate) {
|
|
874
|
-
return quartzDate
|
|
1078
|
+
return monthScopeForRecurrence(quartzDate, ir, opts);
|
|
875
1079
|
}
|
|
876
1080
|
if (isOpenStep(pattern.date)) {
|
|
877
|
-
return
|
|
1081
|
+
return monthScopeForRecurrence(
|
|
1082
|
+
words.stepDate + stepDates(pattern.date),
|
|
1083
|
+
ir,
|
|
1084
|
+
opts
|
|
1085
|
+
);
|
|
878
1086
|
}
|
|
879
1087
|
if (pattern.month !== "*" && !monthFoldsIntoDate(ir)) {
|
|
880
1088
|
return "on the " + dateOrdinals(ir, opts) + monthScope(ir, opts);
|
|
@@ -886,13 +1094,88 @@ function datePhrase(ir, words, opts) {
|
|
|
886
1094
|
}
|
|
887
1095
|
function monthFoldsIntoDate(ir) {
|
|
888
1096
|
return !oddEvenMonth(ir.pattern.month) && // Reached only with a restricted month, which has segments.
|
|
889
|
-
ir
|
|
1097
|
+
segmentsOf(ir, "month").every(function flat(segment) {
|
|
890
1098
|
return segment.kind !== "range";
|
|
891
1099
|
});
|
|
892
1100
|
}
|
|
1101
|
+
function isDayUnion(ir, opts) {
|
|
1102
|
+
return ir.pattern.date !== "*" && ir.pattern.weekday !== "*" && !!opts.style.untilWindow && !opts.short;
|
|
1103
|
+
}
|
|
1104
|
+
function dayUnionCondition(ir, opts) {
|
|
1105
|
+
const pieces = [
|
|
1106
|
+
...dayUnionDatePieces(ir, opts),
|
|
1107
|
+
...dayUnionWeekdayPieces(ir, opts)
|
|
1108
|
+
];
|
|
1109
|
+
return " whenever the day is " + joinOr(pieces, opts);
|
|
1110
|
+
}
|
|
1111
|
+
function dayUnionMonthLead(ir, opts) {
|
|
1112
|
+
if (ir.pattern.month === "*") {
|
|
1113
|
+
return "";
|
|
1114
|
+
}
|
|
1115
|
+
return "in " + monthName(ir, opts) + " ";
|
|
1116
|
+
}
|
|
1117
|
+
function dayUnionDatePieces(ir, opts) {
|
|
1118
|
+
const dateField = ir.pattern.date;
|
|
1119
|
+
const quartz = quartzDatePhrase(dateField, opts);
|
|
1120
|
+
if (quartz) {
|
|
1121
|
+
return [quartz.replace(/^on /, "")];
|
|
1122
|
+
}
|
|
1123
|
+
const oddEven = oddEvenDay(dateField);
|
|
1124
|
+
if (oddEven) {
|
|
1125
|
+
return [oddEven];
|
|
1126
|
+
}
|
|
1127
|
+
const pieces = [];
|
|
1128
|
+
segmentsOf(ir, "date").forEach(function expand(segment) {
|
|
1129
|
+
if (segment.kind === "range") {
|
|
1130
|
+
pieces.push("from the " + getOrdinal(segment.bounds[0]) + through(opts) + "the " + getOrdinal(segment.bounds[1]));
|
|
1131
|
+
} else if (segment.kind === "step") {
|
|
1132
|
+
segment.fires.forEach(function fire(value) {
|
|
1133
|
+
pieces.push("the " + getOrdinal(value));
|
|
1134
|
+
});
|
|
1135
|
+
} else {
|
|
1136
|
+
pieces.push("the " + getOrdinal(segment.value));
|
|
1137
|
+
}
|
|
1138
|
+
});
|
|
1139
|
+
return pieces;
|
|
1140
|
+
}
|
|
1141
|
+
function dayUnionWeekdayPieces(ir, opts) {
|
|
1142
|
+
const weekdayField = ir.pattern.weekday;
|
|
1143
|
+
const quartz = quartzWeekdayPhrase(weekdayField, opts);
|
|
1144
|
+
if (quartz) {
|
|
1145
|
+
return [quartz.replace(/^on /, "")];
|
|
1146
|
+
}
|
|
1147
|
+
const pieces = [];
|
|
1148
|
+
segmentsOf(ir, "weekday").forEach(function expand(segment) {
|
|
1149
|
+
if (segment.kind === "range" && segment.bounds[0] === "1" && segment.bounds[1] === "5") {
|
|
1150
|
+
pieces.push("a weekday");
|
|
1151
|
+
} else if (segment.kind === "range") {
|
|
1152
|
+
pieces.push("a " + getWeekday(segment.bounds[0], opts) + through(opts) + "a " + getWeekday(segment.bounds[1], opts));
|
|
1153
|
+
} else if (segment.kind === "step") {
|
|
1154
|
+
segment.fires.forEach(function fire(value) {
|
|
1155
|
+
pieces.push("a " + getWeekday(value, opts));
|
|
1156
|
+
});
|
|
1157
|
+
} else {
|
|
1158
|
+
pieces.push("a " + getWeekday(segment.value, opts));
|
|
1159
|
+
}
|
|
1160
|
+
});
|
|
1161
|
+
return pieces;
|
|
1162
|
+
}
|
|
1163
|
+
function oddEvenDay(dateField) {
|
|
1164
|
+
if (!isOpenStep(dateField)) {
|
|
1165
|
+
return null;
|
|
1166
|
+
}
|
|
1167
|
+
const [start, step] = dateField.split("/");
|
|
1168
|
+
if (+step !== 2) {
|
|
1169
|
+
return null;
|
|
1170
|
+
}
|
|
1171
|
+
if (start === "*" || start === "1") {
|
|
1172
|
+
return "an odd-numbered day";
|
|
1173
|
+
}
|
|
1174
|
+
return start === "2" ? "an even-numbered day" : null;
|
|
1175
|
+
}
|
|
893
1176
|
function dateOrWeekday(ir, opts) {
|
|
894
1177
|
const pattern = ir.pattern;
|
|
895
|
-
const weekdayPart = quartzWeekdayPhrase(pattern.weekday, opts) || "on " + weekdayPhrase(ir, opts);
|
|
1178
|
+
const weekdayPart = quartzWeekdayPhrase(pattern.weekday, opts) || "on " + weekdayPhrase(ir, false, opts);
|
|
896
1179
|
if (pattern.month !== "*" && monthFoldsIntoDate(ir) && !quartzDatePhrase(pattern.date, opts) && !isOpenStep(pattern.date)) {
|
|
897
1180
|
return "on " + monthDatePhrase(ir, opts) + " or " + weekdayPart + " in " + monthName(ir, opts);
|
|
898
1181
|
}
|
|
@@ -943,10 +1226,13 @@ function quartzWeekdayPhrase(weekdayField, opts) {
|
|
|
943
1226
|
function monthDatePhrase(ir, opts) {
|
|
944
1227
|
const month = monthName(ir, opts);
|
|
945
1228
|
const days = renderSegments(
|
|
946
|
-
ir
|
|
1229
|
+
segmentsOf(ir, "date"),
|
|
947
1230
|
opts.style.ordinals ? getOrdinal : cardinalDay,
|
|
948
1231
|
opts
|
|
949
1232
|
);
|
|
1233
|
+
if (opts.style.dayFirst && ir.shapes.date === "single" && ir.shapes.month !== "single") {
|
|
1234
|
+
return "the " + getOrdinal(ir.pattern.date) + " of " + month;
|
|
1235
|
+
}
|
|
950
1236
|
return opts.style.dayFirst ? days + " " + month : month + " " + days;
|
|
951
1237
|
}
|
|
952
1238
|
function cardinalDay(value) {
|
|
@@ -958,6 +1244,19 @@ function monthScope(ir, opts) {
|
|
|
958
1244
|
}
|
|
959
1245
|
return " in " + monthName(ir, opts);
|
|
960
1246
|
}
|
|
1247
|
+
function monthScopeForRecurrence(phrase, ir, opts) {
|
|
1248
|
+
if (ir.pattern.month === "*") {
|
|
1249
|
+
return phrase;
|
|
1250
|
+
}
|
|
1251
|
+
const carriesRecurrence = phrase.indexOf(" of the month") !== -1;
|
|
1252
|
+
if (carriesRecurrence && ir.shapes.month === "range") {
|
|
1253
|
+
return phrase.replace(" of the month", " of each month") + " from " + monthName(ir, opts);
|
|
1254
|
+
}
|
|
1255
|
+
if (carriesRecurrence && (ir.shapes.month === "single" || ir.shapes.month === "step")) {
|
|
1256
|
+
return phrase.replace(" of the month", "") + " in " + monthName(ir, opts);
|
|
1257
|
+
}
|
|
1258
|
+
return phrase + " in " + monthName(ir, opts);
|
|
1259
|
+
}
|
|
961
1260
|
function stepDates(dateField) {
|
|
962
1261
|
const parts = dateField.split("/");
|
|
963
1262
|
const interval = +parts[1];
|
|
@@ -970,14 +1269,14 @@ function stepDates(dateField) {
|
|
|
970
1269
|
return phrase;
|
|
971
1270
|
}
|
|
972
1271
|
function dateOrdinals(ir, opts) {
|
|
973
|
-
return renderSegments(ir
|
|
1272
|
+
return renderSegments(segmentsOf(ir, "date"), getOrdinal, opts);
|
|
974
1273
|
}
|
|
975
1274
|
function monthName(ir, opts) {
|
|
976
1275
|
const oddEven = oddEvenMonth(ir.pattern.month);
|
|
977
1276
|
if (oddEven) {
|
|
978
1277
|
return oddEven;
|
|
979
1278
|
}
|
|
980
|
-
return renderSegments(ir
|
|
1279
|
+
return renderSegments(segmentsOf(ir, "month"), function name(value) {
|
|
981
1280
|
return getMonth(value, opts);
|
|
982
1281
|
}, opts);
|
|
983
1282
|
}
|
|
@@ -994,11 +1293,21 @@ function oddEvenMonth(monthField) {
|
|
|
994
1293
|
}
|
|
995
1294
|
return start === "2" ? "every even-numbered month" : null;
|
|
996
1295
|
}
|
|
997
|
-
function weekdayPhrase(ir, opts) {
|
|
998
|
-
const segments = orderWeekdaysForDisplay(ir
|
|
999
|
-
|
|
1296
|
+
function weekdayPhrase(ir, recurring, opts) {
|
|
1297
|
+
const segments = orderWeekdaysForDisplay(segmentsOf(ir, "weekday"));
|
|
1298
|
+
const hasRange = segments.some(function range(segment) {
|
|
1299
|
+
return segment.kind === "range";
|
|
1300
|
+
});
|
|
1301
|
+
const name = recurring && !hasRange ? function plural(value) {
|
|
1302
|
+
return pluralWeekday(value, opts);
|
|
1303
|
+
} : function singular(value) {
|
|
1000
1304
|
return getWeekday(value, opts);
|
|
1001
|
-
}
|
|
1305
|
+
};
|
|
1306
|
+
return renderSegments(segments, name, opts);
|
|
1307
|
+
}
|
|
1308
|
+
function pluralWeekday(value, opts) {
|
|
1309
|
+
const name = getWeekday(value, opts);
|
|
1310
|
+
return opts.short ? name : name + "s";
|
|
1002
1311
|
}
|
|
1003
1312
|
function renderSegments(segments, word, opts) {
|
|
1004
1313
|
const pieces = [];
|
|
@@ -1013,16 +1322,13 @@ function renderSegments(segments, word, opts) {
|
|
|
1013
1322
|
});
|
|
1014
1323
|
return joinList(pieces, opts);
|
|
1015
1324
|
}
|
|
1016
|
-
function isOpenStep(field) {
|
|
1017
|
-
return field.indexOf("/") !== -1 && field.indexOf("-") === -1 && field.indexOf(",") === -1;
|
|
1018
|
-
}
|
|
1019
1325
|
function applyYear(description, ir, opts) {
|
|
1020
1326
|
const yearField = ir.pattern.year;
|
|
1021
1327
|
if (yearField === "*") {
|
|
1022
1328
|
return description;
|
|
1023
1329
|
}
|
|
1024
1330
|
if (yearField.indexOf("/") !== -1) {
|
|
1025
|
-
return description + " " + stepYears(yearField, opts);
|
|
1331
|
+
return description + ", " + stepYears(yearField, opts);
|
|
1026
1332
|
}
|
|
1027
1333
|
const label = yearLabel(yearField, opts);
|
|
1028
1334
|
if (yearField.indexOf("-") === -1 && yearField.indexOf(",") === -1 && ir.pattern.date !== "*" && description.indexOf(" at ") !== -1) {
|
|
@@ -1035,6 +1341,9 @@ function yearLabel(yearField, opts) {
|
|
|
1035
1341
|
if (yearField.indexOf(",") !== -1) {
|
|
1036
1342
|
return joinList(yearField.split(","), opts);
|
|
1037
1343
|
}
|
|
1344
|
+
if (yearField.indexOf("-") !== -1) {
|
|
1345
|
+
return yearField.split("-").join(through(opts));
|
|
1346
|
+
}
|
|
1038
1347
|
return yearField;
|
|
1039
1348
|
}
|
|
1040
1349
|
function stepYears(yearField, opts) {
|
|
@@ -1044,7 +1353,7 @@ function stepYears(yearField, opts) {
|
|
|
1044
1353
|
if (interval <= 1) {
|
|
1045
1354
|
return "every year";
|
|
1046
1355
|
}
|
|
1047
|
-
let phrase = "every " + getNumber(interval, opts) + " years";
|
|
1356
|
+
let phrase = interval === 2 ? "every other year" : "every " + getNumber(interval, opts) + " years";
|
|
1048
1357
|
if (start !== "*" && start !== "0") {
|
|
1049
1358
|
phrase += " from " + start;
|
|
1050
1359
|
}
|