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