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.cjs CHANGED
@@ -270,8 +270,21 @@ var lexer = import_moo.default.compile({
270
270
  "sat",
271
271
  "sun"
272
272
  ],
273
+ // Ago keyword for relative dates
274
+ kw_ago: ["ago"],
275
+ // Weekend and night keywords
276
+ kw_weekend: ["weekend"],
277
+ kw_tonight: ["tonight"],
278
+ kw_night: ["night"],
279
+ kw_fortnight: ["fortnight", "fortnights"],
273
280
  // Other keywords
274
- otherKeyword: ["fiscal", "fy", "daily", "weekly", "monthly", "yearly", "annually", "ago", "hence", "later"]
281
+ otherKeyword: ["fiscal", "fy", "daily", "weekly", "monthly", "yearly", "annually", "hence", "later", "earlier"],
282
+ // Business keywords
283
+ kw_business: ["business"],
284
+ // EOD/COB keywords
285
+ kw_eod: ["eod"],
286
+ kw_cob: ["cob"],
287
+ kw_close: ["close"]
275
288
  })
276
289
  // Keywords are matched against lowercase input
277
290
  },
@@ -324,6 +337,13 @@ var makeRelative = (direction, duration) => ({
324
337
  direction,
325
338
  duration
326
339
  });
340
+ var makeRelativeDate = (direction, duration, baseDate, time2) => ({
341
+ nodeType: "relativeDate",
342
+ direction,
343
+ duration,
344
+ baseDate,
345
+ time: time2
346
+ });
327
347
  var makeTitled = (title, expression, titleStart, titleEnd) => ({
328
348
  nodeType: "titled",
329
349
  title: title.trim(),
@@ -559,10 +579,14 @@ var grammar = {
559
579
  { "name": "expression", "symbols": ["range"], "postprocess": first },
560
580
  { "name": "expression", "symbols": ["span"], "postprocess": first },
561
581
  { "name": "expression", "symbols": ["relative"], "postprocess": first },
582
+ { "name": "expression", "symbols": ["relativeDate"], "postprocess": first },
583
+ { "name": "expression", "symbols": ["lastDayOfMonthExpr"], "postprocess": first },
584
+ { "name": "expression", "symbols": ["date"], "postprocess": first },
562
585
  { "name": "expression", "symbols": ["fuzzy"], "postprocess": first },
563
586
  { "name": "expression", "symbols": ["forDuration"], "postprocess": first },
564
587
  { "name": "expression", "symbols": ["duration"], "postprocess": first },
565
- { "name": "expression", "symbols": ["date"], "postprocess": first },
588
+ { "name": "lastDayOfMonthExpr", "symbols": ["lastRelative", "_", "dayUnit", "_", "ofConnector", "_", "month"], "postprocess": (d) => makeDate({ month: d[6], lastDayOfMonth: true }) },
589
+ { "name": "lastDayOfMonthExpr", "symbols": ["lastRelative", "_", "dayUnit", "_", "ofConnector", "_", "theConnector", "_", "monthUnit"], "postprocess": (d) => makeDate({ lastDayOfMonth: true, monthFromRef: true }) },
566
590
  { "name": "forDuration", "symbols": ["forConnector", "_", "duration"], "postprocess": (d) => d[2] },
567
591
  { "name": "range", "symbols": ["date", "_", "toConnector", "_", "date"], "postprocess": (d) => makeRange(d[0], d[4]) },
568
592
  { "name": "range", "symbols": ["fromConnector", "_", "date", "_", "toConnector", "_", "date"], "postprocess": (d) => makeRange(d[2], d[6]) },
@@ -644,6 +668,44 @@ var grammar = {
644
668
  { "name": "relative", "symbols": ["inConnector", "_", "theConnector", "_", "pastRelative", "_", "unit"], "postprocess": (d) => makeRelative("past", makeDuration(1, d[6])) },
645
669
  { "name": "relative", "symbols": ["inConnector", "_", "theConnector", "_", "comingRelative", "_", "unit"], "postprocess": (d) => makeRelative("future", makeDuration(1, d[6])) },
646
670
  { "name": "relative", "symbols": ["inConnector", "_", "theConnector", "_", "upcomingRelative", "_", "unit"], "postprocess": (d) => makeRelative("future", makeDuration(1, d[6])) },
671
+ { "name": "relativeDate", "symbols": ["duration", "_", "agoConnector"], "postprocess": (d) => makeRelativeDate("past", d[0]) },
672
+ { "name": "relativeDate", "symbols": ["duration", "_", "agoConnector", "_", "atConnector", "_", "time"], "postprocess": (d) => makeRelativeDate("past", d[0], void 0, d[6]) },
673
+ { "name": "relativeDate", "symbols": ["duration", "_", "agoConnector", "_", "atConnector", "_", "timeWord"], "postprocess": (d) => makeRelativeDate("past", d[0], void 0, { special: d[6] }) },
674
+ { "name": "relativeDate", "symbols": ["inConnector", "_", "duration"], "postprocess": (d) => makeRelativeDate("future", d[2]) },
675
+ { "name": "relativeDate", "symbols": ["inConnector", "_", "duration", "_", "atConnector", "_", "time"], "postprocess": (d) => makeRelativeDate("future", d[2], void 0, d[6]) },
676
+ { "name": "relativeDate", "symbols": ["inConnector", "_", "duration", "_", "atConnector", "_", "timeWord"], "postprocess": (d) => makeRelativeDate("future", d[2], void 0, { special: d[6] }) },
677
+ { "name": "relativeDate", "symbols": ["duration", "_", "fromConnector", "_", "now"], "postprocess": (d) => makeRelativeDate("future", d[0]) },
678
+ { "name": "relativeDate", "symbols": ["duration", "_", "fromConnector", "_", "now", "_", "atConnector", "_", "time"], "postprocess": (d) => makeRelativeDate("future", d[0], void 0, d[8]) },
679
+ { "name": "relativeDate", "symbols": ["duration", "_", "fromConnector", "_", "now", "_", "atConnector", "_", "timeWord"], "postprocess": (d) => makeRelativeDate("future", d[0], void 0, { special: d[8] }) },
680
+ { "name": "relativeDate", "symbols": ["duration", "_", "fromConnector", "_", "today"], "postprocess": (d) => makeRelativeDate("future", d[0], makeDate({ special: "today" })) },
681
+ { "name": "relativeDate", "symbols": ["duration", "_", "fromConnector", "_", "today", "_", "atConnector", "_", "time"], "postprocess": (d) => makeRelativeDate("future", d[0], makeDate({ special: "today" }), d[8]) },
682
+ { "name": "relativeDate", "symbols": ["duration", "_", "fromConnector", "_", "tomorrow"], "postprocess": (d) => makeRelativeDate("future", d[0], makeDate({ special: "tomorrow" })) },
683
+ { "name": "relativeDate", "symbols": ["duration", "_", "fromConnector", "_", "tomorrow", "_", "atConnector", "_", "time"], "postprocess": (d) => makeRelativeDate("future", d[0], makeDate({ special: "tomorrow" }), d[8]) },
684
+ { "name": "relativeDate", "symbols": ["duration", "_", "fromConnector", "_", "yesterday"], "postprocess": (d) => makeRelativeDate("future", d[0], makeDate({ special: "yesterday" })) },
685
+ { "name": "relativeDate", "symbols": ["duration", "_", "henceConnector"], "postprocess": (d) => makeRelativeDate("future", d[0]) },
686
+ { "name": "relativeDate", "symbols": ["duration", "_", "laterConnector"], "postprocess": (d) => makeRelativeDate("future", d[0]) },
687
+ { "name": "relativeDate", "symbols": ["duration", "_", "beforeConnector", "_", "date"], "postprocess": (d) => makeRelativeDate("past", d[0], d[4]) },
688
+ { "name": "relativeDate", "symbols": ["duration", "_", "afterConnector", "_", "date"], "postprocess": (d) => makeRelativeDate("future", d[0], d[4]) },
689
+ { "name": "relativeDate", "symbols": ["theConnector", "_", "dayUnit", "_", "beforeConnector", "_", "date"], "postprocess": (d) => makeRelativeDate("past", makeDuration(1, "day"), d[6]) },
690
+ { "name": "relativeDate", "symbols": ["theConnector", "_", "dayUnit", "_", "afterConnector", "_", "date"], "postprocess": (d) => makeRelativeDate("future", makeDuration(1, "day"), d[6]) },
691
+ { "name": "relativeDate", "symbols": ["dayUnit", "_", "beforeConnector", "_", "date"], "postprocess": (d) => makeRelativeDate("past", makeDuration(1, "day"), d[4]) },
692
+ { "name": "relativeDate", "symbols": ["dayUnit", "_", "afterConnector", "_", "date"], "postprocess": (d) => makeRelativeDate("future", makeDuration(1, "day"), d[4]) },
693
+ { "name": "relativeDate", "symbols": ["wordNumber", "_", "unit", "_", "beforeConnector", "_", "date"], "postprocess": (d) => makeRelativeDate("past", makeDuration(parseWordNumber(d[0]), d[2]), d[6]) },
694
+ { "name": "relativeDate", "symbols": ["wordNumber", "_", "unit", "_", "afterConnector", "_", "date"], "postprocess": (d) => makeRelativeDate("future", makeDuration(parseWordNumber(d[0]), d[2]), d[6]) },
695
+ { "name": "relativeDate", "symbols": ["nextRelative", "_", "businessKeyword", "_", "dayOrDaysUnit"], "postprocess": (d) => makeRelativeDate("future", makeDuration(1, "businessDay")) },
696
+ { "name": "relativeDate", "symbols": ["inConnector", "_", "number", "_", "businessKeyword", "_", "dayOrDaysUnit"], "postprocess": (d) => makeRelativeDate("future", makeDuration(d[2], "businessDay")) },
697
+ { "name": "relativeDate", "symbols": ["inConnector", "_", "wordNumber", "_", "businessKeyword", "_", "dayOrDaysUnit"], "postprocess": (d) => makeRelativeDate("future", makeDuration(parseWordNumber(d[2]), "businessDay")) },
698
+ { "name": "relativeDate", "symbols": ["number", "_", "businessKeyword", "_", "dayOrDaysUnit", "_", "agoConnector"], "postprocess": (d) => makeRelativeDate("past", makeDuration(d[0], "businessDay")) },
699
+ { "name": "relativeDate", "symbols": ["wordNumber", "_", "businessKeyword", "_", "dayOrDaysUnit", "_", "agoConnector"], "postprocess": (d) => makeRelativeDate("past", makeDuration(parseWordNumber(d[0]), "businessDay")) },
700
+ { "name": "relativeDate", "symbols": ["wordNumber", "_", "fortnightKeyword", "_", "fromConnector", "_", "now"], "postprocess": (d) => makeRelativeDate("future", makeDuration(parseWordNumber(d[0]) * 2, "week")) },
701
+ { "name": "relativeDate", "symbols": ["number", "_", "fortnightKeyword", "_", "fromConnector", "_", "now"], "postprocess": (d) => makeRelativeDate("future", makeDuration(d[0] * 2, "week")) },
702
+ { "name": "relativeDate", "symbols": ["fortnightKeyword", "_", "fromConnector", "_", "now"], "postprocess": (d) => makeRelativeDate("future", makeDuration(2, "week")) },
703
+ { "name": "relativeDate", "symbols": ["inConnector", "_", "wordNumber", "_", "fortnightKeyword"], "postprocess": (d) => makeRelativeDate("future", makeDuration(parseWordNumber(d[2]) * 2, "week")) },
704
+ { "name": "relativeDate", "symbols": ["inConnector", "_", "number", "_", "fortnightKeyword"], "postprocess": (d) => makeRelativeDate("future", makeDuration(d[2] * 2, "week")) },
705
+ { "name": "relativeDate", "symbols": ["inConnector", "_", "fortnightKeyword"], "postprocess": (d) => makeRelativeDate("future", makeDuration(2, "week")) },
706
+ { "name": "relativeDate", "symbols": ["wordNumber", "_", "fortnightKeyword", "_", "agoConnector"], "postprocess": (d) => makeRelativeDate("past", makeDuration(parseWordNumber(d[0]) * 2, "week")) },
707
+ { "name": "relativeDate", "symbols": ["number", "_", "fortnightKeyword", "_", "agoConnector"], "postprocess": (d) => makeRelativeDate("past", makeDuration(d[0] * 2, "week")) },
708
+ { "name": "relativeDate", "symbols": ["fortnightKeyword", "_", "agoConnector"], "postprocess": (d) => makeRelativeDate("past", makeDuration(2, "week")) },
647
709
  { "name": "fuzzy", "symbols": ["quarter"], "postprocess": (d) => makeFuzzy({ period: "quarter", quarter: parseQuarter(d[0].value) }) },
648
710
  { "name": "fuzzy", "symbols": ["quarter", "_", "year"], "postprocess": (d) => makeFuzzy({ period: "quarter", quarter: parseQuarter(d[0].value), year: d[2] }) },
649
711
  { "name": "fuzzy", "symbols": ["half"], "postprocess": (d) => makeFuzzy({ period: "half", half: parseHalf(d[0].value) }) },
@@ -726,8 +788,31 @@ var grammar = {
726
788
  { "name": "fuzzy", "symbols": ["lateModifier", "_", "thisRelative", "_", "unit"], "postprocess": (d) => makeFuzzy({ period: d[4], modifier: "late", relative: "this" }) },
727
789
  { "name": "fuzzy", "symbols": ["beginningConnector", "_", "ofConnector", "_", "theConnector", "_", "unit"], "postprocess": (d) => makeFuzzy({ period: d[6], modifier: "beginning" }) },
728
790
  { "name": "fuzzy", "symbols": ["middleConnector", "_", "ofConnector", "_", "theConnector", "_", "unit"], "postprocess": (d) => makeFuzzy({ period: d[6], modifier: "middle" }) },
729
- { "name": "fuzzy", "symbols": ["lastRelative", "_", "unit", "_", "ofConnector", "_", "month"], "postprocess": (d) => makeFuzzy({ period: "month", month: d[6], modifier: "late" }) },
730
- { "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" }) },
791
+ { "name": "fuzzy", "symbols": ["lastRelative", "_", "unit", "_", "ofConnector", "_", "month"], "postprocess": (d, _, reject) => {
792
+ if (d[2].toLowerCase() === "day" || d[2].toLowerCase() === "days") return reject;
793
+ return makeFuzzy({ period: "month", month: d[6], modifier: "late" });
794
+ } },
795
+ { "name": "fuzzy", "symbols": ["ordinalWord", "_", "unit", "_", "ofConnector", "_", "month"], "postprocess": (d, _, reject) => {
796
+ if (d[2].toLowerCase() === "day" || d[2].toLowerCase() === "days") return reject;
797
+ return makeFuzzy({ period: "month", month: d[6], modifier: parseOrdinalWord(d[0]) <= 2 ? "early" : parseOrdinalWord(d[0]) >= 4 ? "late" : "mid" });
798
+ } },
799
+ { "name": "fuzzy", "symbols": ["weekendKeyword"], "postprocess": (d) => makeFuzzy({ period: "weekend", relative: "this" }) },
800
+ { "name": "fuzzy", "symbols": ["theConnector", "_", "weekendKeyword"], "postprocess": (d) => makeFuzzy({ period: "weekend", relative: "this" }) },
801
+ { "name": "fuzzy", "symbols": ["thisRelative", "_", "weekendKeyword"], "postprocess": (d) => makeFuzzy({ period: "weekend", relative: "this" }) },
802
+ { "name": "fuzzy", "symbols": ["nextRelative", "_", "weekendKeyword"], "postprocess": (d) => makeFuzzy({ period: "weekend", relative: "next" }) },
803
+ { "name": "fuzzy", "symbols": ["lastRelative", "_", "weekendKeyword"], "postprocess": (d) => makeFuzzy({ period: "weekend", relative: "last" }) },
804
+ { "name": "fuzzy", "symbols": ["tonightKeyword"], "postprocess": (d) => makeFuzzy({ period: "tonight" }) },
805
+ { "name": "fuzzy", "symbols": ["nightKeyword"], "postprocess": (d) => makeFuzzy({ period: "night" }) },
806
+ { "name": "fuzzy", "symbols": ["lastRelative", "_", "nightKeyword"], "postprocess": (d) => makeFuzzy({ period: "night", relative: "last" }) },
807
+ { "name": "fuzzy", "symbols": ["tomorrow", "_", "nightKeyword"], "postprocess": (d) => makeFuzzy({ period: "night", relative: "tomorrow" }) },
808
+ { "name": "fuzzy", "symbols": ["weekday", "_", "nightKeyword"], "postprocess": (d) => makeFuzzy({ period: "night", weekday: d[0] }) },
809
+ { "name": "fuzzy", "symbols": ["laterKeyword", "_", "today"], "postprocess": (d) => makeFuzzy({ period: "day", modifier: "later" }) },
810
+ { "name": "fuzzy", "symbols": ["earlierKeyword", "_", "today"], "postprocess": (d) => makeFuzzy({ period: "day", modifier: "earlier" }) },
811
+ { "name": "fuzzy", "symbols": ["laterKeyword", "_", "thisRelative", "_", "unit"], "postprocess": (d) => makeFuzzy({ period: d[4], modifier: "later", relative: "this" }) },
812
+ { "name": "fuzzy", "symbols": ["earlierKeyword", "_", "thisRelative", "_", "unit"], "postprocess": (d) => makeFuzzy({ period: d[4], modifier: "earlier", relative: "this" }) },
813
+ { "name": "fuzzy", "symbols": ["weekKeyword", "_", "number"], "postprocess": (d) => makeFuzzy({ period: "weekNumber", weekNumber: d[2] }) },
814
+ { "name": "fuzzy", "symbols": ["weekKeyword", "_", "number", "_", "year"], "postprocess": (d) => makeFuzzy({ period: "weekNumber", weekNumber: d[2], year: d[4] }) },
815
+ { "name": "fuzzy", "symbols": ["theConnector", "_", "weekKeyword", "_", "ofConnector", "_", "date"], "postprocess": (d) => makeFuzzy({ period: "weekOf", baseDate: d[6] }) },
731
816
  { "name": "duration", "symbols": ["number", "_", "unit"], "postprocess": (d) => makeDuration(d[0], d[2]) },
732
817
  { "name": "duration", "symbols": ["wordNumber", "_", "unit"], "postprocess": (d) => makeDuration(parseWordNumber(d[0]), d[2]) },
733
818
  { "name": "duration", "symbols": ["abbreviatedDuration"], "postprocess": (d) => d[0] },
@@ -755,6 +840,7 @@ var grammar = {
755
840
  return makeDuration(value, unitMap[match[2]]);
756
841
  } },
757
842
  { "name": "date", "symbols": ["specialDay"], "postprocess": (d) => makeDate({ special: d[0] }) },
843
+ { "name": "date", "symbols": ["ordinalWeekdayOfMonth"], "postprocess": first },
758
844
  { "name": "date", "symbols": ["relativeWeekday"], "postprocess": first },
759
845
  { "name": "date", "symbols": ["weekday"], "postprocess": (d) => makeDate({ weekday: d[0] }) },
760
846
  { "name": "date", "symbols": ["monthDayYear"], "postprocess": first },
@@ -769,6 +855,8 @@ var grammar = {
769
855
  { "name": "date", "symbols": ["monthOnly"], "postprocess": first },
770
856
  { "name": "date", "symbols": ["yearOnly"], "postprocess": first },
771
857
  { "name": "date", "symbols": ["timeOnly"], "postprocess": first },
858
+ { "name": "date", "symbols": ["dayOnly"], "postprocess": first },
859
+ { "name": "date", "symbols": ["eodCobDate"], "postprocess": first },
772
860
  { "name": "monthDayCompact", "symbols": [lexer.has("monthDayCompact") ? { type: "monthDayCompact" } : monthDayCompact], "postprocess": (d) => {
773
861
  const parsed = parseMonthDayCompact(d[0].value);
774
862
  return makeDate({ month: parsed.month, day: parsed.day });
@@ -785,6 +873,17 @@ var grammar = {
785
873
  } },
786
874
  { "name": "timeOnly", "symbols": ["time"], "postprocess": (d) => makeDate({ time: d[0], timeOnly: true }) },
787
875
  { "name": "timeOnly", "symbols": ["timeWord"], "postprocess": (d) => makeDate({ time: { special: d[0] }, timeOnly: true }) },
876
+ { "name": "dayOnly", "symbols": ["theConnector", "_", "dayNumber"], "postprocess": (d) => makeDate({ day: d[2], dayOnly: true }) },
877
+ { "name": "dayOnly", "symbols": ["onConnector", "_", "theConnector", "_", "dayNumber"], "postprocess": (d) => makeDate({ day: d[4], dayOnly: true }) },
878
+ { "name": "eodCobDate", "symbols": ["eodKeyword"], "postprocess": (d) => makeDate({ special: "today", time: { hour: 23, minute: 59 } }) },
879
+ { "name": "eodCobDate", "symbols": ["cobKeyword"], "postprocess": (d) => makeDate({ special: "today", time: { hour: 17, minute: 0 } }) },
880
+ { "name": "eodCobDate", "symbols": ["eodKeyword", "_", "weekday"], "postprocess": (d) => makeDate({ weekday: d[2], time: { hour: 23, minute: 59 } }) },
881
+ { "name": "eodCobDate", "symbols": ["cobKeyword", "_", "weekday"], "postprocess": (d) => makeDate({ weekday: d[2], time: { hour: 17, minute: 0 } }) },
882
+ { "name": "eodCobDate", "symbols": ["eodKeyword", "_", "specialDay"], "postprocess": (d) => makeDate({ special: d[2], time: { hour: 23, minute: 59 } }) },
883
+ { "name": "eodCobDate", "symbols": ["cobKeyword", "_", "specialDay"], "postprocess": (d) => makeDate({ special: d[2], time: { hour: 17, minute: 0 } }) },
884
+ { "name": "eodCobDate", "symbols": ["endConnector", "_", "ofConnector", "_", "dayUnit", "_", "specialDay"], "postprocess": (d) => makeDate({ special: d[6], time: { hour: 23, minute: 59 } }) },
885
+ { "name": "eodCobDate", "symbols": ["closeKeyword", "_", "ofConnector", "_", "businessKeyword", "_", "weekday"], "postprocess": (d) => makeDate({ weekday: d[6], time: { hour: 17, minute: 0 } }) },
886
+ { "name": "eodCobDate", "symbols": ["closeKeyword", "_", "ofConnector", "_", "businessKeyword", "_", "specialDay"], "postprocess": (d) => makeDate({ special: d[6], time: { hour: 17, minute: 0 } }) },
788
887
  { "name": "specialDay", "symbols": ["today"], "postprocess": (d) => "today" },
789
888
  { "name": "specialDay", "symbols": ["tomorrow"], "postprocess": (d) => "tomorrow" },
790
889
  { "name": "specialDay", "symbols": ["yesterday"], "postprocess": (d) => "yesterday" },
@@ -793,7 +892,8 @@ var grammar = {
793
892
  { "name": "specialDay", "symbols": ["dayUnit", "_", "afterConnector", "_", "tomorrow"], "postprocess": (d) => "dayAfterTomorrow" },
794
893
  { "name": "specialDay", "symbols": ["theConnector", "_", "dayUnit", "_", "beforeConnector", "_", "yesterday"], "postprocess": (d) => "dayBeforeYesterday" },
795
894
  { "name": "specialDay", "symbols": ["dayUnit", "_", "beforeConnector", "_", "yesterday"], "postprocess": (d) => "dayBeforeYesterday" },
796
- { "name": "dayUnit", "symbols": [lexer.has("unit") ? { type: "unit" } : unit], "postprocess": (d) => d[0].value === "day" ? d[0] : null },
895
+ { "name": "dayUnit", "symbols": [lexer.has("unit") ? { type: "unit" } : unit], "postprocess": (d, _, reject) => d[0].value === "day" ? d[0] : reject },
896
+ { "name": "dayOrDaysUnit", "symbols": [lexer.has("unit") ? { type: "unit" } : unit], "postprocess": (d, _, reject) => d[0].value === "day" || d[0].value === "days" ? d[0] : reject },
797
897
  { "name": "relativeWeekday", "symbols": ["nextRelative", "_", "weekday"], "postprocess": (d) => makeDate({ weekday: d[2], relative: "next" }) },
798
898
  { "name": "relativeWeekday", "symbols": ["lastRelative", "_", "weekday"], "postprocess": (d) => makeDate({ weekday: d[2], relative: "last" }) },
799
899
  { "name": "relativeWeekday", "symbols": ["thisRelative", "_", "weekday"], "postprocess": (d) => makeDate({ weekday: d[2], relative: "this" }) },
@@ -808,6 +908,14 @@ var grammar = {
808
908
  { "name": "monthDay", "symbols": ["theConnector", "_", "dayNumber", "_", "ofConnector", "_", "month"], "postprocess": (d) => makeDate({ month: d[6], day: d[2] }) },
809
909
  { "name": "monthDay", "symbols": ["month", "_", "ordinalWord"], "postprocess": (d) => makeDate({ month: d[0], day: parseOrdinalWord(d[2]) }) },
810
910
  { "name": "monthDay", "symbols": ["month", "_", "theConnector", "_", "dayNumber"], "postprocess": (d) => makeDate({ month: d[0], day: d[4] }) },
911
+ { "name": "ordinalWeekdayOfMonth", "symbols": ["ordinalWord", "_", "weekday", "_", "ofConnector", "_", "month"], "postprocess": (d) => makeDate({ ordinalWeekday: parseOrdinalWord(d[0]), weekday: d[2], month: d[6] }) },
912
+ { "name": "ordinalWeekdayOfMonth", "symbols": ["lastRelative", "_", "weekday", "_", "ofConnector", "_", "month"], "postprocess": (d) => makeDate({ ordinalWeekday: -1, weekday: d[2], month: d[6] }) },
913
+ { "name": "ordinalWeekdayOfMonth", "symbols": ["ordinalWord", "_", "weekday", "_", "ofConnector", "_", "theConnector", "_", "monthUnit"], "postprocess": (d) => makeDate({ ordinalWeekday: parseOrdinalWord(d[0]), weekday: d[2], monthFromRef: true }) },
914
+ { "name": "ordinalWeekdayOfMonth", "symbols": ["lastRelative", "_", "weekday", "_", "ofConnector", "_", "theConnector", "_", "monthUnit"], "postprocess": (d) => makeDate({ ordinalWeekday: -1, weekday: d[2], monthFromRef: true }) },
915
+ { "name": "ordinalWeekdayOfMonth", "symbols": ["ordinalWord", "_", "dayUnit", "_", "ofConnector", "_", "month"], "postprocess": (d) => makeDate({ month: d[6], day: parseOrdinalWord(d[0]) }) },
916
+ { "name": "ordinalWeekdayOfMonth", "symbols": ["lastRelative", "_", "dayUnit", "_", "ofConnector", "_", "month"], "postprocess": (d) => makeDate({ month: d[6], day: -1, lastDayOfMonth: true }) },
917
+ { "name": "ordinalWeekdayOfMonth", "symbols": ["lastRelative", "_", "dayUnit", "_", "ofConnector", "_", "theConnector", "_", "monthUnit"], "postprocess": (d) => makeDate({ lastDayOfMonth: true, monthFromRef: true }) },
918
+ { "name": "ordinalWeekdayOfMonth", "symbols": ["ordinalWord", "_", "dayUnit", "_", "ofConnector", "_", "nextRelative", "_", "monthUnit"], "postprocess": (d) => makeDate({ ordinalWeekday: parseOrdinalWord(d[0]), dayOfMonth: true, nextMonth: true }) },
811
919
  { "name": "monthDayYear", "symbols": ["monthDay", "_", "year"], "postprocess": (d) => ({ ...d[0], year: d[2] }) },
812
920
  { "name": "monthDayYear", "symbols": ["monthDay", lexer.has("comma") ? { type: "comma" } : comma, "_", "year"], "postprocess": (d) => ({ ...d[0], year: d[3] }) },
813
921
  { "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) => {
@@ -829,6 +937,9 @@ var grammar = {
829
937
  { "name": "dateWithTime", "symbols": ["time", "_", "date"], "postprocess": (d) => ({ ...d[2], time: d[0] }) },
830
938
  { "name": "dateWithTime", "symbols": ["timeWord", "_", "date"], "postprocess": (d) => ({ ...d[2], time: { special: d[0] } }) },
831
939
  { "name": "dateWithTime", "symbols": ["date", "_", "time"], "postprocess": (d) => ({ ...d[0], time: d[2] }) },
940
+ { "name": "dateWithTime", "symbols": ["timeWord", "_", "onConnector", "_", "date"], "postprocess": (d) => ({ ...d[4], time: { special: d[0] } }) },
941
+ { "name": "dateWithTime", "symbols": ["timeWord", "_", "onConnector", "_", "weekday"], "postprocess": (d) => makeDate({ weekday: d[4], time: { special: d[0] } }) },
942
+ { "name": "dateWithTime", "symbols": ["midnight", "_", "tonightKeyword"], "postprocess": (d) => makeDate({ special: "tonight", time: { special: "midnight" } }) },
832
943
  { "name": "complexDate", "symbols": ["nextRelative", "_", "unit", "_", "weekday", "_", "time"], "postprocess": (d) => makeDate({ relative: "next", period: d[2], weekday: d[4], time: d[6] }) },
833
944
  { "name": "complexDate", "symbols": ["nextRelative", "_", "unit", "_", "weekday"], "postprocess": (d) => makeDate({ relative: "next", period: d[2], weekday: d[4] }) },
834
945
  { "name": "complexDate", "symbols": ["lastRelative", "_", "unit", "_", "weekday", "_", "time"], "postprocess": (d) => makeDate({ relative: "last", period: d[2], weekday: d[4], time: d[6] }) },
@@ -865,6 +976,45 @@ var grammar = {
865
976
  if (d[1].value === "am" && hour === 12) hour = 0;
866
977
  return { hour, minute: 0 };
867
978
  } },
979
+ { "name": "time", "symbols": ["quarterKeyword", "_", "pastConnector", "_", "hourNumber"], "postprocess": (d) => ({ hour: d[4], minute: 15 }) },
980
+ { "name": "time", "symbols": ["quarterKeyword", "_", "pastConnector", "_", "hourNumber", lexer.has("ampm") ? { type: "ampm" } : ampm], "postprocess": (d) => {
981
+ let hour = d[4];
982
+ if (d[5].value === "pm" && hour !== 12) hour += 12;
983
+ if (d[5].value === "am" && hour === 12) hour = 0;
984
+ return { hour, minute: 15 };
985
+ } },
986
+ { "name": "time", "symbols": ["halfWord", "_", "pastConnector", "_", "hourNumber"], "postprocess": (d) => ({ hour: d[4], minute: 30 }) },
987
+ { "name": "time", "symbols": ["halfWord", "_", "pastConnector", "_", "hourNumber", lexer.has("ampm") ? { type: "ampm" } : ampm], "postprocess": (d) => {
988
+ let hour = d[4];
989
+ if (d[5].value === "pm" && hour !== 12) hour += 12;
990
+ if (d[5].value === "am" && hour === 12) hour = 0;
991
+ return { hour, minute: 30 };
992
+ } },
993
+ { "name": "time", "symbols": ["quarterKeyword", "_", "toConnector", "_", "hourNumber"], "postprocess": (d) => ({ hour: d[4] - 1, minute: 45 }) },
994
+ { "name": "time", "symbols": ["quarterKeyword", "_", "toConnector", "_", "hourNumber", lexer.has("ampm") ? { type: "ampm" } : ampm], "postprocess": (d) => {
995
+ let hour = d[4];
996
+ if (d[5].value === "pm" && hour !== 12) hour += 12;
997
+ if (d[5].value === "am" && hour === 12) hour = 0;
998
+ return { hour: hour - 1, minute: 45 };
999
+ } },
1000
+ { "name": "time", "symbols": ["number", "_", "pastConnector", "_", "hourNumber"], "postprocess": (d) => ({ hour: d[4], minute: d[0] }) },
1001
+ { "name": "time", "symbols": ["number", "_", "pastConnector", "_", "hourNumber", lexer.has("ampm") ? { type: "ampm" } : ampm], "postprocess": (d) => {
1002
+ let hour = d[4];
1003
+ if (d[5].value === "pm" && hour !== 12) hour += 12;
1004
+ if (d[5].value === "am" && hour === 12) hour = 0;
1005
+ return { hour, minute: d[0] };
1006
+ } },
1007
+ { "name": "time", "symbols": ["number", "_", "toConnector", "_", "hourNumber"], "postprocess": (d) => ({ hour: d[4] - 1, minute: 60 - d[0] }) },
1008
+ { "name": "time", "symbols": ["number", "_", "toConnector", "_", "hourNumber", lexer.has("ampm") ? { type: "ampm" } : ampm], "postprocess": (d) => {
1009
+ let hour = d[4];
1010
+ if (d[5].value === "pm" && hour !== 12) hour += 12;
1011
+ if (d[5].value === "am" && hour === 12) hour = 0;
1012
+ return { hour: hour - 1, minute: 60 - d[0] };
1013
+ } },
1014
+ { "name": "time", "symbols": ["number", "_", "toConnector", "_", "noon"], "postprocess": (d) => ({ hour: 11, minute: 60 - d[0] }) },
1015
+ { "name": "time", "symbols": ["number", "_", "toConnector", "_", "midnight"], "postprocess": (d) => ({ hour: 23, minute: 60 - d[0] }) },
1016
+ { "name": "time", "symbols": ["quarterKeyword", "_", "toConnector", "_", "noon"], "postprocess": (d) => ({ hour: 11, minute: 45 }) },
1017
+ { "name": "time", "symbols": ["quarterKeyword", "_", "toConnector", "_", "midnight"], "postprocess": (d) => ({ hour: 23, minute: 45 }) },
868
1018
  { "name": "timeWord", "symbols": ["noon"], "postprocess": (d) => "noon" },
869
1019
  { "name": "timeWord", "symbols": ["midnight"], "postprocess": (d) => "midnight" },
870
1020
  { "name": "number", "symbols": [lexer.has("integer") ? { type: "integer" } : integer], "postprocess": (d) => parseInt(d[0].value, 10) },
@@ -901,6 +1051,11 @@ var grammar = {
901
1051
  if (val === "year" || val === "years" || val === "yr" || val === "yrs") return val;
902
1052
  return reject;
903
1053
  } },
1054
+ { "name": "monthUnit", "symbols": [lexer.has("unit") ? { type: "unit" } : unit], "postprocess": (d, _, reject) => {
1055
+ const val = d[0].value.toLowerCase();
1056
+ if (val === "month" || val === "months" || val === "mo" || val === "mos") return val;
1057
+ return reject;
1058
+ } },
904
1059
  { "name": "wordNumber", "symbols": [lexer.has("wordNumber") ? { type: "wordNumber" } : wordNumber], "postprocess": (d) => d[0].value },
905
1060
  { "name": "ordinalWord", "symbols": [lexer.has("ordinalWord") ? { type: "ordinalWord" } : ordinalWord], "postprocess": (d) => d[0].value },
906
1061
  { "name": "ordinalWord", "symbols": [lexer.has("unit") ? { type: "unit" } : unit], "postprocess": (d, _, reject) => {
@@ -937,6 +1092,9 @@ var grammar = {
937
1092
  { "name": "beginningConnector", "symbols": [lexer.has("kw_beginning") ? { type: "kw_beginning" } : kw_beginning], "postprocess": first },
938
1093
  { "name": "startConnector", "symbols": [lexer.has("kw_start") ? { type: "kw_start" } : kw_start], "postprocess": first },
939
1094
  { "name": "middleConnector", "symbols": [lexer.has("kw_middle") ? { type: "kw_middle" } : kw_middle], "postprocess": first },
1095
+ { "name": "agoConnector", "symbols": [lexer.has("kw_ago") ? { type: "kw_ago" } : kw_ago], "postprocess": first },
1096
+ { "name": "henceConnector", "symbols": [lexer.has("otherKeyword") ? { type: "otherKeyword" } : otherKeyword], "postprocess": (d, _, reject) => d[0].value === "hence" ? d[0] : reject },
1097
+ { "name": "laterConnector", "symbols": [lexer.has("otherKeyword") ? { type: "otherKeyword" } : otherKeyword], "postprocess": (d, _, reject) => d[0].value === "later" ? d[0] : reject },
940
1098
  { "name": "earlyModifier", "symbols": [lexer.has("kw_early") ? { type: "kw_early" } : kw_early], "postprocess": first },
941
1099
  { "name": "midModifier", "symbols": [lexer.has("kw_mid") ? { type: "kw_mid" } : kw_mid], "postprocess": first },
942
1100
  { "name": "lateModifier", "symbols": [lexer.has("kw_late") ? { type: "kw_late" } : kw_late], "postprocess": first },
@@ -953,6 +1111,28 @@ var grammar = {
953
1111
  { "name": "now", "symbols": [lexer.has("kw_now") ? { type: "kw_now" } : kw_now], "postprocess": first },
954
1112
  { "name": "noon", "symbols": [lexer.has("kw_noon") ? { type: "kw_noon" } : kw_noon], "postprocess": first },
955
1113
  { "name": "midnight", "symbols": [lexer.has("kw_midnight") ? { type: "kw_midnight" } : kw_midnight], "postprocess": first },
1114
+ { "name": "quarterKeyword", "symbols": [lexer.has("unit") ? { type: "unit" } : unit], "postprocess": (d, _, reject) => d[0].value === "quarter" ? d[0] : reject },
1115
+ { "name": "hourNumber", "symbols": [lexer.has("integer") ? { type: "integer" } : integer], "postprocess": (d, _, reject) => {
1116
+ const val = parseInt(d[0].value, 10);
1117
+ if (val < 1 || val > 12) return reject;
1118
+ return val;
1119
+ } },
1120
+ { "name": "pastConnector", "symbols": [lexer.has("kw_past") ? { type: "kw_past" } : kw_past], "postprocess": first },
1121
+ { "name": "weekendKeyword", "symbols": [lexer.has("kw_weekend") ? { type: "kw_weekend" } : kw_weekend], "postprocess": first },
1122
+ { "name": "tonightKeyword", "symbols": [lexer.has("kw_tonight") ? { type: "kw_tonight" } : kw_tonight], "postprocess": first },
1123
+ { "name": "nightKeyword", "symbols": [lexer.has("kw_night") ? { type: "kw_night" } : kw_night], "postprocess": first },
1124
+ { "name": "fortnightKeyword", "symbols": [lexer.has("kw_fortnight") ? { type: "kw_fortnight" } : kw_fortnight], "postprocess": first },
1125
+ { "name": "laterKeyword", "symbols": [lexer.has("otherKeyword") ? { type: "otherKeyword" } : otherKeyword], "postprocess": (d, _, reject) => d[0].value === "later" ? d[0] : reject },
1126
+ { "name": "earlierKeyword", "symbols": [lexer.has("otherKeyword") ? { type: "otherKeyword" } : otherKeyword], "postprocess": (d, _, reject) => d[0].value === "earlier" ? d[0] : reject },
1127
+ { "name": "weekKeyword", "symbols": [lexer.has("unit") ? { type: "unit" } : unit], "postprocess": (d, _, reject) => {
1128
+ const val = d[0].value.toLowerCase();
1129
+ if (val === "week" || val === "weeks") return d[0];
1130
+ return reject;
1131
+ } },
1132
+ { "name": "businessKeyword", "symbols": [lexer.has("kw_business") ? { type: "kw_business" } : kw_business], "postprocess": first },
1133
+ { "name": "eodKeyword", "symbols": [lexer.has("kw_eod") ? { type: "kw_eod" } : kw_eod], "postprocess": first },
1134
+ { "name": "cobKeyword", "symbols": [lexer.has("kw_cob") ? { type: "kw_cob" } : kw_cob], "postprocess": first },
1135
+ { "name": "closeKeyword", "symbols": [lexer.has("kw_close") ? { type: "kw_close" } : kw_close], "postprocess": first },
956
1136
  { "name": "_$ebnf$1", "symbols": [] },
957
1137
  { "name": "_$ebnf$1", "symbols": ["_$ebnf$1", lexer.has("ws") ? { type: "ws" } : ws], "postprocess": (d) => d[0].concat([d[1]]) },
958
1138
  { "name": "_", "symbols": ["_$ebnf$1"], "postprocess": nuller }
@@ -1210,6 +1390,114 @@ function convertMonthDay(node, opts) {
1210
1390
  }
1211
1391
  return date;
1212
1392
  }
1393
+ function convertOrdinalWeekdayOfMonth(node, opts) {
1394
+ const ref = opts.referenceDate;
1395
+ const ordinal2 = node.ordinalWeekday;
1396
+ const weekdayName = node.weekday.toLowerCase();
1397
+ const weekdayNum = WEEKDAY_MAP[weekdayName] ?? 0;
1398
+ let month2;
1399
+ let year;
1400
+ if (node.monthFromRef) {
1401
+ month2 = ref.getUTCMonth();
1402
+ year = ref.getUTCFullYear();
1403
+ } else if (node.nextMonth) {
1404
+ month2 = ref.getUTCMonth() + 1;
1405
+ year = ref.getUTCFullYear();
1406
+ if (month2 > 11) {
1407
+ month2 = 0;
1408
+ year++;
1409
+ }
1410
+ } else {
1411
+ month2 = typeof node.month === "number" ? node.month - 1 : ref.getUTCMonth();
1412
+ year = ref.getUTCFullYear();
1413
+ if (month2 < ref.getUTCMonth()) {
1414
+ year++;
1415
+ }
1416
+ }
1417
+ if (ordinal2 === -1) {
1418
+ const lastDay2 = new Date(Date.UTC(year, month2 + 1, 0));
1419
+ let day2 = lastDay2.getUTCDate();
1420
+ let date2 = new Date(Date.UTC(year, month2, day2));
1421
+ while (date2.getUTCDay() !== weekdayNum) {
1422
+ day2--;
1423
+ date2 = new Date(Date.UTC(year, month2, day2));
1424
+ }
1425
+ return date2;
1426
+ }
1427
+ let count = 0;
1428
+ for (let day2 = 1; day2 <= 31; day2++) {
1429
+ const date2 = new Date(Date.UTC(year, month2, day2));
1430
+ if (date2.getUTCMonth() !== month2) {
1431
+ break;
1432
+ }
1433
+ if (date2.getUTCDay() === weekdayNum) {
1434
+ count++;
1435
+ if (count === ordinal2) {
1436
+ return date2;
1437
+ }
1438
+ }
1439
+ }
1440
+ const lastDay = new Date(Date.UTC(year, month2 + 1, 0));
1441
+ let day = lastDay.getUTCDate();
1442
+ let date = new Date(Date.UTC(year, month2, day));
1443
+ while (date.getUTCDay() !== weekdayNum) {
1444
+ day--;
1445
+ date = new Date(Date.UTC(year, month2, day));
1446
+ }
1447
+ return date;
1448
+ }
1449
+ function convertLastDayOfMonth(node, opts) {
1450
+ const ref = opts.referenceDate;
1451
+ let month2;
1452
+ let year;
1453
+ if (node.monthFromRef) {
1454
+ month2 = ref.getUTCMonth();
1455
+ year = ref.getUTCFullYear();
1456
+ } else if (node.nextMonth) {
1457
+ month2 = ref.getUTCMonth() + 1;
1458
+ year = ref.getUTCFullYear();
1459
+ if (month2 > 11) {
1460
+ month2 = 0;
1461
+ year++;
1462
+ }
1463
+ } else {
1464
+ month2 = typeof node.month === "number" ? node.month - 1 : ref.getUTCMonth();
1465
+ year = ref.getUTCFullYear();
1466
+ if (month2 < ref.getUTCMonth()) {
1467
+ year++;
1468
+ }
1469
+ }
1470
+ return new Date(Date.UTC(year, month2 + 1, 0));
1471
+ }
1472
+ function convertDayOnly(node, opts) {
1473
+ const ref = opts.referenceDate;
1474
+ const day = node.day;
1475
+ const refDay = ref.getUTCDate();
1476
+ const refMonth = ref.getUTCMonth();
1477
+ const refYear = ref.getUTCFullYear();
1478
+ let month2 = refMonth;
1479
+ let year = refYear;
1480
+ if (day < refDay) {
1481
+ month2 = refMonth + 1;
1482
+ if (month2 > 11) {
1483
+ month2 = 0;
1484
+ year++;
1485
+ }
1486
+ }
1487
+ let date = new Date(Date.UTC(year, month2, day));
1488
+ while (date.getUTCDate() !== day) {
1489
+ month2++;
1490
+ if (month2 > 11) {
1491
+ month2 = 0;
1492
+ year++;
1493
+ }
1494
+ date = new Date(Date.UTC(year, month2, day));
1495
+ }
1496
+ if (node.time) {
1497
+ applyTime(date, node.time);
1498
+ }
1499
+ return date;
1500
+ }
1213
1501
  function convertRelativePeriod(node, opts) {
1214
1502
  const ref = opts.referenceDate;
1215
1503
  const period = node.period.toLowerCase();
@@ -1269,6 +1557,9 @@ function convertDateNode(node, opts) {
1269
1557
  if (node.timeOnly && node.time) {
1270
1558
  return convertTimeOnly(node, opts);
1271
1559
  }
1560
+ if (node.ordinalWeekday !== void 0 && node.weekday !== void 0) {
1561
+ return convertOrdinalWeekdayOfMonth(node, opts);
1562
+ }
1272
1563
  if (node.weekday) {
1273
1564
  return convertWeekday(node, opts);
1274
1565
  }
@@ -1278,6 +1569,23 @@ function convertDateNode(node, opts) {
1278
1569
  if (node.month !== void 0 && node.day !== void 0) {
1279
1570
  return convertMonthDay(node, opts);
1280
1571
  }
1572
+ if (node.dayOnly && node.day !== void 0) {
1573
+ return convertDayOnly(node, opts);
1574
+ }
1575
+ if (node.lastDayOfMonth) {
1576
+ return convertLastDayOfMonth(node, opts);
1577
+ }
1578
+ if (node.dayOfMonth && node.nextMonth) {
1579
+ const ref = opts.referenceDate;
1580
+ let month2 = ref.getUTCMonth() + 1;
1581
+ let year = ref.getUTCFullYear();
1582
+ if (month2 > 11) {
1583
+ month2 = 0;
1584
+ year++;
1585
+ }
1586
+ const day = node.ordinalWeekday || 1;
1587
+ return new Date(Date.UTC(year, month2, day));
1588
+ }
1281
1589
  if (node.relative && node.period) {
1282
1590
  return convertRelativePeriod(node, opts);
1283
1591
  }
@@ -1349,7 +1657,7 @@ function getHalfDates(half2, year, fiscalYearStart) {
1349
1657
  const start = new Date(Date.UTC(startYear, halfStartMonth, 1));
1350
1658
  const endMonth = (halfStartMonth + 6) % 12;
1351
1659
  let endYear = startYear;
1352
- if (endMonth < halfStartMonth || endMonth === halfStartMonth) {
1660
+ if (endMonth <= halfStartMonth) {
1353
1661
  endYear++;
1354
1662
  }
1355
1663
  const end = new Date(Date.UTC(endYear, endMonth, 0));
@@ -1422,8 +1730,115 @@ function getModifiedPeriod(start, end, modifier) {
1422
1730
  }
1423
1731
 
1424
1732
  // src/converters/fuzzy.ts
1733
+ function getWeekendDates(ref, relative, opts) {
1734
+ const currentDay = ref.getUTCDay();
1735
+ const saturdayOffset = (6 - currentDay + 7) % 7;
1736
+ let saturday = new Date(
1737
+ Date.UTC(
1738
+ ref.getUTCFullYear(),
1739
+ ref.getUTCMonth(),
1740
+ ref.getUTCDate() + saturdayOffset
1741
+ )
1742
+ );
1743
+ if (relative === "next") {
1744
+ saturday = new Date(saturday.getTime() + MS_PER_WEEK);
1745
+ } else if (relative === "last") {
1746
+ if (currentDay === 6 || currentDay === 0) {
1747
+ saturday = new Date(saturday.getTime() - MS_PER_WEEK);
1748
+ }
1749
+ saturday = new Date(saturday.getTime() - MS_PER_WEEK);
1750
+ }
1751
+ const sunday = new Date(saturday.getTime() + MS_PER_DAY);
1752
+ const sundayEnd = new Date(
1753
+ Date.UTC(
1754
+ sunday.getUTCFullYear(),
1755
+ sunday.getUTCMonth(),
1756
+ sunday.getUTCDate(),
1757
+ 23,
1758
+ 59
1759
+ )
1760
+ );
1761
+ return { start: saturday, end: sundayEnd };
1762
+ }
1763
+ function getNightDates(ref, relative, weekday2) {
1764
+ let baseDate = new Date(ref);
1765
+ if (relative === "last") {
1766
+ baseDate = new Date(
1767
+ Date.UTC(
1768
+ ref.getUTCFullYear(),
1769
+ ref.getUTCMonth(),
1770
+ ref.getUTCDate() - 1
1771
+ )
1772
+ );
1773
+ } else if (relative === "tomorrow") {
1774
+ baseDate = new Date(
1775
+ Date.UTC(
1776
+ ref.getUTCFullYear(),
1777
+ ref.getUTCMonth(),
1778
+ ref.getUTCDate() + 1
1779
+ )
1780
+ );
1781
+ } else if (weekday2) {
1782
+ const targetDay = WEEKDAY_MAP[weekday2.toLowerCase()];
1783
+ if (targetDay !== void 0) {
1784
+ const currentDay = ref.getUTCDay();
1785
+ let daysToAdd = (targetDay - currentDay + 7) % 7;
1786
+ if (daysToAdd === 0) {
1787
+ daysToAdd = 7;
1788
+ }
1789
+ baseDate = new Date(
1790
+ Date.UTC(
1791
+ ref.getUTCFullYear(),
1792
+ ref.getUTCMonth(),
1793
+ ref.getUTCDate() + daysToAdd
1794
+ )
1795
+ );
1796
+ }
1797
+ }
1798
+ const start = new Date(
1799
+ Date.UTC(
1800
+ baseDate.getUTCFullYear(),
1801
+ baseDate.getUTCMonth(),
1802
+ baseDate.getUTCDate(),
1803
+ 18,
1804
+ 0
1805
+ )
1806
+ );
1807
+ const end = new Date(
1808
+ Date.UTC(
1809
+ baseDate.getUTCFullYear(),
1810
+ baseDate.getUTCMonth(),
1811
+ baseDate.getUTCDate(),
1812
+ 23,
1813
+ 59
1814
+ )
1815
+ );
1816
+ return { start, end };
1817
+ }
1425
1818
  function getBasePeriodDates(node, opts, year) {
1426
1819
  const ref = opts.referenceDate;
1820
+ if (node.period === "weekend") {
1821
+ return getWeekendDates(ref, node.relative || "this", opts);
1822
+ }
1823
+ if (node.period === "tonight") {
1824
+ return getNightDates(ref, void 0, void 0);
1825
+ }
1826
+ if (node.period === "night") {
1827
+ return getNightDates(ref, node.relative, node.weekday);
1828
+ }
1829
+ if (node.period === "fortnight") {
1830
+ const count = node.count || 1;
1831
+ const relative = node.relative;
1832
+ const twoWeeks = 14 * MS_PER_DAY;
1833
+ if (relative === "last") {
1834
+ const end2 = new Date(ref);
1835
+ const start2 = new Date(ref.getTime() - twoWeeks);
1836
+ return { start: start2, end: end2 };
1837
+ }
1838
+ const start = new Date(ref);
1839
+ const end = new Date(ref.getTime() + count * twoWeeks);
1840
+ return { start, end };
1841
+ }
1427
1842
  if (node.period === "quarter" && node.quarter) {
1428
1843
  return getQuarterDates(
1429
1844
  node.quarter,
@@ -1463,6 +1878,42 @@ function getBasePeriodDates(node, opts, year) {
1463
1878
  const end = new Date(start.getTime() + 6 * MS_PER_DAY);
1464
1879
  return { start, end };
1465
1880
  }
1881
+ if (node.period === "weekNumber" && node.weekNumber !== void 0) {
1882
+ const weekNum = node.weekNumber;
1883
+ const targetYear = node.year ?? ref.getUTCFullYear();
1884
+ const weekStart = opts.weekStartsOn === "monday" ? 1 : 0;
1885
+ const jan1 = new Date(Date.UTC(targetYear, 0, 1));
1886
+ const jan1Day = jan1.getUTCDay();
1887
+ const daysToFirstWeekStart = (weekStart - jan1Day + 7) % 7;
1888
+ const firstWeekStart = new Date(jan1.getTime() + daysToFirstWeekStart * MS_PER_DAY);
1889
+ const start = new Date(firstWeekStart.getTime() + (weekNum - 1) * MS_PER_WEEK);
1890
+ const end = new Date(start.getTime() + 6 * MS_PER_DAY);
1891
+ return { start, end };
1892
+ }
1893
+ if (node.period === "weekOf" && node.baseDate) {
1894
+ const baseDate = node.baseDate;
1895
+ let targetDate;
1896
+ if (baseDate.month !== void 0 && baseDate.day !== void 0) {
1897
+ const month2 = typeof baseDate.month === "number" ? baseDate.month - 1 : 0;
1898
+ const day = baseDate.day;
1899
+ const targetYear = baseDate.year ?? ref.getUTCFullYear();
1900
+ targetDate = new Date(Date.UTC(targetYear, month2, day));
1901
+ } else {
1902
+ targetDate = ref;
1903
+ }
1904
+ const weekStart = opts.weekStartsOn === "monday" ? 1 : 0;
1905
+ const currentDay = targetDate.getUTCDay();
1906
+ const daysFromStart = (currentDay - weekStart + 7) % 7;
1907
+ const start = new Date(
1908
+ Date.UTC(
1909
+ targetDate.getUTCFullYear(),
1910
+ targetDate.getUTCMonth(),
1911
+ targetDate.getUTCDate() - daysFromStart
1912
+ )
1913
+ );
1914
+ const end = new Date(start.getTime() + 6 * MS_PER_DAY);
1915
+ return { start, end };
1916
+ }
1466
1917
  if (node.period === "day") {
1467
1918
  const start = new Date(
1468
1919
  Date.UTC(ref.getUTCFullYear(), ref.getUTCMonth(), ref.getUTCDate())
@@ -1586,7 +2037,7 @@ function convertASTToResult(ast, opts, originalInput) {
1586
2037
  end = new Date(Date.UTC(year, month2 + 1, 0));
1587
2038
  }
1588
2039
  if (endNode.yearOnly) {
1589
- const year = endNode.year;
2040
+ const year = endNode.year ?? opts.referenceDate.getUTCFullYear();
1590
2041
  end = new Date(Date.UTC(year, 11, 31));
1591
2042
  }
1592
2043
  } else if (endNode.nodeType === "fuzzy") {
@@ -1617,6 +2068,7 @@ function convertASTToResult(ast, opts, originalInput) {
1617
2068
  case "fuzzy": {
1618
2069
  const { start, end } = convertFuzzyNode(expression, opts);
1619
2070
  const mod = expression.modifier;
2071
+ const period = expression.period;
1620
2072
  if (mod === "start" || mod === "beginning") {
1621
2073
  const periodDates = convertFuzzyNodeWithoutModifier(expression, opts);
1622
2074
  return { type: "date", date: periodDates.start, title };
@@ -1625,6 +2077,10 @@ function convertASTToResult(ast, opts, originalInput) {
1625
2077
  const periodDates = convertFuzzyNodeWithoutModifier(expression, opts);
1626
2078
  return { type: "date", date: periodDates.end, title };
1627
2079
  }
2080
+ if (period === "weekend" || period === "night" || period === "tonight" || period === "fortnight" || period === "weekNumber" || period === "weekOf") {
2081
+ const duration = end.getTime() - start.getTime();
2082
+ return { type: "span", start, end, duration, title };
2083
+ }
1628
2084
  return { type: "fuzzy", start, end, approximate: true, title };
1629
2085
  }
1630
2086
  case "relative": {
@@ -1641,6 +2097,91 @@ function convertASTToResult(ast, opts, originalInput) {
1641
2097
  }
1642
2098
  return { type: "span", start, end, duration, title };
1643
2099
  }
2100
+ case "relativeDate": {
2101
+ const durationNode = expression.duration;
2102
+ const baseNode = expression.baseDate;
2103
+ const timeSpec = expression.time;
2104
+ let baseDate;
2105
+ if (baseNode) {
2106
+ baseDate = convertDateNode(baseNode, opts);
2107
+ } else {
2108
+ baseDate = new Date(opts.referenceDate);
2109
+ }
2110
+ const durationValue = durationNode.value;
2111
+ const durationUnit = durationNode.unit;
2112
+ const direction = expression.direction === "past" ? -1 : 1;
2113
+ let resultDate;
2114
+ if (durationUnit === "month") {
2115
+ resultDate = new Date(
2116
+ Date.UTC(
2117
+ baseDate.getUTCFullYear(),
2118
+ baseDate.getUTCMonth() + direction * durationValue,
2119
+ baseDate.getUTCDate(),
2120
+ baseDate.getUTCHours(),
2121
+ baseDate.getUTCMinutes()
2122
+ )
2123
+ );
2124
+ } else if (durationUnit === "year") {
2125
+ resultDate = new Date(
2126
+ Date.UTC(
2127
+ baseDate.getUTCFullYear() + direction * durationValue,
2128
+ baseDate.getUTCMonth(),
2129
+ baseDate.getUTCDate(),
2130
+ baseDate.getUTCHours(),
2131
+ baseDate.getUTCMinutes()
2132
+ )
2133
+ );
2134
+ } else if (durationUnit === "businessDay" || durationUnit === "businessday") {
2135
+ resultDate = new Date(Date.UTC(baseDate.getUTCFullYear(), baseDate.getUTCMonth(), baseDate.getUTCDate()));
2136
+ let daysToAdd = durationValue;
2137
+ while (daysToAdd > 0) {
2138
+ resultDate.setUTCDate(resultDate.getUTCDate() + direction);
2139
+ const dayOfWeek = resultDate.getUTCDay();
2140
+ if (dayOfWeek !== 0 && dayOfWeek !== 6) {
2141
+ daysToAdd--;
2142
+ }
2143
+ }
2144
+ } else {
2145
+ const durationMs = convertDurationNode(durationNode);
2146
+ resultDate = new Date(baseDate.getTime() + direction * durationMs);
2147
+ }
2148
+ if (timeSpec) {
2149
+ if ("special" in timeSpec) {
2150
+ if (timeSpec.special === "noon") {
2151
+ resultDate = new Date(
2152
+ Date.UTC(
2153
+ resultDate.getUTCFullYear(),
2154
+ resultDate.getUTCMonth(),
2155
+ resultDate.getUTCDate(),
2156
+ 12,
2157
+ 0
2158
+ )
2159
+ );
2160
+ } else if (timeSpec.special === "midnight") {
2161
+ resultDate = new Date(
2162
+ Date.UTC(
2163
+ resultDate.getUTCFullYear(),
2164
+ resultDate.getUTCMonth(),
2165
+ resultDate.getUTCDate(),
2166
+ 0,
2167
+ 0
2168
+ )
2169
+ );
2170
+ }
2171
+ } else {
2172
+ resultDate = new Date(
2173
+ Date.UTC(
2174
+ resultDate.getUTCFullYear(),
2175
+ resultDate.getUTCMonth(),
2176
+ resultDate.getUTCDate(),
2177
+ timeSpec.hour,
2178
+ timeSpec.minute
2179
+ )
2180
+ );
2181
+ }
2182
+ }
2183
+ return { type: "date", date: resultDate, title };
2184
+ }
1644
2185
  default:
1645
2186
  return null;
1646
2187
  }
@@ -1663,13 +2204,13 @@ function stripUnmatchedPunctuation(input) {
1663
2204
  openParens = (result.match(/\(/g) || []).length;
1664
2205
  closeParens = (result.match(/\)/g) || []).length;
1665
2206
  while (openParens > closeParens) {
1666
- result = result.replace("(", "");
2207
+ result = result.replace(/\(/, "");
1667
2208
  openParens--;
1668
2209
  }
1669
2210
  openBrackets = (result.match(/\[/g) || []).length;
1670
2211
  closeBrackets = (result.match(/]/g) || []).length;
1671
2212
  while (openBrackets > closeBrackets) {
1672
- result = result.replace("[", "");
2213
+ result = result.replace(/\[/, "");
1673
2214
  openBrackets--;
1674
2215
  }
1675
2216
  return {