cronli5 0.8.0 → 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 +34 -0
- package/README.md +6 -6
- package/cronli5.min.js +2 -2
- package/dist/cronli5.cjs +8 -4
- package/dist/cronli5.js +8 -4
- package/dist/lang/de.cjs +9 -9
- package/dist/lang/de.js +9 -9
- package/dist/lang/en.cjs +8 -4
- package/dist/lang/en.js +8 -4
- package/dist/lang/es.cjs +6 -3
- package/dist/lang/es.js +6 -3
- package/dist/lang/fi.cjs +10 -6
- package/dist/lang/fi.js +10 -6
- package/dist/lang/fr.cjs +6 -3
- package/dist/lang/fr.js +6 -3
- package/dist/lang/pt.cjs +6 -3
- package/dist/lang/pt.js +6 -3
- package/dist/lang/zh.cjs +28 -8
- package/dist/lang/zh.js +28 -8
- package/package.json +1 -1
- package/src/lang/de/index.ts +40 -30
- package/src/lang/en/index.ts +24 -7
- package/src/lang/es/index.ts +22 -7
- package/src/lang/fi/index.ts +35 -18
- package/src/lang/fr/index.ts +22 -7
- package/src/lang/pt/index.ts +22 -6
- package/src/lang/zh/index.ts +80 -12
package/src/lang/en/index.ts
CHANGED
|
@@ -581,8 +581,11 @@ function renderMinuteFrequency(schedule: Schedule,
|
|
|
581
581
|
else if (plan.hours.kind === 'step') {
|
|
582
582
|
// The plan carries a step only for a clean stride (dividing the day),
|
|
583
583
|
// which confines the cadence to every Nth hour; a stepped hour field's
|
|
584
|
-
// first segment is a step segment.
|
|
585
|
-
|
|
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 ? ' ' : ', ') +
|
|
586
589
|
everyNthHour(stepSegment(schedule, 'hour'), opts);
|
|
587
590
|
}
|
|
588
591
|
|
|
@@ -703,13 +706,14 @@ function renderMinuteSpanAcrossHourStep(schedule: Schedule,
|
|
|
703
706
|
}
|
|
704
707
|
|
|
705
708
|
// A minute list keeps the same cadence clause; only its lead differs. An
|
|
706
|
-
// offset/uneven step the core enumerated to that list reads as a stride.
|
|
707
|
-
|
|
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' ?
|
|
708
712
|
strideFromSegments(segmentsOf(schedule, 'minute'), 'minute', 'hour',
|
|
709
713
|
opts) ??
|
|
710
714
|
listPastThe(segmentWords(segmentsOf(schedule, 'minute'), opts),
|
|
711
715
|
'minute', 'hour', opts) :
|
|
712
|
-
minuteRangeLead(schedule.pattern.minute, opts);
|
|
716
|
+
minuteRangeLead(schedule.pattern.minute, opts));
|
|
713
717
|
// A bounded or uneven hour step reads as its endpoint-pinning cadence after
|
|
714
718
|
// the minute lead, not a wall of clock-time columns; an offset-clean step
|
|
715
719
|
// keeps its existing per-step phrasing.
|
|
@@ -932,10 +936,14 @@ function renderCompactClockTimes(schedule: Schedule,
|
|
|
932
936
|
listPastThe(segmentWords(segmentsOf(schedule, 'minute'), opts),
|
|
933
937
|
'minute', 'hour', opts);
|
|
934
938
|
// A uneven hour stride reads as a cadence after the minute lead, not a wall
|
|
935
|
-
// 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.
|
|
936
943
|
const cadence = unevenHourCadence(schedule, opts);
|
|
937
944
|
const phrase = cadence ?
|
|
938
|
-
minuteLead + ', ' + cadence +
|
|
945
|
+
withoutHourAnchor(minuteLead) + ', ' + cadence +
|
|
946
|
+
trailingQualifier(schedule, opts) :
|
|
939
947
|
minuteLead +
|
|
940
948
|
', at ' +
|
|
941
949
|
hourSegmentTimes(schedule, {minute: 0, second: null}, true, opts) +
|
|
@@ -1748,6 +1756,15 @@ function listPastThe(words: (string | number)[], unit: string, anchor: string,
|
|
|
1748
1756
|
anchor;
|
|
1749
1757
|
}
|
|
1750
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
|
+
|
|
1751
1768
|
// A clock time reads as a word ("noon"/"midnight") only at exact 12:00 or
|
|
1752
1769
|
// 0:00 with no minute or second.
|
|
1753
1770
|
function wordTime(hour: number | string, minute: number | string,
|
package/src/lang/es/index.ts
CHANGED
|
@@ -467,6 +467,16 @@ function minutesList(schedule: Schedule, opts: Opts): string {
|
|
|
467
467
|
joinList(segmentWords(segmentsOf(schedule, 'minute'))) + ' de cada hora';
|
|
468
468
|
}
|
|
469
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
|
+
|
|
470
480
|
// "cada minuto del 0 al 30". The standalone renderer adds "de cada hora";
|
|
471
481
|
// when an hour qualifier follows ("..., a las 09:00", "..., cada dos
|
|
472
482
|
// horas") it would contradict, so it is not baked in here.
|
|
@@ -594,8 +604,10 @@ function renderMinuteFrequency(
|
|
|
594
604
|
}
|
|
595
605
|
else if (plan.hours.kind === 'step') {
|
|
596
606
|
// A clean stride is a confinement ("las horas pares", or the active-hour
|
|
597
|
-
// list), never a juxtaposed cadence ("cada dos horas").
|
|
598
|
-
|
|
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);
|
|
599
611
|
}
|
|
600
612
|
|
|
601
613
|
return phrase + trailingQualifier(schedule, opts);
|
|
@@ -680,10 +692,10 @@ function renderMinuteSpanAcrossHourStep(
|
|
|
680
692
|
|
|
681
693
|
// A minute list keeps the same cadence clause as the range; only its lead
|
|
682
694
|
// differs ("en los minutos 5 y 30 de cada hora" vs "cada minuto del 0 al
|
|
683
|
-
// 30").
|
|
684
|
-
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' ?
|
|
685
697
|
minutesList(schedule, opts) :
|
|
686
|
-
minuteRangeLead(schedule.pattern.minute);
|
|
698
|
+
minuteRangeLead(schedule.pattern.minute));
|
|
687
699
|
|
|
688
700
|
return lead + ', ' +
|
|
689
701
|
(cadence ?? stepHours(segment, opts)) + trailingQualifier(schedule, opts);
|
|
@@ -1271,10 +1283,13 @@ function renderCompactClockTimes(
|
|
|
1271
1283
|
}
|
|
1272
1284
|
|
|
1273
1285
|
// A uneven hour stride reads as a cadence after the minute lead, not a wall
|
|
1274
|
-
// 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.
|
|
1275
1290
|
const cadence = unevenHourCadence(schedule, opts);
|
|
1276
1291
|
const phrase = cadence ?
|
|
1277
|
-
minutesList(schedule, opts) + ', ' + cadence +
|
|
1292
|
+
withoutHourAnchor(minutesList(schedule, opts)) + ', ' + cadence +
|
|
1278
1293
|
trailingQualifier(schedule, opts) :
|
|
1279
1294
|
minutesList(schedule, opts) + ', ' +
|
|
1280
1295
|
hourContextTimes(schedule, opts) + trailingQualifier(schedule, opts);
|
package/src/lang/fi/index.ts
CHANGED
|
@@ -426,15 +426,15 @@ function renderComposeSeconds(
|
|
|
426
426
|
return composeSecondsOverMinuteStep(schedule, plan.rest, opts);
|
|
427
427
|
}
|
|
428
428
|
|
|
429
|
-
// A
|
|
430
|
-
// clock-time rest would
|
|
431
|
-
//
|
|
432
|
-
// the seconds to the explicit clock minute with the "minuutin
|
|
433
|
-
// frame (an "of"/during form, never a range) and trail the day
|
|
434
|
-
// ("joka sekunti minuutin 9.
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
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);
|
|
438
438
|
}
|
|
439
439
|
|
|
440
440
|
// A wildcard second under a minute */2 with a wildcard hour juxtaposes two
|
|
@@ -473,11 +473,13 @@ function isEveryOtherMinuteSeconds(
|
|
|
473
473
|
return seg.startToken === '*' && seg.interval === 2;
|
|
474
474
|
}
|
|
475
475
|
|
|
476
|
-
// The minute
|
|
477
|
-
// in the "minuutin/minuuttien HH.
|
|
478
|
-
// a range — a range would round-trip back to the whole hour) and
|
|
479
|
-
// qualifier ("joka sekunti minuutin 9.
|
|
480
|
-
|
|
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(
|
|
481
483
|
schedule: Schedule,
|
|
482
484
|
rest: Extract<PlanNode, {kind: 'clockTimes'}>,
|
|
483
485
|
opts: NormalizedOptions
|
|
@@ -695,8 +697,10 @@ function renderMinuteFrequency(
|
|
|
695
697
|
const cadence = unevenHourCadence(schedule, opts);
|
|
696
698
|
|
|
697
699
|
if (cadence !== null) {
|
|
698
|
-
|
|
699
|
-
|
|
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);
|
|
700
704
|
}
|
|
701
705
|
|
|
702
706
|
// When the step renders as anchored ("kohdalla"), the per-hour windows
|
|
@@ -714,13 +718,16 @@ function renderMinuteFrequency(
|
|
|
714
718
|
trailingQualifier(schedule, opts);
|
|
715
719
|
}
|
|
716
720
|
|
|
717
|
-
|
|
721
|
+
const phraseBase = stepCycle60(seg, units.minute, opts);
|
|
722
|
+
let phrase = phraseBase;
|
|
718
723
|
|
|
719
724
|
if (plan.hours.kind === 'window') {
|
|
720
725
|
phrase += ' ' + hourWindow(plan.hours, opts);
|
|
721
726
|
}
|
|
722
727
|
else if (plan.hours.kind === 'step') {
|
|
723
|
-
|
|
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) + ' ' +
|
|
724
731
|
everyNthHour(stepSegment(schedule, 'hour'), opts);
|
|
725
732
|
}
|
|
726
733
|
|
|
@@ -1167,6 +1174,16 @@ function stepCycle60(
|
|
|
1167
1174
|
}, opts);
|
|
1168
1175
|
}
|
|
1169
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
|
+
|
|
1170
1187
|
// "kahden tunnin välein", "klo 0, 10 ja 20", or "viiden tunnin välein
|
|
1171
1188
|
// klo 1 alkaen".
|
|
1172
1189
|
function stepHours(segment: StepSegment, opts: NormalizedOptions): string {
|
package/src/lang/fr/index.ts
CHANGED
|
@@ -476,6 +476,16 @@ function minutesList(schedule: Schedule, opts: Opts): string {
|
|
|
476
476
|
joinList(segmentWords(segmentsOf(schedule, 'minute'))) + ' de chaque heure';
|
|
477
477
|
}
|
|
478
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
|
+
|
|
479
489
|
// "chaque minute de 0 à 30". The standalone renderer adds "de chaque heure";
|
|
480
490
|
// when an hour qualifier follows ("..., à 9 h", "..., toutes les deux heures")
|
|
481
491
|
// it would contradict, so it is not baked in here.
|
|
@@ -550,8 +560,10 @@ function renderMinuteFrequency(
|
|
|
550
560
|
}
|
|
551
561
|
else if (plan.hours.kind === 'step') {
|
|
552
562
|
// A clean stride is a confinement ("les heures paires", or the active-hour
|
|
553
|
-
// list), never a juxtaposed cadence ("toutes les deux heures").
|
|
554
|
-
|
|
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);
|
|
555
567
|
}
|
|
556
568
|
|
|
557
569
|
return phrase + trailingQualifier(schedule, opts);
|
|
@@ -635,10 +647,10 @@ function renderMinuteSpanAcrossHourStep(
|
|
|
635
647
|
|
|
636
648
|
// A minute list keeps the same cadence clause as the range; only its lead
|
|
637
649
|
// differs ("aux minutes 5 et 30 de chaque heure" vs "chaque minute de 0 à
|
|
638
|
-
// 30").
|
|
639
|
-
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' ?
|
|
640
652
|
minutesList(schedule, opts) :
|
|
641
|
-
minuteRangeLead(schedule.pattern.minute);
|
|
653
|
+
minuteRangeLead(schedule.pattern.minute));
|
|
642
654
|
|
|
643
655
|
return lead + ', ' +
|
|
644
656
|
(cadence ?? stepHours(segment, opts)) + trailingQualifier(schedule, opts);
|
|
@@ -991,10 +1003,13 @@ function renderCompactClockTimes(
|
|
|
991
1003
|
}
|
|
992
1004
|
|
|
993
1005
|
// A uneven hour stride reads as a cadence after the minute lead, not a wall
|
|
994
|
-
// 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.
|
|
995
1010
|
const cadence = unevenHourCadence(schedule, opts);
|
|
996
1011
|
const phrase = cadence ?
|
|
997
|
-
minutesList(schedule, opts) + ', ' + cadence +
|
|
1012
|
+
withoutHourAnchor(minutesList(schedule, opts)) + ', ' + cadence +
|
|
998
1013
|
trailingQualifier(schedule, opts) :
|
|
999
1014
|
minutesList(schedule, opts) + ', ' +
|
|
1000
1015
|
hourContextTimes(schedule, opts) + trailingQualifier(schedule, opts);
|
package/src/lang/pt/index.ts
CHANGED
|
@@ -616,6 +616,16 @@ function minutesList(schedule: Schedule, opts: Opts): string {
|
|
|
616
616
|
joinList(segmentWords(segmentsOf(schedule, 'minute'))) + ' de cada hora';
|
|
617
617
|
}
|
|
618
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
|
+
|
|
619
629
|
// "a cada minuto do 0 ao 30". The standalone renderer adds "de cada hora";
|
|
620
630
|
// when an hour qualifier follows ("..., às 09:00", "..., a cada duas horas")
|
|
621
631
|
// it would contradict, so it is not baked in here.
|
|
@@ -746,8 +756,10 @@ function renderMinuteFrequency(
|
|
|
746
756
|
}
|
|
747
757
|
else if (plan.hours.kind === 'step') {
|
|
748
758
|
// A clean stride is a confinement ("as horas pares", or the active-hour
|
|
749
|
-
// list), never a juxtaposed cadence ("a cada duas horas").
|
|
750
|
-
|
|
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);
|
|
751
763
|
}
|
|
752
764
|
|
|
753
765
|
return phrase + trailingQualifier(schedule, opts);
|
|
@@ -832,9 +844,10 @@ function renderMinuteSpanAcrossHourStep(
|
|
|
832
844
|
|
|
833
845
|
// A minute list keeps the same cadence clause as the range; only its lead
|
|
834
846
|
// differs ("nos minutos 5 e 30 de cada hora" vs "a cada minuto do 0 ao 30").
|
|
835
|
-
|
|
847
|
+
// The hour step scopes the hours, so the lead drops "de cada hora".
|
|
848
|
+
const lead = withoutHourAnchor(plan.form === 'list' ?
|
|
836
849
|
minutesList(schedule, opts) :
|
|
837
|
-
minuteRangeLead(schedule.pattern.minute);
|
|
850
|
+
minuteRangeLead(schedule.pattern.minute));
|
|
838
851
|
|
|
839
852
|
return lead + ', ' +
|
|
840
853
|
(cadence ?? stepHours(segment, opts)) + trailingQualifier(schedule, opts);
|
|
@@ -1423,10 +1436,13 @@ function renderCompactClockTimes(
|
|
|
1423
1436
|
}
|
|
1424
1437
|
|
|
1425
1438
|
// A uneven hour stride reads as a cadence after the minute lead, not a wall
|
|
1426
|
-
// 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.
|
|
1427
1443
|
const cadence = unevenHourCadence(schedule, opts);
|
|
1428
1444
|
const phrase = cadence ?
|
|
1429
|
-
minutesList(schedule, opts) + ', ' + cadence +
|
|
1445
|
+
withoutHourAnchor(minutesList(schedule, opts)) + ', ' + cadence +
|
|
1430
1446
|
trailingQualifier(schedule, opts) :
|
|
1431
1447
|
minutesList(schedule, opts) + ', ' +
|
|
1432
1448
|
hourContextTimes(schedule, opts) + trailingQualifier(schedule, opts);
|
package/src/lang/zh/index.ts
CHANGED
|
@@ -327,6 +327,16 @@ function renderMinutePast(schedule: Schedule): string {
|
|
|
327
327
|
return minuteHourClause(schedule);
|
|
328
328
|
}
|
|
329
329
|
|
|
330
|
+
// Strip the generic "每小时" (every-hour) anchor that leads a minute clause.
|
|
331
|
+
// Under an hour STEP the hour cadence is the sole hour authority, so the minute
|
|
332
|
+
// clause must not also assert "每小时" — alongside a stepped hour ("每4小时…每小
|
|
333
|
+
// 时…") it reads as a conflicting every-hour scope. An hour WINDOW and an
|
|
334
|
+
// unrestricted hour keep "每小时" (the window already names the hours; an open
|
|
335
|
+
// hour has no other hour statement).
|
|
336
|
+
function withoutHourAnchor(clause: string): string {
|
|
337
|
+
return clause.replace(/^每小时/, '');
|
|
338
|
+
}
|
|
339
|
+
|
|
330
340
|
// One hour segment as clock words by its form: a range is a span ("9点至20点"),
|
|
331
341
|
// a single is one clock word ("22点"), a step keeps its fires enumerated as
|
|
332
342
|
// clock words ("9点、11点、13点"). A range stated as a list element should read
|
|
@@ -383,7 +393,14 @@ function renderMinuteFrequency(schedule: Schedule, plan: PlanNode): string {
|
|
|
383
393
|
const hourCad = unevenHourCadence(schedule);
|
|
384
394
|
|
|
385
395
|
if (hourCad !== null) {
|
|
386
|
-
|
|
396
|
+
// An hour STEP is the sole hour authority, so an offset minute cadence
|
|
397
|
+
// drops its leading "每小时" ("每4小时从5分起每10分钟"); a discrete hour
|
|
398
|
+
// list (during) keeps it. Only the step path reaches a non-null cadence
|
|
399
|
+
// here — an irregular list falls through to the enumerated frame below.
|
|
400
|
+
const minuteBase = hours.kind === 'step' ?
|
|
401
|
+
withoutHourAnchor(base) : base;
|
|
402
|
+
|
|
403
|
+
return hourCad + (hourCad.indexOf('至') === -1 ? '' : ',') + minuteBase;
|
|
387
404
|
}
|
|
388
405
|
}
|
|
389
406
|
|
|
@@ -445,15 +462,17 @@ function renderMinuteSpanAcrossHourStep(
|
|
|
445
462
|
const {form} = plan as Extract<PlanNode, {kind: 'minuteSpanAcrossHourStep'}>;
|
|
446
463
|
|
|
447
464
|
// A minute list reads as the hour cadence plus the minute list ("每2小时,
|
|
448
|
-
//
|
|
449
|
-
//
|
|
465
|
+
// 0、25、50分"; offset "从1点起每2小时,5分和30分"), the same compaction the
|
|
466
|
+
// wildcard/range minute already uses, rather than the enumerated hours. The
|
|
467
|
+
// hour cadence scopes the hours, so the minute clause drops its "每小时".
|
|
450
468
|
if (form === 'list') {
|
|
451
|
-
return hourCadencePhrase(schedule) + ',' +
|
|
469
|
+
return hourCadencePhrase(schedule) + ',' +
|
|
470
|
+
withoutHourAnchor(renderMinutePast(schedule));
|
|
452
471
|
}
|
|
453
472
|
|
|
454
473
|
const minuteTail = form === 'wildcard' ?
|
|
455
474
|
'每分钟' :
|
|
456
|
-
minuteHourClause(schedule) + ',每分钟';
|
|
475
|
+
withoutHourAnchor(minuteHourClause(schedule)) + ',每分钟';
|
|
457
476
|
|
|
458
477
|
// An offset or non-tiling stride (2/6 fires at 2,8,14,20) reads as its
|
|
459
478
|
// cadence ("从2点起每6小时"). A wildcard minute hangs off it with a comma; a
|
|
@@ -518,9 +537,13 @@ function renderCompactClockTimes(schedule: Schedule, plan: PlanNode): string {
|
|
|
518
537
|
if (!compact.fold) {
|
|
519
538
|
const hourCad = unevenHourCadence(schedule);
|
|
520
539
|
|
|
540
|
+
// A bounded/uneven hour step leads as the cadence and is the sole hour
|
|
541
|
+
// authority, so the minute clause drops its generic "每小时" every-hour
|
|
542
|
+
// scope; an enumerated hour list (hourCad null) names specific hours and
|
|
543
|
+
// keeps the anchor.
|
|
521
544
|
return hourCad === null ?
|
|
522
545
|
minuteHourClause(schedule) + ',在' + hourList(schedule) + tail :
|
|
523
|
-
hourCad + ',' + minuteHourClause(schedule) + tail;
|
|
546
|
+
hourCad + ',' + withoutHourAnchor(minuteHourClause(schedule)) + tail;
|
|
524
547
|
}
|
|
525
548
|
|
|
526
549
|
// A single pinned minute past 0 leads with its clause; a pinned 0 folds into
|
|
@@ -870,6 +893,15 @@ function composeSecondsOnHour(
|
|
|
870
893
|
return composeMinuteZeroClocks(schedule, sec);
|
|
871
894
|
}
|
|
872
895
|
|
|
896
|
+
// A single fixed (non-zero) minute under enumerated clock times fuses the
|
|
897
|
+
// seconds onto the composed clock time the same way ("0点2分的每一秒").
|
|
898
|
+
const fusedSingleMinute =
|
|
899
|
+
composeSingleMinuteClocks(schedule, rest, sec, opts);
|
|
900
|
+
|
|
901
|
+
if (fusedSingleMinute !== null) {
|
|
902
|
+
return fusedSingleMinute;
|
|
903
|
+
}
|
|
904
|
+
|
|
873
905
|
const restText = render(schedule, rest, opts);
|
|
874
906
|
const secTail = clockRestCarriesSecond(rest) ? '' : sec;
|
|
875
907
|
|
|
@@ -886,6 +918,29 @@ function composeSecondsOnHour(
|
|
|
886
918
|
return restText + secTail;
|
|
887
919
|
}
|
|
888
920
|
|
|
921
|
+
// A single fixed (non-zero) minute under enumerated clock times: each clock
|
|
922
|
+
// point already names the minute ("0点2分", "9点5分和17点5分"), so bind the
|
|
923
|
+
// seconds to it with "的" — the same fusion the minute-0 ("0分的每一秒") and
|
|
924
|
+
// minute-step ("5、20…分的每一秒") cases use — rather than leaving a bare
|
|
925
|
+
// trailing "每秒" that floats as a second, unlinked adverbial. A single second
|
|
926
|
+
// already folded into each clock time ("9点5分30秒") is not re-appended. The
|
|
927
|
+
// compactClockTimes window form states its minute separately ("每小时5分") and
|
|
928
|
+
// keeps its own seconds clause, so it does not qualify (returns null). minute 0
|
|
929
|
+
// is handled by composeMinuteZeroClocks before this point.
|
|
930
|
+
function composeSingleMinuteClocks(
|
|
931
|
+
schedule: Schedule, rest: PlanNode, sec: string, opts: Opts
|
|
932
|
+
): string | null {
|
|
933
|
+
if (rest.kind !== 'clockTimes' || schedule.shapes.minute !== 'single' ||
|
|
934
|
+
clockRestCarriesSecond(rest)) {
|
|
935
|
+
return null;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
const core =
|
|
939
|
+
render(schedule, rest, opts) + minuteZeroSecondTail(schedule, sec);
|
|
940
|
+
|
|
941
|
+
return isDaily(schedule) ? '每天' + core : core;
|
|
942
|
+
}
|
|
943
|
+
|
|
889
944
|
// A minute pinned to 0 under specific clock hours (not a compacted cadence): a
|
|
890
945
|
// bare clock word ("9点") would hide the :00 and leave the second dangling
|
|
891
946
|
// ("…9点每秒"), reading as the whole hour. Fuse the seconds with the explicit
|
|
@@ -907,14 +962,24 @@ function composeMinuteZeroClocks(schedule: Schedule, sec: string): string {
|
|
|
907
962
|
// midnight (凌晨0点) and other hours still need it to pin the minute.
|
|
908
963
|
return hour === 12 ? '正午' : hourWord(hour) + '0分';
|
|
909
964
|
});
|
|
910
|
-
|
|
911
|
-
|
|
965
|
+
const core = joinAnd(clocks) + minuteZeroSecondTail(schedule, sec);
|
|
966
|
+
|
|
967
|
+
return isDaily(schedule) ? '每天' + core : core;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
// The "的"-fused second tail for a clock time that already names a single pinned
|
|
971
|
+
// minute ("…的每一秒" for a wildcard second, else "…的" + the second's clause).
|
|
972
|
+
// A pinned minute makes the seconds' own "每分钟" anchor misleading (it is a
|
|
973
|
+
// single minute, not every minute), so a stride here drops it.
|
|
974
|
+
function minuteZeroSecondTail(schedule: Schedule, sec: string): string {
|
|
975
|
+
if (sec === '每秒') {
|
|
976
|
+
return '的每一秒';
|
|
977
|
+
}
|
|
978
|
+
|
|
912
979
|
const nested =
|
|
913
980
|
strideFromSegments(segmentsOf(schedule, 'second'), '秒', '秒', '');
|
|
914
|
-
const tail = sec === '每秒' ? '的每一秒' : '的' + (nested ?? sec);
|
|
915
|
-
const core = joinAnd(clocks) + tail;
|
|
916
981
|
|
|
917
|
-
return
|
|
982
|
+
return '的' + (nested ?? sec);
|
|
918
983
|
}
|
|
919
984
|
|
|
920
985
|
// Whether the hour field is a range — or a list whose segments include a
|
|
@@ -1010,7 +1075,10 @@ function composeSecondsListed(schedule: Schedule): string {
|
|
|
1010
1075
|
const hourCad = unevenHourCadence(schedule);
|
|
1011
1076
|
|
|
1012
1077
|
if (hourCad !== null) {
|
|
1013
|
-
|
|
1078
|
+
// An hour STEP cadence is the sole hour authority, so the minute clause
|
|
1079
|
+
// drops its "每小时" ("每2小时,0至30分,每秒"); a discrete hour list keeps it
|
|
1080
|
+
// (it falls through to the hourFrame branch below with a null cadence).
|
|
1081
|
+
return hourCad + ',' + withoutHourAnchor(minutes) + ',' + sec;
|
|
1014
1082
|
}
|
|
1015
1083
|
|
|
1016
1084
|
return hourFrame(schedule) + minutes + ',' + sec;
|