cronli5 0.1.5 → 0.1.7

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/dist/lang/zh.cjs CHANGED
@@ -44,6 +44,26 @@ function arithmeticStep(values) {
44
44
  }
45
45
  return { start: values[0], interval, last: values[values.length - 1] };
46
46
  }
47
+ function weekdayDisplayKey(value) {
48
+ return value === 0 ? 7 : value;
49
+ }
50
+ function orderWeekdaysForDisplay(segments) {
51
+ const flattened = segments.flatMap(function flat(segment) {
52
+ return segment.kind === "step" ? segment.fires.map(function single(value) {
53
+ return { kind: "single", value: "" + value };
54
+ }) : [segment];
55
+ });
56
+ function key(segment) {
57
+ return segment.kind === "range" ? weekdayDisplayKey(+segment.bounds[0]) : weekdayDisplayKey(+segment.value);
58
+ }
59
+ return flattened.map(function index(segment, position) {
60
+ return [segment, position];
61
+ }).sort(function byDisplayKey(a, b) {
62
+ return key(a[0]) - key(b[0]) || a[1] - b[1];
63
+ }).map(function unwrap(pair) {
64
+ return pair[0];
65
+ });
66
+ }
47
67
  function toFieldNumber(token, numberMap) {
48
68
  return isNonNegativeInteger(token) ? +token : numberMap[token.toUpperCase()];
49
69
  }
@@ -249,8 +269,18 @@ function minuteHourClause(ir) {
249
269
  function renderMinutePast(ir) {
250
270
  return minuteHourClause(ir);
251
271
  }
272
+ function hourSegmentWords(segment) {
273
+ if (segment.kind === "range") {
274
+ return [hourWord(+segment.bounds[0]) + "\u81F3" + hourWord(+segment.bounds[1])];
275
+ }
276
+ if (segment.kind === "step") {
277
+ return segment.fires.map(hourWord);
278
+ }
279
+ return [hourWord(+segment.value)];
280
+ }
252
281
  function hourList(ir) {
253
- return joinAnd(hourFires(ir).map(hourWord));
282
+ const words = fieldSegments(ir, "hour").flatMap(hourSegmentWords);
283
+ return joinAnd(words);
254
284
  }
255
285
  function hourFrame(ir) {
256
286
  if (ir.shapes.hour === "range") {
@@ -263,9 +293,11 @@ function renderMinuteFrequency(ir, plan) {
263
293
  const minuteStep = stepSegment(ir, "minute");
264
294
  const base = stepClause(minuteStep, UNITS.minute, "\u5206", "\u6BCF\u5C0F\u65F6");
265
295
  const { hours } = plan;
266
- if (hours.kind === "step") {
267
- const hourStep = stepSegment(ir, "hour");
268
- return hourStep.startToken === "*" ? cadence(hourStep.interval, UNITS.hour) + base : "\u5728" + hourList(ir) + "\uFF0C" + base;
296
+ if (hours.kind === "step" || hours.kind === "during") {
297
+ const hourCad = hourCadencePhrase(ir);
298
+ if (hourCad !== null) {
299
+ return hourCad + (hourCad.indexOf("\u81F3") === -1 ? "" : "\uFF0C") + base;
300
+ }
269
301
  }
270
302
  if (hours.kind === "single" || hours.kind === "window" && hours.from === hours.to) {
271
303
  return "\u5728" + hourWord(hours.from) + "\u81F3" + hours.from + "\u70B9" + hours.last + "\u5206\u4E4B\u95F4\uFF0C" + base;
@@ -287,20 +319,21 @@ function renderMinuteSpanInHour(ir, plan) {
287
319
  }
288
320
  function renderMinutesAcrossHours(ir, plan) {
289
321
  const { form } = plan;
322
+ const hourCad = hourCadencePhrase(ir);
290
323
  if (form === "wildcard") {
291
- return "\u5728" + hourList(ir) + "\uFF0C\u6BCF\u5206\u949F";
324
+ return hourCad === null ? "\u5728" + hourList(ir) + "\uFF0C\u6BCF\u5206\u949F" : hourCad + "\uFF0C\u6BCF\u5206\u949F";
292
325
  }
293
- return hourList(ir) + "\uFF0C" + minuteHourClause(ir) + "\uFF0C\u6BCF\u5206\u949F";
326
+ return (hourCad ?? hourList(ir)) + "\uFF0C" + minuteHourClause(ir) + "\uFF0C\u6BCF\u5206\u949F";
294
327
  }
295
328
  function renderMinuteSpanAcrossHourStep(ir, plan) {
296
329
  const hourStep = stepSegment(ir, "hour");
297
330
  const { form } = plan;
298
331
  if (form === "list") {
299
- return renderMinutePast(ir) + "\uFF0C\u5728" + hourList(ir);
332
+ return hourCadencePhrase(ir) + "\uFF0C" + renderMinutePast(ir);
300
333
  }
301
334
  const minuteTail = form === "wildcard" ? "\u6BCF\u5206\u949F" : minuteHourClause(ir) + "\uFF0C\u6BCF\u5206\u949F";
302
335
  if (hourStep.startToken !== "*") {
303
- return form === "wildcard" ? "\u5728" + hourList(ir) + "\uFF0C" + minuteTail : hourList(ir) + "\uFF0C" + minuteTail;
336
+ return hourCadencePhrase(ir) + "\uFF0C" + minuteTail;
304
337
  }
305
338
  if (hourStep.interval === 2 && form === "wildcard") {
306
339
  return "\u5728\u5076\u6570\u5C0F\u65F6\uFF0C" + minuteTail;
@@ -309,26 +342,26 @@ function renderMinuteSpanAcrossHourStep(ir, plan) {
309
342
  return form === "wildcard" ? cad + "\u5185\uFF0C" + minuteTail : cad + "\uFF0C" + minuteTail;
310
343
  }
311
344
  function renderClockTimes(ir, plan, opts) {
312
- if (ir.shapes.minute === "single") {
313
- const cad = hourCadence(ir);
314
- if (cad !== null) {
315
- return cad;
316
- }
345
+ const cad = hourCadenceText(ir);
346
+ if (cad !== null) {
347
+ return cad;
317
348
  }
318
349
  const { times } = plan;
319
350
  return joinAnd(times.map((t) => clockTime(t.hour, t.minute, t.second, opts)));
320
351
  }
321
352
  function renderCompactClockTimes(ir, plan) {
322
- if (ir.shapes.minute === "single") {
323
- const cad = hourCadence(ir);
324
- if (cad !== null) {
325
- return cad;
326
- }
353
+ const cad = hourCadenceText(ir);
354
+ if (cad !== null) {
355
+ return cad;
327
356
  }
328
- const { minute } = plan;
357
+ const compact = plan;
329
358
  const secs = fieldSegments(ir, "second");
330
359
  const tail = secs.length && ir.pattern.second !== "0" ? "\uFF0C\u7B2C" + valueText(secs) + "\u79D2" : "";
331
- if (minute > 0) {
360
+ if (!compact.fold) {
361
+ const hourCad = hourCadencePhrase(ir);
362
+ return hourCad === null ? minuteHourClause(ir) + "\uFF0C\u5728" + hourList(ir) + tail : hourCad + "\uFF0C" + minuteHourClause(ir) + tail;
363
+ }
364
+ if (compact.minute > 0) {
332
365
  return minuteHourClause(ir) + "\uFF0C\u5728" + hourList(ir) + tail;
333
366
  }
334
367
  return hourList(ir) + tail;
@@ -347,54 +380,71 @@ function renderHourRange(ir, plan) {
347
380
  }
348
381
  function renderHourStep(ir) {
349
382
  const segment = stepSegment(ir, "hour");
350
- if (segment.startToken !== "*") {
351
- return hourList(ir);
352
- }
353
383
  if (segment.fires.length <= 2) {
354
384
  return joinAnd(segment.fires.map(hourWord));
355
385
  }
356
- return cadence(segment.interval, UNITS.hour);
386
+ return hourCadencePhrase(ir);
357
387
  }
358
388
  function hourStride(ir) {
359
389
  const segments = fieldSegments(ir, "hour");
360
390
  if (segments.length === 1 && segments[0].kind === "step") {
361
- const segment = segments[0];
362
- const start = segment.startToken === "*" ? 0 : +segment.startToken;
363
- if (start !== 0 || 24 % segment.interval !== 0) {
364
- return null;
365
- }
366
- return {
367
- interval: segment.interval,
368
- last: segment.fires[segment.fires.length - 1]
369
- };
391
+ const { fires, interval } = segments[0];
392
+ return { interval, start: fires[0], last: fires[fires.length - 1] };
370
393
  }
371
394
  const values = singleValues(segments);
372
- const step = values && arithmeticStep(values);
373
- if (!step || step.start !== 0 || 24 % step.interval !== 0) {
374
- return null;
395
+ return values && arithmeticStep(values);
396
+ }
397
+ function hourCadencePhrase(ir) {
398
+ const stride = hourStride(ir);
399
+ return stride && renderStride({
400
+ ...stride,
401
+ cycle: 24,
402
+ unit: UNITS.hour,
403
+ mark: "\u70B9",
404
+ anchor: ""
405
+ });
406
+ }
407
+ function minuteZeroConfinement(ir, stride, prefix) {
408
+ if (stride.interval === 2 && stride.start === 0) {
409
+ return "\u5728\u5076\u6570\u5C0F\u65F60\u5206" + secondTail(ir);
410
+ }
411
+ if (prefix.indexOf("\u4ECE") !== -1) {
412
+ return prefix + "0\u5206" + secondTail(ir);
375
413
  }
376
- return { interval: step.interval, last: step.last };
414
+ return null;
377
415
  }
378
416
  function hourCadence(ir) {
379
417
  const stride = hourStride(ir);
380
418
  if (!stride) {
381
419
  return null;
382
420
  }
383
- const fires = stride.last / stride.interval + 1;
421
+ const fires = (stride.last - stride.start) / stride.interval + 1;
384
422
  if (ir.pattern.second === "0" && fires <= maxClockTimes) {
385
423
  return null;
386
424
  }
387
- const prefix = cadence(stride.interval, UNITS.hour);
425
+ const prefix = hourCadencePhrase(ir);
426
+ if (prefix.indexOf("\u81F3") !== -1) {
427
+ return null;
428
+ }
388
429
  const minute = +ir.pattern.minute;
389
430
  const subMinute = ir.pattern.second === "*" || ir.shapes.second === "step";
390
431
  if (minute === 0 && subMinute) {
391
- return stride.interval === 2 ? "\u5728\u5076\u6570\u5C0F\u65F60\u5206" + secondTail(ir) : null;
432
+ return minuteZeroConfinement(ir, stride, prefix);
392
433
  }
393
434
  if (minute === 0) {
394
435
  return prefix + "0\u5206" + secondTail(ir);
395
436
  }
396
437
  return ir.pattern.second === "0" ? prefix + minute + "\u5206" : prefix + minute + "\u5206" + secondTail(ir);
397
438
  }
439
+ function hourCadenceText(ir) {
440
+ if (ir.shapes.minute !== "single") {
441
+ return null;
442
+ }
443
+ if (+ir.pattern.minute === 0 && ir.pattern.second === "0") {
444
+ return hourCadencePhrase(ir);
445
+ }
446
+ return hourCadence(ir);
447
+ }
398
448
  function secondTail(ir) {
399
449
  const sec = secondClause(ir);
400
450
  return sec === "\u6BCF\u79D2" ? "\u7684\u6BCF\u4E00\u79D2" : "\u7684" + sec;
@@ -442,8 +492,8 @@ function minuteClause(ir) {
442
492
  }
443
493
  return valueList(fieldSegments(ir, "minute"), "\u5206");
444
494
  }
445
- function isHourCadence(ir) {
446
- return ir.shapes.hour === "step" && stepSegment(ir, "hour").startToken === "*";
495
+ function clockRestCarriesSecond(rest) {
496
+ return rest.kind === "clockTimes" && rest.times.some((time) => Boolean(time.second));
447
497
  }
448
498
  function composeSecondsOnHour(ir, plan, opts) {
449
499
  const sec = secondClause(ir);
@@ -459,17 +509,19 @@ function composeSecondsOnHour(ir, plan, opts) {
459
509
  return composeMinuteZeroClocks(ir, sec);
460
510
  }
461
511
  const restText = render(ir, rest, opts);
462
- if (rest.kind === "clockTimes" || rest.kind === "compactClockTimes") {
463
- if (isDaily(ir)) {
464
- return "\u6BCF\u5929" + restText + sec;
465
- }
512
+ const secTail = clockRestCarriesSecond(rest) ? "" : sec;
513
+ if (composedClock && isDaily(ir)) {
514
+ return "\u6BCF\u5929" + restText + secTail;
466
515
  }
467
516
  if (rest.kind === "singleMinute") {
468
517
  return restText + "\uFF0C" + sec;
469
518
  }
470
- return restText + sec;
519
+ return restText + secTail;
471
520
  }
472
521
  function composeMinuteZeroClocks(ir, sec) {
522
+ if (hasHourWindow(ir)) {
523
+ return isDaily(ir) ? "\u6BCF\u5929" + hourRangeWindow(ir, sec) : hourRangeWindow(ir, sec);
524
+ }
473
525
  const clocks = hourFires(ir).map(function clock(hour) {
474
526
  return hour === 12 ? "\u6B63\u5348" : hourWord(hour) + "0\u5206";
475
527
  });
@@ -478,11 +530,24 @@ function composeMinuteZeroClocks(ir, sec) {
478
530
  const core = joinAnd(clocks) + tail;
479
531
  return isDaily(ir) ? "\u6BCF\u5929" + core : core;
480
532
  }
533
+ function hasHourWindow(ir) {
534
+ return fieldSegments(ir, "hour").some(function range(segment) {
535
+ return segment.kind === "range";
536
+ });
537
+ }
538
+ function hourRangeWindow(ir, sec) {
539
+ const span = hourList(ir);
540
+ if (ir.pattern.second === "*" || ir.shapes.second === "step") {
541
+ return span + "0\u5206" + (sec === "\u6BCF\u79D2" ? "\u7684\u6BCF\u4E00\u79D2" : "\u7684" + sec);
542
+ }
543
+ return span + "\uFF0C" + sec;
544
+ }
481
545
  function composeSecondsCadence(ir) {
482
546
  const sec = secondClause(ir);
483
547
  const tail = minuteClause(ir) + sec;
484
- if (isHourCadence(ir)) {
485
- return cadence(stepSegment(ir, "hour").interval, UNITS.hour) + "\u7684" + tail;
548
+ const hourCad = hourCadencePhrase(ir);
549
+ if (hourCad !== null) {
550
+ return hourCad + (hourCad.indexOf("\u81F3") === -1 ? "\u7684" : "\uFF0C") + tail;
486
551
  }
487
552
  if (ir.shapes.hour === "single") {
488
553
  return hourWord(hourFires(ir)[0]) + "\u7684" + tail;
@@ -509,8 +574,9 @@ function composeSecondsListed(ir) {
509
574
  if (ir.shapes.hour === "wildcard") {
510
575
  return minutes + "\uFF0C" + sec;
511
576
  }
512
- if (isHourCadence(ir)) {
513
- return cadence(stepSegment(ir, "hour").interval, UNITS.hour) + "\uFF0C" + minutes + "\uFF0C" + sec;
577
+ const hourCad = hourCadencePhrase(ir);
578
+ if (hourCad !== null) {
579
+ return hourCad + "\uFF0C" + minutes + "\uFF0C" + sec;
514
580
  }
515
581
  return hourFrame(ir) + minutes + "\uFF0C" + sec;
516
582
  }
@@ -597,7 +663,7 @@ function quartzDate(token, monthPrefix) {
597
663
  if (token.startsWith("L-")) {
598
664
  return monthPrefix + "\u6700\u540E\u7B2C" + token.slice(2) + "\u5929";
599
665
  }
600
- return "\u6700\u63A5\u8FD1" + token.slice(0, -1) + "\u65E5\u7684\u5DE5\u4F5C\u65E5";
666
+ return monthPrefix + "\u6700\u63A5\u8FD1" + token.slice(0, -1) + "\u65E5\u7684\u5DE5\u4F5C\u65E5";
601
667
  }
602
668
  function datePhrase(ir) {
603
669
  const month = monthPhrase(ir);
@@ -611,7 +677,11 @@ function datePhrase(ir) {
611
677
  if (ir.shapes.date === "step") {
612
678
  return month + cadence(stepSegment(ir, "date").interval, "\u5929");
613
679
  }
614
- return month ? month + dayList(ir) : "\u6BCF\u6708" + dayList(ir);
680
+ if (!month) {
681
+ return "\u6BCF\u6708" + dayList(ir);
682
+ }
683
+ const monthMulti = ir.shapes.month === "range" || ir.shapes.month === "list";
684
+ return month + (monthMulti ? "\uFF0C" : "") + dayList(ir);
615
685
  }
616
686
  function dateCore(ir, quartzPrefix) {
617
687
  if (ir.shapes.date === "quartz") {
@@ -653,10 +723,8 @@ function weekdayPhrase(ir, orContext, monthPrefix) {
653
723
  return "\u6BCF" + weekdayName(from) + "\u81F3" + weekdayName(to);
654
724
  }
655
725
  const days = [];
656
- segs.forEach(function expand(seg) {
657
- if (seg.kind === "step") {
658
- days.push(...seg.fires);
659
- } else if (seg.kind === "single") {
726
+ orderWeekdaysForDisplay(segs).forEach(function expand(seg) {
727
+ if (seg.kind === "single") {
660
728
  days.push(toFieldNumber(seg.value, weekdayNumbers));
661
729
  }
662
730
  });
@@ -722,7 +790,7 @@ function composeWindow(ir, core) {
722
790
  return qualifier(ir) + core;
723
791
  }
724
792
  function hourCadenceApplies(ir) {
725
- return ir.shapes.minute === "single" && hourCadence(ir) !== null;
793
+ return hourCadenceText(ir) !== null;
726
794
  }
727
795
  function describe(ir, opts) {
728
796
  const { kind } = ir.plan;
package/dist/lang/zh.js CHANGED
@@ -18,6 +18,26 @@ function arithmeticStep(values) {
18
18
  }
19
19
  return { start: values[0], interval, last: values[values.length - 1] };
20
20
  }
21
+ function weekdayDisplayKey(value) {
22
+ return value === 0 ? 7 : value;
23
+ }
24
+ function orderWeekdaysForDisplay(segments) {
25
+ const flattened = segments.flatMap(function flat(segment) {
26
+ return segment.kind === "step" ? segment.fires.map(function single(value) {
27
+ return { kind: "single", value: "" + value };
28
+ }) : [segment];
29
+ });
30
+ function key(segment) {
31
+ return segment.kind === "range" ? weekdayDisplayKey(+segment.bounds[0]) : weekdayDisplayKey(+segment.value);
32
+ }
33
+ return flattened.map(function index(segment, position) {
34
+ return [segment, position];
35
+ }).sort(function byDisplayKey(a, b) {
36
+ return key(a[0]) - key(b[0]) || a[1] - b[1];
37
+ }).map(function unwrap(pair) {
38
+ return pair[0];
39
+ });
40
+ }
21
41
  function toFieldNumber(token, numberMap) {
22
42
  return isNonNegativeInteger(token) ? +token : numberMap[token.toUpperCase()];
23
43
  }
@@ -223,8 +243,18 @@ function minuteHourClause(ir) {
223
243
  function renderMinutePast(ir) {
224
244
  return minuteHourClause(ir);
225
245
  }
246
+ function hourSegmentWords(segment) {
247
+ if (segment.kind === "range") {
248
+ return [hourWord(+segment.bounds[0]) + "\u81F3" + hourWord(+segment.bounds[1])];
249
+ }
250
+ if (segment.kind === "step") {
251
+ return segment.fires.map(hourWord);
252
+ }
253
+ return [hourWord(+segment.value)];
254
+ }
226
255
  function hourList(ir) {
227
- return joinAnd(hourFires(ir).map(hourWord));
256
+ const words = fieldSegments(ir, "hour").flatMap(hourSegmentWords);
257
+ return joinAnd(words);
228
258
  }
229
259
  function hourFrame(ir) {
230
260
  if (ir.shapes.hour === "range") {
@@ -237,9 +267,11 @@ function renderMinuteFrequency(ir, plan) {
237
267
  const minuteStep = stepSegment(ir, "minute");
238
268
  const base = stepClause(minuteStep, UNITS.minute, "\u5206", "\u6BCF\u5C0F\u65F6");
239
269
  const { hours } = plan;
240
- if (hours.kind === "step") {
241
- const hourStep = stepSegment(ir, "hour");
242
- return hourStep.startToken === "*" ? cadence(hourStep.interval, UNITS.hour) + base : "\u5728" + hourList(ir) + "\uFF0C" + base;
270
+ if (hours.kind === "step" || hours.kind === "during") {
271
+ const hourCad = hourCadencePhrase(ir);
272
+ if (hourCad !== null) {
273
+ return hourCad + (hourCad.indexOf("\u81F3") === -1 ? "" : "\uFF0C") + base;
274
+ }
243
275
  }
244
276
  if (hours.kind === "single" || hours.kind === "window" && hours.from === hours.to) {
245
277
  return "\u5728" + hourWord(hours.from) + "\u81F3" + hours.from + "\u70B9" + hours.last + "\u5206\u4E4B\u95F4\uFF0C" + base;
@@ -261,20 +293,21 @@ function renderMinuteSpanInHour(ir, plan) {
261
293
  }
262
294
  function renderMinutesAcrossHours(ir, plan) {
263
295
  const { form } = plan;
296
+ const hourCad = hourCadencePhrase(ir);
264
297
  if (form === "wildcard") {
265
- return "\u5728" + hourList(ir) + "\uFF0C\u6BCF\u5206\u949F";
298
+ return hourCad === null ? "\u5728" + hourList(ir) + "\uFF0C\u6BCF\u5206\u949F" : hourCad + "\uFF0C\u6BCF\u5206\u949F";
266
299
  }
267
- return hourList(ir) + "\uFF0C" + minuteHourClause(ir) + "\uFF0C\u6BCF\u5206\u949F";
300
+ return (hourCad ?? hourList(ir)) + "\uFF0C" + minuteHourClause(ir) + "\uFF0C\u6BCF\u5206\u949F";
268
301
  }
269
302
  function renderMinuteSpanAcrossHourStep(ir, plan) {
270
303
  const hourStep = stepSegment(ir, "hour");
271
304
  const { form } = plan;
272
305
  if (form === "list") {
273
- return renderMinutePast(ir) + "\uFF0C\u5728" + hourList(ir);
306
+ return hourCadencePhrase(ir) + "\uFF0C" + renderMinutePast(ir);
274
307
  }
275
308
  const minuteTail = form === "wildcard" ? "\u6BCF\u5206\u949F" : minuteHourClause(ir) + "\uFF0C\u6BCF\u5206\u949F";
276
309
  if (hourStep.startToken !== "*") {
277
- return form === "wildcard" ? "\u5728" + hourList(ir) + "\uFF0C" + minuteTail : hourList(ir) + "\uFF0C" + minuteTail;
310
+ return hourCadencePhrase(ir) + "\uFF0C" + minuteTail;
278
311
  }
279
312
  if (hourStep.interval === 2 && form === "wildcard") {
280
313
  return "\u5728\u5076\u6570\u5C0F\u65F6\uFF0C" + minuteTail;
@@ -283,26 +316,26 @@ function renderMinuteSpanAcrossHourStep(ir, plan) {
283
316
  return form === "wildcard" ? cad + "\u5185\uFF0C" + minuteTail : cad + "\uFF0C" + minuteTail;
284
317
  }
285
318
  function renderClockTimes(ir, plan, opts) {
286
- if (ir.shapes.minute === "single") {
287
- const cad = hourCadence(ir);
288
- if (cad !== null) {
289
- return cad;
290
- }
319
+ const cad = hourCadenceText(ir);
320
+ if (cad !== null) {
321
+ return cad;
291
322
  }
292
323
  const { times } = plan;
293
324
  return joinAnd(times.map((t) => clockTime(t.hour, t.minute, t.second, opts)));
294
325
  }
295
326
  function renderCompactClockTimes(ir, plan) {
296
- if (ir.shapes.minute === "single") {
297
- const cad = hourCadence(ir);
298
- if (cad !== null) {
299
- return cad;
300
- }
327
+ const cad = hourCadenceText(ir);
328
+ if (cad !== null) {
329
+ return cad;
301
330
  }
302
- const { minute } = plan;
331
+ const compact = plan;
303
332
  const secs = fieldSegments(ir, "second");
304
333
  const tail = secs.length && ir.pattern.second !== "0" ? "\uFF0C\u7B2C" + valueText(secs) + "\u79D2" : "";
305
- if (minute > 0) {
334
+ if (!compact.fold) {
335
+ const hourCad = hourCadencePhrase(ir);
336
+ return hourCad === null ? minuteHourClause(ir) + "\uFF0C\u5728" + hourList(ir) + tail : hourCad + "\uFF0C" + minuteHourClause(ir) + tail;
337
+ }
338
+ if (compact.minute > 0) {
306
339
  return minuteHourClause(ir) + "\uFF0C\u5728" + hourList(ir) + tail;
307
340
  }
308
341
  return hourList(ir) + tail;
@@ -321,54 +354,71 @@ function renderHourRange(ir, plan) {
321
354
  }
322
355
  function renderHourStep(ir) {
323
356
  const segment = stepSegment(ir, "hour");
324
- if (segment.startToken !== "*") {
325
- return hourList(ir);
326
- }
327
357
  if (segment.fires.length <= 2) {
328
358
  return joinAnd(segment.fires.map(hourWord));
329
359
  }
330
- return cadence(segment.interval, UNITS.hour);
360
+ return hourCadencePhrase(ir);
331
361
  }
332
362
  function hourStride(ir) {
333
363
  const segments = fieldSegments(ir, "hour");
334
364
  if (segments.length === 1 && segments[0].kind === "step") {
335
- const segment = segments[0];
336
- const start = segment.startToken === "*" ? 0 : +segment.startToken;
337
- if (start !== 0 || 24 % segment.interval !== 0) {
338
- return null;
339
- }
340
- return {
341
- interval: segment.interval,
342
- last: segment.fires[segment.fires.length - 1]
343
- };
365
+ const { fires, interval } = segments[0];
366
+ return { interval, start: fires[0], last: fires[fires.length - 1] };
344
367
  }
345
368
  const values = singleValues(segments);
346
- const step = values && arithmeticStep(values);
347
- if (!step || step.start !== 0 || 24 % step.interval !== 0) {
348
- return null;
369
+ return values && arithmeticStep(values);
370
+ }
371
+ function hourCadencePhrase(ir) {
372
+ const stride = hourStride(ir);
373
+ return stride && renderStride({
374
+ ...stride,
375
+ cycle: 24,
376
+ unit: UNITS.hour,
377
+ mark: "\u70B9",
378
+ anchor: ""
379
+ });
380
+ }
381
+ function minuteZeroConfinement(ir, stride, prefix) {
382
+ if (stride.interval === 2 && stride.start === 0) {
383
+ return "\u5728\u5076\u6570\u5C0F\u65F60\u5206" + secondTail(ir);
384
+ }
385
+ if (prefix.indexOf("\u4ECE") !== -1) {
386
+ return prefix + "0\u5206" + secondTail(ir);
349
387
  }
350
- return { interval: step.interval, last: step.last };
388
+ return null;
351
389
  }
352
390
  function hourCadence(ir) {
353
391
  const stride = hourStride(ir);
354
392
  if (!stride) {
355
393
  return null;
356
394
  }
357
- const fires = stride.last / stride.interval + 1;
395
+ const fires = (stride.last - stride.start) / stride.interval + 1;
358
396
  if (ir.pattern.second === "0" && fires <= maxClockTimes) {
359
397
  return null;
360
398
  }
361
- const prefix = cadence(stride.interval, UNITS.hour);
399
+ const prefix = hourCadencePhrase(ir);
400
+ if (prefix.indexOf("\u81F3") !== -1) {
401
+ return null;
402
+ }
362
403
  const minute = +ir.pattern.minute;
363
404
  const subMinute = ir.pattern.second === "*" || ir.shapes.second === "step";
364
405
  if (minute === 0 && subMinute) {
365
- return stride.interval === 2 ? "\u5728\u5076\u6570\u5C0F\u65F60\u5206" + secondTail(ir) : null;
406
+ return minuteZeroConfinement(ir, stride, prefix);
366
407
  }
367
408
  if (minute === 0) {
368
409
  return prefix + "0\u5206" + secondTail(ir);
369
410
  }
370
411
  return ir.pattern.second === "0" ? prefix + minute + "\u5206" : prefix + minute + "\u5206" + secondTail(ir);
371
412
  }
413
+ function hourCadenceText(ir) {
414
+ if (ir.shapes.minute !== "single") {
415
+ return null;
416
+ }
417
+ if (+ir.pattern.minute === 0 && ir.pattern.second === "0") {
418
+ return hourCadencePhrase(ir);
419
+ }
420
+ return hourCadence(ir);
421
+ }
372
422
  function secondTail(ir) {
373
423
  const sec = secondClause(ir);
374
424
  return sec === "\u6BCF\u79D2" ? "\u7684\u6BCF\u4E00\u79D2" : "\u7684" + sec;
@@ -416,8 +466,8 @@ function minuteClause(ir) {
416
466
  }
417
467
  return valueList(fieldSegments(ir, "minute"), "\u5206");
418
468
  }
419
- function isHourCadence(ir) {
420
- return ir.shapes.hour === "step" && stepSegment(ir, "hour").startToken === "*";
469
+ function clockRestCarriesSecond(rest) {
470
+ return rest.kind === "clockTimes" && rest.times.some((time) => Boolean(time.second));
421
471
  }
422
472
  function composeSecondsOnHour(ir, plan, opts) {
423
473
  const sec = secondClause(ir);
@@ -433,17 +483,19 @@ function composeSecondsOnHour(ir, plan, opts) {
433
483
  return composeMinuteZeroClocks(ir, sec);
434
484
  }
435
485
  const restText = render(ir, rest, opts);
436
- if (rest.kind === "clockTimes" || rest.kind === "compactClockTimes") {
437
- if (isDaily(ir)) {
438
- return "\u6BCF\u5929" + restText + sec;
439
- }
486
+ const secTail = clockRestCarriesSecond(rest) ? "" : sec;
487
+ if (composedClock && isDaily(ir)) {
488
+ return "\u6BCF\u5929" + restText + secTail;
440
489
  }
441
490
  if (rest.kind === "singleMinute") {
442
491
  return restText + "\uFF0C" + sec;
443
492
  }
444
- return restText + sec;
493
+ return restText + secTail;
445
494
  }
446
495
  function composeMinuteZeroClocks(ir, sec) {
496
+ if (hasHourWindow(ir)) {
497
+ return isDaily(ir) ? "\u6BCF\u5929" + hourRangeWindow(ir, sec) : hourRangeWindow(ir, sec);
498
+ }
447
499
  const clocks = hourFires(ir).map(function clock(hour) {
448
500
  return hour === 12 ? "\u6B63\u5348" : hourWord(hour) + "0\u5206";
449
501
  });
@@ -452,11 +504,24 @@ function composeMinuteZeroClocks(ir, sec) {
452
504
  const core = joinAnd(clocks) + tail;
453
505
  return isDaily(ir) ? "\u6BCF\u5929" + core : core;
454
506
  }
507
+ function hasHourWindow(ir) {
508
+ return fieldSegments(ir, "hour").some(function range(segment) {
509
+ return segment.kind === "range";
510
+ });
511
+ }
512
+ function hourRangeWindow(ir, sec) {
513
+ const span = hourList(ir);
514
+ if (ir.pattern.second === "*" || ir.shapes.second === "step") {
515
+ return span + "0\u5206" + (sec === "\u6BCF\u79D2" ? "\u7684\u6BCF\u4E00\u79D2" : "\u7684" + sec);
516
+ }
517
+ return span + "\uFF0C" + sec;
518
+ }
455
519
  function composeSecondsCadence(ir) {
456
520
  const sec = secondClause(ir);
457
521
  const tail = minuteClause(ir) + sec;
458
- if (isHourCadence(ir)) {
459
- return cadence(stepSegment(ir, "hour").interval, UNITS.hour) + "\u7684" + tail;
522
+ const hourCad = hourCadencePhrase(ir);
523
+ if (hourCad !== null) {
524
+ return hourCad + (hourCad.indexOf("\u81F3") === -1 ? "\u7684" : "\uFF0C") + tail;
460
525
  }
461
526
  if (ir.shapes.hour === "single") {
462
527
  return hourWord(hourFires(ir)[0]) + "\u7684" + tail;
@@ -483,8 +548,9 @@ function composeSecondsListed(ir) {
483
548
  if (ir.shapes.hour === "wildcard") {
484
549
  return minutes + "\uFF0C" + sec;
485
550
  }
486
- if (isHourCadence(ir)) {
487
- return cadence(stepSegment(ir, "hour").interval, UNITS.hour) + "\uFF0C" + minutes + "\uFF0C" + sec;
551
+ const hourCad = hourCadencePhrase(ir);
552
+ if (hourCad !== null) {
553
+ return hourCad + "\uFF0C" + minutes + "\uFF0C" + sec;
488
554
  }
489
555
  return hourFrame(ir) + minutes + "\uFF0C" + sec;
490
556
  }
@@ -571,7 +637,7 @@ function quartzDate(token, monthPrefix) {
571
637
  if (token.startsWith("L-")) {
572
638
  return monthPrefix + "\u6700\u540E\u7B2C" + token.slice(2) + "\u5929";
573
639
  }
574
- return "\u6700\u63A5\u8FD1" + token.slice(0, -1) + "\u65E5\u7684\u5DE5\u4F5C\u65E5";
640
+ return monthPrefix + "\u6700\u63A5\u8FD1" + token.slice(0, -1) + "\u65E5\u7684\u5DE5\u4F5C\u65E5";
575
641
  }
576
642
  function datePhrase(ir) {
577
643
  const month = monthPhrase(ir);
@@ -585,7 +651,11 @@ function datePhrase(ir) {
585
651
  if (ir.shapes.date === "step") {
586
652
  return month + cadence(stepSegment(ir, "date").interval, "\u5929");
587
653
  }
588
- return month ? month + dayList(ir) : "\u6BCF\u6708" + dayList(ir);
654
+ if (!month) {
655
+ return "\u6BCF\u6708" + dayList(ir);
656
+ }
657
+ const monthMulti = ir.shapes.month === "range" || ir.shapes.month === "list";
658
+ return month + (monthMulti ? "\uFF0C" : "") + dayList(ir);
589
659
  }
590
660
  function dateCore(ir, quartzPrefix) {
591
661
  if (ir.shapes.date === "quartz") {
@@ -627,10 +697,8 @@ function weekdayPhrase(ir, orContext, monthPrefix) {
627
697
  return "\u6BCF" + weekdayName(from) + "\u81F3" + weekdayName(to);
628
698
  }
629
699
  const days = [];
630
- segs.forEach(function expand(seg) {
631
- if (seg.kind === "step") {
632
- days.push(...seg.fires);
633
- } else if (seg.kind === "single") {
700
+ orderWeekdaysForDisplay(segs).forEach(function expand(seg) {
701
+ if (seg.kind === "single") {
634
702
  days.push(toFieldNumber(seg.value, weekdayNumbers));
635
703
  }
636
704
  });
@@ -696,7 +764,7 @@ function composeWindow(ir, core) {
696
764
  return qualifier(ir) + core;
697
765
  }
698
766
  function hourCadenceApplies(ir) {
699
- return ir.shapes.minute === "single" && hourCadence(ir) !== null;
767
+ return hourCadenceText(ir) !== null;
700
768
  }
701
769
  function describe(ir, opts) {
702
770
  const { kind } = ir.plan;