cronli5 0.2.0 → 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 +25 -0
- package/README.md +4 -4
- package/cronli5.min.js +2 -2
- package/dist/cronli5.cjs +123 -104
- package/dist/cronli5.js +123 -104
- package/dist/lang/de.cjs +65 -65
- package/dist/lang/de.js +65 -65
- package/dist/lang/en.cjs +122 -101
- package/dist/lang/en.js +122 -101
- 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 +8 -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/index.ts +163 -188
- 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 +7 -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/src/lang/de/index.ts
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
import {pad} from '../../core/format.js';
|
|
5
5
|
import {maxClockTimes, weekdayNumbers} from '../../core/specs.js';
|
|
6
6
|
import {
|
|
7
|
-
arithmeticStep, orderWeekdaysForDisplay,
|
|
7
|
+
arithmeticStep, hourListStride, offsetCleanStride, orderWeekdaysForDisplay,
|
|
8
|
+
segmentsOf, singleValues, stepSegment, toFieldNumber
|
|
8
9
|
} from '../../core/util.js';
|
|
9
10
|
import type {Cronli5Options} from '../../types.js';
|
|
10
11
|
import type {
|
|
@@ -59,11 +60,6 @@ function withAnchor(clause: string, anchor: string): string {
|
|
|
59
60
|
return anchor ? clause + ' ' + anchor : clause;
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
// The first segment of a step field, which the plan guarantees is step-kinded.
|
|
63
|
-
function stepSegment(segments: Segment[] | null): StepSegment {
|
|
64
|
-
return (segments as Segment[])[0] as StepSegment;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
63
|
// A step is "clean" when it starts at 0 and evenly divides its cycle (60 for
|
|
68
64
|
// minutes/seconds, 24 for hours) — only then does "alle N" describe it; an
|
|
69
65
|
// uneven step fires at discrete points that must be listed.
|
|
@@ -131,22 +127,6 @@ function stepClause(segment: StepSegment, unit: Unit, anchor: string): string {
|
|
|
131
127
|
});
|
|
132
128
|
}
|
|
133
129
|
|
|
134
|
-
// The sorted numeric values a field's segments cover, or null if any segment
|
|
135
|
-
// is not a discrete single (a range or sub-step is not a plain fire list).
|
|
136
|
-
function singleValues(segments: Segment[]): number[] | null {
|
|
137
|
-
const values: number[] = [];
|
|
138
|
-
|
|
139
|
-
for (const segment of segments) {
|
|
140
|
-
if (segment.kind !== 'single') {
|
|
141
|
-
return null;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
values.push(+segment.value);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return values;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
130
|
// Speak a minute/second field's enumerated fires as a step cadence when they
|
|
151
131
|
// form an arithmetic progression long enough to beat the list (the core
|
|
152
132
|
// enumerates an offset/uneven step to this fire list; the IR is unchanged, so
|
|
@@ -177,10 +157,6 @@ const weekdayNames = [
|
|
|
177
157
|
'freitags', 'samstags'
|
|
178
158
|
];
|
|
179
159
|
|
|
180
|
-
function fieldSegments(ir: IR, field: Field): Segment[] {
|
|
181
|
-
return ir.analyses.segments[field] as Segment[];
|
|
182
|
-
}
|
|
183
|
-
|
|
184
160
|
// Expand step segments into their fires as singles so a name list reads flat.
|
|
185
161
|
function flattenSteps(segments: Segment[]): NameSegment[] {
|
|
186
162
|
return segments.flatMap(function flat(segment): NameSegment[] {
|
|
@@ -219,7 +195,7 @@ function weekdayRange(bounds: [string, string]): string {
|
|
|
219
195
|
function weekdayQualifier(ir: IR): string {
|
|
220
196
|
// Weekday lists display Monday-first (Sunday last); a lone range keeps its
|
|
221
197
|
// form. The IR stays canonical (Sunday=0). The helper flattens steps.
|
|
222
|
-
const segments = orderWeekdaysForDisplay(
|
|
198
|
+
const segments = orderWeekdaysForDisplay(segmentsOf(ir, 'weekday'));
|
|
223
199
|
|
|
224
200
|
if (segments.length === 1 && segments[0].kind === 'range') {
|
|
225
201
|
return weekdayRange(segments[0].bounds);
|
|
@@ -329,7 +305,7 @@ function monthRange(bounds: [string, string], months: Months): string {
|
|
|
329
305
|
|
|
330
306
|
// Bare month names: "Januar", "Januar und Juli", "von Juni bis August".
|
|
331
307
|
function monthNamesList(ir: IR, months: Months): string {
|
|
332
|
-
return joinList(flattenSteps(
|
|
308
|
+
return joinList(flattenSteps(segmentsOf(ir, 'month'))
|
|
333
309
|
.map(function name(segment): string {
|
|
334
310
|
return segment.kind === 'range' ?
|
|
335
311
|
monthRange(segment.bounds, months) :
|
|
@@ -340,7 +316,7 @@ function monthNamesList(ir: IR, months: Months): string {
|
|
|
340
316
|
// The month qualifier: "im Januar", "im Januar und Juli", "von Juni bis
|
|
341
317
|
// August". A lone range carries its own "von … bis"; names take "im".
|
|
342
318
|
function monthClause(ir: IR, months: Months): string {
|
|
343
|
-
const segments = flattenSteps(
|
|
319
|
+
const segments = flattenSteps(segmentsOf(ir, 'month'));
|
|
344
320
|
|
|
345
321
|
if (segments.length === 1 && segments[0].kind === 'range') {
|
|
346
322
|
return monthRange(segments[0].bounds, months);
|
|
@@ -367,7 +343,7 @@ function dateRange(bounds: [string, string]): string {
|
|
|
367
343
|
// The bare date clause, without a month: "am 1.", "am 1. und 15.", "vom 1.
|
|
368
344
|
// bis zum 5.", "vom 1. bis zum 5. und am 10.".
|
|
369
345
|
function dateClauseBare(ir: IR): string {
|
|
370
|
-
const segments = flattenSteps(
|
|
346
|
+
const segments = flattenSteps(segmentsOf(ir, 'date'));
|
|
371
347
|
|
|
372
348
|
if (segments.length === 1 && segments[0].kind === 'range') {
|
|
373
349
|
return dateRange(segments[0].bounds);
|
|
@@ -399,7 +375,7 @@ function datePhrase(ir: IR, months: Months): string {
|
|
|
399
375
|
return clause;
|
|
400
376
|
}
|
|
401
377
|
|
|
402
|
-
const monthRanged = flattenSteps(
|
|
378
|
+
const monthRanged = flattenSteps(segmentsOf(ir, 'month'))
|
|
403
379
|
.some((segment) => segment.kind === 'range');
|
|
404
380
|
|
|
405
381
|
return monthRanged ?
|
|
@@ -450,7 +426,7 @@ function hourWindow(
|
|
|
450
426
|
|
|
451
427
|
// A field's values as strings, a range rendered "a bis b".
|
|
452
428
|
function fieldValues(ir: IR, field: Field): string[] {
|
|
453
|
-
return flattenSteps(
|
|
429
|
+
return flattenSteps(segmentsOf(ir, field)).map(function value(segment) {
|
|
454
430
|
return segment.kind === 'range' ?
|
|
455
431
|
segment.bounds[0] + ' bis ' + segment.bounds[1] :
|
|
456
432
|
String(segment.value);
|
|
@@ -504,7 +480,7 @@ function secondsClause(ir: IR, anchor: string): string {
|
|
|
504
480
|
// enumerated to a list is recognized as a progression. Both fall back to the
|
|
505
481
|
// counted list (a short or irregular set).
|
|
506
482
|
if (ir.shapes.second === 'step') {
|
|
507
|
-
return stepClause(stepSegment(
|
|
483
|
+
return stepClause(stepSegment(ir, 'second'), UNITS.second, anchor);
|
|
508
484
|
}
|
|
509
485
|
|
|
510
486
|
return strideFromSegments(segments as Segment[], UNITS.second, anchor) ??
|
|
@@ -523,7 +499,7 @@ function atHours(hours: number[]): string {
|
|
|
523
499
|
|
|
524
500
|
// The discrete hour fires, single and step values flattened: [9, 17, 19, …].
|
|
525
501
|
function hourFires(ir: IR): number[] {
|
|
526
|
-
return flattenSteps(
|
|
502
|
+
return flattenSteps(segmentsOf(ir, 'hour')).map(function fire(segment) {
|
|
527
503
|
return segment.kind === 'range' ? +segment.bounds[0] : +segment.value;
|
|
528
504
|
});
|
|
529
505
|
}
|
|
@@ -550,7 +526,7 @@ function hourSegmentParts(
|
|
|
550
526
|
second: number | undefined,
|
|
551
527
|
sep: string
|
|
552
528
|
): string[] {
|
|
553
|
-
return
|
|
529
|
+
return segmentsOf(ir, 'hour').map(function part(segment): string {
|
|
554
530
|
if (segment.kind === 'range') {
|
|
555
531
|
return 'von ' + partTime(+segment.bounds[0], minute, second, sep) +
|
|
556
532
|
' bis ' + partTime(+segment.bounds[1], minute, second, sep) + ' Uhr';
|
|
@@ -575,7 +551,7 @@ function duringWindows(ir: IR, times: HourTimesPlan, sep: string): string[] {
|
|
|
575
551
|
});
|
|
576
552
|
}
|
|
577
553
|
|
|
578
|
-
return
|
|
554
|
+
return segmentsOf(ir, 'hour').flatMap(function part(segment): string[] {
|
|
579
555
|
if (segment.kind === 'range') {
|
|
580
556
|
return [hourWindow(+segment.bounds[0], +segment.bounds[1], 59, sep)];
|
|
581
557
|
}
|
|
@@ -634,7 +610,7 @@ function renderSeconds(ir: IR): string {
|
|
|
634
610
|
// enumerated to this list reads as a stride cadence when the fires form a
|
|
635
611
|
// long-enough progression ("alle 2 Minuten von Minute 3 bis 59 jeder Stunde").
|
|
636
612
|
function minutePastClause(ir: IR): string {
|
|
637
|
-
return strideFromSegments(
|
|
613
|
+
return strideFromSegments(segmentsOf(ir, 'minute'), UNITS.minute,
|
|
638
614
|
'jeder Stunde') ??
|
|
639
615
|
countedPhrase(ir, 'minute', 'Minute', 'Minuten') + ' jeder Stunde';
|
|
640
616
|
}
|
|
@@ -706,7 +682,7 @@ function isEveryOtherMinuteSeconds(
|
|
|
706
682
|
return false;
|
|
707
683
|
}
|
|
708
684
|
|
|
709
|
-
const minuteStep = stepSegment(ir
|
|
685
|
+
const minuteStep = stepSegment(ir, 'minute');
|
|
710
686
|
|
|
711
687
|
return minuteStep.startToken === '*' && minuteStep.interval === 2;
|
|
712
688
|
}
|
|
@@ -806,7 +782,7 @@ function renderMinutesAcrossHours(
|
|
|
806
782
|
}
|
|
807
783
|
|
|
808
784
|
const minuteLead =
|
|
809
|
-
strideFromSegments(
|
|
785
|
+
strideFromSegments(segmentsOf(ir, 'minute'), UNITS.minute, '') ??
|
|
810
786
|
countedPhrase(ir, 'minute', 'Minute', 'Minuten');
|
|
811
787
|
|
|
812
788
|
if (cadence !== null) {
|
|
@@ -836,19 +812,19 @@ function renderMinuteSpanAcrossHourStep(
|
|
|
836
812
|
// bounded or uneven step routes through minutesAcrossHours instead).
|
|
837
813
|
if (plan.form === 'wildcard') {
|
|
838
814
|
return 'jede Minute ' +
|
|
839
|
-
everyNthHour(stepSegment(ir
|
|
815
|
+
everyNthHour(stepSegment(ir, 'hour'));
|
|
840
816
|
}
|
|
841
817
|
|
|
842
818
|
// The minute (range or list) leads; the hour trails. A clean stride confines
|
|
843
819
|
// to "in jeder N-ten Stunde" — the same cadence the wildcard form and the
|
|
844
820
|
// minute-step compositions use, never a juxtaposed second frequency. A
|
|
845
821
|
// bounded or uneven stride trails its endpoint-pinning cadence instead.
|
|
846
|
-
const segment = stepSegment(ir
|
|
822
|
+
const segment = stepSegment(ir, 'hour');
|
|
847
823
|
const hours = cadence ?? (confinedHourStride(segment) ?
|
|
848
824
|
everyNthHour(segment) :
|
|
849
825
|
atHours(segment.fires));
|
|
850
826
|
|
|
851
|
-
return (strideFromSegments(
|
|
827
|
+
return (strideFromSegments(segmentsOf(ir, 'minute'), UNITS.minute, '') ??
|
|
852
828
|
countedPhrase(ir, 'minute', 'Minute', 'Minuten')) + ', ' + hours;
|
|
853
829
|
}
|
|
854
830
|
|
|
@@ -875,7 +851,7 @@ function renderCompactClockTimes(
|
|
|
875
851
|
return cadence;
|
|
876
852
|
}
|
|
877
853
|
|
|
878
|
-
const hourly =
|
|
854
|
+
const hourly = segmentsOf(ir, 'hour')
|
|
879
855
|
.some((segment) => segment.kind === 'range');
|
|
880
856
|
|
|
881
857
|
return (hourly ? 'stündlich ' : 'täglich ') +
|
|
@@ -885,7 +861,7 @@ function renderCompactClockTimes(
|
|
|
885
861
|
// A bounded or uneven hour stride reads as its endpoint-pinning cadence; else
|
|
886
862
|
// a range among the hours reads as a window, otherwise a flat hour list.
|
|
887
863
|
const hours = unevenHourCadence(ir) ??
|
|
888
|
-
(
|
|
864
|
+
(segmentsOf(ir, 'hour').some((segment) => segment.kind === 'range') ?
|
|
889
865
|
joinList(hourSegmentParts(ir, 0, 0, sep)) :
|
|
890
866
|
atHours(hourFires(ir)));
|
|
891
867
|
|
|
@@ -896,7 +872,7 @@ function renderCompactClockTimes(
|
|
|
896
872
|
countedPhrase(ir, 'second', 'Sekunde', 'Sekunden') + ', ' : '';
|
|
897
873
|
|
|
898
874
|
return lead +
|
|
899
|
-
(strideFromSegments(
|
|
875
|
+
(strideFromSegments(segmentsOf(ir, 'minute'), UNITS.minute, '') ??
|
|
900
876
|
countedPhrase(ir, 'minute', 'Minute', 'Minuten')) + ', ' + hours;
|
|
901
877
|
}
|
|
902
878
|
|
|
@@ -907,7 +883,7 @@ function renderMinuteFrequency(
|
|
|
907
883
|
plan: Extract<PlanNode, {kind: 'minuteFrequency'}>,
|
|
908
884
|
opts: Opts
|
|
909
885
|
): string {
|
|
910
|
-
const segment = stepSegment(ir
|
|
886
|
+
const segment = stepSegment(ir, 'minute');
|
|
911
887
|
const sep = opts.style.sep;
|
|
912
888
|
const clean = cleanStep(segment, 60);
|
|
913
889
|
|
|
@@ -941,7 +917,7 @@ function renderMinuteFrequency(
|
|
|
941
917
|
// The plan carries a step only for a clean step (dividing the day):
|
|
942
918
|
// confine the cadence to every Nth hour ("in jeder zweiten Stunde").
|
|
943
919
|
return base + ' ' +
|
|
944
|
-
everyNthHour(stepSegment(ir
|
|
920
|
+
everyNthHour(stepSegment(ir, 'hour'));
|
|
945
921
|
}
|
|
946
922
|
|
|
947
923
|
return base;
|
|
@@ -961,7 +937,7 @@ function hourStepPhrase(ir: IR): string {
|
|
|
961
937
|
return cadence;
|
|
962
938
|
}
|
|
963
939
|
|
|
964
|
-
const segment = stepSegment(ir
|
|
940
|
+
const segment = stepSegment(ir, 'hour');
|
|
965
941
|
|
|
966
942
|
if (cleanStep(segment, 24)) {
|
|
967
943
|
return everyN(segment.interval, UNITS.hour);
|
|
@@ -1020,51 +996,6 @@ function hourStrideCadence(
|
|
|
1020
996
|
return cadence + ' von ' + start + ' bis ' + last + ' Uhr';
|
|
1021
997
|
}
|
|
1022
998
|
|
|
1023
|
-
// An hour list's arithmetic progression, or null when its values are not a step
|
|
1024
|
-
// the renderer should speak as a cadence. The core rewrites a uneven hour step
|
|
1025
|
-
// (whose interval does not tile 24, e.g. `*/5` → 0,5,10,15,20) to its literal
|
|
1026
|
-
// fire list, indistinguishable in the IR from a hand-written list; the renderer
|
|
1027
|
-
// recovers the cadence from the values. A progression starting at zero is a
|
|
1028
|
-
// `*/n` step however short (0,7,14,21 is `*/7`); a non-zero progression is only
|
|
1029
|
-
// a step when it is too long to be a deliberate clock-time list (9,17 is two
|
|
1030
|
-
// named times, not a cadence). Interval one is a plain range, never a step.
|
|
1031
|
-
function hourListStride(
|
|
1032
|
-
values: number[]
|
|
1033
|
-
): {start: number; interval: number; last: number} | null {
|
|
1034
|
-
if (values.length < 2) {
|
|
1035
|
-
return null;
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
|
-
const interval = values[1] - values[0];
|
|
1039
|
-
|
|
1040
|
-
if (interval < 2) {
|
|
1041
|
-
return null;
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
for (let i = 2; i < values.length; i += 1) {
|
|
1045
|
-
if (values[i] - values[i - 1] !== interval) {
|
|
1046
|
-
return null;
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
if (values[0] !== 0 && values.length < 5) {
|
|
1051
|
-
return null;
|
|
1052
|
-
}
|
|
1053
|
-
|
|
1054
|
-
return {interval, last: values[values.length - 1], start: values[0]};
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
// Whether an hour stride wraps the day cleanly from within its first interval
|
|
1058
|
-
// (a `*/n` from the top, or a `m/n` offset with m < n that divides 24): such a
|
|
1059
|
-
// stride has no distinct endpoint and keeps its bare or "ab" cadence. Every
|
|
1060
|
-
// other stride — a uneven interval, or one starting at or past its interval (a
|
|
1061
|
-
// bounded `a-b/n`) — is a bounded set the cadence pins both endpoints of.
|
|
1062
|
-
function offsetCleanStride(
|
|
1063
|
-
stride: {start: number; interval: number}
|
|
1064
|
-
): boolean {
|
|
1065
|
-
return stride.start < stride.interval && 24 % stride.interval === 0;
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
999
|
// The hour field's stride, or null when the hour is not a cadence: a step
|
|
1069
1000
|
// segment yields its {start, interval, last} directly; an all-single hour list
|
|
1070
1001
|
// yields one only when its values form a step progression (so an irregular list
|
|
@@ -1073,7 +1004,7 @@ function offsetCleanStride(
|
|
|
1073
1004
|
function hourStride(
|
|
1074
1005
|
ir: IR
|
|
1075
1006
|
): {start: number; interval: number; last: number} | null {
|
|
1076
|
-
const segments =
|
|
1007
|
+
const segments = segmentsOf(ir, 'hour');
|
|
1077
1008
|
|
|
1078
1009
|
// A wildcard hour carries no segments (no discrete hours to stride over).
|
|
1079
1010
|
if (!segments) {
|
|
@@ -1186,9 +1117,9 @@ function hourCadence(ir: IR, minute: number): string | null {
|
|
|
1186
1117
|
// stride is a confinement, not a juxtaposed cadence: it reads "für eine
|
|
1187
1118
|
// Minute in jeder zweiten Stunde", reusing the every-Nth-hour idiom so the
|
|
1188
1119
|
// minute-0 window is never heard as the bare hour cadence.
|
|
1189
|
-
const segment =
|
|
1120
|
+
const segment = segmentsOf(ir, 'hour')[0];
|
|
1190
1121
|
const confined = minute === 0 && subMinuteSecond(ir) &&
|
|
1191
|
-
|
|
1122
|
+
segmentsOf(ir, 'hour').length === 1 && segment.kind === 'step' &&
|
|
1192
1123
|
confinedHourStride(segment);
|
|
1193
1124
|
|
|
1194
1125
|
if (confined) {
|
|
@@ -1225,7 +1156,7 @@ function hourCadenceApplies(ir: IR): boolean {
|
|
|
1225
1156
|
// A pure single-value list (9,17) has no range to span and still enumerates;
|
|
1226
1157
|
// a step is handled by hourStride/hourCadence.
|
|
1227
1158
|
function hasHourWindow(ir: IR): boolean {
|
|
1228
|
-
const segments =
|
|
1159
|
+
const segments = segmentsOf(ir, 'hour');
|
|
1229
1160
|
|
|
1230
1161
|
return !!segments && segments.some(function range(segment) {
|
|
1231
1162
|
return segment.kind === 'range';
|
|
@@ -1288,7 +1219,7 @@ function renderHourRange(
|
|
|
1288
1219
|
// bounded cadence ("alle 2 Minuten von Minute 3 bis 59 jeder Stunde") instead
|
|
1289
1220
|
// of the wall of fires; an irregular list or a single minute keeps the
|
|
1290
1221
|
// counted form.
|
|
1291
|
-
return (strideFromSegments(
|
|
1222
|
+
return (strideFromSegments(segmentsOf(ir, 'minute'), UNITS.minute,
|
|
1292
1223
|
'jeder Stunde') ??
|
|
1293
1224
|
countedPhrase(ir, 'minute', 'Minute', 'Minuten') + ' jeder Stunde') +
|
|
1294
1225
|
', ' + window;
|
|
@@ -1412,7 +1343,7 @@ function needsDailyFrame(ir: IR): boolean {
|
|
|
1412
1343
|
// frequency, not a daily clock-time list, so it takes no "täglich" frame —
|
|
1413
1344
|
// only a bounded `a-b/n` step that enumerates its hours ("um 1, 3, … Uhr")
|
|
1414
1345
|
// needs the recurring frame.
|
|
1415
|
-
const segment = stepSegment(ir
|
|
1346
|
+
const segment = stepSegment(ir, 'hour');
|
|
1416
1347
|
|
|
1417
1348
|
return !cleanStep(segment, 24) && !openOffsetCleanStride(ir, segment);
|
|
1418
1349
|
}
|