cronli5 0.7.2 → 0.8.2
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 +57 -0
- package/README.md +30 -13
- package/cronli5.min.js +2 -2
- package/dist/cronli5.cjs +67 -7
- package/dist/cronli5.js +67 -7
- package/dist/lang/de.cjs +10 -9
- package/dist/lang/de.js +10 -9
- package/dist/lang/en.cjs +9 -4
- package/dist/lang/en.js +9 -4
- package/dist/lang/es.cjs +7 -3
- package/dist/lang/es.js +7 -3
- package/dist/lang/fi.cjs +11 -6
- package/dist/lang/fi.js +11 -6
- package/dist/lang/fr.cjs +7 -3
- package/dist/lang/fr.js +7 -3
- package/dist/lang/pt.cjs +7 -3
- package/dist/lang/pt.js +7 -3
- package/dist/lang/zh.cjs +29 -8
- package/dist/lang/zh.js +29 -8
- package/package.json +1 -1
- package/src/core/index.ts +7 -3
- package/src/core/quartz.ts +97 -0
- package/src/core/schedule.ts +1 -0
- package/src/core/specs.ts +2 -2
- package/src/cronli5.ts +20 -3
- package/src/lang/de/index.ts +41 -30
- package/src/lang/en/index.ts +25 -7
- package/src/lang/es/index.ts +23 -7
- package/src/lang/fi/index.ts +36 -18
- package/src/lang/fr/index.ts +23 -7
- package/src/lang/pt/index.ts +23 -6
- package/src/lang/zh/index.ts +81 -12
- package/src/types.ts +44 -0
- package/types/core/quartz.d.ts +4 -0
- package/types/core/schedule.d.ts +1 -0
- package/types/cronli5.d.ts +4 -4
- package/types/types.d.ts +39 -0
package/src/lang/de/index.ts
CHANGED
|
@@ -738,12 +738,13 @@ function renderComposeSeconds(
|
|
|
738
738
|
}
|
|
739
739
|
}
|
|
740
740
|
|
|
741
|
-
// A
|
|
742
|
-
// clock-time rest would
|
|
743
|
-
//
|
|
744
|
-
// Bind the seconds into the explicit clock minute in the genitive
|
|
745
|
-
// Minute 9:
|
|
746
|
-
|
|
741
|
+
// A second over a single fixed minute and a specific hour is a single fixed
|
|
742
|
+
// timestamp: the clock-time rest would float the seconds as a separate
|
|
743
|
+
// apposition ("jede Sekunde, um 9:02 Uhr"), hiding that they belong to that
|
|
744
|
+
// one minute. Bind the seconds into the explicit clock minute in the genitive
|
|
745
|
+
// ("der Minute 9:02"), the same fusion the minute-0 case ("der Minute 9:00")
|
|
746
|
+
// uses; the recurring "täglich"/day frame is added in `describe`.
|
|
747
|
+
if (composeSingleMinute(schedule, plan)) {
|
|
747
748
|
return secondsLead(schedule) + ' ' +
|
|
748
749
|
clockMinuteGenitive(plan.rest.times, opts.style.sep);
|
|
749
750
|
}
|
|
@@ -765,21 +766,23 @@ function renderComposeSeconds(
|
|
|
765
766
|
return lead + render(schedule, plan.rest, opts);
|
|
766
767
|
}
|
|
767
768
|
|
|
768
|
-
// True when a compose-seconds plan is a sub-minute second over a
|
|
769
|
-
// clock-time rest — the
|
|
770
|
-
// the
|
|
771
|
-
|
|
769
|
+
// True when a compose-seconds plan is a sub-minute second over a single fixed
|
|
770
|
+
// minute's clock-time rest — the single fixed timestamp whose seconds must fuse
|
|
771
|
+
// to the explicit clock minute rather than float as a separate apposition.
|
|
772
|
+
// Minute 0 ("der Minute 0:00") is just this with the minute being 0; any single
|
|
773
|
+
// fixed minute fuses the same way.
|
|
774
|
+
function composeSingleMinute(
|
|
772
775
|
schedule: Schedule,
|
|
773
776
|
plan: Extract<PlanNode, {kind: 'composeSeconds'}>
|
|
774
777
|
): plan is Extract<PlanNode, {kind: 'composeSeconds'}> &
|
|
775
778
|
{rest: Extract<PlanNode, {kind: 'clockTimes'}>} {
|
|
776
779
|
return plan.rest.kind === 'clockTimes' &&
|
|
777
|
-
|
|
780
|
+
schedule.shapes.minute === 'single';
|
|
778
781
|
}
|
|
779
782
|
|
|
780
|
-
// The pinned clock minute in the genitive: "der Minute 9:
|
|
781
|
-
// "der Minuten 9:00, 10:00 und 17:00" for several — the explicit
|
|
782
|
-
// minute
|
|
783
|
+
// The pinned clock minute in the genitive: "der Minute 9:02" for one hour,
|
|
784
|
+
// "der Minuten 9:00, 10:00 und 17:00" for several — the explicit minute so the
|
|
785
|
+
// single-fixed-minute confinement stays visible.
|
|
783
786
|
function clockMinuteGenitive(
|
|
784
787
|
times: {hour: number; minute: number}[],
|
|
785
788
|
sep: string
|
|
@@ -940,18 +943,22 @@ function renderMinuteFrequency(
|
|
|
940
943
|
if (plan.hours.kind === 'during') {
|
|
941
944
|
// A bounded or uneven hour stride confines the minute cadence to its own
|
|
942
945
|
// endpoint-pinning hour cadence ("alle 15 Minuten, alle 5 Stunden von 0 bis
|
|
943
|
-
// 20 Uhr").
|
|
946
|
+
// 20 Uhr"). That hour step is the sole hour authority, so an offset minute
|
|
947
|
+
// cadence drops its generic "jeder Stunde" (an every-hour scope conflicting
|
|
948
|
+
// with the step); a list of specific hours keeps it.
|
|
944
949
|
const cadence = unevenHourCadence(schedule);
|
|
945
950
|
|
|
946
951
|
return cadence ?
|
|
947
|
-
|
|
952
|
+
stepClause(segment, UNITS.minute, '') + ', ' + cadence :
|
|
948
953
|
base + ' ' + duringHours(schedule, plan.hours.times, sep);
|
|
949
954
|
}
|
|
950
955
|
|
|
951
956
|
if (plan.hours.kind === 'step') {
|
|
952
957
|
// The plan carries a step only for a clean step (dividing the day):
|
|
953
|
-
// confine the cadence to every Nth hour ("in jeder zweiten Stunde").
|
|
954
|
-
|
|
958
|
+
// confine the cadence to every Nth hour ("in jeder zweiten Stunde"). The
|
|
959
|
+
// hour step is the sole hour authority, so the minute cadence drops its
|
|
960
|
+
// generic "jeder Stunde".
|
|
961
|
+
return stepClause(segment, UNITS.minute, '') + ' ' +
|
|
955
962
|
everyNthHour(stepSegment(schedule, 'hour'));
|
|
956
963
|
}
|
|
957
964
|
|
|
@@ -1405,28 +1412,31 @@ function qualifier(schedule: Schedule, months: Months): string {
|
|
|
1405
1412
|
|
|
1406
1413
|
// Plan kinds whose clause is a clock time: the qualifier leads them ("montags
|
|
1407
1414
|
// um 9 Uhr"); a frequency clause trails it ("jede Minute montags"). The
|
|
1408
|
-
// minute
|
|
1409
|
-
// qualifier leads it ("montags jede Sekunde der Minute 9:00").
|
|
1415
|
+
// single-fixed-minute compose-seconds clause is anchored on a clock minute too,
|
|
1416
|
+
// so the qualifier leads it ("montags jede Sekunde der Minute 9:00").
|
|
1410
1417
|
const LEADING_PLANS = new Set(['clockTimes']);
|
|
1411
1418
|
|
|
1412
1419
|
// True when the leading qualifier should precede the clause: a clock-time
|
|
1413
|
-
// plan, or the minute
|
|
1420
|
+
// plan, or the single-fixed-minute compose-seconds clause that surfaces a clock
|
|
1421
|
+
// minute.
|
|
1414
1422
|
function leadsQualifier(schedule: Schedule): boolean {
|
|
1415
|
-
return LEADING_PLANS.has(schedule.plan.kind) ||
|
|
1423
|
+
return LEADING_PLANS.has(schedule.plan.kind) ||
|
|
1424
|
+
isComposeSingleMinute(schedule);
|
|
1416
1425
|
}
|
|
1417
1426
|
|
|
1418
|
-
// Whether the planned clause is the minute
|
|
1419
|
-
// (a sub-minute second over a minute
|
|
1420
|
-
|
|
1427
|
+
// Whether the planned clause is the single-fixed-minute compose-seconds
|
|
1428
|
+
// confinement (a sub-minute second over a single fixed minute's clock-time
|
|
1429
|
+
// rest).
|
|
1430
|
+
function isComposeSingleMinute(schedule: Schedule): boolean {
|
|
1421
1431
|
return schedule.plan.kind === 'composeSeconds' &&
|
|
1422
|
-
|
|
1432
|
+
composeSingleMinute(schedule, schedule.plan);
|
|
1423
1433
|
}
|
|
1424
1434
|
|
|
1425
1435
|
// True when the clause is a bare daily clock-time list and so needs the
|
|
1426
1436
|
// "täglich" frame to read as recurring, not a one-off: clockTimes always, the
|
|
1427
|
-
// minute
|
|
1428
|
-
// hour step (rendered as its fire list "um 0, 5, … Uhr", not the cadence
|
|
1429
|
-
// N Stunden"). A frequency clause already implies recurrence.
|
|
1437
|
+
// single-fixed-minute compose-seconds clause (a recurring clock minute), and an
|
|
1438
|
+
// uneven hour step (rendered as its fire list "um 0, 5, … Uhr", not the cadence
|
|
1439
|
+
// "alle N Stunden"). A frequency clause already implies recurrence.
|
|
1430
1440
|
function needsDailyFrame(schedule: Schedule): boolean {
|
|
1431
1441
|
// An hour cadence is a sub-daily frequency, not a daily clock-time list, so
|
|
1432
1442
|
// it must not take the "täglich" frame ("alle 2 Stunden", not "täglich alle
|
|
@@ -1435,7 +1445,7 @@ function needsDailyFrame(schedule: Schedule): boolean {
|
|
|
1435
1445
|
return false;
|
|
1436
1446
|
}
|
|
1437
1447
|
|
|
1438
|
-
if (schedule.plan.kind === 'clockTimes' ||
|
|
1448
|
+
if (schedule.plan.kind === 'clockTimes' || isComposeSingleMinute(schedule)) {
|
|
1439
1449
|
return true;
|
|
1440
1450
|
}
|
|
1441
1451
|
|
|
@@ -1464,6 +1474,7 @@ function normalizeOptions(options?: Cronli5Options): Opts {
|
|
|
1464
1474
|
return {
|
|
1465
1475
|
ampm: typeof options.ampm === 'boolean' ? options.ampm : false,
|
|
1466
1476
|
lenient: !!options.lenient,
|
|
1477
|
+
quartz: !!options.quartz,
|
|
1467
1478
|
seconds: !!options.seconds,
|
|
1468
1479
|
short: !!options.short,
|
|
1469
1480
|
style,
|
package/src/lang/en/index.ts
CHANGED
|
@@ -137,6 +137,7 @@ function normalizeOptions(options?: Cronli5Options): NormalizedOptions {
|
|
|
137
137
|
return {
|
|
138
138
|
ampm: typeof options.ampm === 'boolean' ? options.ampm : true,
|
|
139
139
|
lenient: !!options.lenient,
|
|
140
|
+
quartz: !!options.quartz,
|
|
140
141
|
seconds: !!options.seconds,
|
|
141
142
|
short: !!options.short,
|
|
142
143
|
style: resolveDialect(options.dialect),
|
|
@@ -580,8 +581,11 @@ function renderMinuteFrequency(schedule: Schedule,
|
|
|
580
581
|
else if (plan.hours.kind === 'step') {
|
|
581
582
|
// The plan carries a step only for a clean stride (dividing the day),
|
|
582
583
|
// which confines the cadence to every Nth hour; a stepped hour field's
|
|
583
|
-
// first segment is a step segment.
|
|
584
|
-
|
|
584
|
+
// first segment is a step segment. The hour step scopes the hours, so an
|
|
585
|
+
// offset cadence drops "past the hour" and joins with a comma.
|
|
586
|
+
const bound = withoutHourAnchor(phrase);
|
|
587
|
+
|
|
588
|
+
phrase = bound + (bound === phrase ? ' ' : ', ') +
|
|
585
589
|
everyNthHour(stepSegment(schedule, 'hour'), opts);
|
|
586
590
|
}
|
|
587
591
|
|
|
@@ -702,13 +706,14 @@ function renderMinuteSpanAcrossHourStep(schedule: Schedule,
|
|
|
702
706
|
}
|
|
703
707
|
|
|
704
708
|
// A minute list keeps the same cadence clause; only its lead differs. An
|
|
705
|
-
// offset/uneven step the core enumerated to that list reads as a stride.
|
|
706
|
-
|
|
709
|
+
// offset/uneven step the core enumerated to that list reads as a stride. The
|
|
710
|
+
// hour step scopes the hours, so the lead drops its generic "past the hour".
|
|
711
|
+
const lead = withoutHourAnchor(plan.form === 'list' ?
|
|
707
712
|
strideFromSegments(segmentsOf(schedule, 'minute'), 'minute', 'hour',
|
|
708
713
|
opts) ??
|
|
709
714
|
listPastThe(segmentWords(segmentsOf(schedule, 'minute'), opts),
|
|
710
715
|
'minute', 'hour', opts) :
|
|
711
|
-
minuteRangeLead(schedule.pattern.minute, opts);
|
|
716
|
+
minuteRangeLead(schedule.pattern.minute, opts));
|
|
712
717
|
// A bounded or uneven hour step reads as its endpoint-pinning cadence after
|
|
713
718
|
// the minute lead, not a wall of clock-time columns; an offset-clean step
|
|
714
719
|
// keeps its existing per-step phrasing.
|
|
@@ -931,10 +936,14 @@ function renderCompactClockTimes(schedule: Schedule,
|
|
|
931
936
|
listPastThe(segmentWords(segmentsOf(schedule, 'minute'), opts),
|
|
932
937
|
'minute', 'hour', opts);
|
|
933
938
|
// A uneven hour stride reads as a cadence after the minute lead, not a wall
|
|
934
|
-
// of clock-time columns.
|
|
939
|
+
// of clock-time columns. The hour step is the sole hour authority there, so
|
|
940
|
+
// the minute lead drops its generic "past the hour" (an every-hour scope that
|
|
941
|
+
// would conflict with the step); the clock-time branch keeps it, naming
|
|
942
|
+
// specific hours rather than a step.
|
|
935
943
|
const cadence = unevenHourCadence(schedule, opts);
|
|
936
944
|
const phrase = cadence ?
|
|
937
|
-
minuteLead + ', ' + cadence +
|
|
945
|
+
withoutHourAnchor(minuteLead) + ', ' + cadence +
|
|
946
|
+
trailingQualifier(schedule, opts) :
|
|
938
947
|
minuteLead +
|
|
939
948
|
', at ' +
|
|
940
949
|
hourSegmentTimes(schedule, {minute: 0, second: null}, true, opts) +
|
|
@@ -1747,6 +1756,15 @@ function listPastThe(words: (string | number)[], unit: string, anchor: string,
|
|
|
1747
1756
|
anchor;
|
|
1748
1757
|
}
|
|
1749
1758
|
|
|
1759
|
+
// Strip the generic "past the hour" anchor from a minute-cadence lead. When the
|
|
1760
|
+
// hour field is restricted (a step or window), the hour clause is the sole hour
|
|
1761
|
+
// authority, so the cadence must not also assert "every hour" — "past the hour"
|
|
1762
|
+
// alongside a stepped/windowed hour reads as a conflicting every-hour scope. An
|
|
1763
|
+
// unrestricted hour keeps the anchor (it is the only hour statement there).
|
|
1764
|
+
function withoutHourAnchor(lead: string): string {
|
|
1765
|
+
return lead.replace(/ past the hour$/, '');
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1750
1768
|
// A clock time reads as a word ("noon"/"midnight") only at exact 12:00 or
|
|
1751
1769
|
// 0:00 with no minute or second.
|
|
1752
1770
|
function wordTime(hour: number | string, minute: number | string,
|
package/src/lang/es/index.ts
CHANGED
|
@@ -125,6 +125,7 @@ function normalizeOptions(options?: Cronli5Options): Opts {
|
|
|
125
125
|
// 12-hour for Mexico/US); an explicit `{ampm}` option overrides it.
|
|
126
126
|
ampm: typeof options.ampm === 'boolean' ? options.ampm : style.ampm,
|
|
127
127
|
lenient: !!options.lenient,
|
|
128
|
+
quartz: !!options.quartz,
|
|
128
129
|
seconds: !!options.seconds,
|
|
129
130
|
short: !!options.short,
|
|
130
131
|
style,
|
|
@@ -466,6 +467,16 @@ function minutesList(schedule: Schedule, opts: Opts): string {
|
|
|
466
467
|
joinList(segmentWords(segmentsOf(schedule, 'minute'))) + ' de cada hora';
|
|
467
468
|
}
|
|
468
469
|
|
|
470
|
+
// Strip the generic "de cada hora" anchor from a minute-cadence lead. Under an
|
|
471
|
+
// hour STEP the hour clause is the sole hour authority, so the cadence must not
|
|
472
|
+
// also assert "de cada hora" — alongside a stepped hour it reads as a
|
|
473
|
+
// conflicting every-hour scope ("de cada hora, cada cuatro horas"). An hour
|
|
474
|
+
// WINDOW and an unrestricted hour keep the anchor (the window already names the
|
|
475
|
+
// hours; an open hour has no other hour statement).
|
|
476
|
+
function withoutHourAnchor(lead: string): string {
|
|
477
|
+
return lead.replace(/ de cada hora$/, '');
|
|
478
|
+
}
|
|
479
|
+
|
|
469
480
|
// "cada minuto del 0 al 30". The standalone renderer adds "de cada hora";
|
|
470
481
|
// when an hour qualifier follows ("..., a las 09:00", "..., cada dos
|
|
471
482
|
// horas") it would contradict, so it is not baked in here.
|
|
@@ -593,8 +604,10 @@ function renderMinuteFrequency(
|
|
|
593
604
|
}
|
|
594
605
|
else if (plan.hours.kind === 'step') {
|
|
595
606
|
// A clean stride is a confinement ("las horas pares", or the active-hour
|
|
596
|
-
// list), never a juxtaposed cadence ("cada dos horas").
|
|
597
|
-
|
|
607
|
+
// list), never a juxtaposed cadence ("cada dos horas"). The hour step
|
|
608
|
+
// scopes the hours, so an offset cadence drops "de cada hora".
|
|
609
|
+
phrase = withoutHourAnchor(phrase) + ', ' +
|
|
610
|
+
stepHourSpan(stepSegment(schedule, 'hour'), opts);
|
|
598
611
|
}
|
|
599
612
|
|
|
600
613
|
return phrase + trailingQualifier(schedule, opts);
|
|
@@ -679,10 +692,10 @@ function renderMinuteSpanAcrossHourStep(
|
|
|
679
692
|
|
|
680
693
|
// A minute list keeps the same cadence clause as the range; only its lead
|
|
681
694
|
// differs ("en los minutos 5 y 30 de cada hora" vs "cada minuto del 0 al
|
|
682
|
-
// 30").
|
|
683
|
-
const lead = plan.form === 'list' ?
|
|
695
|
+
// 30"). The hour step scopes the hours, so the lead drops "de cada hora".
|
|
696
|
+
const lead = withoutHourAnchor(plan.form === 'list' ?
|
|
684
697
|
minutesList(schedule, opts) :
|
|
685
|
-
minuteRangeLead(schedule.pattern.minute);
|
|
698
|
+
minuteRangeLead(schedule.pattern.minute));
|
|
686
699
|
|
|
687
700
|
return lead + ', ' +
|
|
688
701
|
(cadence ?? stepHours(segment, opts)) + trailingQualifier(schedule, opts);
|
|
@@ -1270,10 +1283,13 @@ function renderCompactClockTimes(
|
|
|
1270
1283
|
}
|
|
1271
1284
|
|
|
1272
1285
|
// A uneven hour stride reads as a cadence after the minute lead, not a wall
|
|
1273
|
-
// of clock-time columns.
|
|
1286
|
+
// of clock-time columns. That hour step is the sole hour authority, so the
|
|
1287
|
+
// minute lead drops its generic "de cada hora" (an every-hour scope that
|
|
1288
|
+
// would conflict with the step); the clock-time branch keeps it, naming
|
|
1289
|
+
// specific hours rather than a step.
|
|
1274
1290
|
const cadence = unevenHourCadence(schedule, opts);
|
|
1275
1291
|
const phrase = cadence ?
|
|
1276
|
-
minutesList(schedule, opts) + ', ' + cadence +
|
|
1292
|
+
withoutHourAnchor(minutesList(schedule, opts)) + ', ' + cadence +
|
|
1277
1293
|
trailingQualifier(schedule, opts) :
|
|
1278
1294
|
minutesList(schedule, opts) + ', ' +
|
|
1279
1295
|
hourContextTimes(schedule, opts) + trailingQualifier(schedule, opts);
|
package/src/lang/fi/index.ts
CHANGED
|
@@ -200,6 +200,7 @@ function normalizeOptions(options?: Cronli5Options): NormalizedOptions {
|
|
|
200
200
|
return {
|
|
201
201
|
ampm: false,
|
|
202
202
|
lenient: !!options.lenient,
|
|
203
|
+
quartz: !!options.quartz,
|
|
203
204
|
seconds: !!options.seconds,
|
|
204
205
|
short: !!options.short,
|
|
205
206
|
style: resolveDialect(options.dialect),
|
|
@@ -425,15 +426,15 @@ function renderComposeSeconds(
|
|
|
425
426
|
return composeSecondsOverMinuteStep(schedule, plan.rest, opts);
|
|
426
427
|
}
|
|
427
428
|
|
|
428
|
-
// A
|
|
429
|
-
// clock-time rest would
|
|
430
|
-
//
|
|
431
|
-
// the seconds to the explicit clock minute with the "minuutin
|
|
432
|
-
// frame (an "of"/during form, never a range) and trail the day
|
|
433
|
-
// ("joka sekunti minuutin 9.
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
return
|
|
429
|
+
// A second over a single fixed minute and a specific hour is a single fixed
|
|
430
|
+
// timestamp: the clock-time rest would float the seconds as a separate clause
|
|
431
|
+
// ("joka sekunti, joka päivä klo 9.02"), hiding that they belong to that one
|
|
432
|
+
// minute. Bind the seconds to the explicit clock minute with the "minuutin
|
|
433
|
+
// HH.MM aikana" frame (an "of"/during form, never a range) and trail the day
|
|
434
|
+
// qualifier ("joka sekunti minuutin 9.02 aikana, joka päivä") — the same
|
|
435
|
+
// fusion the minute-0 case ("minuutin 9.00 aikana") uses.
|
|
436
|
+
if (plan.rest.kind === 'clockTimes' && schedule.shapes.minute === 'single') {
|
|
437
|
+
return composeSingleMinute(schedule, plan.rest, opts);
|
|
437
438
|
}
|
|
438
439
|
|
|
439
440
|
// A wildcard second under a minute */2 with a wildcard hour juxtaposes two
|
|
@@ -472,11 +473,13 @@ function isEveryOtherMinuteSeconds(
|
|
|
472
473
|
return seg.startToken === '*' && seg.interval === 2;
|
|
473
474
|
}
|
|
474
475
|
|
|
475
|
-
// The minute
|
|
476
|
-
// in the "minuutin/minuuttien HH.
|
|
477
|
-
// a range — a range would round-trip back to the whole hour) and
|
|
478
|
-
// qualifier ("joka sekunti minuutin 9.
|
|
479
|
-
|
|
476
|
+
// The single-fixed-minute confinement: bind the seconds to the explicit clock
|
|
477
|
+
// minute(s) in the "minuutin/minuuttien HH.MM aikana" frame (an "of"/during
|
|
478
|
+
// form, never a range — a range would round-trip back to the whole hour) and
|
|
479
|
+
// trail the day qualifier ("joka sekunti minuutin 9.02 aikana, joka päivä").
|
|
480
|
+
// Minute 0 ("minuutin 9.00 aikana") is just this with the minute being 0; any
|
|
481
|
+
// single fixed minute fuses the same way.
|
|
482
|
+
function composeSingleMinute(
|
|
480
483
|
schedule: Schedule,
|
|
481
484
|
rest: Extract<PlanNode, {kind: 'clockTimes'}>,
|
|
482
485
|
opts: NormalizedOptions
|
|
@@ -694,8 +697,10 @@ function renderMinuteFrequency(
|
|
|
694
697
|
const cadence = unevenHourCadence(schedule, opts);
|
|
695
698
|
|
|
696
699
|
if (cadence !== null) {
|
|
697
|
-
|
|
698
|
-
|
|
700
|
+
// The hour step is the sole hour authority, so an offset minute cadence
|
|
701
|
+
// drops its generic "jokaisen tunnin" every-hour scope.
|
|
702
|
+
return withoutHourAnchor(stepCycle60(seg, units.minute, opts)) + ', ' +
|
|
703
|
+
cadence + trailingQualifier(schedule, opts);
|
|
699
704
|
}
|
|
700
705
|
|
|
701
706
|
// When the step renders as anchored ("kohdalla"), the per-hour windows
|
|
@@ -713,13 +718,16 @@ function renderMinuteFrequency(
|
|
|
713
718
|
trailingQualifier(schedule, opts);
|
|
714
719
|
}
|
|
715
720
|
|
|
716
|
-
|
|
721
|
+
const phraseBase = stepCycle60(seg, units.minute, opts);
|
|
722
|
+
let phrase = phraseBase;
|
|
717
723
|
|
|
718
724
|
if (plan.hours.kind === 'window') {
|
|
719
725
|
phrase += ' ' + hourWindow(plan.hours, opts);
|
|
720
726
|
}
|
|
721
727
|
else if (plan.hours.kind === 'step') {
|
|
722
|
-
|
|
728
|
+
// The hour step is the sole hour authority, so the minute cadence drops its
|
|
729
|
+
// generic "jokaisen tunnin" every-hour scope.
|
|
730
|
+
phrase = withoutHourAnchor(phraseBase) + ' ' +
|
|
723
731
|
everyNthHour(stepSegment(schedule, 'hour'), opts);
|
|
724
732
|
}
|
|
725
733
|
|
|
@@ -1166,6 +1174,16 @@ function stepCycle60(
|
|
|
1166
1174
|
}, opts);
|
|
1167
1175
|
}
|
|
1168
1176
|
|
|
1177
|
+
// Strip the generic "jokaisen tunnin" anchor from an offset minute-cadence
|
|
1178
|
+
// lead. When the hour field is a restricted step, the hour clause is the sole
|
|
1179
|
+
// hour authority, so the cadence must not also assert "jokaisen tunnin" (every
|
|
1180
|
+
// hour) — alongside a stepped hour it conflicts as an every-hour scope.
|
|
1181
|
+
// An unrestricted hour, and an hour WINDOW, keep the anchor (the window names
|
|
1182
|
+
// the hours without an every-hour-of-the-day conflict).
|
|
1183
|
+
function withoutHourAnchor(lead: string): string {
|
|
1184
|
+
return lead.replace(' ' + units.minute.anchor + ' ', ' ');
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1169
1187
|
// "kahden tunnin välein", "klo 0, 10 ja 20", or "viiden tunnin välein
|
|
1170
1188
|
// klo 1 alkaen".
|
|
1171
1189
|
function stepHours(segment: StepSegment, opts: NormalizedOptions): string {
|
package/src/lang/fr/index.ts
CHANGED
|
@@ -138,6 +138,7 @@ function normalizeOptions(options?: Cronli5Options): Opts {
|
|
|
138
138
|
// satisfied without the 12-hour machinery the es donor carried.
|
|
139
139
|
ampm: false,
|
|
140
140
|
lenient: !!options.lenient,
|
|
141
|
+
quartz: !!options.quartz,
|
|
141
142
|
seconds: !!options.seconds,
|
|
142
143
|
short: !!options.short,
|
|
143
144
|
style,
|
|
@@ -475,6 +476,16 @@ function minutesList(schedule: Schedule, opts: Opts): string {
|
|
|
475
476
|
joinList(segmentWords(segmentsOf(schedule, 'minute'))) + ' de chaque heure';
|
|
476
477
|
}
|
|
477
478
|
|
|
479
|
+
// Strip the generic "de chaque heure" anchor from a minute-cadence lead. Under
|
|
480
|
+
// an hour STEP the hour clause is the sole hour authority, so the cadence must
|
|
481
|
+
// not also assert "de chaque heure" — alongside a stepped hour it reads as a
|
|
482
|
+
// conflicting every-hour scope ("de chaque heure, toutes les quatre heures").
|
|
483
|
+
// An hour WINDOW and an unrestricted hour keep the anchor (the window already
|
|
484
|
+
// names the hours; an open hour has no other hour statement).
|
|
485
|
+
function withoutHourAnchor(lead: string): string {
|
|
486
|
+
return lead.replace(/ de chaque heure$/, '');
|
|
487
|
+
}
|
|
488
|
+
|
|
478
489
|
// "chaque minute de 0 à 30". The standalone renderer adds "de chaque heure";
|
|
479
490
|
// when an hour qualifier follows ("..., à 9 h", "..., toutes les deux heures")
|
|
480
491
|
// it would contradict, so it is not baked in here.
|
|
@@ -549,8 +560,10 @@ function renderMinuteFrequency(
|
|
|
549
560
|
}
|
|
550
561
|
else if (plan.hours.kind === 'step') {
|
|
551
562
|
// A clean stride is a confinement ("les heures paires", or the active-hour
|
|
552
|
-
// list), never a juxtaposed cadence ("toutes les deux heures").
|
|
553
|
-
|
|
563
|
+
// list), never a juxtaposed cadence ("toutes les deux heures"). The hour
|
|
564
|
+
// step scopes the hours, so an offset cadence drops "de chaque heure".
|
|
565
|
+
phrase = withoutHourAnchor(phrase) + ', ' +
|
|
566
|
+
stepHourSpan(stepSegment(schedule, 'hour'), opts);
|
|
554
567
|
}
|
|
555
568
|
|
|
556
569
|
return phrase + trailingQualifier(schedule, opts);
|
|
@@ -634,10 +647,10 @@ function renderMinuteSpanAcrossHourStep(
|
|
|
634
647
|
|
|
635
648
|
// A minute list keeps the same cadence clause as the range; only its lead
|
|
636
649
|
// differs ("aux minutes 5 et 30 de chaque heure" vs "chaque minute de 0 à
|
|
637
|
-
// 30").
|
|
638
|
-
const lead = plan.form === 'list' ?
|
|
650
|
+
// 30"). The hour step scopes the hours, so the lead drops "de chaque heure".
|
|
651
|
+
const lead = withoutHourAnchor(plan.form === 'list' ?
|
|
639
652
|
minutesList(schedule, opts) :
|
|
640
|
-
minuteRangeLead(schedule.pattern.minute);
|
|
653
|
+
minuteRangeLead(schedule.pattern.minute));
|
|
641
654
|
|
|
642
655
|
return lead + ', ' +
|
|
643
656
|
(cadence ?? stepHours(segment, opts)) + trailingQualifier(schedule, opts);
|
|
@@ -990,10 +1003,13 @@ function renderCompactClockTimes(
|
|
|
990
1003
|
}
|
|
991
1004
|
|
|
992
1005
|
// A uneven hour stride reads as a cadence after the minute lead, not a wall
|
|
993
|
-
// of clock-time columns.
|
|
1006
|
+
// of clock-time columns. That hour step is the sole hour authority, so the
|
|
1007
|
+
// minute lead drops its generic "de chaque heure" (an every-hour scope that
|
|
1008
|
+
// would conflict with the step); the clock-time branch keeps it, naming
|
|
1009
|
+
// specific hours rather than a step.
|
|
994
1010
|
const cadence = unevenHourCadence(schedule, opts);
|
|
995
1011
|
const phrase = cadence ?
|
|
996
|
-
minutesList(schedule, opts) + ', ' + cadence +
|
|
1012
|
+
withoutHourAnchor(minutesList(schedule, opts)) + ', ' + cadence +
|
|
997
1013
|
trailingQualifier(schedule, opts) :
|
|
998
1014
|
minutesList(schedule, opts) + ', ' +
|
|
999
1015
|
hourContextTimes(schedule, opts) + trailingQualifier(schedule, opts);
|
package/src/lang/pt/index.ts
CHANGED
|
@@ -269,6 +269,7 @@ function normalizeOptions(options?: Cronli5Options): Opts {
|
|
|
269
269
|
// `{ampm}` option overrides it.
|
|
270
270
|
ampm: typeof options.ampm === 'boolean' ? options.ampm : style.ampm,
|
|
271
271
|
lenient: !!options.lenient,
|
|
272
|
+
quartz: !!options.quartz,
|
|
272
273
|
seconds: !!options.seconds,
|
|
273
274
|
short: !!options.short,
|
|
274
275
|
style,
|
|
@@ -615,6 +616,16 @@ function minutesList(schedule: Schedule, opts: Opts): string {
|
|
|
615
616
|
joinList(segmentWords(segmentsOf(schedule, 'minute'))) + ' de cada hora';
|
|
616
617
|
}
|
|
617
618
|
|
|
619
|
+
// Strip the generic "de cada hora" anchor from a minute-cadence lead. Under an
|
|
620
|
+
// hour STEP the hour clause is the sole hour authority, so the cadence must not
|
|
621
|
+
// also assert "de cada hora" — alongside a stepped hour it reads as a
|
|
622
|
+
// conflicting every-hour scope ("de cada hora, a cada quatro horas"). An hour
|
|
623
|
+
// WINDOW and an unrestricted hour keep the anchor (the window already names the
|
|
624
|
+
// hours; an open hour has no other hour statement).
|
|
625
|
+
function withoutHourAnchor(lead: string): string {
|
|
626
|
+
return lead.replace(/ de cada hora$/, '');
|
|
627
|
+
}
|
|
628
|
+
|
|
618
629
|
// "a cada minuto do 0 ao 30". The standalone renderer adds "de cada hora";
|
|
619
630
|
// when an hour qualifier follows ("..., às 09:00", "..., a cada duas horas")
|
|
620
631
|
// it would contradict, so it is not baked in here.
|
|
@@ -745,8 +756,10 @@ function renderMinuteFrequency(
|
|
|
745
756
|
}
|
|
746
757
|
else if (plan.hours.kind === 'step') {
|
|
747
758
|
// A clean stride is a confinement ("as horas pares", or the active-hour
|
|
748
|
-
// list), never a juxtaposed cadence ("a cada duas horas").
|
|
749
|
-
|
|
759
|
+
// list), never a juxtaposed cadence ("a cada duas horas"). The hour step
|
|
760
|
+
// scopes the hours, so an offset cadence drops "de cada hora".
|
|
761
|
+
phrase = withoutHourAnchor(phrase) + ', ' +
|
|
762
|
+
stepHourSpan(stepSegment(schedule, 'hour'), opts);
|
|
750
763
|
}
|
|
751
764
|
|
|
752
765
|
return phrase + trailingQualifier(schedule, opts);
|
|
@@ -831,9 +844,10 @@ function renderMinuteSpanAcrossHourStep(
|
|
|
831
844
|
|
|
832
845
|
// A minute list keeps the same cadence clause as the range; only its lead
|
|
833
846
|
// differs ("nos minutos 5 e 30 de cada hora" vs "a cada minuto do 0 ao 30").
|
|
834
|
-
|
|
847
|
+
// The hour step scopes the hours, so the lead drops "de cada hora".
|
|
848
|
+
const lead = withoutHourAnchor(plan.form === 'list' ?
|
|
835
849
|
minutesList(schedule, opts) :
|
|
836
|
-
minuteRangeLead(schedule.pattern.minute);
|
|
850
|
+
minuteRangeLead(schedule.pattern.minute));
|
|
837
851
|
|
|
838
852
|
return lead + ', ' +
|
|
839
853
|
(cadence ?? stepHours(segment, opts)) + trailingQualifier(schedule, opts);
|
|
@@ -1422,10 +1436,13 @@ function renderCompactClockTimes(
|
|
|
1422
1436
|
}
|
|
1423
1437
|
|
|
1424
1438
|
// A uneven hour stride reads as a cadence after the minute lead, not a wall
|
|
1425
|
-
// of clock-time columns.
|
|
1439
|
+
// of clock-time columns. That hour step is the sole hour authority, so the
|
|
1440
|
+
// minute lead drops its generic "de cada hora" (an every-hour scope that
|
|
1441
|
+
// would conflict with the step); the clock-time branch keeps it, naming
|
|
1442
|
+
// specific hours rather than a step.
|
|
1426
1443
|
const cadence = unevenHourCadence(schedule, opts);
|
|
1427
1444
|
const phrase = cadence ?
|
|
1428
|
-
minutesList(schedule, opts) + ', ' + cadence +
|
|
1445
|
+
withoutHourAnchor(minutesList(schedule, opts)) + ', ' + cadence +
|
|
1429
1446
|
trailingQualifier(schedule, opts) :
|
|
1430
1447
|
minutesList(schedule, opts) + ', ' +
|
|
1431
1448
|
hourContextTimes(schedule, opts) + trailingQualifier(schedule, opts);
|