spreadsheet
typeScript/javascript spreadsheet parser, with formulas.
git clone https://git.vogt.world/spreadsheet.git
Log | Files | README.md
← Commit log
commit
message
parsing YYYY/MM/DD HH:mm:ss values for DATEVALUE
author
Ben Vogt <[email protected]>
date
2017-03-14 01:47:59
stats
3 file(s) changed, 142 insertions(+), 9 deletions(-)
files
README.md
src/RawFormulas/Date.ts
tests/FormulasTest.ts
  1diff --git a/README.md b/README.md
  2index 606eaca..f9dff09 100644
  3--- a/README.md
  4+++ b/README.md
  5@@ -62,4 +62,6 @@ Right now we're just using the number of days since 1900, but we should check th
  6 
  7 #### YYYY/MM/DD HH:mm needs more thurough testing
  8 
  9-#### Verify that all white-space wild cards are implemented properly
 10\ No newline at end of file
 11+#### Verify that all white-space wild cards are implemented properly
 12+
 13+#### Verify that all N-times ({2,9}) are correct, and we're not parsing numbers too big.
 14\ No newline at end of file
 15diff --git a/src/RawFormulas/Date.ts b/src/RawFormulas/Date.ts
 16index c9e1c25..9631be1 100644
 17--- a/src/RawFormulas/Date.ts
 18+++ b/src/RawFormulas/Date.ts
 19@@ -52,8 +52,8 @@ var DATE = function (...values) {
 20  * "1999/1/13 10am"         DONE
 21  * "1999/1/13 10:22"        DONE
 22  * "1999/1/13 10:10am"      DONE
 23- * "1999/1/13 10:10:10"
 24- * "1999/1/13 10:10:10pm"
 25+ * "1999/1/13 10:10:10"     DONE
 26+ * "1999/1/13 10:10:10pm"   DONE
 27  * "Sun Feb 09 2017"
 28  * "09 Feb 2017"
 29  * "Feb-2017"
 30@@ -78,7 +78,7 @@ var DATEVALUE = function (...values) : number {
 31     if (matches && matches.length === 6) {
 32       var years = parseInt(matches[1]);
 33       var months = parseInt(matches[4]) - 1; // Months are zero indexed.
 34-      var days = parseInt(matches[5]) - 1;// Days are zero indexed.
 35+      var days = parseInt(matches[5]) - 1; // Days are zero indexed.
 36       var actualYear = years;
 37       if (years >= 0 && years < 30) {
 38         actualYear = Y2K_YEAR + years;
 39@@ -126,7 +126,7 @@ var DATEVALUE = function (...values) : number {
 40     if (matches && matches.length === 8) {
 41       var years = parseInt(matches[1]);
 42       var months = parseInt(matches[4]) - 1; // Months are zero indexed.
 43-      var days = parseInt(matches[5]) - 1;// Days are zero indexed.
 44+      var days = parseInt(matches[5]) - 1; // Days are zero indexed.
 45       var actualYear = years;
 46       if (years >= 0 && years < 30) {
 47         actualYear = Y2K_YEAR + years;
 48@@ -150,7 +150,7 @@ var DATEVALUE = function (...values) : number {
 49     if (matches && matches.length === 8) {
 50       var years = parseInt(matches[1]);
 51       var months = parseInt(matches[4]) - 1; // Months are zero indexed.
 52-      var days = parseInt(matches[5]) - 1;// Days are zero indexed.
 53+      var days = parseInt(matches[5]) - 1; // Days are zero indexed.
 54       var hours = parseInt(matches[6]);
 55       var minutes = parseInt(matches[7]);
 56       var actualYear = years;
 57@@ -176,7 +176,7 @@ var DATEVALUE = function (...values) : number {
 58     if (matches && matches.length === 9) {
 59       var years = parseInt(matches[1]);
 60       var months = parseInt(matches[4]) - 1; // Months are zero indexed.
 61-      var days = parseInt(matches[5]) - 1;// Days are zero indexed.
 62+      var days = parseInt(matches[5]) - 1; // Days are zero indexed.
 63       var hours = parseInt(matches[6]);
 64       var minutes = parseInt(matches[7]);
 65       var pm = matches[8].toLowerCase() === "pm";
 66@@ -194,9 +194,9 @@ var DATEVALUE = function (...values) : number {
 67       }
 68       tmpMoment.add({"days": days});
 69       if (pm) {
 70-        if (hours === 12) {
 71+        if (hours === 12) { // 12pm is just 0am
 72           tmpMoment.set('hours', hours);
 73-        } else {
 74+        } else { // eg: 4pm is 16
 75           tmpMoment.set('hours', 12 + hours);
 76         }
 77       } else {
 78@@ -210,12 +210,68 @@ var DATEVALUE = function (...values) : number {
 79 
 80   // Check YYYY/MM/DD HH:mm:ss
 81   if (m === undefined) {
 82-    // TODO: This.
 83+    // For reference: https://regex101.com/r/fYZcgP/5
 84+    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*([0-9]{1,9}):\s*([0-9]{1,9}):\s*([0-9]{1,9})\s*$/);
 85+    if (matches && matches.length === 9) {
 86+      var years = parseInt(matches[1]);
 87+      var months = parseInt(matches[4]) - 1; // Months are zero indexed.
 88+      var days = parseInt(matches[5]) - 1; // Days are zero indexed.
 89+      var hours = parseInt(matches[6]);
 90+      var minutes = parseInt(matches[7]);
 91+      var seconds = parseInt(matches[8]);
 92+      var actualYear = years;
 93+      if (years >= 0 && years < 30) {
 94+        actualYear = Y2K_YEAR + years;
 95+      } else if (years >= 30 && years < 100) {
 96+        actualYear = FIRST_YEAR + years;
 97+      }
 98+      var tmpMoment = moment.utc([actualYear])
 99+        .add({"months": months});
100+      if (days > tmpMoment.daysInMonth() - 1) {
101+        throw new CellError(VALUE_ERROR, "DATEVALUE parameter '" + dateString + "' cannot be parsed to date/time.");
102+      }
103+      m = tmpMoment.add({"days": days, "hours": hours, "minutes": minutes, "seconds": seconds}).set('hours', 0).set('minutes', 0);
104+    }
105   }
106 
107   // Check YYYY/MM/DD HH:mm:ss(am|pm)
108   if (m === undefined) {
109-    // TODO: This.
110+    // For reference: https://regex101.com/r/6zublm/3
111+    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*([0-9]|0[0-9]|1[0-2]):\s*([0-9]{1,9}):\s*([0-9]{1,9})\s*(am|pm)\s*$/i);
112+    if (matches && matches.length === 10) {
113+      var years = parseInt(matches[1]);
114+      var months = parseInt(matches[4]) - 1; // Months are zero indexed.
115+      var days = parseInt(matches[5]) - 1; // Days are zero indexed.
116+      var hours = parseInt(matches[6]);
117+      var minutes = parseInt(matches[7]);
118+      var seconds = parseInt(matches[8]);
119+      var pm = matches[9].toLowerCase() === "pm";
120+      var actualYear = years;
121+      if (years >= 0 && years < 30) {
122+        actualYear = Y2K_YEAR + years;
123+      } else if (years >= 30 && years < 100) {
124+        actualYear = FIRST_YEAR + years;
125+      }
126+      var tmpMoment = moment.utc([actualYear])
127+        .add({"months": months});
128+      // If we're specifying more days than there are in this month
129+      if (days > tmpMoment.daysInMonth() - 1) {
130+        throw new CellError(VALUE_ERROR, "DATEVALUE parameter '" + dateString + "' cannot be parsed to date/time.");
131+      }
132+      tmpMoment.add({"days": days});
133+      if (pm) {
134+        if (hours === 12) { // 12pm is just 0am
135+          tmpMoment.set('hours', hours);
136+        } else { // eg: 4pm is 16
137+          tmpMoment.set('hours', 12 + hours);
138+        }
139+      } else {
140+        if (hours !== 12) {
141+          tmpMoment.set('hours', hours);
142+        }
143+      }
144+      m = tmpMoment.add({"minutes": minutes, "seconds": seconds}).set('hours', 0).set('minutes', 0).set('seconds', 0);
145+    }
146   }
147 
148   // If we've not been able to parse the date by now, then we cannot parse it at all.
149diff --git a/tests/FormulasTest.ts b/tests/FormulasTest.ts
150index 7d9be9c..967b77b 100644
151--- a/tests/FormulasTest.ts
152+++ b/tests/FormulasTest.ts
153@@ -974,6 +974,78 @@ assertEquals(DATEVALUE("1992/1/13 12:100000000am"), 103060); // overload minutes
154 assertEquals(DATEVALUE("1992/1/13 12:0912347287am"), 667190); // overload minutes
155 assertEquals(DATEVALUE("1992/1/13 12:00000912347287am"), 667190); // overload minutes
156 assertEquals(DATEVALUE("1992/1/13 12:1989198298am"), 1415003); // overload minutes
157+// YYYY/MM/DD HH:mm:ss
158+assertEquals(DATEVALUE("1992/6/24 0:0:0"), 33779);
159+assertEquals(DATEVALUE("1992/6/24 0000:0000:0000"), 33779);
160+assertEquals(DATEVALUE("0000/01/13 3:33:999999999"), 48112); // overload seconds
161+assertEquals(DATEVALUE("1992/1/13 6:22222222:0"), 49048); // overload minutes
162+assertEquals(DATEVALUE("1992/1/13 12:912347287:10"), 667191); // overload minutes
163+assertEquals(DATEVALUE("1992/1/13 12:100000000:10"), 103060); // overload minutes
164+assertEquals(DATEVALUE("1992/1/13 23:720:10"), 33617); // overload minutes
165+assertEquals(DATEVALUE("1992/1/13 23:719:60"), 33617); // overload minutes, seconds
166+assertEquals(DATEVALUE("1992/6/24 24:00:00"), 33780); // overload hours
167+assertEquals(DATEVALUE("1999/1/01 200000000:999999999:923231312"), 9074624); // overload hours, minutes, seconds
168+assertEquals(DATEVALUE("  1992/04/19   12:  33: 11  "), 33713);
169+assertEquals(DATEVALUE("0000/01/13 3:33:33"), 36538);
170+assertEquals(DATEVALUE("4243/11/3 200000000:33:444"), 9189404);
171+// YYYY/MM/DD HH:mm:ss(am|pm)
172+assertEquals(DATEVALUE("1999/1/13 10:10:10pm"), 36173);
173+assertEquals(DATEVALUE("1992/6/24 0:0:0pm"), 33779);
174+assertEquals(DATEVALUE("1992/6/24 00:0000:0000pm"), 33779);
175+assertEquals(DATEVALUE("0000/01/13 3:33:999999999pm"), 48112); // overload seconds
176+assertEquals(DATEVALUE("1992/1/13 6:22222222:0pm"), 49048); // overload minutes
177+assertEquals(DATEVALUE("1992/1/13 12:912347287:10pm"), 667191); // overload minutes
178+assertEquals(DATEVALUE("1992/1/13 12:100000000:10pm"), 103060); // overload minutes
179+assertEquals(DATEVALUE("1992/6/24 00:00:00am"), 33779);
180+assertEquals(DATEVALUE("1992/06/24 01:44:00am "), 33779);
181+assertEquals(DATEVALUE("1999/1/01 02:59:00pm"), 36161);
182+assertEquals(DATEVALUE("2222/1/01 03:33:00pm"), 117610);
183+assertEquals(DATEVALUE("1902/9/02 12:33:00pm"), 976);
184+assertEquals(DATEVALUE("1902/9/2 12:33:00pm"), 976);
185+assertEquals(DATEVALUE("4243/11/3 12:33:00pm"), 856071);
186+assertEquals(DATEVALUE("  1992/04/19   12:  33:  00  pm   "), 33713);
187+assertEquals(DATEVALUE("1992/5/20 01:33:44am"), 33744);
188+assertEquals(DATEVALUE("1992/6/21  3:33:44pm"), 33776);
189+assertEquals(DATEVALUE("1992/9/29 3:33:44pm"), 33876);
190+assertEquals(DATEVALUE("1992/1/24 3:33:44pm"), 33627);
191+assertEquals(DATEVALUE("1992/12/21 3:33:44pm"), 33959);
192+assertEquals(DATEVALUE("1992/01/31 3:33:44pm"), 33634);
193+assertEquals(DATEVALUE("1992/1/13 3:33:44pm"), 33616);
194+assertEquals(DATEVALUE("2004/2/29 3:33:44pm"), 38046);
195+assertEquals(DATEVALUE("2004/2/28  3:33:44pm "), 38045);
196+assertEquals(DATEVALUE("1999/1/13 3:33:44pm"), 36173);
197+assertEquals(DATEVALUE("1999/01/13 3:33:44pm"), 36173);
198+assertEquals(DATEVALUE("0999/01/13 3:33:44pm"), -329069);
199+assertEquals(DATEVALUE("1200/01/13 3:33:44pm"), -255656);
200+assertEquals(DATEVALUE("0029/01/13 3:33:44pm"), 47131);
201+assertEquals(DATEVALUE("0030/01/13 3:33:44pm"), 10971);
202+assertEquals(DATEVALUE("0044/01/13 3:33:44pm"), 16084);
203+assertEquals(DATEVALUE("0050/01/13 3:33:44pm"), 18276);
204+assertEquals(DATEVALUE("0097/01/13 00:33:44pm"), 35443);
205+assertEquals(DATEVALUE("0099/01/13 3:33:44pm"), 36173);
206+assertEquals(DATEVALUE("0000/01/13 3:33:44pm"), 36538);
207+assertEquals(DATEVALUE("0101/01/13 3:33:44pm"), -657057);
208+assertEquals(DATEVALUE("0100/01/13 3:33:44pm"), -657422);
209+assertEquals(DATEVALUE("100/12/31 3:33:44pm"), -657070);
210+assertEquals(DATEVALUE("122/11/10 3:33:44pm"), -649086);
211+assertEquals(DATEVALUE("2222/1/22 3:33:44pm"), 117631);
212+assertEquals(DATEVALUE("222/1/22 3:33:44pm"), -612854);
213+assertEquals(DATEVALUE("1992/1/13 6:22222222:44am"), 49048); // overload minutes
214+assertEquals(DATEVALUE("1992/1/13 12:720:00pm"), 33617); // overload minutes
215+assertEquals(DATEVALUE("1992/1/13 00:720:00pm"), 33617); // overload minutes
216+assertEquals(DATEVALUE("1992/1/13 12:719:00pm"), 33616); // overload minutes
217+assertEquals(DATEVALUE("1992/1/13 00:720:00am"), 33616); // overload minutes
218+assertEquals(DATEVALUE("1992/1/13 12:719:60pm"), 33617); // overload minutes
219+assertEquals(DATEVALUE("1992/1/13 00:720:00am"), 33616); // overload minutes
220+assertEquals(DATEVALUE("1992/1/13 00:01:00pm"), 33616); // overload minutes
221+assertEquals(DATEVALUE("1992/1/13 12:66669:00pm"), 33662); // overload minutes
222+assertEquals(DATEVALUE("1992/1/13 12:66669:00am"), 33662); // overload minutes
223+assertEquals(DATEVALUE("1992/1/13 12:66249:00pm"), 33662); // overload minutes
224+assertEquals(DATEVALUE("1992/1/13 12:66249:00am"), 33662); // overload minutes
225+assertEquals(DATEVALUE("1992/1/13 12:666669:00am"), 34078); // overload minutes
226+assertEquals(DATEVALUE("1992/1/13 12:666669:00pm"), 34079); // overload minutes
227+assertEquals(DATEVALUE("1992/1/13 12:100000000:00am"), 103060); // overload minutes
228+assertEquals(DATEVALUE("1992/1/13 12:912347287:00am"), 667190); // overload minutes
229 
230 
231