cronli5 0.8.2 → 0.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +62 -0
- package/cronli5.min.js +2 -2
- package/dist/cronli5.cjs +69 -5
- package/dist/cronli5.js +69 -5
- package/dist/lang/de.cjs +86 -1
- package/dist/lang/de.js +86 -1
- package/dist/lang/en.cjs +69 -5
- package/dist/lang/en.js +69 -5
- package/dist/lang/es.cjs +105 -3
- package/dist/lang/es.js +105 -3
- package/dist/lang/fi.cjs +70 -0
- package/dist/lang/fi.js +70 -0
- package/dist/lang/fr.cjs +77 -0
- package/dist/lang/fr.js +77 -0
- package/dist/lang/pt.cjs +78 -0
- package/dist/lang/pt.js +78 -0
- package/dist/lang/zh.cjs +36 -4
- package/dist/lang/zh.js +36 -4
- package/package.json +1 -1
- package/src/lang/de/index.ts +190 -1
- package/src/lang/en/index.ts +154 -24
- package/src/lang/es/index.ts +236 -6
- package/src/lang/fi/index.ts +163 -0
- package/src/lang/fr/index.ts +178 -0
- package/src/lang/pt/index.ts +174 -0
- package/src/lang/zh/index.ts +97 -6
package/src/lang/pt/index.ts
CHANGED
|
@@ -348,6 +348,14 @@ function renderSecondsWithinMinute(
|
|
|
348
348
|
trailingQualifier(schedule, opts);
|
|
349
349
|
}
|
|
350
350
|
|
|
351
|
+
// A second LIST or RANGE under a single minute confines that minute in the
|
|
352
|
+
// genitive ("nos segundos 5 e 10 do minuto 30 de cada hora"), never the comma
|
|
353
|
+
// juxtaposition; a STEP second is a cadence and keeps its own lead.
|
|
354
|
+
if (secondsConfinesMinute(schedule)) {
|
|
355
|
+
return secondsBareLead(schedule) + ' ' +
|
|
356
|
+
confinedMinutePhrase(schedule) + trailingQualifier(schedule, opts);
|
|
357
|
+
}
|
|
358
|
+
|
|
351
359
|
return secondsLeadClause(schedule, opts) + ', no minuto ' + minuteField +
|
|
352
360
|
' de cada hora' + trailingQualifier(schedule, opts);
|
|
353
361
|
}
|
|
@@ -413,6 +421,161 @@ function isPinnedMinuteSeconds(
|
|
|
413
421
|
schedule.shapes.second === 'step');
|
|
414
422
|
}
|
|
415
423
|
|
|
424
|
+
// The minute field's step stride for the confinement frame, or null when the
|
|
425
|
+
// minute is not a stepped cadence. A `step`-shaped field reads its segment; a
|
|
426
|
+
// `list`-shaped field the core enumerated from a uneven step (`2/7` → 2,9,…,58)
|
|
427
|
+
// recovers the progression from its values.
|
|
428
|
+
function minuteStride(
|
|
429
|
+
schedule: Schedule
|
|
430
|
+
): {start: number; interval: number; last: number} | null {
|
|
431
|
+
if (schedule.shapes.minute === 'step') {
|
|
432
|
+
const segment = stepSegment(schedule, 'minute');
|
|
433
|
+
const start = segment.startToken === '*' ? 0 : +segment.startToken;
|
|
434
|
+
|
|
435
|
+
return {interval: segment.interval, last:
|
|
436
|
+
segment.fires[segment.fires.length - 1], start};
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const values = singleValues(segmentsOf(schedule, 'minute'));
|
|
440
|
+
|
|
441
|
+
return values && arithmeticStep(values);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// A stepped minute under a wildcard/stepped second and wildcard hour: bind the
|
|
445
|
+
// second clause leads, a COMMA, then the minute's own STANDALONE cardinal
|
|
446
|
+
// cadence ("a cada segundo, a cada seis minutos a partir do minuto 4 de cada
|
|
447
|
+
// hora"; "nos segundos 5, 10 e 15, a cada seis minutos …"). The ordinal "no
|
|
448
|
+
// sexto minuto" read as a single minute (the 10th), not the every-sixth series;
|
|
449
|
+
// the standalone cardinal "a cada seis minutos" reads it correctly and handles
|
|
450
|
+
// every stride (offset, bounded, uneven) for free. The lead is the cadence
|
|
451
|
+
// clause for a wildcard/stepped second, the bare clock-point clause otherwise.
|
|
452
|
+
function steppedMinuteConfinement(
|
|
453
|
+
schedule: Schedule,
|
|
454
|
+
plan: Extract<PlanNode, {kind: 'composeSeconds'}>,
|
|
455
|
+
lead: string,
|
|
456
|
+
opts: Opts
|
|
457
|
+
): string {
|
|
458
|
+
return lead + ', ' + render(schedule, plan.rest, opts) +
|
|
459
|
+
trailingQualifier(schedule, opts);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Whether a stepped minute fills a wildcard hour under a wildcard/stepped
|
|
463
|
+
// second — the shape the confinement frame above handles.
|
|
464
|
+
function isSteppedMinuteSeconds(
|
|
465
|
+
schedule: Schedule,
|
|
466
|
+
plan: Extract<PlanNode, {kind: 'composeSeconds'}>
|
|
467
|
+
): boolean {
|
|
468
|
+
return (plan.rest.kind === 'minuteFrequency' ||
|
|
469
|
+
plan.rest.kind === 'multipleMinutes') &&
|
|
470
|
+
(schedule.shapes.second === 'wildcard' ||
|
|
471
|
+
schedule.shapes.second === 'step') &&
|
|
472
|
+
schedule.shapes.hour === 'wildcard' &&
|
|
473
|
+
schedule.pattern.minute !== '*/2' &&
|
|
474
|
+
minuteStride(schedule) !== null;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// The leading seconds words for a clock-point second, WITHOUT the trailing "de
|
|
478
|
+
// cada minuto" anchor: a confined second attaches to the CONFINED minute ("do
|
|
479
|
+
// sexto minuto…"), so the generic minute anchor would be redundant.
|
|
480
|
+
function secondsBareLead(schedule: Schedule): string {
|
|
481
|
+
const secondField = schedule.pattern.second;
|
|
482
|
+
const shape = schedule.shapes.second;
|
|
483
|
+
|
|
484
|
+
if (shape === 'range') {
|
|
485
|
+
const bounds = secondField.split('-');
|
|
486
|
+
|
|
487
|
+
return 'a cada segundo do ' + bounds[0] + ' ao ' + bounds[1];
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if (shape === 'single') {
|
|
491
|
+
return 'no segundo ' + secondField;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
return 'nos segundos ' +
|
|
495
|
+
joinList(segmentWords(segmentsOf(schedule, 'second')));
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// The CONFINED-minute genitive phrase a clock-point second attaches to ("dos
|
|
499
|
+
// minutos 0, 15 e 30 de cada hora", "do minuto 30 de cada hora", "de cada
|
|
500
|
+
// minuto do 0 ao 30 de cada hora"). A stepped minute is handled by the
|
|
501
|
+
// standalone-cadence confinement before this point; a list, range, or single
|
|
502
|
+
// names the minute(s) — so the bare seconds lead never stacks a redundant "de
|
|
503
|
+
// cada minuto".
|
|
504
|
+
function confinedMinutePhrase(schedule: Schedule): string {
|
|
505
|
+
if (schedule.shapes.minute === 'range') {
|
|
506
|
+
// `minuteRangeLead` is "a cada minuto do 0 ao 30"; the genitive "de"
|
|
507
|
+
// absorbs its leading "a" ("de cada minuto …", not "de a cada minuto").
|
|
508
|
+
const range = minuteRangeLead(schedule.pattern.minute).replace(/^a /u, '');
|
|
509
|
+
|
|
510
|
+
return 'de ' + range + ' de cada hora';
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
if (schedule.shapes.minute === 'list') {
|
|
514
|
+
return 'dos minutos ' +
|
|
515
|
+
joinList(segmentWords(segmentsOf(schedule, 'minute'))) + ' de cada hora';
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
return 'do minuto ' + schedule.pattern.minute + ' de cada hora';
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// Whether a clock-point second (list, range, or single) sits under a restricted
|
|
522
|
+
// minute and a wildcard hour — the shape that must CONFINE the minute in the
|
|
523
|
+
// genitive rather than juxtapose it behind a comma (two independent schedules).
|
|
524
|
+
// A second LIST the core enumerated from a step (`3/2`) is really a stride
|
|
525
|
+
// cadence and stays out. The single-second + single-minute pair folds into one
|
|
526
|
+
// coherent clock point and is excluded.
|
|
527
|
+
function secondsConfinesMinute(schedule: Schedule): boolean {
|
|
528
|
+
const {second, minute, hour} = schedule.shapes;
|
|
529
|
+
|
|
530
|
+
if (second === 'list') {
|
|
531
|
+
const values = singleValues(segmentsOf(schedule, 'second'));
|
|
532
|
+
|
|
533
|
+
if (values && arithmeticStep(values)) {
|
|
534
|
+
return false;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
const clockPoint = second === 'single' || second === 'range' ||
|
|
539
|
+
second === 'list';
|
|
540
|
+
|
|
541
|
+
return clockPoint && minute !== 'wildcard' && hour === 'wildcard' &&
|
|
542
|
+
!(second === 'single' && minute === 'single');
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// The minute-confinement rendering for a compose-seconds plan, or null when the
|
|
546
|
+
// plan is not one. A CADENCE second over a stepped minute uses the ordinal
|
|
547
|
+
// cadence form; a CLOCK-POINT second (list/range/single) over any restricted
|
|
548
|
+
// minute uses the genitive form anchored to the confined minute. Both bind the
|
|
549
|
+
// second beneath the minute instead of juxtaposing the two behind a comma.
|
|
550
|
+
// Folded into one helper so `renderComposeSeconds` carries a single branch.
|
|
551
|
+
function minuteConfinementRender(
|
|
552
|
+
plan: Extract<PlanNode, {kind: 'composeSeconds'}>,
|
|
553
|
+
schedule: Schedule, opts: Opts
|
|
554
|
+
): string | null {
|
|
555
|
+
if (isSteppedMinuteSeconds(schedule, plan)) {
|
|
556
|
+
return steppedMinuteConfinement(schedule, plan,
|
|
557
|
+
secondsLeadClause(schedule, opts), opts);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
const minuteRest = plan.rest.kind === 'minuteFrequency' ||
|
|
561
|
+
plan.rest.kind === 'multipleMinutes' ||
|
|
562
|
+
plan.rest.kind === 'rangeOfMinutes';
|
|
563
|
+
|
|
564
|
+
if (minuteRest && secondsConfinesMinute(schedule)) {
|
|
565
|
+
// A clock-point second over a STEPPED minute reuses the standalone cardinal
|
|
566
|
+
// cadence the same way; only a list/range/single minute keeps the genitive.
|
|
567
|
+
if (minuteStride(schedule) && schedule.pattern.minute !== '*/2') {
|
|
568
|
+
return steppedMinuteConfinement(schedule, plan,
|
|
569
|
+
secondsBareLead(schedule), opts);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
return secondsBareLead(schedule) + ' ' +
|
|
573
|
+
confinedMinutePhrase(schedule) + trailingQualifier(schedule, opts);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
return null;
|
|
577
|
+
}
|
|
578
|
+
|
|
416
579
|
function renderComposeSeconds(
|
|
417
580
|
schedule: Schedule,
|
|
418
581
|
plan: Extract<PlanNode, {kind: 'composeSeconds'}>,
|
|
@@ -456,6 +619,17 @@ function renderComposeSeconds(
|
|
|
456
619
|
return dayFrame + ', ' + window + ', ' + cadence;
|
|
457
620
|
}
|
|
458
621
|
|
|
622
|
+
// A second confines the minute restriction (open hour), never the comma
|
|
623
|
+
// juxtaposition that reads as two independent cadences: a CADENCE second over
|
|
624
|
+
// a stepped minute uses the ordinal-cadence form ("a cada segundo no sexto
|
|
625
|
+
// minuto …"); a CLOCK-POINT second uses the genitive form anchored to the
|
|
626
|
+
// confined minute ("nos segundos 5, 10 e 15 do sexto minuto …").
|
|
627
|
+
const confined = minuteConfinementRender(plan, schedule, opts);
|
|
628
|
+
|
|
629
|
+
if (confined !== null) {
|
|
630
|
+
return confined;
|
|
631
|
+
}
|
|
632
|
+
|
|
459
633
|
// A wildcard second under a minute */2 with a wildcard hour juxtaposes two
|
|
460
634
|
// cadences that read as contradictory ("a cada segundo, a cada dois
|
|
461
635
|
// minutos"). Bind them with the genitive "de" ("a cada segundo de cada dois
|
package/src/lang/zh/index.ts
CHANGED
|
@@ -833,8 +833,12 @@ function secondClause(schedule: Schedule): string {
|
|
|
833
833
|
|
|
834
834
|
const first = segs[0];
|
|
835
835
|
|
|
836
|
-
|
|
837
|
-
|
|
836
|
+
// A STEP-shaped second reads as its stride cadence ("每6秒"), whether written
|
|
837
|
+
// "*/6" or the offset-clean "0/6" — both fire 0,6,…,54 — never the enumerated
|
|
838
|
+
// "第0、6、…、54秒". stepClause routes a clean/offset-clean stride through the
|
|
839
|
+
// bare cadence and only lists a bounded `a-b/n` or a short offset.
|
|
840
|
+
if (segs.length === 1 && first.kind === 'step') {
|
|
841
|
+
return stepClause(first, '秒', '秒', '每分钟');
|
|
838
842
|
}
|
|
839
843
|
|
|
840
844
|
// An offset/uneven step the core enumerated to this list reads as a stride
|
|
@@ -909,10 +913,14 @@ function composeSecondsOnHour(
|
|
|
909
913
|
return '每天' + restText + secTail;
|
|
910
914
|
}
|
|
911
915
|
|
|
912
|
-
// A stated minute (
|
|
913
|
-
// "
|
|
916
|
+
// A stated single minute (minute 0 under an open hour) confines the second
|
|
917
|
+
// beneath it with "的" when the second is a cadence ("每小时0分的每一秒"), the
|
|
918
|
+
// same fusion the other pinned-minute paths use; the bare comma ("每小时0分,
|
|
919
|
+
// 每秒") reads as two independent cadences. A single/list/range second is a
|
|
920
|
+
// clock-point, not a cadence, so it keeps the "," connector.
|
|
914
921
|
if (rest.kind === 'singleMinute') {
|
|
915
|
-
return
|
|
922
|
+
return secondIsCadence(schedule) ?
|
|
923
|
+
restText + confinedSecondTail(sec) : restText + ',' + sec;
|
|
916
924
|
}
|
|
917
925
|
|
|
918
926
|
return restText + secTail;
|
|
@@ -1040,6 +1048,13 @@ function composeSecondsCadence(schedule: Schedule): string {
|
|
|
1040
1048
|
}
|
|
1041
1049
|
}
|
|
1042
1050
|
|
|
1051
|
+
// A CLOCK-POINT second (a single/list/range, not a stride cadence) under a
|
|
1052
|
+
// clean minute step fuses beneath the minute with "的" ("每6分钟的第30秒"),
|
|
1053
|
+
// never the comma that reads as two independent schedules.
|
|
1054
|
+
if (!secondIsCadence(schedule) && !secondIsStride(schedule)) {
|
|
1055
|
+
return minuteClause(schedule) + '的' + sec;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1043
1058
|
return sec + ',' + minuteClause(schedule);
|
|
1044
1059
|
}
|
|
1045
1060
|
|
|
@@ -1069,7 +1084,14 @@ function composeSecondsListed(schedule: Schedule): string {
|
|
|
1069
1084
|
}
|
|
1070
1085
|
|
|
1071
1086
|
if (schedule.shapes.hour === 'wildcard') {
|
|
1072
|
-
|
|
1087
|
+
// The minute(s) are stated and the hour is open, so the second — whether a
|
|
1088
|
+
// cadence ("每秒"/"每N秒") or a clock-point ("第5、10、15秒") — fuses beneath
|
|
1089
|
+
// the minute(s) with "的" ("每小时30分的每一秒", "每小时0、15、30分的第5、10、15
|
|
1090
|
+
// 秒"). The bare comma ("…,第5、10、15秒") reads as two independent schedules.
|
|
1091
|
+
// A second STRIDE the core enumerated to a list ("3/2" → "每2秒,至59秒") is a
|
|
1092
|
+
// bounded cadence with its own trailing ",至N秒"; it keeps the comma.
|
|
1093
|
+
return secondIsStride(schedule) ?
|
|
1094
|
+
minutes + ',' + sec : minutes + confinedSecondTail(sec);
|
|
1073
1095
|
}
|
|
1074
1096
|
|
|
1075
1097
|
const hourCad = unevenHourCadence(schedule);
|
|
@@ -1084,6 +1106,68 @@ function composeSecondsListed(schedule: Schedule): string {
|
|
|
1084
1106
|
return hourFrame(schedule) + minutes + ',' + sec;
|
|
1085
1107
|
}
|
|
1086
1108
|
|
|
1109
|
+
// Whether the minute field is a stepped cadence (a clean `*/n`, an offset
|
|
1110
|
+
// `m/n`, or a uneven step the core enumerated to an arithmetic fire list). The
|
|
1111
|
+
// shape the seconds-wildcard confinement below fuses with "的".
|
|
1112
|
+
function isMinuteStride(schedule: Schedule): boolean {
|
|
1113
|
+
if (schedule.shapes.minute === 'step') {
|
|
1114
|
+
return true;
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
const values = singleValues(segmentsOf(schedule, 'minute'));
|
|
1118
|
+
|
|
1119
|
+
return values !== null && arithmeticStep(values) !== null;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
// Whether the second is a CADENCE (wildcard "每秒" or a clean step "每N秒") rather
|
|
1123
|
+
// than a clock-point (a single/list/range). A cadence second under a stated
|
|
1124
|
+
// minute fuses beneath it with "的"; a clock-point keeps the "," connector.
|
|
1125
|
+
function secondIsCadence(schedule: Schedule): boolean {
|
|
1126
|
+
return schedule.pattern.second === '*' || schedule.shapes.second === 'step';
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
// Whether a second LIST is really a stride the core enumerated from a step
|
|
1130
|
+
// ("3/2" → 3,5,…,59), spoken as a bounded cadence ("每2秒,至59秒") with its own
|
|
1131
|
+
// trailing comma — not a clock-point list ("第5、10、15秒"). Such a stride keeps
|
|
1132
|
+
// its comma form rather than fusing beneath the minute with "的".
|
|
1133
|
+
function secondIsStride(schedule: Schedule): boolean {
|
|
1134
|
+
if (schedule.shapes.second !== 'list') {
|
|
1135
|
+
return false;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
const values = singleValues(segmentsOf(schedule, 'second'));
|
|
1139
|
+
|
|
1140
|
+
return values !== null && arithmeticStep(values) !== null;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
// The "的"-fused second tail for a clause that already states its minute(s):
|
|
1144
|
+
// "的每一秒" for a wildcard second, else "的" + the second's own cadence clause.
|
|
1145
|
+
// The fusion binds the second beneath the minute rather than leaving a bare
|
|
1146
|
+
// trailing "每秒" that reads as a second, independent cadence.
|
|
1147
|
+
function confinedSecondTail(sec: string): string {
|
|
1148
|
+
return sec === '每秒' ? '的每一秒' : '的' + sec;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
// Whether a compose-seconds plan is a stepped minute under a cadence second and
|
|
1152
|
+
// wildcard hour — the shape the "的"-fused confinement below handles, kept
|
|
1153
|
+
// distinct from the */2 even-minutes idiom and the composed-clock paths.
|
|
1154
|
+
function isSteppedMinuteSeconds(
|
|
1155
|
+
schedule: Schedule, composedClock: boolean
|
|
1156
|
+
): boolean {
|
|
1157
|
+
return !composedClock && schedule.shapes.hour === 'wildcard' &&
|
|
1158
|
+
secondIsCadence(schedule) && schedule.pattern.minute !== '*/2' &&
|
|
1159
|
+
isMinuteStride(schedule);
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
// A stepped minute under a cadence second and wildcard hour: fuse the minute
|
|
1163
|
+
// cadence and the second cadence with "的" ("每小时从4分起每6分钟的每一秒"), never
|
|
1164
|
+
// the comma juxtaposition ("…每6分钟,每秒") that reads as two independent
|
|
1165
|
+
// cadences. The minute clause carries the offset/bound ("从4分起" / ",至58分").
|
|
1166
|
+
function minuteStrideConfinement(schedule: Schedule): string {
|
|
1167
|
+
return minuteHourClause(schedule) +
|
|
1168
|
+
confinedSecondTail(secondClause(schedule));
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1087
1171
|
// Seconds composed with the minute/hour structure, dispatched on the minute.
|
|
1088
1172
|
// A single minute over a composed clock-time rest (the core already joined the
|
|
1089
1173
|
// lone hour and minute into "N点M分") keeps that composition, attaching the
|
|
@@ -1096,6 +1180,13 @@ function renderComposeSeconds(
|
|
|
1096
1180
|
const composedClock =
|
|
1097
1181
|
rest.kind === 'clockTimes' || rest.kind === 'compactClockTimes';
|
|
1098
1182
|
|
|
1183
|
+
// A stepped minute under a cadence second and wildcard hour confines the
|
|
1184
|
+
// second beneath the minute cadence with "的", never the comma that reads as
|
|
1185
|
+
// two independent cadences. The */2 step keeps its own "每偶数分钟" idiom.
|
|
1186
|
+
if (isSteppedMinuteSeconds(schedule, composedClock)) {
|
|
1187
|
+
return minuteStrideConfinement(schedule);
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1099
1190
|
if (schedule.pattern.minute === '0' ||
|
|
1100
1191
|
composedClock && schedule.shapes.minute === 'single') {
|
|
1101
1192
|
return composeSecondsOnHour(schedule, plan, opts);
|