@supercmd/calculator 1.0.1 → 1.0.3
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 +150 -9
- package/dist/index.mjs +150 -9
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1798,7 +1798,7 @@ var TIME_IN_PATTERNS = [
|
|
|
1798
1798
|
/^(?:what time is it|whats the time|what is the time|current time|time now) (?:in|at) (.+)$/i,
|
|
1799
1799
|
/^(?:now|current time) in (.+)$/i
|
|
1800
1800
|
];
|
|
1801
|
-
var TIME_EXPLICIT_CONVERT_PATTERN = /^(midnight|noon|\d{1,2}(?::\d{2})?(?:\s*(?:am|pm))?)\s+(.+?)\s+to\s+(.+?)
|
|
1801
|
+
var TIME_EXPLICIT_CONVERT_PATTERN = /^(midnight|noon|\d{1,2}(?::\d{2})?(?:\s*(?:am|pm))?)\s+(?:in\s+)?(.+?)\s+to\s+(.+?)(?:\s+time)?$/i;
|
|
1802
1802
|
var TIME_CONVERT_PATTERN = /^(.+?) to (.+?)(?: time)?$/i;
|
|
1803
1803
|
var TIME_NOW_PATTERN = /^(?:what(?:'s| is) )?(?:the )?(?:current )?time(?: now)?$/i;
|
|
1804
1804
|
function tryTimeIntent(input) {
|
|
@@ -1806,7 +1806,7 @@ function tryTimeIntent(input) {
|
|
|
1806
1806
|
for (const pattern of TIME_IN_PATTERNS) {
|
|
1807
1807
|
match = input.match(pattern);
|
|
1808
1808
|
if (match) {
|
|
1809
|
-
const place = match[1]
|
|
1809
|
+
const place = normalizeTimePlace(match[1]);
|
|
1810
1810
|
const tz2 = resolveTimezone(place);
|
|
1811
1811
|
if (tz2) return { kind: "time", query: input, to: tz2 };
|
|
1812
1812
|
return { kind: "time", query: input, to: place };
|
|
@@ -1815,8 +1815,8 @@ function tryTimeIntent(input) {
|
|
|
1815
1815
|
match = input.match(TIME_EXPLICIT_CONVERT_PATTERN);
|
|
1816
1816
|
if (match) {
|
|
1817
1817
|
const time = match[1].trim();
|
|
1818
|
-
const from = match[2]
|
|
1819
|
-
const to = match[3]
|
|
1818
|
+
const from = normalizeTimePlace(match[2]);
|
|
1819
|
+
const to = normalizeTimePlace(match[3]);
|
|
1820
1820
|
const fromTz = resolveTimezone(from);
|
|
1821
1821
|
const toTz = resolveTimezone(to);
|
|
1822
1822
|
if (fromTz && toTz) {
|
|
@@ -1825,8 +1825,8 @@ function tryTimeIntent(input) {
|
|
|
1825
1825
|
}
|
|
1826
1826
|
match = input.match(TIME_CONVERT_PATTERN);
|
|
1827
1827
|
if (match) {
|
|
1828
|
-
const from = match[1]
|
|
1829
|
-
const to = match[2]
|
|
1828
|
+
const from = normalizeTimePlace(match[1]);
|
|
1829
|
+
const to = normalizeTimePlace(match[2]);
|
|
1830
1830
|
const fromTz = resolveTimezone(from);
|
|
1831
1831
|
const toTz = resolveTimezone(to);
|
|
1832
1832
|
if (fromTz || toTz) {
|
|
@@ -1842,11 +1842,11 @@ function tryTimeIntent(input) {
|
|
|
1842
1842
|
}
|
|
1843
1843
|
const placeSuffixMatch = input.match(/^(.+?)\s+(?:time|now|time now)$/i);
|
|
1844
1844
|
if (placeSuffixMatch) {
|
|
1845
|
-
const place = placeSuffixMatch[1]
|
|
1845
|
+
const place = normalizeTimePlace(placeSuffixMatch[1]);
|
|
1846
1846
|
const tz2 = resolveTimezone(place);
|
|
1847
1847
|
if (tz2) return { kind: "time", query: place, to: tz2 };
|
|
1848
1848
|
}
|
|
1849
|
-
const tz = resolveTimezone(input);
|
|
1849
|
+
const tz = resolveTimezone(normalizeTimePlace(input));
|
|
1850
1850
|
if (tz) {
|
|
1851
1851
|
return { kind: "time", query: input, to: tz };
|
|
1852
1852
|
}
|
|
@@ -1870,7 +1870,10 @@ var DATE_PATTERNS = [
|
|
|
1870
1870
|
/^(?:what(?:'s| is) )?(?:the )?(?:current )?date(?: today)?$/i,
|
|
1871
1871
|
/^(?:(?:what(?:'s|s| is))\s+today|(?:what(?:'s|s| is))\s+today'?s date|what day is it|what date is .+|todays date|today'?s date|date today|current date|what day is .+|when is .+)$/i,
|
|
1872
1872
|
// Days between dates
|
|
1873
|
-
/^(?:days?\s+)?(?:between|from)\s+.+\s+(?:to|and|until)\s+.+$/i
|
|
1873
|
+
/^(?:days?\s+)?(?:between|from)\s+.+\s+(?:to|and|until)\s+.+$/i,
|
|
1874
|
+
// Days until
|
|
1875
|
+
/^(?:days?\s+)?(?:until|till|to)\s+.+$/i,
|
|
1876
|
+
/^(?:how many days?\s+)?(?:until|till|to|before)\s+.+$/i
|
|
1874
1877
|
];
|
|
1875
1878
|
function tryDateIntent(input) {
|
|
1876
1879
|
for (const pattern of DATE_PATTERNS) {
|
|
@@ -1936,6 +1939,9 @@ function normalizeConversionInput(input) {
|
|
|
1936
1939
|
normalized = normalized.replace(/^([$€£¥₹₩₽₺₦₵₪฿])\s*([\d.,]+)/, "$2 $1");
|
|
1937
1940
|
return normalized;
|
|
1938
1941
|
}
|
|
1942
|
+
function normalizeTimePlace(value) {
|
|
1943
|
+
return normalizeWhitespace(value).replace(/^(?:time|current time|time now|now)\s+(?:in|at)\s+/i, "").replace(/^(?:in|at)\s+/i, "").replace(/\s+(?:time|now|time now)$/i, "").trim();
|
|
1944
|
+
}
|
|
1939
1945
|
|
|
1940
1946
|
// src/evaluators/math.ts
|
|
1941
1947
|
var CONSTANTS = {
|
|
@@ -2640,6 +2646,25 @@ function evaluateDate(query) {
|
|
|
2640
2646
|
return formatDateResult(query, d, { iso: d.toISOString(), unix: Math.floor(d.getTime() / 1e3) });
|
|
2641
2647
|
}
|
|
2642
2648
|
}
|
|
2649
|
+
const daysUntilMatch = lower.match(/^(?:(?:how many )?days?\s+)?(?:until|till|to|before)\s+(.+)$/);
|
|
2650
|
+
if (daysUntilMatch) {
|
|
2651
|
+
const target = parseDateTarget(daysUntilMatch[1].trim());
|
|
2652
|
+
if (target) {
|
|
2653
|
+
const now = /* @__PURE__ */ new Date();
|
|
2654
|
+
now.setHours(0, 0, 0, 0);
|
|
2655
|
+
const targetDate = new Date(target);
|
|
2656
|
+
targetDate.setHours(0, 0, 0, 0);
|
|
2657
|
+
const diffMs = targetDate.getTime() - now.getTime();
|
|
2658
|
+
const diffDays = Math.round(diffMs / (1e3 * 60 * 60 * 24));
|
|
2659
|
+
return {
|
|
2660
|
+
type: "date",
|
|
2661
|
+
input: query,
|
|
2662
|
+
result: diffDays,
|
|
2663
|
+
formatted: `${diffDays} days`,
|
|
2664
|
+
metadata: { from: now.toISOString(), to: target.toISOString() }
|
|
2665
|
+
};
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2643
2668
|
const betweenMatch = lower.match(/^(?:days?\s+)?(?:between|from)\s+(.+)\s+(?:to|and|until)\s+(.+)$/);
|
|
2644
2669
|
if (betweenMatch) {
|
|
2645
2670
|
const d1 = parseFlexibleDate(betweenMatch[1].trim());
|
|
@@ -2732,6 +2757,82 @@ function formatDateResult(input, d, extraMeta) {
|
|
|
2732
2757
|
}
|
|
2733
2758
|
};
|
|
2734
2759
|
}
|
|
2760
|
+
function getNextOccurrence(month, day) {
|
|
2761
|
+
const now = /* @__PURE__ */ new Date();
|
|
2762
|
+
const thisYear = new Date(now.getFullYear(), month - 1, day);
|
|
2763
|
+
if (thisYear.getTime() >= new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime()) {
|
|
2764
|
+
return thisYear;
|
|
2765
|
+
}
|
|
2766
|
+
return new Date(now.getFullYear() + 1, month - 1, day);
|
|
2767
|
+
}
|
|
2768
|
+
var NAMED_DATES = {
|
|
2769
|
+
"christmas": () => getNextOccurrence(12, 25),
|
|
2770
|
+
"christmas day": () => getNextOccurrence(12, 25),
|
|
2771
|
+
"christmas eve": () => getNextOccurrence(12, 24),
|
|
2772
|
+
"new year": () => getNextOccurrence(1, 1),
|
|
2773
|
+
"new years": () => getNextOccurrence(1, 1),
|
|
2774
|
+
"new year's": () => getNextOccurrence(1, 1),
|
|
2775
|
+
"new year's day": () => getNextOccurrence(1, 1),
|
|
2776
|
+
"new years day": () => getNextOccurrence(1, 1),
|
|
2777
|
+
"new years eve": () => getNextOccurrence(12, 31),
|
|
2778
|
+
"new year's eve": () => getNextOccurrence(12, 31),
|
|
2779
|
+
"valentines": () => getNextOccurrence(2, 14),
|
|
2780
|
+
"valentines day": () => getNextOccurrence(2, 14),
|
|
2781
|
+
"valentine's day": () => getNextOccurrence(2, 14),
|
|
2782
|
+
"halloween": () => getNextOccurrence(10, 31),
|
|
2783
|
+
"independence day": () => getNextOccurrence(7, 4),
|
|
2784
|
+
"st patricks day": () => getNextOccurrence(3, 17),
|
|
2785
|
+
"st patrick's day": () => getNextOccurrence(3, 17)
|
|
2786
|
+
};
|
|
2787
|
+
var MONTH_NAMES = {
|
|
2788
|
+
january: 1,
|
|
2789
|
+
jan: 1,
|
|
2790
|
+
february: 2,
|
|
2791
|
+
feb: 2,
|
|
2792
|
+
march: 3,
|
|
2793
|
+
mar: 3,
|
|
2794
|
+
april: 4,
|
|
2795
|
+
apr: 4,
|
|
2796
|
+
may: 5,
|
|
2797
|
+
june: 6,
|
|
2798
|
+
jun: 6,
|
|
2799
|
+
july: 7,
|
|
2800
|
+
jul: 7,
|
|
2801
|
+
august: 8,
|
|
2802
|
+
aug: 8,
|
|
2803
|
+
september: 9,
|
|
2804
|
+
sep: 9,
|
|
2805
|
+
sept: 9,
|
|
2806
|
+
october: 10,
|
|
2807
|
+
oct: 10,
|
|
2808
|
+
november: 11,
|
|
2809
|
+
nov: 11,
|
|
2810
|
+
december: 12,
|
|
2811
|
+
dec: 12
|
|
2812
|
+
};
|
|
2813
|
+
function parseMonthDay(str) {
|
|
2814
|
+
const cleaned = str.replace(/(\d+)(?:st|nd|rd|th)/gi, "$1");
|
|
2815
|
+
let match = cleaned.match(/^(\d{1,2})\s+([a-z]+)$/i);
|
|
2816
|
+
if (match) {
|
|
2817
|
+
const day = parseInt(match[1]);
|
|
2818
|
+
const month = MONTH_NAMES[match[2].toLowerCase()];
|
|
2819
|
+
if (month && day >= 1 && day <= 31) return getNextOccurrence(month, day);
|
|
2820
|
+
}
|
|
2821
|
+
match = cleaned.match(/^([a-z]+)\s+(\d{1,2})$/i);
|
|
2822
|
+
if (match) {
|
|
2823
|
+
const month = MONTH_NAMES[match[1].toLowerCase()];
|
|
2824
|
+
const day = parseInt(match[2]);
|
|
2825
|
+
if (month && day >= 1 && day <= 31) return getNextOccurrence(month, day);
|
|
2826
|
+
}
|
|
2827
|
+
return null;
|
|
2828
|
+
}
|
|
2829
|
+
function parseDateTarget(str) {
|
|
2830
|
+
const lower = str.toLowerCase().trim();
|
|
2831
|
+
if (NAMED_DATES[lower]) return NAMED_DATES[lower]();
|
|
2832
|
+
const monthDay = parseMonthDay(lower);
|
|
2833
|
+
if (monthDay) return monthDay;
|
|
2834
|
+
return parseFlexibleDate(lower);
|
|
2835
|
+
}
|
|
2735
2836
|
function parseFlexibleDate(str) {
|
|
2736
2837
|
if (str === "today" || str === "now" || str === "date") return /* @__PURE__ */ new Date();
|
|
2737
2838
|
if (str === "tomorrow") {
|
|
@@ -2870,6 +2971,46 @@ async function calculate(input, options) {
|
|
|
2870
2971
|
const locale = options?.locale ?? "en-US";
|
|
2871
2972
|
const precision = options?.precision ?? 10;
|
|
2872
2973
|
const rateProvider = options?.rateProvider ?? getDefaultProvider();
|
|
2974
|
+
const convMathMatch = trimmed.match(
|
|
2975
|
+
/^(.+?\s+(?:to|in)\s+[a-zA-Z$€£¥₹₩₽₺₦₵₪฿]+(?:\s+[a-zA-Z]+)?)\s*([+\-*/^])\s*(.+)$/i
|
|
2976
|
+
);
|
|
2977
|
+
if (convMathMatch) {
|
|
2978
|
+
const convPart = convMathMatch[1].trim();
|
|
2979
|
+
const operator = convMathMatch[2];
|
|
2980
|
+
const mathPart = convMathMatch[3].trim();
|
|
2981
|
+
const convIntent = detectIntent(convPart);
|
|
2982
|
+
if (convIntent.kind === "currency" || convIntent.kind === "crypto" || convIntent.kind === "unit") {
|
|
2983
|
+
let convResult;
|
|
2984
|
+
switch (convIntent.kind) {
|
|
2985
|
+
case "currency":
|
|
2986
|
+
convResult = await evaluateCurrency(convIntent.amount, convIntent.from, convIntent.to, rateProvider, locale, precision);
|
|
2987
|
+
break;
|
|
2988
|
+
case "crypto":
|
|
2989
|
+
convResult = await evaluateCrypto(convIntent.amount, convIntent.from, convIntent.to, rateProvider, locale, precision);
|
|
2990
|
+
break;
|
|
2991
|
+
case "unit":
|
|
2992
|
+
convResult = evaluateUnit(convIntent.amount, convIntent.from, convIntent.to, locale, precision);
|
|
2993
|
+
break;
|
|
2994
|
+
}
|
|
2995
|
+
if (convResult.type !== "error" && typeof convResult.result === "number") {
|
|
2996
|
+
const mathExpr = `${convResult.result} ${operator} ${mathPart}`;
|
|
2997
|
+
const mathResult = evaluateMath(mathExpr, locale, precision);
|
|
2998
|
+
if (mathResult.type !== "error") {
|
|
2999
|
+
return {
|
|
3000
|
+
...mathResult,
|
|
3001
|
+
type: convResult.type,
|
|
3002
|
+
input: trimmed,
|
|
3003
|
+
metadata: {
|
|
3004
|
+
...mathResult.metadata,
|
|
3005
|
+
conversionResult: convResult.result,
|
|
3006
|
+
conversionFormatted: convResult.formatted,
|
|
3007
|
+
operation: `${convResult.formatted} ${operator} ${mathPart}`
|
|
3008
|
+
}
|
|
3009
|
+
};
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
3012
|
+
}
|
|
3013
|
+
}
|
|
2873
3014
|
const intent = detectIntent(trimmed);
|
|
2874
3015
|
switch (intent.kind) {
|
|
2875
3016
|
case "time":
|
package/dist/index.mjs
CHANGED
|
@@ -1761,7 +1761,7 @@ var TIME_IN_PATTERNS = [
|
|
|
1761
1761
|
/^(?:what time is it|whats the time|what is the time|current time|time now) (?:in|at) (.+)$/i,
|
|
1762
1762
|
/^(?:now|current time) in (.+)$/i
|
|
1763
1763
|
];
|
|
1764
|
-
var TIME_EXPLICIT_CONVERT_PATTERN = /^(midnight|noon|\d{1,2}(?::\d{2})?(?:\s*(?:am|pm))?)\s+(.+?)\s+to\s+(.+?)
|
|
1764
|
+
var TIME_EXPLICIT_CONVERT_PATTERN = /^(midnight|noon|\d{1,2}(?::\d{2})?(?:\s*(?:am|pm))?)\s+(?:in\s+)?(.+?)\s+to\s+(.+?)(?:\s+time)?$/i;
|
|
1765
1765
|
var TIME_CONVERT_PATTERN = /^(.+?) to (.+?)(?: time)?$/i;
|
|
1766
1766
|
var TIME_NOW_PATTERN = /^(?:what(?:'s| is) )?(?:the )?(?:current )?time(?: now)?$/i;
|
|
1767
1767
|
function tryTimeIntent(input) {
|
|
@@ -1769,7 +1769,7 @@ function tryTimeIntent(input) {
|
|
|
1769
1769
|
for (const pattern of TIME_IN_PATTERNS) {
|
|
1770
1770
|
match = input.match(pattern);
|
|
1771
1771
|
if (match) {
|
|
1772
|
-
const place = match[1]
|
|
1772
|
+
const place = normalizeTimePlace(match[1]);
|
|
1773
1773
|
const tz2 = resolveTimezone(place);
|
|
1774
1774
|
if (tz2) return { kind: "time", query: input, to: tz2 };
|
|
1775
1775
|
return { kind: "time", query: input, to: place };
|
|
@@ -1778,8 +1778,8 @@ function tryTimeIntent(input) {
|
|
|
1778
1778
|
match = input.match(TIME_EXPLICIT_CONVERT_PATTERN);
|
|
1779
1779
|
if (match) {
|
|
1780
1780
|
const time = match[1].trim();
|
|
1781
|
-
const from = match[2]
|
|
1782
|
-
const to = match[3]
|
|
1781
|
+
const from = normalizeTimePlace(match[2]);
|
|
1782
|
+
const to = normalizeTimePlace(match[3]);
|
|
1783
1783
|
const fromTz = resolveTimezone(from);
|
|
1784
1784
|
const toTz = resolveTimezone(to);
|
|
1785
1785
|
if (fromTz && toTz) {
|
|
@@ -1788,8 +1788,8 @@ function tryTimeIntent(input) {
|
|
|
1788
1788
|
}
|
|
1789
1789
|
match = input.match(TIME_CONVERT_PATTERN);
|
|
1790
1790
|
if (match) {
|
|
1791
|
-
const from = match[1]
|
|
1792
|
-
const to = match[2]
|
|
1791
|
+
const from = normalizeTimePlace(match[1]);
|
|
1792
|
+
const to = normalizeTimePlace(match[2]);
|
|
1793
1793
|
const fromTz = resolveTimezone(from);
|
|
1794
1794
|
const toTz = resolveTimezone(to);
|
|
1795
1795
|
if (fromTz || toTz) {
|
|
@@ -1805,11 +1805,11 @@ function tryTimeIntent(input) {
|
|
|
1805
1805
|
}
|
|
1806
1806
|
const placeSuffixMatch = input.match(/^(.+?)\s+(?:time|now|time now)$/i);
|
|
1807
1807
|
if (placeSuffixMatch) {
|
|
1808
|
-
const place = placeSuffixMatch[1]
|
|
1808
|
+
const place = normalizeTimePlace(placeSuffixMatch[1]);
|
|
1809
1809
|
const tz2 = resolveTimezone(place);
|
|
1810
1810
|
if (tz2) return { kind: "time", query: place, to: tz2 };
|
|
1811
1811
|
}
|
|
1812
|
-
const tz = resolveTimezone(input);
|
|
1812
|
+
const tz = resolveTimezone(normalizeTimePlace(input));
|
|
1813
1813
|
if (tz) {
|
|
1814
1814
|
return { kind: "time", query: input, to: tz };
|
|
1815
1815
|
}
|
|
@@ -1833,7 +1833,10 @@ var DATE_PATTERNS = [
|
|
|
1833
1833
|
/^(?:what(?:'s| is) )?(?:the )?(?:current )?date(?: today)?$/i,
|
|
1834
1834
|
/^(?:(?:what(?:'s|s| is))\s+today|(?:what(?:'s|s| is))\s+today'?s date|what day is it|what date is .+|todays date|today'?s date|date today|current date|what day is .+|when is .+)$/i,
|
|
1835
1835
|
// Days between dates
|
|
1836
|
-
/^(?:days?\s+)?(?:between|from)\s+.+\s+(?:to|and|until)\s+.+$/i
|
|
1836
|
+
/^(?:days?\s+)?(?:between|from)\s+.+\s+(?:to|and|until)\s+.+$/i,
|
|
1837
|
+
// Days until
|
|
1838
|
+
/^(?:days?\s+)?(?:until|till|to)\s+.+$/i,
|
|
1839
|
+
/^(?:how many days?\s+)?(?:until|till|to|before)\s+.+$/i
|
|
1837
1840
|
];
|
|
1838
1841
|
function tryDateIntent(input) {
|
|
1839
1842
|
for (const pattern of DATE_PATTERNS) {
|
|
@@ -1899,6 +1902,9 @@ function normalizeConversionInput(input) {
|
|
|
1899
1902
|
normalized = normalized.replace(/^([$€£¥₹₩₽₺₦₵₪฿])\s*([\d.,]+)/, "$2 $1");
|
|
1900
1903
|
return normalized;
|
|
1901
1904
|
}
|
|
1905
|
+
function normalizeTimePlace(value) {
|
|
1906
|
+
return normalizeWhitespace(value).replace(/^(?:time|current time|time now|now)\s+(?:in|at)\s+/i, "").replace(/^(?:in|at)\s+/i, "").replace(/\s+(?:time|now|time now)$/i, "").trim();
|
|
1907
|
+
}
|
|
1902
1908
|
|
|
1903
1909
|
// src/evaluators/math.ts
|
|
1904
1910
|
var CONSTANTS = {
|
|
@@ -2603,6 +2609,25 @@ function evaluateDate(query) {
|
|
|
2603
2609
|
return formatDateResult(query, d, { iso: d.toISOString(), unix: Math.floor(d.getTime() / 1e3) });
|
|
2604
2610
|
}
|
|
2605
2611
|
}
|
|
2612
|
+
const daysUntilMatch = lower.match(/^(?:(?:how many )?days?\s+)?(?:until|till|to|before)\s+(.+)$/);
|
|
2613
|
+
if (daysUntilMatch) {
|
|
2614
|
+
const target = parseDateTarget(daysUntilMatch[1].trim());
|
|
2615
|
+
if (target) {
|
|
2616
|
+
const now = /* @__PURE__ */ new Date();
|
|
2617
|
+
now.setHours(0, 0, 0, 0);
|
|
2618
|
+
const targetDate = new Date(target);
|
|
2619
|
+
targetDate.setHours(0, 0, 0, 0);
|
|
2620
|
+
const diffMs = targetDate.getTime() - now.getTime();
|
|
2621
|
+
const diffDays = Math.round(diffMs / (1e3 * 60 * 60 * 24));
|
|
2622
|
+
return {
|
|
2623
|
+
type: "date",
|
|
2624
|
+
input: query,
|
|
2625
|
+
result: diffDays,
|
|
2626
|
+
formatted: `${diffDays} days`,
|
|
2627
|
+
metadata: { from: now.toISOString(), to: target.toISOString() }
|
|
2628
|
+
};
|
|
2629
|
+
}
|
|
2630
|
+
}
|
|
2606
2631
|
const betweenMatch = lower.match(/^(?:days?\s+)?(?:between|from)\s+(.+)\s+(?:to|and|until)\s+(.+)$/);
|
|
2607
2632
|
if (betweenMatch) {
|
|
2608
2633
|
const d1 = parseFlexibleDate(betweenMatch[1].trim());
|
|
@@ -2695,6 +2720,82 @@ function formatDateResult(input, d, extraMeta) {
|
|
|
2695
2720
|
}
|
|
2696
2721
|
};
|
|
2697
2722
|
}
|
|
2723
|
+
function getNextOccurrence(month, day) {
|
|
2724
|
+
const now = /* @__PURE__ */ new Date();
|
|
2725
|
+
const thisYear = new Date(now.getFullYear(), month - 1, day);
|
|
2726
|
+
if (thisYear.getTime() >= new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime()) {
|
|
2727
|
+
return thisYear;
|
|
2728
|
+
}
|
|
2729
|
+
return new Date(now.getFullYear() + 1, month - 1, day);
|
|
2730
|
+
}
|
|
2731
|
+
var NAMED_DATES = {
|
|
2732
|
+
"christmas": () => getNextOccurrence(12, 25),
|
|
2733
|
+
"christmas day": () => getNextOccurrence(12, 25),
|
|
2734
|
+
"christmas eve": () => getNextOccurrence(12, 24),
|
|
2735
|
+
"new year": () => getNextOccurrence(1, 1),
|
|
2736
|
+
"new years": () => getNextOccurrence(1, 1),
|
|
2737
|
+
"new year's": () => getNextOccurrence(1, 1),
|
|
2738
|
+
"new year's day": () => getNextOccurrence(1, 1),
|
|
2739
|
+
"new years day": () => getNextOccurrence(1, 1),
|
|
2740
|
+
"new years eve": () => getNextOccurrence(12, 31),
|
|
2741
|
+
"new year's eve": () => getNextOccurrence(12, 31),
|
|
2742
|
+
"valentines": () => getNextOccurrence(2, 14),
|
|
2743
|
+
"valentines day": () => getNextOccurrence(2, 14),
|
|
2744
|
+
"valentine's day": () => getNextOccurrence(2, 14),
|
|
2745
|
+
"halloween": () => getNextOccurrence(10, 31),
|
|
2746
|
+
"independence day": () => getNextOccurrence(7, 4),
|
|
2747
|
+
"st patricks day": () => getNextOccurrence(3, 17),
|
|
2748
|
+
"st patrick's day": () => getNextOccurrence(3, 17)
|
|
2749
|
+
};
|
|
2750
|
+
var MONTH_NAMES = {
|
|
2751
|
+
january: 1,
|
|
2752
|
+
jan: 1,
|
|
2753
|
+
february: 2,
|
|
2754
|
+
feb: 2,
|
|
2755
|
+
march: 3,
|
|
2756
|
+
mar: 3,
|
|
2757
|
+
april: 4,
|
|
2758
|
+
apr: 4,
|
|
2759
|
+
may: 5,
|
|
2760
|
+
june: 6,
|
|
2761
|
+
jun: 6,
|
|
2762
|
+
july: 7,
|
|
2763
|
+
jul: 7,
|
|
2764
|
+
august: 8,
|
|
2765
|
+
aug: 8,
|
|
2766
|
+
september: 9,
|
|
2767
|
+
sep: 9,
|
|
2768
|
+
sept: 9,
|
|
2769
|
+
october: 10,
|
|
2770
|
+
oct: 10,
|
|
2771
|
+
november: 11,
|
|
2772
|
+
nov: 11,
|
|
2773
|
+
december: 12,
|
|
2774
|
+
dec: 12
|
|
2775
|
+
};
|
|
2776
|
+
function parseMonthDay(str) {
|
|
2777
|
+
const cleaned = str.replace(/(\d+)(?:st|nd|rd|th)/gi, "$1");
|
|
2778
|
+
let match = cleaned.match(/^(\d{1,2})\s+([a-z]+)$/i);
|
|
2779
|
+
if (match) {
|
|
2780
|
+
const day = parseInt(match[1]);
|
|
2781
|
+
const month = MONTH_NAMES[match[2].toLowerCase()];
|
|
2782
|
+
if (month && day >= 1 && day <= 31) return getNextOccurrence(month, day);
|
|
2783
|
+
}
|
|
2784
|
+
match = cleaned.match(/^([a-z]+)\s+(\d{1,2})$/i);
|
|
2785
|
+
if (match) {
|
|
2786
|
+
const month = MONTH_NAMES[match[1].toLowerCase()];
|
|
2787
|
+
const day = parseInt(match[2]);
|
|
2788
|
+
if (month && day >= 1 && day <= 31) return getNextOccurrence(month, day);
|
|
2789
|
+
}
|
|
2790
|
+
return null;
|
|
2791
|
+
}
|
|
2792
|
+
function parseDateTarget(str) {
|
|
2793
|
+
const lower = str.toLowerCase().trim();
|
|
2794
|
+
if (NAMED_DATES[lower]) return NAMED_DATES[lower]();
|
|
2795
|
+
const monthDay = parseMonthDay(lower);
|
|
2796
|
+
if (monthDay) return monthDay;
|
|
2797
|
+
return parseFlexibleDate(lower);
|
|
2798
|
+
}
|
|
2698
2799
|
function parseFlexibleDate(str) {
|
|
2699
2800
|
if (str === "today" || str === "now" || str === "date") return /* @__PURE__ */ new Date();
|
|
2700
2801
|
if (str === "tomorrow") {
|
|
@@ -2833,6 +2934,46 @@ async function calculate(input, options) {
|
|
|
2833
2934
|
const locale = options?.locale ?? "en-US";
|
|
2834
2935
|
const precision = options?.precision ?? 10;
|
|
2835
2936
|
const rateProvider = options?.rateProvider ?? getDefaultProvider();
|
|
2937
|
+
const convMathMatch = trimmed.match(
|
|
2938
|
+
/^(.+?\s+(?:to|in)\s+[a-zA-Z$€£¥₹₩₽₺₦₵₪฿]+(?:\s+[a-zA-Z]+)?)\s*([+\-*/^])\s*(.+)$/i
|
|
2939
|
+
);
|
|
2940
|
+
if (convMathMatch) {
|
|
2941
|
+
const convPart = convMathMatch[1].trim();
|
|
2942
|
+
const operator = convMathMatch[2];
|
|
2943
|
+
const mathPart = convMathMatch[3].trim();
|
|
2944
|
+
const convIntent = detectIntent(convPart);
|
|
2945
|
+
if (convIntent.kind === "currency" || convIntent.kind === "crypto" || convIntent.kind === "unit") {
|
|
2946
|
+
let convResult;
|
|
2947
|
+
switch (convIntent.kind) {
|
|
2948
|
+
case "currency":
|
|
2949
|
+
convResult = await evaluateCurrency(convIntent.amount, convIntent.from, convIntent.to, rateProvider, locale, precision);
|
|
2950
|
+
break;
|
|
2951
|
+
case "crypto":
|
|
2952
|
+
convResult = await evaluateCrypto(convIntent.amount, convIntent.from, convIntent.to, rateProvider, locale, precision);
|
|
2953
|
+
break;
|
|
2954
|
+
case "unit":
|
|
2955
|
+
convResult = evaluateUnit(convIntent.amount, convIntent.from, convIntent.to, locale, precision);
|
|
2956
|
+
break;
|
|
2957
|
+
}
|
|
2958
|
+
if (convResult.type !== "error" && typeof convResult.result === "number") {
|
|
2959
|
+
const mathExpr = `${convResult.result} ${operator} ${mathPart}`;
|
|
2960
|
+
const mathResult = evaluateMath(mathExpr, locale, precision);
|
|
2961
|
+
if (mathResult.type !== "error") {
|
|
2962
|
+
return {
|
|
2963
|
+
...mathResult,
|
|
2964
|
+
type: convResult.type,
|
|
2965
|
+
input: trimmed,
|
|
2966
|
+
metadata: {
|
|
2967
|
+
...mathResult.metadata,
|
|
2968
|
+
conversionResult: convResult.result,
|
|
2969
|
+
conversionFormatted: convResult.formatted,
|
|
2970
|
+
operation: `${convResult.formatted} ${operator} ${mathPart}`
|
|
2971
|
+
}
|
|
2972
|
+
};
|
|
2973
|
+
}
|
|
2974
|
+
}
|
|
2975
|
+
}
|
|
2976
|
+
}
|
|
2836
2977
|
const intent = detectIntent(trimmed);
|
|
2837
2978
|
switch (intent.kind) {
|
|
2838
2979
|
case "time":
|
package/package.json
CHANGED