commit
message
Using DateRegExBuilder to capture MONTHDIG_DAY_YEAR, MM(fd)DD(fd)YYYY, '06/24/1992'
author
Ben Vogt <[email protected]>
date
2017-04-01 00:30:02
stats
3 file(s) changed,
86 insertions(+),
122 deletions(-)
files
src/RawFormulas/Date.ts
src/RawFormulas/Utils.ts
tests/DateFormulasTest.ts
1diff --git a/src/RawFormulas/Date.ts b/src/RawFormulas/Date.ts
2index e88136f..a9ae66b 100644
3--- a/src/RawFormulas/Date.ts
4+++ b/src/RawFormulas/Date.ts
5@@ -48,7 +48,11 @@ const YEAR_MONTHDIG_DAY = DateRegExBuilder.DateRegExBuilder()
6 .OPTIONAL_DAYNAME().OPTIONAL_COMMA().YYYY().FLEX_DELIMITER().MM().FLEX_DELIMITER().DD()
7 .end()
8 .build();
9-// const MONTHDIG_DAY_YEAR
10+const MONTHDIG_DAY_YEAR = DateRegExBuilder.DateRegExBuilder()
11+ .start()
12+ .OPTIONAL_DAYNAME().OPTIONAL_COMMA().MM().FLEX_DELIMITER().DD().FLEX_DELIMITER().YYYY14()
13+ .end()
14+ .build();
15 // const MONTHNAME_DAY_YEAR;
16 // const DAY_MONTHNAME_YEAR;
17 // const YEAR_MONTHDIG;
18@@ -71,9 +75,24 @@ var DATEVALUE = function (...values) : number {
19 var dateString = TypeCaster.firstValueAsString(values[0]);
20 var m;
21
22- // Check YEAR_MONTHDIG_DAY_SLASH_DELIMIT, YYYY/MM/DD, "1992/06/24"
23+ function createMoment(years, months, days) {
24+ var actualYear = years;
25+ if (years >= 0 && years < 30) {
26+ actualYear = Y2K_YEAR + years;
27+ } else if (years >= 30 && years < 100) {
28+ actualYear = FIRST_YEAR + years;
29+ }
30+ var tmpMoment = moment.utc([actualYear])
31+ .add(months, 'months');
32+ // If we're specifying more days than there are in this month
33+ if (days > tmpMoment.daysInMonth() - 1) {
34+ throw new CellError(VALUE_ERROR, "DATEVALUE parameter '" + dateString + "' cannot be parsed to date/time.");
35+ }
36+ return tmpMoment.add(days, 'days');
37+ }
38+
39+ // Check YEAR_MONTHDIG_DAY, YYYY(fd)MM(fd)DD, "1992/06/24"
40 if (m === undefined) {
41- // For reference: https://regex101.com/r/uusfi7/5
42 var matches = dateString.match(YEAR_MONTHDIG_DAY);
43 if (matches && matches.length === 8) {
44 // Check delimiters. If they're not the same, throw error.
45@@ -83,19 +102,22 @@ var DATEVALUE = function (...values) : number {
46 var years = parseInt(matches[3]);
47 var months = parseInt(matches[5]) - 1; // Months are zero indexed.
48 var days = parseInt(matches[7]) - 1; // Days are zero indexed.
49- var actualYear = years;
50- if (years >= 0 && years < 30) {
51- actualYear = Y2K_YEAR + years;
52- } else if (years >= 30 && years < 100) {
53- actualYear = FIRST_YEAR + years;
54- }
55- var tmpMoment = moment.utc([actualYear])
56- .add(months, 'months');
57- // If we're specifying more days than there are in this month
58- if (days > tmpMoment.daysInMonth() - 1) {
59+ m = createMoment(years, months, days);
60+ }
61+ }
62+
63+ // Check MONTHDIG_DAY_YEAR, MM(fd)DD(fd)YYYY, "06/24/1992"
64+ if (m === undefined) {
65+ var matches = dateString.match(MONTHDIG_DAY_YEAR);
66+ if (matches && matches.length === 8) {
67+ // Check delimiters. If they're not the same, throw error.
68+ if (matches[4].replace(/\s*/g, '') !== matches[6].replace(/\s*/g, '')) {
69 throw new CellError(VALUE_ERROR, "DATEVALUE parameter '" + dateString + "' cannot be parsed to date/time.");
70 }
71- m = tmpMoment.add(days, 'days');
72+ var years = parseInt(matches[7]);
73+ var months = parseInt(matches[3]) - 1; // Months are zero indexed.
74+ var days = parseInt(matches[5]) - 1; // Days are zero indexed.
75+ m = createMoment(years, months, days);
76 }
77 }
78
79diff --git a/src/RawFormulas/Utils.ts b/src/RawFormulas/Utils.ts
80index 3ef6196..3319aeb 100644
81--- a/src/RawFormulas/Utils.ts
82+++ b/src/RawFormulas/Utils.ts
83@@ -418,134 +418,66 @@ class DateRegExBuilder {
84 return this;
85 }
86
87- /**
88- *
89- * @returns {DateRegExBuilder}
90- * @constructor
91- */
92 OPTIONAL_DAYNAME() : DateRegExBuilder {
93 this.regexString += "(sunday|monday|tuesday|wednesday|thursday|friday|saturday|sun|mon|tue|wed|thu|fri|sat)?";
94 return this;
95 }
96
97- /**
98- *
99- * @returns {DateRegExBuilder}
100- * @constructor
101- */
102 OPTIONAL_COMMA() : DateRegExBuilder {
103 this.regexString += "(,?\\s+)?";
104 return this;
105 }
106
107- /**
108- * Adds month digit to the regular expression.
109- * @returns {DateRegExBuilder}
110- */
111 MM() : DateRegExBuilder {
112 this.regexString += "([1-9]|0[1-9]|1[0-2])";
113 return this;
114 }
115
116- /**
117- *
118- * @returns {DateRegExBuilder}
119- */
120 DD() : DateRegExBuilder {
121 this.regexString += "(0?[0-9]|1[0-9]|2[0-9]|3[0-1])";
122 return this;
123 }
124
125 YYYY() : DateRegExBuilder {
126- this.regexString += "([0-9][0-9][0-9][0-9]|[1-9][0-9][0-9])";
127+ this.regexString += "([0-9]{4}|[1-9][0-9][0-9])";
128 return this;
129 }
130
131- YYYY_SIMPLE() : DateRegExBuilder {
132- this.regexString += "([0-9]{4})";
133+ YYYY14() : DateRegExBuilder {
134+ this.regexString += "([0-9]{1,4})";
135 return this;
136 }
137
138- COMMON_DELIMITERS() : DateRegExBuilder {
139- this.regexString += "(,?\\s*|\\s*-?\\.?-?\\/?\\s*)";
140- return this;
141- }
142-
143-
144 FLEX_DELIMITER() : DateRegExBuilder {
145 this.regexString += "(,?\\s+|\\s*-?\\.?-?\\/?\\s*)";
146 return this;
147 }
148
149- /**
150- *
151- * @returns {DateRegExBuilder}
152- * @constructor
153- */
154- YY_OP_YY() : DateRegExBuilder {
155- this.regexString += "(([0-9][0-9][0-9][0-9])|([1-9][0-9][0-9])|[0-9]{0,3})";
156- return this;
157- }
158-
159- YYY_OR_YYYY() : DateRegExBuilder {
160- this.regexString += "([0-9]{4}|[1-9][0-9]{2}|[0-9]{2})";
161- return this;
162- }
163-
164- /**
165- *
166- * @returns {DateRegExBuilder}
167- * @constructor
168- */
169 HH(): DateRegExBuilder {
170 this.regexString += "([0-9]|0[0-9]|1[0-2])";
171 return this;
172 }
173
174- /**
175- *
176- * @returns {DateRegExBuilder}
177- * @constructor
178- */
179 MERIDIEM(): DateRegExBuilder {
180 this.regexString += "(am|pm)";
181 return this;
182 }
183
184- /**
185- *
186- * @returns {DateRegExBuilder}
187- * @constructor
188- */
189 OVERLOAD_MINITES() : DateRegExBuilder {
190 this.regexString += "([0-9]{2,})";
191 return this;
192 }
193
194- /**
195- *
196- * @returns {DateRegExBuilder}
197- * @constructor
198- */
199 OVERLOAD_HH() : DateRegExBuilder {
200 this.regexString += "([0-9]{1,})";
201 return this;
202 }
203
204- /**
205- *
206- * @returns {DateRegExBuilder}
207- * @constructor
208- */
209 SEMICOLON(): DateRegExBuilder {
210 this.regexString += ":" + DateRegExBuilder.ZERO_OR_MORE_SPACES;
211 return this;
212 }
213
214- /**
215- * Builds the regular expression.
216- * @returns {RegExp} Built up string as a regular expression.
217- */
218 build() : RegExp {
219 // Always ignore case.
220 return new RegExp(this.regexString, 'i');
221diff --git a/tests/DateFormulasTest.ts b/tests/DateFormulasTest.ts
222index 1bc8f67..e45db40 100644
223--- a/tests/DateFormulasTest.ts
224+++ b/tests/DateFormulasTest.ts
225@@ -45,45 +45,53 @@ assertEquals(DATE(-1900, 1, 1).toNumber(), 2);
226
227 // Test DATEVALUE
228 // MM/DD/YYYY
229-// assertEquals(DATEVALUE("6/24/92"), 33779);
230-// assertEquals(DATEVALUE("6/24/1992"), 33779);
231-// assertEquals(DATEVALUE("06/24/1992"), 33779);
232-// assertEquals(DATEVALUE("1/01/1999"), 36161);
233-// assertEquals(DATEVALUE("1/01/99"), 36161);
234-// assertEquals(DATEVALUE("1/01/2222"), 117610);
235-// assertEquals(DATEVALUE("9/02/1902"), 976);
236-// assertEquals(DATEVALUE("9/2/1902"), 976);
237-// assertEquals(DATEVALUE("11/3/4243"), 856071);
238-// assertEquals(DATEVALUE(" 04/19/1992 "), 33713);
239-// assertEquals(DATEVALUE("5/20/1992"), 33744);
240-// assertEquals(DATEVALUE("6/21/1992"), 33776);
241-// assertEquals(DATEVALUE("9/29/1992"), 33876);
242-// assertEquals(DATEVALUE("1/24/1992"), 33627);
243-// assertEquals(DATEVALUE("12/21/1992"), 33959);
244-// assertEquals(DATEVALUE("01/31/1992"), 33634);
245-// assertEquals(DATEVALUE("1/13/1992"), 33616);
246-// assertEquals(DATEVALUE("2/29/2004"), 38046);
247-// assertEquals(DATEVALUE("2/28/2004"), 38045);
248-// assertEquals(DATEVALUE("2/28/004"), 38045);
249-// assertEquals(DATEVALUE("2/28/04"), 38045);
250-// assertEquals(DATEVALUE("2/28/4"), 38045);
251-// assertEquals(DATEVALUE("1/13/1999"), 36173);
252-// assertEquals(DATEVALUE("01/13/1999"), 36173);
253-// assertEquals(DATEVALUE("01/13/0999"), -329069);
254-// assertEquals(DATEVALUE("01/13/1200"), -255656);
255-// assertEquals(DATEVALUE("01/13/0029"), 47131);
256-// assertEquals(DATEVALUE("01/13/0030"), 10971);
257-// assertEquals(DATEVALUE("01/13/0044"), 16084);
258-// assertEquals(DATEVALUE("01/13/0050"), 18276);
259-// assertEquals(DATEVALUE("01/13/0097"), 35443);
260-// assertEquals(DATEVALUE("01/13/0099"), 36173);
261-// assertEquals(DATEVALUE("01/13/0000"), 36538);
262-// assertEquals(DATEVALUE("01/13/0101"), -657057);
263-// assertEquals(DATEVALUE("01/13/0100"), -657422);
264-// assertEquals(DATEVALUE("12/31/100"), -657070);
265-// assertEquals(DATEVALUE("11/10/122"), -649086);
266-// assertEquals(DATEVALUE("1/22/2222"), 117631);
267-// assertEquals(DATEVALUE("1/22/222"), -612854);
268+assertEquals(DATEVALUE("6/24/92"), 33779);
269+assertEquals(DATEVALUE("6/24/1992"), 33779);
270+assertEquals(DATEVALUE("06/24/1992"), 33779);
271+assertEquals(DATEVALUE("1/01/1999"), 36161);
272+assertEquals(DATEVALUE("1/01/99"), 36161);
273+assertEquals(DATEVALUE("1/01/2222"), 117610);
274+assertEquals(DATEVALUE("9/02/1902"), 976);
275+assertEquals(DATEVALUE("9/2/1902"), 976);
276+assertEquals(DATEVALUE("11/3/4243"), 856071);
277+assertEquals(DATEVALUE(" 04/19/1992 "), 33713);
278+assertEquals(DATEVALUE("5/20/1992"), 33744);
279+assertEquals(DATEVALUE("6/21/1992"), 33776);
280+assertEquals(DATEVALUE("9/29/1992"), 33876);
281+assertEquals(DATEVALUE("1/24/1992"), 33627);
282+assertEquals(DATEVALUE("12/21/1992"), 33959);
283+assertEquals(DATEVALUE("01/31/1992"), 33634);
284+assertEquals(DATEVALUE("1/13/1992"), 33616);
285+assertEquals(DATEVALUE("2/29/2004"), 38046);
286+assertEquals(DATEVALUE("2/28/2004"), 38045);
287+assertEquals(DATEVALUE("2/28/004"), 38045);
288+assertEquals(DATEVALUE("2/28/04"), 38045);
289+assertEquals(DATEVALUE("2/28/4"), 38045);
290+assertEquals(DATEVALUE("1/13/1999"), 36173);
291+assertEquals(DATEVALUE("01/13/1999"), 36173);
292+assertEquals(DATEVALUE("01/13/0999"), -329069);
293+assertEquals(DATEVALUE("01/13/1200"), -255656);
294+assertEquals(DATEVALUE("01/13/0029"), 47131);
295+assertEquals(DATEVALUE("01/13/0030"), 10971);
296+assertEquals(DATEVALUE("01/13/0044"), 16084);
297+assertEquals(DATEVALUE("01/13/0050"), 18276);
298+assertEquals(DATEVALUE("01/13/0097"), 35443);
299+assertEquals(DATEVALUE("01/13/0099"), 36173);
300+assertEquals(DATEVALUE("01/13/0000"), 36538);
301+assertEquals(DATEVALUE("01/13/0101"), -657057);
302+assertEquals(DATEVALUE("01/13/0100"), -657422);
303+assertEquals(DATEVALUE("12/31/100"), -657070);
304+assertEquals(DATEVALUE("11/10/122"), -649086);
305+assertEquals(DATEVALUE("1/22/2222"), 117631);
306+assertEquals(DATEVALUE("1/22/222"), -612854);
307+// MM/DD/YYYY delimiter tests
308+assertEquals(DATEVALUE("6-24-92"), 33779);
309+assertEquals(DATEVALUE("6/24/92"), 33779);
310+assertEquals(DATEVALUE("6 24 92"), 33779);
311+assertEquals(DATEVALUE("6.24.92"), 33779);
312+assertEquals(DATEVALUE("6 . 24 . 92"), 33779);
313+assertEquals(DATEVALUE("6 / 24 / 92"), 33779);
314+assertEquals(DATEVALUE("6, 24, 92"), 33779);
315 // YYYY/MM/DD
316 assertEquals(DATEVALUE("1992/6/24"), 33779);
317 assertEquals(DATEVALUE("1992/06/24"), 33779);
318@@ -135,6 +143,14 @@ assertEquals(DATEVALUE("Thu 1992/6/24"), 33779);
319 assertEquals(DATEVALUE("Fri 1992/6/24"), 33779);
320 assertEquals(DATEVALUE("Sat 1992/6/24"), 33779);
321 assertEquals(DATEVALUE("Sunday, 1992/6/24"), 33779);
322+// YYYY/MM/DD delimiter tests
323+assertEquals(DATEVALUE("1992-6-24"), 33779);
324+assertEquals(DATEVALUE("1992/6/24"), 33779);
325+assertEquals(DATEVALUE("1992 6 24"), 33779);
326+assertEquals(DATEVALUE("1992 6 24"), 33779);
327+assertEquals(DATEVALUE("1992 . 6 . 24"), 33779);
328+assertEquals(DATEVALUE("1992 / 6 / 24"), 33779);
329+assertEquals(DATEVALUE("1992, 6, 24"), 33779);
330 catchAndAssertEquals(function() {
331 DATEVALUE("Sunday,1992/6/24");// flex delimiter should not allow a comma without a space after it.
332 }, ERRORS.VALUE_ERROR);