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