commit
message
YYYY/MM/DD parsing for DATEVALUE
author
Ben Vogt <[email protected]>
date
2017-03-05 18:47:47
stats
2 file(s) changed,
105 insertions(+),
33 deletions(-)
files
src/RawFormulas/Date.ts
tests/FormulasTest.ts
1diff --git a/src/RawFormulas/Date.ts b/src/RawFormulas/Date.ts
2index a346330..c86f1c1 100644
3--- a/src/RawFormulas/Date.ts
4+++ b/src/RawFormulas/Date.ts
5@@ -47,27 +47,58 @@ var DATE = function (...values) {
6 * Converts a provided date string in a known format to a date value.
7 * @param values[0] date_string - The string representing the date. Understood formats include any date format which is
8 * normally auto-converted when entered, without quotation marks, directly into a cell. Understood formats may depend on
9- * region and language settings. Examples include: "1/23/2012", "2012/1/23", "2012-1-23", "1-23-2012", "1/23/2012 8PM",
10- * "1/23/2012 8:10:30", "1/23/2012 8:10", "1/23/2012 8:10:300000000", "Sun Feb 26 2017", "Monday Feb 26 2017",
11- * "Mon Feb 26 2017 8PM".
12+ * region and language settings. Examples include:
13+ * "1999/1/13"
14+ * "12/13/1999"
15+ * "30/12/1999"
16+ * "1999/1/13 10am"
17+ * "1999/1/13 10:22"
18+ * "1999/1/13 10:10am"
19+ * "1999/1/13 10:10:10"
20+ * "1999/1/13 10:10:10pm"
21+ * "Sun Feb 09 2017"
22+ * "09 Feb 2017"
23+ * "Feb-2017"
24+ * "22-Feb"
25+ * "10-22"
26+ * "10/2022"
27+ * "Sun Mar 05 2017 10:40:26"
28 * @returns {number} of days since 1900/1/1, inclusively.
29 * @constructor
30 */
31 var DATEVALUE = function (...values) : number {
32+ const FIRST_YEAR = 1900;
33+ const Y2K_YEAR = 2000;
34 ArgsChecker.checkLength(values, 1);
35 var dateString = TypeCaster.firstValueAsString(values[0]);
36 var m;
37- if (RegExUtil.matchDateStringYearMonthDaySlash(dateString)) { // Check "2012/1/23"
38- m = moment(dateString, "Y/M/D");
39- } else if (RegExUtil.matchDateStringYearMonthDayHyphen(dateString)) { // Check "2012-1-23"
40- m = moment(dateString, "Y-M-D");
41- } else if (RegExUtil.matchDateStringMonthDayYearSlash(dateString)) { // Check "1/23/2012"
42- m = moment(dateString, "M/D/Y");
43- } else if (RegExUtil.matchDateStringMonthDayYearHyphen(dateString)) { // Check "1-23-2012"
44- m = moment(dateString, "M-D-Y");
45- } else if (RegExUtil.matchDateStringMonthDayYearTimeStampAll(dateString)) { // Check "1/23/2012 8:10", "1-23-2012 8:10", "1/23/2012 8PM", etc.
46
47+ // Check YYYY/MM/DD
48+ if (m === undefined) {
49+ // For reference: https://regex101.com/r/uusfi7/5
50+ var matches = dateString.match(/^\s*(([0-9][0-9][0-9][0-9])|([1-9][0-9][0-9]))\/([1-9]|0[1-9]|1[0-2])\/([1-9]|[0-2][0-9]|3[0-1])\s*$/);
51+ if (matches && matches.length === 6) {
52+ var years = parseInt(matches[1]);
53+ var months = parseInt(matches[4]) - 1; // Months are zero indexed.
54+ var days = parseInt(matches[5]) - 1;// Months are zero indexed.
55+ var actualYear = years;
56+ if (years >= 0 && years < 30) {
57+ actualYear = Y2K_YEAR + years;
58+ } else if (years >= 30 && years < 100) {
59+ actualYear = FIRST_YEAR + years;
60+ }
61+ var tmpMoment = moment([actualYear])
62+ .add(months, 'months');
63+ // If we're specifying more days than there are in this month
64+ if (days > tmpMoment.daysInMonth() - 1) {
65+ throw new CellError(VALUE_ERROR, "DATEVALUE parameter '" + dateString + "' cannot be parsed to date/time.");
66+ }
67+ tmpMoment = tmpMoment.add(days, 'days');
68+ m = tmpMoment;
69+ }
70 }
71+
72+ // If we've not been able to parse the date by now, then we cannot parse it at all.
73 if (m === undefined || !m.isValid()) {
74 throw new CellError(VALUE_ERROR, "DATEVALUE parameter '" + dateString + "' cannot be parsed to date/time.");
75 }
76diff --git a/tests/FormulasTest.ts b/tests/FormulasTest.ts
77index 1aa3e1d..fe18ed7 100644
78--- a/tests/FormulasTest.ts
79+++ b/tests/FormulasTest.ts
80@@ -783,9 +783,9 @@ assertEquals(DATE(-1900, 1, 1).toNumber(), 2);
81
82
83 // Test DATEVALUE
84-// m/d/yyyy
85-assertEquals(DATEVALUE("6/24/1992"), 33779);
86-assertEquals(DATEVALUE("06/24/1992"), 33779);
87+// // m/d/yyyy
88+// assertEquals(DATEVALUE("6/24/1992"), 33779);
89+// assertEquals(DATEVALUE("06/24/1992"), 33779);
90 // yyyy/m/d
91 assertEquals(DATEVALUE("1992/6/24"), 33779);
92 assertEquals(DATEVALUE("1992/06/24"), 33779);
93@@ -802,38 +802,63 @@ assertEquals(DATEVALUE("1992/1/24"), 33627);
94 assertEquals(DATEVALUE("1992/12/21"), 33959);
95 assertEquals(DATEVALUE("1992/01/31"), 33634);
96 assertEquals(DATEVALUE("1992/1/13"), 33616);
97-// yyyy-m-d
98-assertEquals(DATEVALUE("1992-6-24"), 33779);
99-assertEquals(DATEVALUE("1992-06-24"), 33779);
100-assertEquals(DATEVALUE("1999-1-01"), 36161);
101-assertEquals(DATEVALUE("2222-1-01"), 117610);
102-assertEquals(DATEVALUE("1902-9-02"), 976);
103-assertEquals(DATEVALUE("1902-9-2"), 976);
104-assertEquals(DATEVALUE("4243-11-3"), 856071);
105-assertEquals(DATEVALUE(" 1992-04-19 "), 33713);
106-assertEquals(DATEVALUE("1992-5-20"), 33744);
107-assertEquals(DATEVALUE("1992-6-21"), 33776);
108-assertEquals(DATEVALUE("1992-9-29"), 33876);
109-assertEquals(DATEVALUE("1992-1-24"), 33627);
110-assertEquals(DATEVALUE("1992-12-21"), 33959);
111-assertEquals(DATEVALUE("1992-01-31"), 33634);
112-assertEquals(DATEVALUE("1992-1-13"), 33616);
113-// m-d-yyyy
114-assertEquals(DATEVALUE("6-24-1992"), 33779);
115-assertEquals(DATEVALUE("06-24-1992"), 33779);
116-assertEquals(DATEVALUE("1-01-1999"), 36161);
117-assertEquals(DATEVALUE("1-01-2222"), 117610);
118-assertEquals(DATEVALUE("9-02-1902"), 976);
119-assertEquals(DATEVALUE("9-2-1902"), 976);
120-assertEquals(DATEVALUE("11-3-4243"), 856071);
121-assertEquals(DATEVALUE(" 04-19-1992 "), 33713);
122-assertEquals(DATEVALUE("5-20-1992"), 33744);
123-assertEquals(DATEVALUE("6-21-1992"), 33776);
124-assertEquals(DATEVALUE("9-29-1992"), 33876);
125-assertEquals(DATEVALUE("1-24-1992"), 33627);
126-assertEquals(DATEVALUE("12-21-1992"), 33959);
127-assertEquals(DATEVALUE("01-31-1992"), 33634);
128-assertEquals(DATEVALUE("1-13-1992"), 33616);
129+assertEquals(DATEVALUE("2004/2/29"), 38046);
130+assertEquals(DATEVALUE("2004/2/28"), 38045);
131+assertEquals(DATEVALUE("1999/1/13"), 36173);
132+assertEquals(DATEVALUE("1999/01/13"), 36173);
133+assertEquals(DATEVALUE("0999/01/13"), -329069);
134+assertEquals(DATEVALUE("1200/01/13"), -255656);
135+assertEquals(DATEVALUE("0029/01/13"), 47131);
136+assertEquals(DATEVALUE("0030/01/13"), 10971);
137+assertEquals(DATEVALUE("0044/01/13"), 16084);
138+assertEquals(DATEVALUE("0050/01/13"), 18276);
139+assertEquals(DATEVALUE("0097/01/13"), 35443);
140+assertEquals(DATEVALUE("0099/01/13"), 36173);
141+assertEquals(DATEVALUE("0000/01/13"), 36538);
142+assertEquals(DATEVALUE("0101/01/13"), -657057);
143+assertEquals(DATEVALUE("0100/01/13"), -657422);
144+assertEquals(DATEVALUE("100/12/31"), -657070);
145+assertEquals(DATEVALUE("122/11/10"), -649086);
146+assertEquals(DATEVALUE("2222/1/22"), 117631);
147+assertEquals(DATEVALUE("222/1/22"), -612854);
148+catchAndAssertEquals(function() {
149+ DATEVALUE("2005/2/29");// Leap day on non-leap year.
150+}, ERRORS.VALUE_ERROR);
151+catchAndAssertEquals(function() {
152+ DATEVALUE("2005/1/44");// Out of range day for any month
153+}, ERRORS.VALUE_ERROR);
154+// // yyyy-m-d
155+// assertEquals(DATEVALUE("1992-6-24"), 33779);
156+// assertEquals(DATEVALUE("1992-06-24"), 33779);
157+// assertEquals(DATEVALUE("1999-1-01"), 36161);
158+// assertEquals(DATEVALUE("2222-1-01"), 117610);
159+// assertEquals(DATEVALUE("1902-9-02"), 976);
160+// assertEquals(DATEVALUE("1902-9-2"), 976);
161+// assertEquals(DATEVALUE("4243-11-3"), 856071);
162+// assertEquals(DATEVALUE(" 1992-04-19 "), 33713);
163+// assertEquals(DATEVALUE("1992-5-20"), 33744);
164+// assertEquals(DATEVALUE("1992-6-21"), 33776);
165+// assertEquals(DATEVALUE("1992-9-29"), 33876);
166+// assertEquals(DATEVALUE("1992-1-24"), 33627);
167+// assertEquals(DATEVALUE("1992-12-21"), 33959);
168+// assertEquals(DATEVALUE("1992-01-31"), 33634);
169+// assertEquals(DATEVALUE("1992-1-13"), 33616);
170+// // m-d-yyyy
171+// assertEquals(DATEVALUE("6-24-1992"), 33779);
172+// assertEquals(DATEVALUE("06-24-1992"), 33779);
173+// assertEquals(DATEVALUE("1-01-1999"), 36161);
174+// assertEquals(DATEVALUE("1-01-2222"), 117610);
175+// assertEquals(DATEVALUE("9-02-1902"), 976);
176+// assertEquals(DATEVALUE("9-2-1902"), 976);
177+// assertEquals(DATEVALUE("11-3-4243"), 856071);
178+// assertEquals(DATEVALUE(" 04-19-1992 "), 33713);
179+// assertEquals(DATEVALUE("5-20-1992"), 33744);
180+// assertEquals(DATEVALUE("6-21-1992"), 33776);
181+// assertEquals(DATEVALUE("9-29-1992"), 33876);
182+// assertEquals(DATEVALUE("1-24-1992"), 33627);
183+// assertEquals(DATEVALUE("12-21-1992"), 33959);
184+// assertEquals(DATEVALUE("01-31-1992"), 33634);
185+// assertEquals(DATEVALUE("1-13-1992"), 33616);
186
187
188