timelang 0.1.0 → 0.3.0

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/index.js CHANGED
@@ -230,8 +230,21 @@ var lexer = moo.compile({
230
230
  "sat",
231
231
  "sun"
232
232
  ],
233
+ // Ago keyword for relative dates
234
+ kw_ago: ["ago"],
235
+ // Weekend and night keywords
236
+ kw_weekend: ["weekend"],
237
+ kw_tonight: ["tonight"],
238
+ kw_night: ["night"],
239
+ kw_fortnight: ["fortnight", "fortnights"],
233
240
  // Other keywords
234
- otherKeyword: ["fiscal", "fy", "daily", "weekly", "monthly", "yearly", "annually", "ago", "hence", "later"]
241
+ otherKeyword: ["fiscal", "fy", "daily", "weekly", "monthly", "yearly", "annually", "hence", "later", "earlier"],
242
+ // Business keywords
243
+ kw_business: ["business"],
244
+ // EOD/COB keywords
245
+ kw_eod: ["eod"],
246
+ kw_cob: ["cob"],
247
+ kw_close: ["close"]
235
248
  })
236
249
  // Keywords are matched against lowercase input
237
250
  },
@@ -284,6 +297,13 @@ var makeRelative = (direction, duration) => ({
284
297
  direction,
285
298
  duration
286
299
  });
300
+ var makeRelativeDate = (direction, duration, baseDate, time2) => ({
301
+ nodeType: "relativeDate",
302
+ direction,
303
+ duration,
304
+ baseDate,
305
+ time: time2
306
+ });
287
307
  var makeTitled = (title, expression, titleStart, titleEnd) => ({
288
308
  nodeType: "titled",
289
309
  title: title.trim(),
@@ -519,10 +539,14 @@ var grammar = {
519
539
  { "name": "expression", "symbols": ["range"], "postprocess": first },
520
540
  { "name": "expression", "symbols": ["span"], "postprocess": first },
521
541
  { "name": "expression", "symbols": ["relative"], "postprocess": first },
542
+ { "name": "expression", "symbols": ["relativeDate"], "postprocess": first },
543
+ { "name": "expression", "symbols": ["lastDayOfMonthExpr"], "postprocess": first },
544
+ { "name": "expression", "symbols": ["date"], "postprocess": first },
522
545
  { "name": "expression", "symbols": ["fuzzy"], "postprocess": first },
523
546
  { "name": "expression", "symbols": ["forDuration"], "postprocess": first },
524
547
  { "name": "expression", "symbols": ["duration"], "postprocess": first },
525
- { "name": "expression", "symbols": ["date"], "postprocess": first },
548
+ { "name": "lastDayOfMonthExpr", "symbols": ["lastRelative", "_", "dayUnit", "_", "ofConnector", "_", "month"], "postprocess": (d) => makeDate({ month: d[6], lastDayOfMonth: true }) },
549
+ { "name": "lastDayOfMonthExpr", "symbols": ["lastRelative", "_", "dayUnit", "_", "ofConnector", "_", "theConnector", "_", "monthUnit"], "postprocess": (d) => makeDate({ lastDayOfMonth: true, monthFromRef: true }) },
526
550
  { "name": "forDuration", "symbols": ["forConnector", "_", "duration"], "postprocess": (d) => d[2] },
527
551
  { "name": "range", "symbols": ["date", "_", "toConnector", "_", "date"], "postprocess": (d) => makeRange(d[0], d[4]) },
528
552
  { "name": "range", "symbols": ["fromConnector", "_", "date", "_", "toConnector", "_", "date"], "postprocess": (d) => makeRange(d[2], d[6]) },
@@ -604,6 +628,44 @@ var grammar = {
604
628
  { "name": "relative", "symbols": ["inConnector", "_", "theConnector", "_", "pastRelative", "_", "unit"], "postprocess": (d) => makeRelative("past", makeDuration(1, d[6])) },
605
629
  { "name": "relative", "symbols": ["inConnector", "_", "theConnector", "_", "comingRelative", "_", "unit"], "postprocess": (d) => makeRelative("future", makeDuration(1, d[6])) },
606
630
  { "name": "relative", "symbols": ["inConnector", "_", "theConnector", "_", "upcomingRelative", "_", "unit"], "postprocess": (d) => makeRelative("future", makeDuration(1, d[6])) },
631
+ { "name": "relativeDate", "symbols": ["duration", "_", "agoConnector"], "postprocess": (d) => makeRelativeDate("past", d[0]) },
632
+ { "name": "relativeDate", "symbols": ["duration", "_", "agoConnector", "_", "atConnector", "_", "time"], "postprocess": (d) => makeRelativeDate("past", d[0], void 0, d[6]) },
633
+ { "name": "relativeDate", "symbols": ["duration", "_", "agoConnector", "_", "atConnector", "_", "timeWord"], "postprocess": (d) => makeRelativeDate("past", d[0], void 0, { special: d[6] }) },
634
+ { "name": "relativeDate", "symbols": ["inConnector", "_", "duration"], "postprocess": (d) => makeRelativeDate("future", d[2]) },
635
+ { "name": "relativeDate", "symbols": ["inConnector", "_", "duration", "_", "atConnector", "_", "time"], "postprocess": (d) => makeRelativeDate("future", d[2], void 0, d[6]) },
636
+ { "name": "relativeDate", "symbols": ["inConnector", "_", "duration", "_", "atConnector", "_", "timeWord"], "postprocess": (d) => makeRelativeDate("future", d[2], void 0, { special: d[6] }) },
637
+ { "name": "relativeDate", "symbols": ["duration", "_", "fromConnector", "_", "now"], "postprocess": (d) => makeRelativeDate("future", d[0]) },
638
+ { "name": "relativeDate", "symbols": ["duration", "_", "fromConnector", "_", "now", "_", "atConnector", "_", "time"], "postprocess": (d) => makeRelativeDate("future", d[0], void 0, d[8]) },
639
+ { "name": "relativeDate", "symbols": ["duration", "_", "fromConnector", "_", "now", "_", "atConnector", "_", "timeWord"], "postprocess": (d) => makeRelativeDate("future", d[0], void 0, { special: d[8] }) },
640
+ { "name": "relativeDate", "symbols": ["duration", "_", "fromConnector", "_", "today"], "postprocess": (d) => makeRelativeDate("future", d[0], makeDate({ special: "today" })) },
641
+ { "name": "relativeDate", "symbols": ["duration", "_", "fromConnector", "_", "today", "_", "atConnector", "_", "time"], "postprocess": (d) => makeRelativeDate("future", d[0], makeDate({ special: "today" }), d[8]) },
642
+ { "name": "relativeDate", "symbols": ["duration", "_", "fromConnector", "_", "tomorrow"], "postprocess": (d) => makeRelativeDate("future", d[0], makeDate({ special: "tomorrow" })) },
643
+ { "name": "relativeDate", "symbols": ["duration", "_", "fromConnector", "_", "tomorrow", "_", "atConnector", "_", "time"], "postprocess": (d) => makeRelativeDate("future", d[0], makeDate({ special: "tomorrow" }), d[8]) },
644
+ { "name": "relativeDate", "symbols": ["duration", "_", "fromConnector", "_", "yesterday"], "postprocess": (d) => makeRelativeDate("future", d[0], makeDate({ special: "yesterday" })) },
645
+ { "name": "relativeDate", "symbols": ["duration", "_", "henceConnector"], "postprocess": (d) => makeRelativeDate("future", d[0]) },
646
+ { "name": "relativeDate", "symbols": ["duration", "_", "laterConnector"], "postprocess": (d) => makeRelativeDate("future", d[0]) },
647
+ { "name": "relativeDate", "symbols": ["duration", "_", "beforeConnector", "_", "date"], "postprocess": (d) => makeRelativeDate("past", d[0], d[4]) },
648
+ { "name": "relativeDate", "symbols": ["duration", "_", "afterConnector", "_", "date"], "postprocess": (d) => makeRelativeDate("future", d[0], d[4]) },
649
+ { "name": "relativeDate", "symbols": ["theConnector", "_", "dayUnit", "_", "beforeConnector", "_", "date"], "postprocess": (d) => makeRelativeDate("past", makeDuration(1, "day"), d[6]) },
650
+ { "name": "relativeDate", "symbols": ["theConnector", "_", "dayUnit", "_", "afterConnector", "_", "date"], "postprocess": (d) => makeRelativeDate("future", makeDuration(1, "day"), d[6]) },
651
+ { "name": "relativeDate", "symbols": ["dayUnit", "_", "beforeConnector", "_", "date"], "postprocess": (d) => makeRelativeDate("past", makeDuration(1, "day"), d[4]) },
652
+ { "name": "relativeDate", "symbols": ["dayUnit", "_", "afterConnector", "_", "date"], "postprocess": (d) => makeRelativeDate("future", makeDuration(1, "day"), d[4]) },
653
+ { "name": "relativeDate", "symbols": ["wordNumber", "_", "unit", "_", "beforeConnector", "_", "date"], "postprocess": (d) => makeRelativeDate("past", makeDuration(parseWordNumber(d[0]), d[2]), d[6]) },
654
+ { "name": "relativeDate", "symbols": ["wordNumber", "_", "unit", "_", "afterConnector", "_", "date"], "postprocess": (d) => makeRelativeDate("future", makeDuration(parseWordNumber(d[0]), d[2]), d[6]) },
655
+ { "name": "relativeDate", "symbols": ["nextRelative", "_", "businessKeyword", "_", "dayOrDaysUnit"], "postprocess": (d) => makeRelativeDate("future", makeDuration(1, "businessDay")) },
656
+ { "name": "relativeDate", "symbols": ["inConnector", "_", "number", "_", "businessKeyword", "_", "dayOrDaysUnit"], "postprocess": (d) => makeRelativeDate("future", makeDuration(d[2], "businessDay")) },
657
+ { "name": "relativeDate", "symbols": ["inConnector", "_", "wordNumber", "_", "businessKeyword", "_", "dayOrDaysUnit"], "postprocess": (d) => makeRelativeDate("future", makeDuration(parseWordNumber(d[2]), "businessDay")) },
658
+ { "name": "relativeDate", "symbols": ["number", "_", "businessKeyword", "_", "dayOrDaysUnit", "_", "agoConnector"], "postprocess": (d) => makeRelativeDate("past", makeDuration(d[0], "businessDay")) },
659
+ { "name": "relativeDate", "symbols": ["wordNumber", "_", "businessKeyword", "_", "dayOrDaysUnit", "_", "agoConnector"], "postprocess": (d) => makeRelativeDate("past", makeDuration(parseWordNumber(d[0]), "businessDay")) },
660
+ { "name": "relativeDate", "symbols": ["wordNumber", "_", "fortnightKeyword", "_", "fromConnector", "_", "now"], "postprocess": (d) => makeRelativeDate("future", makeDuration(parseWordNumber(d[0]) * 2, "week")) },
661
+ { "name": "relativeDate", "symbols": ["number", "_", "fortnightKeyword", "_", "fromConnector", "_", "now"], "postprocess": (d) => makeRelativeDate("future", makeDuration(d[0] * 2, "week")) },
662
+ { "name": "relativeDate", "symbols": ["fortnightKeyword", "_", "fromConnector", "_", "now"], "postprocess": (d) => makeRelativeDate("future", makeDuration(2, "week")) },
663
+ { "name": "relativeDate", "symbols": ["inConnector", "_", "wordNumber", "_", "fortnightKeyword"], "postprocess": (d) => makeRelativeDate("future", makeDuration(parseWordNumber(d[2]) * 2, "week")) },
664
+ { "name": "relativeDate", "symbols": ["inConnector", "_", "number", "_", "fortnightKeyword"], "postprocess": (d) => makeRelativeDate("future", makeDuration(d[2] * 2, "week")) },
665
+ { "name": "relativeDate", "symbols": ["inConnector", "_", "fortnightKeyword"], "postprocess": (d) => makeRelativeDate("future", makeDuration(2, "week")) },
666
+ { "name": "relativeDate", "symbols": ["wordNumber", "_", "fortnightKeyword", "_", "agoConnector"], "postprocess": (d) => makeRelativeDate("past", makeDuration(parseWordNumber(d[0]) * 2, "week")) },
667
+ { "name": "relativeDate", "symbols": ["number", "_", "fortnightKeyword", "_", "agoConnector"], "postprocess": (d) => makeRelativeDate("past", makeDuration(d[0] * 2, "week")) },
668
+ { "name": "relativeDate", "symbols": ["fortnightKeyword", "_", "agoConnector"], "postprocess": (d) => makeRelativeDate("past", makeDuration(2, "week")) },
607
669
  { "name": "fuzzy", "symbols": ["quarter"], "postprocess": (d) => makeFuzzy({ period: "quarter", quarter: parseQuarter(d[0].value) }) },
608
670
  { "name": "fuzzy", "symbols": ["quarter", "_", "year"], "postprocess": (d) => makeFuzzy({ period: "quarter", quarter: parseQuarter(d[0].value), year: d[2] }) },
609
671
  { "name": "fuzzy", "symbols": ["half"], "postprocess": (d) => makeFuzzy({ period: "half", half: parseHalf(d[0].value) }) },
@@ -686,8 +748,31 @@ var grammar = {
686
748
  { "name": "fuzzy", "symbols": ["lateModifier", "_", "thisRelative", "_", "unit"], "postprocess": (d) => makeFuzzy({ period: d[4], modifier: "late", relative: "this" }) },
687
749
  { "name": "fuzzy", "symbols": ["beginningConnector", "_", "ofConnector", "_", "theConnector", "_", "unit"], "postprocess": (d) => makeFuzzy({ period: d[6], modifier: "beginning" }) },
688
750
  { "name": "fuzzy", "symbols": ["middleConnector", "_", "ofConnector", "_", "theConnector", "_", "unit"], "postprocess": (d) => makeFuzzy({ period: d[6], modifier: "middle" }) },
689
- { "name": "fuzzy", "symbols": ["lastRelative", "_", "unit", "_", "ofConnector", "_", "month"], "postprocess": (d) => makeFuzzy({ period: "month", month: d[6], modifier: "late" }) },
690
- { "name": "fuzzy", "symbols": ["ordinalWord", "_", "unit", "_", "ofConnector", "_", "month"], "postprocess": (d) => makeFuzzy({ period: "month", month: d[6], modifier: parseOrdinalWord(d[0]) <= 2 ? "early" : parseOrdinalWord(d[0]) >= 4 ? "late" : "mid" }) },
751
+ { "name": "fuzzy", "symbols": ["lastRelative", "_", "unit", "_", "ofConnector", "_", "month"], "postprocess": (d, _, reject) => {
752
+ if (d[2].toLowerCase() === "day" || d[2].toLowerCase() === "days") return reject;
753
+ return makeFuzzy({ period: "month", month: d[6], modifier: "late" });
754
+ } },
755
+ { "name": "fuzzy", "symbols": ["ordinalWord", "_", "unit", "_", "ofConnector", "_", "month"], "postprocess": (d, _, reject) => {
756
+ if (d[2].toLowerCase() === "day" || d[2].toLowerCase() === "days") return reject;
757
+ return makeFuzzy({ period: "month", month: d[6], modifier: parseOrdinalWord(d[0]) <= 2 ? "early" : parseOrdinalWord(d[0]) >= 4 ? "late" : "mid" });
758
+ } },
759
+ { "name": "fuzzy", "symbols": ["weekendKeyword"], "postprocess": (d) => makeFuzzy({ period: "weekend", relative: "this" }) },
760
+ { "name": "fuzzy", "symbols": ["theConnector", "_", "weekendKeyword"], "postprocess": (d) => makeFuzzy({ period: "weekend", relative: "this" }) },
761
+ { "name": "fuzzy", "symbols": ["thisRelative", "_", "weekendKeyword"], "postprocess": (d) => makeFuzzy({ period: "weekend", relative: "this" }) },
762
+ { "name": "fuzzy", "symbols": ["nextRelative", "_", "weekendKeyword"], "postprocess": (d) => makeFuzzy({ period: "weekend", relative: "next" }) },
763
+ { "name": "fuzzy", "symbols": ["lastRelative", "_", "weekendKeyword"], "postprocess": (d) => makeFuzzy({ period: "weekend", relative: "last" }) },
764
+ { "name": "fuzzy", "symbols": ["tonightKeyword"], "postprocess": (d) => makeFuzzy({ period: "tonight" }) },
765
+ { "name": "fuzzy", "symbols": ["nightKeyword"], "postprocess": (d) => makeFuzzy({ period: "night" }) },
766
+ { "name": "fuzzy", "symbols": ["lastRelative", "_", "nightKeyword"], "postprocess": (d) => makeFuzzy({ period: "night", relative: "last" }) },
767
+ { "name": "fuzzy", "symbols": ["tomorrow", "_", "nightKeyword"], "postprocess": (d) => makeFuzzy({ period: "night", relative: "tomorrow" }) },
768
+ { "name": "fuzzy", "symbols": ["weekday", "_", "nightKeyword"], "postprocess": (d) => makeFuzzy({ period: "night", weekday: d[0] }) },
769
+ { "name": "fuzzy", "symbols": ["laterKeyword", "_", "today"], "postprocess": (d) => makeFuzzy({ period: "day", modifier: "later" }) },
770
+ { "name": "fuzzy", "symbols": ["earlierKeyword", "_", "today"], "postprocess": (d) => makeFuzzy({ period: "day", modifier: "earlier" }) },
771
+ { "name": "fuzzy", "symbols": ["laterKeyword", "_", "thisRelative", "_", "unit"], "postprocess": (d) => makeFuzzy({ period: d[4], modifier: "later", relative: "this" }) },
772
+ { "name": "fuzzy", "symbols": ["earlierKeyword", "_", "thisRelative", "_", "unit"], "postprocess": (d) => makeFuzzy({ period: d[4], modifier: "earlier", relative: "this" }) },
773
+ { "name": "fuzzy", "symbols": ["weekKeyword", "_", "number"], "postprocess": (d) => makeFuzzy({ period: "weekNumber", weekNumber: d[2] }) },
774
+ { "name": "fuzzy", "symbols": ["weekKeyword", "_", "number", "_", "year"], "postprocess": (d) => makeFuzzy({ period: "weekNumber", weekNumber: d[2], year: d[4] }) },
775
+ { "name": "fuzzy", "symbols": ["theConnector", "_", "weekKeyword", "_", "ofConnector", "_", "date"], "postprocess": (d) => makeFuzzy({ period: "weekOf", baseDate: d[6] }) },
691
776
  { "name": "duration", "symbols": ["number", "_", "unit"], "postprocess": (d) => makeDuration(d[0], d[2]) },
692
777
  { "name": "duration", "symbols": ["wordNumber", "_", "unit"], "postprocess": (d) => makeDuration(parseWordNumber(d[0]), d[2]) },
693
778
  { "name": "duration", "symbols": ["abbreviatedDuration"], "postprocess": (d) => d[0] },
@@ -715,6 +800,7 @@ var grammar = {
715
800
  return makeDuration(value, unitMap[match[2]]);
716
801
  } },
717
802
  { "name": "date", "symbols": ["specialDay"], "postprocess": (d) => makeDate({ special: d[0] }) },
803
+ { "name": "date", "symbols": ["ordinalWeekdayOfMonth"], "postprocess": first },
718
804
  { "name": "date", "symbols": ["relativeWeekday"], "postprocess": first },
719
805
  { "name": "date", "symbols": ["weekday"], "postprocess": (d) => makeDate({ weekday: d[0] }) },
720
806
  { "name": "date", "symbols": ["monthDayYear"], "postprocess": first },
@@ -729,6 +815,8 @@ var grammar = {
729
815
  { "name": "date", "symbols": ["monthOnly"], "postprocess": first },
730
816
  { "name": "date", "symbols": ["yearOnly"], "postprocess": first },
731
817
  { "name": "date", "symbols": ["timeOnly"], "postprocess": first },
818
+ { "name": "date", "symbols": ["dayOnly"], "postprocess": first },
819
+ { "name": "date", "symbols": ["eodCobDate"], "postprocess": first },
732
820
  { "name": "monthDayCompact", "symbols": [lexer.has("monthDayCompact") ? { type: "monthDayCompact" } : monthDayCompact], "postprocess": (d) => {
733
821
  const parsed = parseMonthDayCompact(d[0].value);
734
822
  return makeDate({ month: parsed.month, day: parsed.day });
@@ -745,6 +833,17 @@ var grammar = {
745
833
  } },
746
834
  { "name": "timeOnly", "symbols": ["time"], "postprocess": (d) => makeDate({ time: d[0], timeOnly: true }) },
747
835
  { "name": "timeOnly", "symbols": ["timeWord"], "postprocess": (d) => makeDate({ time: { special: d[0] }, timeOnly: true }) },
836
+ { "name": "dayOnly", "symbols": ["theConnector", "_", "dayNumber"], "postprocess": (d) => makeDate({ day: d[2], dayOnly: true }) },
837
+ { "name": "dayOnly", "symbols": ["onConnector", "_", "theConnector", "_", "dayNumber"], "postprocess": (d) => makeDate({ day: d[4], dayOnly: true }) },
838
+ { "name": "eodCobDate", "symbols": ["eodKeyword"], "postprocess": (d) => makeDate({ special: "today", time: { hour: 23, minute: 59 } }) },
839
+ { "name": "eodCobDate", "symbols": ["cobKeyword"], "postprocess": (d) => makeDate({ special: "today", time: { hour: 17, minute: 0 } }) },
840
+ { "name": "eodCobDate", "symbols": ["eodKeyword", "_", "weekday"], "postprocess": (d) => makeDate({ weekday: d[2], time: { hour: 23, minute: 59 } }) },
841
+ { "name": "eodCobDate", "symbols": ["cobKeyword", "_", "weekday"], "postprocess": (d) => makeDate({ weekday: d[2], time: { hour: 17, minute: 0 } }) },
842
+ { "name": "eodCobDate", "symbols": ["eodKeyword", "_", "specialDay"], "postprocess": (d) => makeDate({ special: d[2], time: { hour: 23, minute: 59 } }) },
843
+ { "name": "eodCobDate", "symbols": ["cobKeyword", "_", "specialDay"], "postprocess": (d) => makeDate({ special: d[2], time: { hour: 17, minute: 0 } }) },
844
+ { "name": "eodCobDate", "symbols": ["endConnector", "_", "ofConnector", "_", "dayUnit", "_", "specialDay"], "postprocess": (d) => makeDate({ special: d[6], time: { hour: 23, minute: 59 } }) },
845
+ { "name": "eodCobDate", "symbols": ["closeKeyword", "_", "ofConnector", "_", "businessKeyword", "_", "weekday"], "postprocess": (d) => makeDate({ weekday: d[6], time: { hour: 17, minute: 0 } }) },
846
+ { "name": "eodCobDate", "symbols": ["closeKeyword", "_", "ofConnector", "_", "businessKeyword", "_", "specialDay"], "postprocess": (d) => makeDate({ special: d[6], time: { hour: 17, minute: 0 } }) },
748
847
  { "name": "specialDay", "symbols": ["today"], "postprocess": (d) => "today" },
749
848
  { "name": "specialDay", "symbols": ["tomorrow"], "postprocess": (d) => "tomorrow" },
750
849
  { "name": "specialDay", "symbols": ["yesterday"], "postprocess": (d) => "yesterday" },
@@ -753,7 +852,8 @@ var grammar = {
753
852
  { "name": "specialDay", "symbols": ["dayUnit", "_", "afterConnector", "_", "tomorrow"], "postprocess": (d) => "dayAfterTomorrow" },
754
853
  { "name": "specialDay", "symbols": ["theConnector", "_", "dayUnit", "_", "beforeConnector", "_", "yesterday"], "postprocess": (d) => "dayBeforeYesterday" },
755
854
  { "name": "specialDay", "symbols": ["dayUnit", "_", "beforeConnector", "_", "yesterday"], "postprocess": (d) => "dayBeforeYesterday" },
756
- { "name": "dayUnit", "symbols": [lexer.has("unit") ? { type: "unit" } : unit], "postprocess": (d) => d[0].value === "day" ? d[0] : null },
855
+ { "name": "dayUnit", "symbols": [lexer.has("unit") ? { type: "unit" } : unit], "postprocess": (d, _, reject) => d[0].value === "day" ? d[0] : reject },
856
+ { "name": "dayOrDaysUnit", "symbols": [lexer.has("unit") ? { type: "unit" } : unit], "postprocess": (d, _, reject) => d[0].value === "day" || d[0].value === "days" ? d[0] : reject },
757
857
  { "name": "relativeWeekday", "symbols": ["nextRelative", "_", "weekday"], "postprocess": (d) => makeDate({ weekday: d[2], relative: "next" }) },
758
858
  { "name": "relativeWeekday", "symbols": ["lastRelative", "_", "weekday"], "postprocess": (d) => makeDate({ weekday: d[2], relative: "last" }) },
759
859
  { "name": "relativeWeekday", "symbols": ["thisRelative", "_", "weekday"], "postprocess": (d) => makeDate({ weekday: d[2], relative: "this" }) },
@@ -768,6 +868,14 @@ var grammar = {
768
868
  { "name": "monthDay", "symbols": ["theConnector", "_", "dayNumber", "_", "ofConnector", "_", "month"], "postprocess": (d) => makeDate({ month: d[6], day: d[2] }) },
769
869
  { "name": "monthDay", "symbols": ["month", "_", "ordinalWord"], "postprocess": (d) => makeDate({ month: d[0], day: parseOrdinalWord(d[2]) }) },
770
870
  { "name": "monthDay", "symbols": ["month", "_", "theConnector", "_", "dayNumber"], "postprocess": (d) => makeDate({ month: d[0], day: d[4] }) },
871
+ { "name": "ordinalWeekdayOfMonth", "symbols": ["ordinalWord", "_", "weekday", "_", "ofConnector", "_", "month"], "postprocess": (d) => makeDate({ ordinalWeekday: parseOrdinalWord(d[0]), weekday: d[2], month: d[6] }) },
872
+ { "name": "ordinalWeekdayOfMonth", "symbols": ["lastRelative", "_", "weekday", "_", "ofConnector", "_", "month"], "postprocess": (d) => makeDate({ ordinalWeekday: -1, weekday: d[2], month: d[6] }) },
873
+ { "name": "ordinalWeekdayOfMonth", "symbols": ["ordinalWord", "_", "weekday", "_", "ofConnector", "_", "theConnector", "_", "monthUnit"], "postprocess": (d) => makeDate({ ordinalWeekday: parseOrdinalWord(d[0]), weekday: d[2], monthFromRef: true }) },
874
+ { "name": "ordinalWeekdayOfMonth", "symbols": ["lastRelative", "_", "weekday", "_", "ofConnector", "_", "theConnector", "_", "monthUnit"], "postprocess": (d) => makeDate({ ordinalWeekday: -1, weekday: d[2], monthFromRef: true }) },
875
+ { "name": "ordinalWeekdayOfMonth", "symbols": ["ordinalWord", "_", "dayUnit", "_", "ofConnector", "_", "month"], "postprocess": (d) => makeDate({ month: d[6], day: parseOrdinalWord(d[0]) }) },
876
+ { "name": "ordinalWeekdayOfMonth", "symbols": ["lastRelative", "_", "dayUnit", "_", "ofConnector", "_", "month"], "postprocess": (d) => makeDate({ month: d[6], day: -1, lastDayOfMonth: true }) },
877
+ { "name": "ordinalWeekdayOfMonth", "symbols": ["lastRelative", "_", "dayUnit", "_", "ofConnector", "_", "theConnector", "_", "monthUnit"], "postprocess": (d) => makeDate({ lastDayOfMonth: true, monthFromRef: true }) },
878
+ { "name": "ordinalWeekdayOfMonth", "symbols": ["ordinalWord", "_", "dayUnit", "_", "ofConnector", "_", "nextRelative", "_", "monthUnit"], "postprocess": (d) => makeDate({ ordinalWeekday: parseOrdinalWord(d[0]), dayOfMonth: true, nextMonth: true }) },
771
879
  { "name": "monthDayYear", "symbols": ["monthDay", "_", "year"], "postprocess": (d) => ({ ...d[0], year: d[2] }) },
772
880
  { "name": "monthDayYear", "symbols": ["monthDay", lexer.has("comma") ? { type: "comma" } : comma, "_", "year"], "postprocess": (d) => ({ ...d[0], year: d[3] }) },
773
881
  { "name": "dateFormat", "symbols": [lexer.has("integer") ? { type: "integer" } : integer, lexer.has("dash") ? { type: "dash" } : dash, lexer.has("integer") ? { type: "integer" } : integer, lexer.has("dash") ? { type: "dash" } : dash, lexer.has("integer") ? { type: "integer" } : integer], "postprocess": (d) => {
@@ -789,6 +897,9 @@ var grammar = {
789
897
  { "name": "dateWithTime", "symbols": ["time", "_", "date"], "postprocess": (d) => ({ ...d[2], time: d[0] }) },
790
898
  { "name": "dateWithTime", "symbols": ["timeWord", "_", "date"], "postprocess": (d) => ({ ...d[2], time: { special: d[0] } }) },
791
899
  { "name": "dateWithTime", "symbols": ["date", "_", "time"], "postprocess": (d) => ({ ...d[0], time: d[2] }) },
900
+ { "name": "dateWithTime", "symbols": ["timeWord", "_", "onConnector", "_", "date"], "postprocess": (d) => ({ ...d[4], time: { special: d[0] } }) },
901
+ { "name": "dateWithTime", "symbols": ["timeWord", "_", "onConnector", "_", "weekday"], "postprocess": (d) => makeDate({ weekday: d[4], time: { special: d[0] } }) },
902
+ { "name": "dateWithTime", "symbols": ["midnight", "_", "tonightKeyword"], "postprocess": (d) => makeDate({ special: "tonight", time: { special: "midnight" } }) },
792
903
  { "name": "complexDate", "symbols": ["nextRelative", "_", "unit", "_", "weekday", "_", "time"], "postprocess": (d) => makeDate({ relative: "next", period: d[2], weekday: d[4], time: d[6] }) },
793
904
  { "name": "complexDate", "symbols": ["nextRelative", "_", "unit", "_", "weekday"], "postprocess": (d) => makeDate({ relative: "next", period: d[2], weekday: d[4] }) },
794
905
  { "name": "complexDate", "symbols": ["lastRelative", "_", "unit", "_", "weekday", "_", "time"], "postprocess": (d) => makeDate({ relative: "last", period: d[2], weekday: d[4], time: d[6] }) },
@@ -825,6 +936,45 @@ var grammar = {
825
936
  if (d[1].value === "am" && hour === 12) hour = 0;
826
937
  return { hour, minute: 0 };
827
938
  } },
939
+ { "name": "time", "symbols": ["quarterKeyword", "_", "pastConnector", "_", "hourNumber"], "postprocess": (d) => ({ hour: d[4], minute: 15 }) },
940
+ { "name": "time", "symbols": ["quarterKeyword", "_", "pastConnector", "_", "hourNumber", lexer.has("ampm") ? { type: "ampm" } : ampm], "postprocess": (d) => {
941
+ let hour = d[4];
942
+ if (d[5].value === "pm" && hour !== 12) hour += 12;
943
+ if (d[5].value === "am" && hour === 12) hour = 0;
944
+ return { hour, minute: 15 };
945
+ } },
946
+ { "name": "time", "symbols": ["halfWord", "_", "pastConnector", "_", "hourNumber"], "postprocess": (d) => ({ hour: d[4], minute: 30 }) },
947
+ { "name": "time", "symbols": ["halfWord", "_", "pastConnector", "_", "hourNumber", lexer.has("ampm") ? { type: "ampm" } : ampm], "postprocess": (d) => {
948
+ let hour = d[4];
949
+ if (d[5].value === "pm" && hour !== 12) hour += 12;
950
+ if (d[5].value === "am" && hour === 12) hour = 0;
951
+ return { hour, minute: 30 };
952
+ } },
953
+ { "name": "time", "symbols": ["quarterKeyword", "_", "toConnector", "_", "hourNumber"], "postprocess": (d) => ({ hour: d[4] - 1, minute: 45 }) },
954
+ { "name": "time", "symbols": ["quarterKeyword", "_", "toConnector", "_", "hourNumber", lexer.has("ampm") ? { type: "ampm" } : ampm], "postprocess": (d) => {
955
+ let hour = d[4];
956
+ if (d[5].value === "pm" && hour !== 12) hour += 12;
957
+ if (d[5].value === "am" && hour === 12) hour = 0;
958
+ return { hour: hour - 1, minute: 45 };
959
+ } },
960
+ { "name": "time", "symbols": ["number", "_", "pastConnector", "_", "hourNumber"], "postprocess": (d) => ({ hour: d[4], minute: d[0] }) },
961
+ { "name": "time", "symbols": ["number", "_", "pastConnector", "_", "hourNumber", lexer.has("ampm") ? { type: "ampm" } : ampm], "postprocess": (d) => {
962
+ let hour = d[4];
963
+ if (d[5].value === "pm" && hour !== 12) hour += 12;
964
+ if (d[5].value === "am" && hour === 12) hour = 0;
965
+ return { hour, minute: d[0] };
966
+ } },
967
+ { "name": "time", "symbols": ["number", "_", "toConnector", "_", "hourNumber"], "postprocess": (d) => ({ hour: d[4] - 1, minute: 60 - d[0] }) },
968
+ { "name": "time", "symbols": ["number", "_", "toConnector", "_", "hourNumber", lexer.has("ampm") ? { type: "ampm" } : ampm], "postprocess": (d) => {
969
+ let hour = d[4];
970
+ if (d[5].value === "pm" && hour !== 12) hour += 12;
971
+ if (d[5].value === "am" && hour === 12) hour = 0;
972
+ return { hour: hour - 1, minute: 60 - d[0] };
973
+ } },
974
+ { "name": "time", "symbols": ["number", "_", "toConnector", "_", "noon"], "postprocess": (d) => ({ hour: 11, minute: 60 - d[0] }) },
975
+ { "name": "time", "symbols": ["number", "_", "toConnector", "_", "midnight"], "postprocess": (d) => ({ hour: 23, minute: 60 - d[0] }) },
976
+ { "name": "time", "symbols": ["quarterKeyword", "_", "toConnector", "_", "noon"], "postprocess": (d) => ({ hour: 11, minute: 45 }) },
977
+ { "name": "time", "symbols": ["quarterKeyword", "_", "toConnector", "_", "midnight"], "postprocess": (d) => ({ hour: 23, minute: 45 }) },
828
978
  { "name": "timeWord", "symbols": ["noon"], "postprocess": (d) => "noon" },
829
979
  { "name": "timeWord", "symbols": ["midnight"], "postprocess": (d) => "midnight" },
830
980
  { "name": "number", "symbols": [lexer.has("integer") ? { type: "integer" } : integer], "postprocess": (d) => parseInt(d[0].value, 10) },
@@ -861,6 +1011,11 @@ var grammar = {
861
1011
  if (val === "year" || val === "years" || val === "yr" || val === "yrs") return val;
862
1012
  return reject;
863
1013
  } },
1014
+ { "name": "monthUnit", "symbols": [lexer.has("unit") ? { type: "unit" } : unit], "postprocess": (d, _, reject) => {
1015
+ const val = d[0].value.toLowerCase();
1016
+ if (val === "month" || val === "months" || val === "mo" || val === "mos") return val;
1017
+ return reject;
1018
+ } },
864
1019
  { "name": "wordNumber", "symbols": [lexer.has("wordNumber") ? { type: "wordNumber" } : wordNumber], "postprocess": (d) => d[0].value },
865
1020
  { "name": "ordinalWord", "symbols": [lexer.has("ordinalWord") ? { type: "ordinalWord" } : ordinalWord], "postprocess": (d) => d[0].value },
866
1021
  { "name": "ordinalWord", "symbols": [lexer.has("unit") ? { type: "unit" } : unit], "postprocess": (d, _, reject) => {
@@ -897,6 +1052,9 @@ var grammar = {
897
1052
  { "name": "beginningConnector", "symbols": [lexer.has("kw_beginning") ? { type: "kw_beginning" } : kw_beginning], "postprocess": first },
898
1053
  { "name": "startConnector", "symbols": [lexer.has("kw_start") ? { type: "kw_start" } : kw_start], "postprocess": first },
899
1054
  { "name": "middleConnector", "symbols": [lexer.has("kw_middle") ? { type: "kw_middle" } : kw_middle], "postprocess": first },
1055
+ { "name": "agoConnector", "symbols": [lexer.has("kw_ago") ? { type: "kw_ago" } : kw_ago], "postprocess": first },
1056
+ { "name": "henceConnector", "symbols": [lexer.has("otherKeyword") ? { type: "otherKeyword" } : otherKeyword], "postprocess": (d, _, reject) => d[0].value === "hence" ? d[0] : reject },
1057
+ { "name": "laterConnector", "symbols": [lexer.has("otherKeyword") ? { type: "otherKeyword" } : otherKeyword], "postprocess": (d, _, reject) => d[0].value === "later" ? d[0] : reject },
900
1058
  { "name": "earlyModifier", "symbols": [lexer.has("kw_early") ? { type: "kw_early" } : kw_early], "postprocess": first },
901
1059
  { "name": "midModifier", "symbols": [lexer.has("kw_mid") ? { type: "kw_mid" } : kw_mid], "postprocess": first },
902
1060
  { "name": "lateModifier", "symbols": [lexer.has("kw_late") ? { type: "kw_late" } : kw_late], "postprocess": first },
@@ -913,6 +1071,28 @@ var grammar = {
913
1071
  { "name": "now", "symbols": [lexer.has("kw_now") ? { type: "kw_now" } : kw_now], "postprocess": first },
914
1072
  { "name": "noon", "symbols": [lexer.has("kw_noon") ? { type: "kw_noon" } : kw_noon], "postprocess": first },
915
1073
  { "name": "midnight", "symbols": [lexer.has("kw_midnight") ? { type: "kw_midnight" } : kw_midnight], "postprocess": first },
1074
+ { "name": "quarterKeyword", "symbols": [lexer.has("unit") ? { type: "unit" } : unit], "postprocess": (d, _, reject) => d[0].value === "quarter" ? d[0] : reject },
1075
+ { "name": "hourNumber", "symbols": [lexer.has("integer") ? { type: "integer" } : integer], "postprocess": (d, _, reject) => {
1076
+ const val = parseInt(d[0].value, 10);
1077
+ if (val < 1 || val > 12) return reject;
1078
+ return val;
1079
+ } },
1080
+ { "name": "pastConnector", "symbols": [lexer.has("kw_past") ? { type: "kw_past" } : kw_past], "postprocess": first },
1081
+ { "name": "weekendKeyword", "symbols": [lexer.has("kw_weekend") ? { type: "kw_weekend" } : kw_weekend], "postprocess": first },
1082
+ { "name": "tonightKeyword", "symbols": [lexer.has("kw_tonight") ? { type: "kw_tonight" } : kw_tonight], "postprocess": first },
1083
+ { "name": "nightKeyword", "symbols": [lexer.has("kw_night") ? { type: "kw_night" } : kw_night], "postprocess": first },
1084
+ { "name": "fortnightKeyword", "symbols": [lexer.has("kw_fortnight") ? { type: "kw_fortnight" } : kw_fortnight], "postprocess": first },
1085
+ { "name": "laterKeyword", "symbols": [lexer.has("otherKeyword") ? { type: "otherKeyword" } : otherKeyword], "postprocess": (d, _, reject) => d[0].value === "later" ? d[0] : reject },
1086
+ { "name": "earlierKeyword", "symbols": [lexer.has("otherKeyword") ? { type: "otherKeyword" } : otherKeyword], "postprocess": (d, _, reject) => d[0].value === "earlier" ? d[0] : reject },
1087
+ { "name": "weekKeyword", "symbols": [lexer.has("unit") ? { type: "unit" } : unit], "postprocess": (d, _, reject) => {
1088
+ const val = d[0].value.toLowerCase();
1089
+ if (val === "week" || val === "weeks") return d[0];
1090
+ return reject;
1091
+ } },
1092
+ { "name": "businessKeyword", "symbols": [lexer.has("kw_business") ? { type: "kw_business" } : kw_business], "postprocess": first },
1093
+ { "name": "eodKeyword", "symbols": [lexer.has("kw_eod") ? { type: "kw_eod" } : kw_eod], "postprocess": first },
1094
+ { "name": "cobKeyword", "symbols": [lexer.has("kw_cob") ? { type: "kw_cob" } : kw_cob], "postprocess": first },
1095
+ { "name": "closeKeyword", "symbols": [lexer.has("kw_close") ? { type: "kw_close" } : kw_close], "postprocess": first },
916
1096
  { "name": "_$ebnf$1", "symbols": [] },
917
1097
  { "name": "_$ebnf$1", "symbols": ["_$ebnf$1", lexer.has("ws") ? { type: "ws" } : ws], "postprocess": (d) => d[0].concat([d[1]]) },
918
1098
  { "name": "_", "symbols": ["_$ebnf$1"], "postprocess": nuller }
@@ -1170,6 +1350,114 @@ function convertMonthDay(node, opts) {
1170
1350
  }
1171
1351
  return date;
1172
1352
  }
1353
+ function convertOrdinalWeekdayOfMonth(node, opts) {
1354
+ const ref = opts.referenceDate;
1355
+ const ordinal2 = node.ordinalWeekday;
1356
+ const weekdayName = node.weekday.toLowerCase();
1357
+ const weekdayNum = WEEKDAY_MAP[weekdayName] ?? 0;
1358
+ let month2;
1359
+ let year;
1360
+ if (node.monthFromRef) {
1361
+ month2 = ref.getUTCMonth();
1362
+ year = ref.getUTCFullYear();
1363
+ } else if (node.nextMonth) {
1364
+ month2 = ref.getUTCMonth() + 1;
1365
+ year = ref.getUTCFullYear();
1366
+ if (month2 > 11) {
1367
+ month2 = 0;
1368
+ year++;
1369
+ }
1370
+ } else {
1371
+ month2 = typeof node.month === "number" ? node.month - 1 : ref.getUTCMonth();
1372
+ year = ref.getUTCFullYear();
1373
+ if (month2 < ref.getUTCMonth()) {
1374
+ year++;
1375
+ }
1376
+ }
1377
+ if (ordinal2 === -1) {
1378
+ const lastDay2 = new Date(Date.UTC(year, month2 + 1, 0));
1379
+ let day2 = lastDay2.getUTCDate();
1380
+ let date2 = new Date(Date.UTC(year, month2, day2));
1381
+ while (date2.getUTCDay() !== weekdayNum) {
1382
+ day2--;
1383
+ date2 = new Date(Date.UTC(year, month2, day2));
1384
+ }
1385
+ return date2;
1386
+ }
1387
+ let count = 0;
1388
+ for (let day2 = 1; day2 <= 31; day2++) {
1389
+ const date2 = new Date(Date.UTC(year, month2, day2));
1390
+ if (date2.getUTCMonth() !== month2) {
1391
+ break;
1392
+ }
1393
+ if (date2.getUTCDay() === weekdayNum) {
1394
+ count++;
1395
+ if (count === ordinal2) {
1396
+ return date2;
1397
+ }
1398
+ }
1399
+ }
1400
+ const lastDay = new Date(Date.UTC(year, month2 + 1, 0));
1401
+ let day = lastDay.getUTCDate();
1402
+ let date = new Date(Date.UTC(year, month2, day));
1403
+ while (date.getUTCDay() !== weekdayNum) {
1404
+ day--;
1405
+ date = new Date(Date.UTC(year, month2, day));
1406
+ }
1407
+ return date;
1408
+ }
1409
+ function convertLastDayOfMonth(node, opts) {
1410
+ const ref = opts.referenceDate;
1411
+ let month2;
1412
+ let year;
1413
+ if (node.monthFromRef) {
1414
+ month2 = ref.getUTCMonth();
1415
+ year = ref.getUTCFullYear();
1416
+ } else if (node.nextMonth) {
1417
+ month2 = ref.getUTCMonth() + 1;
1418
+ year = ref.getUTCFullYear();
1419
+ if (month2 > 11) {
1420
+ month2 = 0;
1421
+ year++;
1422
+ }
1423
+ } else {
1424
+ month2 = typeof node.month === "number" ? node.month - 1 : ref.getUTCMonth();
1425
+ year = ref.getUTCFullYear();
1426
+ if (month2 < ref.getUTCMonth()) {
1427
+ year++;
1428
+ }
1429
+ }
1430
+ return new Date(Date.UTC(year, month2 + 1, 0));
1431
+ }
1432
+ function convertDayOnly(node, opts) {
1433
+ const ref = opts.referenceDate;
1434
+ const day = node.day;
1435
+ const refDay = ref.getUTCDate();
1436
+ const refMonth = ref.getUTCMonth();
1437
+ const refYear = ref.getUTCFullYear();
1438
+ let month2 = refMonth;
1439
+ let year = refYear;
1440
+ if (day < refDay) {
1441
+ month2 = refMonth + 1;
1442
+ if (month2 > 11) {
1443
+ month2 = 0;
1444
+ year++;
1445
+ }
1446
+ }
1447
+ let date = new Date(Date.UTC(year, month2, day));
1448
+ while (date.getUTCDate() !== day) {
1449
+ month2++;
1450
+ if (month2 > 11) {
1451
+ month2 = 0;
1452
+ year++;
1453
+ }
1454
+ date = new Date(Date.UTC(year, month2, day));
1455
+ }
1456
+ if (node.time) {
1457
+ applyTime(date, node.time);
1458
+ }
1459
+ return date;
1460
+ }
1173
1461
  function convertRelativePeriod(node, opts) {
1174
1462
  const ref = opts.referenceDate;
1175
1463
  const period = node.period.toLowerCase();
@@ -1229,6 +1517,9 @@ function convertDateNode(node, opts) {
1229
1517
  if (node.timeOnly && node.time) {
1230
1518
  return convertTimeOnly(node, opts);
1231
1519
  }
1520
+ if (node.ordinalWeekday !== void 0 && node.weekday !== void 0) {
1521
+ return convertOrdinalWeekdayOfMonth(node, opts);
1522
+ }
1232
1523
  if (node.weekday) {
1233
1524
  return convertWeekday(node, opts);
1234
1525
  }
@@ -1238,6 +1529,23 @@ function convertDateNode(node, opts) {
1238
1529
  if (node.month !== void 0 && node.day !== void 0) {
1239
1530
  return convertMonthDay(node, opts);
1240
1531
  }
1532
+ if (node.dayOnly && node.day !== void 0) {
1533
+ return convertDayOnly(node, opts);
1534
+ }
1535
+ if (node.lastDayOfMonth) {
1536
+ return convertLastDayOfMonth(node, opts);
1537
+ }
1538
+ if (node.dayOfMonth && node.nextMonth) {
1539
+ const ref = opts.referenceDate;
1540
+ let month2 = ref.getUTCMonth() + 1;
1541
+ let year = ref.getUTCFullYear();
1542
+ if (month2 > 11) {
1543
+ month2 = 0;
1544
+ year++;
1545
+ }
1546
+ const day = node.ordinalWeekday || 1;
1547
+ return new Date(Date.UTC(year, month2, day));
1548
+ }
1241
1549
  if (node.relative && node.period) {
1242
1550
  return convertRelativePeriod(node, opts);
1243
1551
  }
@@ -1309,7 +1617,7 @@ function getHalfDates(half2, year, fiscalYearStart) {
1309
1617
  const start = new Date(Date.UTC(startYear, halfStartMonth, 1));
1310
1618
  const endMonth = (halfStartMonth + 6) % 12;
1311
1619
  let endYear = startYear;
1312
- if (endMonth < halfStartMonth || endMonth === halfStartMonth) {
1620
+ if (endMonth <= halfStartMonth) {
1313
1621
  endYear++;
1314
1622
  }
1315
1623
  const end = new Date(Date.UTC(endYear, endMonth, 0));
@@ -1382,8 +1690,115 @@ function getModifiedPeriod(start, end, modifier) {
1382
1690
  }
1383
1691
 
1384
1692
  // src/converters/fuzzy.ts
1693
+ function getWeekendDates(ref, relative, opts) {
1694
+ const currentDay = ref.getUTCDay();
1695
+ const saturdayOffset = (6 - currentDay + 7) % 7;
1696
+ let saturday = new Date(
1697
+ Date.UTC(
1698
+ ref.getUTCFullYear(),
1699
+ ref.getUTCMonth(),
1700
+ ref.getUTCDate() + saturdayOffset
1701
+ )
1702
+ );
1703
+ if (relative === "next") {
1704
+ saturday = new Date(saturday.getTime() + MS_PER_WEEK);
1705
+ } else if (relative === "last") {
1706
+ if (currentDay === 6 || currentDay === 0) {
1707
+ saturday = new Date(saturday.getTime() - MS_PER_WEEK);
1708
+ }
1709
+ saturday = new Date(saturday.getTime() - MS_PER_WEEK);
1710
+ }
1711
+ const sunday = new Date(saturday.getTime() + MS_PER_DAY);
1712
+ const sundayEnd = new Date(
1713
+ Date.UTC(
1714
+ sunday.getUTCFullYear(),
1715
+ sunday.getUTCMonth(),
1716
+ sunday.getUTCDate(),
1717
+ 23,
1718
+ 59
1719
+ )
1720
+ );
1721
+ return { start: saturday, end: sundayEnd };
1722
+ }
1723
+ function getNightDates(ref, relative, weekday2) {
1724
+ let baseDate = new Date(ref);
1725
+ if (relative === "last") {
1726
+ baseDate = new Date(
1727
+ Date.UTC(
1728
+ ref.getUTCFullYear(),
1729
+ ref.getUTCMonth(),
1730
+ ref.getUTCDate() - 1
1731
+ )
1732
+ );
1733
+ } else if (relative === "tomorrow") {
1734
+ baseDate = new Date(
1735
+ Date.UTC(
1736
+ ref.getUTCFullYear(),
1737
+ ref.getUTCMonth(),
1738
+ ref.getUTCDate() + 1
1739
+ )
1740
+ );
1741
+ } else if (weekday2) {
1742
+ const targetDay = WEEKDAY_MAP[weekday2.toLowerCase()];
1743
+ if (targetDay !== void 0) {
1744
+ const currentDay = ref.getUTCDay();
1745
+ let daysToAdd = (targetDay - currentDay + 7) % 7;
1746
+ if (daysToAdd === 0) {
1747
+ daysToAdd = 7;
1748
+ }
1749
+ baseDate = new Date(
1750
+ Date.UTC(
1751
+ ref.getUTCFullYear(),
1752
+ ref.getUTCMonth(),
1753
+ ref.getUTCDate() + daysToAdd
1754
+ )
1755
+ );
1756
+ }
1757
+ }
1758
+ const start = new Date(
1759
+ Date.UTC(
1760
+ baseDate.getUTCFullYear(),
1761
+ baseDate.getUTCMonth(),
1762
+ baseDate.getUTCDate(),
1763
+ 18,
1764
+ 0
1765
+ )
1766
+ );
1767
+ const end = new Date(
1768
+ Date.UTC(
1769
+ baseDate.getUTCFullYear(),
1770
+ baseDate.getUTCMonth(),
1771
+ baseDate.getUTCDate(),
1772
+ 23,
1773
+ 59
1774
+ )
1775
+ );
1776
+ return { start, end };
1777
+ }
1385
1778
  function getBasePeriodDates(node, opts, year) {
1386
1779
  const ref = opts.referenceDate;
1780
+ if (node.period === "weekend") {
1781
+ return getWeekendDates(ref, node.relative || "this", opts);
1782
+ }
1783
+ if (node.period === "tonight") {
1784
+ return getNightDates(ref, void 0, void 0);
1785
+ }
1786
+ if (node.period === "night") {
1787
+ return getNightDates(ref, node.relative, node.weekday);
1788
+ }
1789
+ if (node.period === "fortnight") {
1790
+ const count = node.count || 1;
1791
+ const relative = node.relative;
1792
+ const twoWeeks = 14 * MS_PER_DAY;
1793
+ if (relative === "last") {
1794
+ const end2 = new Date(ref);
1795
+ const start2 = new Date(ref.getTime() - twoWeeks);
1796
+ return { start: start2, end: end2 };
1797
+ }
1798
+ const start = new Date(ref);
1799
+ const end = new Date(ref.getTime() + count * twoWeeks);
1800
+ return { start, end };
1801
+ }
1387
1802
  if (node.period === "quarter" && node.quarter) {
1388
1803
  return getQuarterDates(
1389
1804
  node.quarter,
@@ -1423,6 +1838,42 @@ function getBasePeriodDates(node, opts, year) {
1423
1838
  const end = new Date(start.getTime() + 6 * MS_PER_DAY);
1424
1839
  return { start, end };
1425
1840
  }
1841
+ if (node.period === "weekNumber" && node.weekNumber !== void 0) {
1842
+ const weekNum = node.weekNumber;
1843
+ const targetYear = node.year ?? ref.getUTCFullYear();
1844
+ const weekStart = opts.weekStartsOn === "monday" ? 1 : 0;
1845
+ const jan1 = new Date(Date.UTC(targetYear, 0, 1));
1846
+ const jan1Day = jan1.getUTCDay();
1847
+ const daysToFirstWeekStart = (weekStart - jan1Day + 7) % 7;
1848
+ const firstWeekStart = new Date(jan1.getTime() + daysToFirstWeekStart * MS_PER_DAY);
1849
+ const start = new Date(firstWeekStart.getTime() + (weekNum - 1) * MS_PER_WEEK);
1850
+ const end = new Date(start.getTime() + 6 * MS_PER_DAY);
1851
+ return { start, end };
1852
+ }
1853
+ if (node.period === "weekOf" && node.baseDate) {
1854
+ const baseDate = node.baseDate;
1855
+ let targetDate;
1856
+ if (baseDate.month !== void 0 && baseDate.day !== void 0) {
1857
+ const month2 = typeof baseDate.month === "number" ? baseDate.month - 1 : 0;
1858
+ const day = baseDate.day;
1859
+ const targetYear = baseDate.year ?? ref.getUTCFullYear();
1860
+ targetDate = new Date(Date.UTC(targetYear, month2, day));
1861
+ } else {
1862
+ targetDate = ref;
1863
+ }
1864
+ const weekStart = opts.weekStartsOn === "monday" ? 1 : 0;
1865
+ const currentDay = targetDate.getUTCDay();
1866
+ const daysFromStart = (currentDay - weekStart + 7) % 7;
1867
+ const start = new Date(
1868
+ Date.UTC(
1869
+ targetDate.getUTCFullYear(),
1870
+ targetDate.getUTCMonth(),
1871
+ targetDate.getUTCDate() - daysFromStart
1872
+ )
1873
+ );
1874
+ const end = new Date(start.getTime() + 6 * MS_PER_DAY);
1875
+ return { start, end };
1876
+ }
1426
1877
  if (node.period === "day") {
1427
1878
  const start = new Date(
1428
1879
  Date.UTC(ref.getUTCFullYear(), ref.getUTCMonth(), ref.getUTCDate())
@@ -1546,7 +1997,7 @@ function convertASTToResult(ast, opts, originalInput) {
1546
1997
  end = new Date(Date.UTC(year, month2 + 1, 0));
1547
1998
  }
1548
1999
  if (endNode.yearOnly) {
1549
- const year = endNode.year;
2000
+ const year = endNode.year ?? opts.referenceDate.getUTCFullYear();
1550
2001
  end = new Date(Date.UTC(year, 11, 31));
1551
2002
  }
1552
2003
  } else if (endNode.nodeType === "fuzzy") {
@@ -1577,6 +2028,7 @@ function convertASTToResult(ast, opts, originalInput) {
1577
2028
  case "fuzzy": {
1578
2029
  const { start, end } = convertFuzzyNode(expression, opts);
1579
2030
  const mod = expression.modifier;
2031
+ const period = expression.period;
1580
2032
  if (mod === "start" || mod === "beginning") {
1581
2033
  const periodDates = convertFuzzyNodeWithoutModifier(expression, opts);
1582
2034
  return { type: "date", date: periodDates.start, title };
@@ -1585,6 +2037,10 @@ function convertASTToResult(ast, opts, originalInput) {
1585
2037
  const periodDates = convertFuzzyNodeWithoutModifier(expression, opts);
1586
2038
  return { type: "date", date: periodDates.end, title };
1587
2039
  }
2040
+ if (period === "weekend" || period === "night" || period === "tonight" || period === "fortnight" || period === "weekNumber" || period === "weekOf") {
2041
+ const duration = end.getTime() - start.getTime();
2042
+ return { type: "span", start, end, duration, title };
2043
+ }
1588
2044
  return { type: "fuzzy", start, end, approximate: true, title };
1589
2045
  }
1590
2046
  case "relative": {
@@ -1601,6 +2057,91 @@ function convertASTToResult(ast, opts, originalInput) {
1601
2057
  }
1602
2058
  return { type: "span", start, end, duration, title };
1603
2059
  }
2060
+ case "relativeDate": {
2061
+ const durationNode = expression.duration;
2062
+ const baseNode = expression.baseDate;
2063
+ const timeSpec = expression.time;
2064
+ let baseDate;
2065
+ if (baseNode) {
2066
+ baseDate = convertDateNode(baseNode, opts);
2067
+ } else {
2068
+ baseDate = new Date(opts.referenceDate);
2069
+ }
2070
+ const durationValue = durationNode.value;
2071
+ const durationUnit = durationNode.unit;
2072
+ const direction = expression.direction === "past" ? -1 : 1;
2073
+ let resultDate;
2074
+ if (durationUnit === "month") {
2075
+ resultDate = new Date(
2076
+ Date.UTC(
2077
+ baseDate.getUTCFullYear(),
2078
+ baseDate.getUTCMonth() + direction * durationValue,
2079
+ baseDate.getUTCDate(),
2080
+ baseDate.getUTCHours(),
2081
+ baseDate.getUTCMinutes()
2082
+ )
2083
+ );
2084
+ } else if (durationUnit === "year") {
2085
+ resultDate = new Date(
2086
+ Date.UTC(
2087
+ baseDate.getUTCFullYear() + direction * durationValue,
2088
+ baseDate.getUTCMonth(),
2089
+ baseDate.getUTCDate(),
2090
+ baseDate.getUTCHours(),
2091
+ baseDate.getUTCMinutes()
2092
+ )
2093
+ );
2094
+ } else if (durationUnit === "businessDay" || durationUnit === "businessday") {
2095
+ resultDate = new Date(Date.UTC(baseDate.getUTCFullYear(), baseDate.getUTCMonth(), baseDate.getUTCDate()));
2096
+ let daysToAdd = durationValue;
2097
+ while (daysToAdd > 0) {
2098
+ resultDate.setUTCDate(resultDate.getUTCDate() + direction);
2099
+ const dayOfWeek = resultDate.getUTCDay();
2100
+ if (dayOfWeek !== 0 && dayOfWeek !== 6) {
2101
+ daysToAdd--;
2102
+ }
2103
+ }
2104
+ } else {
2105
+ const durationMs = convertDurationNode(durationNode);
2106
+ resultDate = new Date(baseDate.getTime() + direction * durationMs);
2107
+ }
2108
+ if (timeSpec) {
2109
+ if ("special" in timeSpec) {
2110
+ if (timeSpec.special === "noon") {
2111
+ resultDate = new Date(
2112
+ Date.UTC(
2113
+ resultDate.getUTCFullYear(),
2114
+ resultDate.getUTCMonth(),
2115
+ resultDate.getUTCDate(),
2116
+ 12,
2117
+ 0
2118
+ )
2119
+ );
2120
+ } else if (timeSpec.special === "midnight") {
2121
+ resultDate = new Date(
2122
+ Date.UTC(
2123
+ resultDate.getUTCFullYear(),
2124
+ resultDate.getUTCMonth(),
2125
+ resultDate.getUTCDate(),
2126
+ 0,
2127
+ 0
2128
+ )
2129
+ );
2130
+ }
2131
+ } else {
2132
+ resultDate = new Date(
2133
+ Date.UTC(
2134
+ resultDate.getUTCFullYear(),
2135
+ resultDate.getUTCMonth(),
2136
+ resultDate.getUTCDate(),
2137
+ timeSpec.hour,
2138
+ timeSpec.minute
2139
+ )
2140
+ );
2141
+ }
2142
+ }
2143
+ return { type: "date", date: resultDate, title };
2144
+ }
1604
2145
  default:
1605
2146
  return null;
1606
2147
  }
@@ -1623,13 +2164,13 @@ function stripUnmatchedPunctuation(input) {
1623
2164
  openParens = (result.match(/\(/g) || []).length;
1624
2165
  closeParens = (result.match(/\)/g) || []).length;
1625
2166
  while (openParens > closeParens) {
1626
- result = result.replace("(", "");
2167
+ result = result.replace(/\(/, "");
1627
2168
  openParens--;
1628
2169
  }
1629
2170
  openBrackets = (result.match(/\[/g) || []).length;
1630
2171
  closeBrackets = (result.match(/]/g) || []).length;
1631
2172
  while (openBrackets > closeBrackets) {
1632
- result = result.replace("[", "");
2173
+ result = result.replace(/\[/, "");
1633
2174
  openBrackets--;
1634
2175
  }
1635
2176
  return {