spreadsheet
typeScript/javascript spreadsheet parser, with formulas.
git clone https://git.vogt.world/spreadsheet.git
Log | Files | README.md
← Commit log
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