name:
dist/Utilities/TypeConverter.js
-rw-r--r--
32168
1"use strict";
2exports.__esModule = true;
3var moment = require("moment");
4var Errors_1 = require("../Errors");
5var DateRegExBuilder_1 = require("./DateRegExBuilder");
6var Cell_1 = require("../Cell");
7var MONTHDIG_DAYDIG = DateRegExBuilder_1.DateRegExBuilder.DateRegExBuilder()
8 .start()
9 .MM().FLEX_DELIMITER().DD_W_SPACE().OPTIONAL_TIMESTAMP_CAPTURE_GROUP()
10 .end()
11 .build();
12var YEAR_MONTHDIG_DAY = DateRegExBuilder_1.DateRegExBuilder.DateRegExBuilder()
13 .start()
14 .OPTIONAL_DAYNAME().OPTIONAL_COMMA().YYYY().FLEX_DELIMITER_LOOSEDOT().MM().FLEX_DELIMITER_LOOSEDOT().DD_W_SPACE().OPTIONAL_TIMESTAMP_CAPTURE_GROUP()
15 .end()
16 .build();
17var MONTHDIG_DAY_YEAR = DateRegExBuilder_1.DateRegExBuilder.DateRegExBuilder()
18 .start()
19 .OPTIONAL_DAYNAME().OPTIONAL_COMMA().MM().FLEX_DELIMITER_LOOSEDOT().DD().FLEX_DELIMITER_LOOSEDOT().YYYY14_W_SPACE().OPTIONAL_TIMESTAMP_CAPTURE_GROUP()
20 .end()
21 .build();
22var DAY_MONTHNAME_YEAR = DateRegExBuilder_1.DateRegExBuilder.DateRegExBuilder()
23 .start()
24 .OPTIONAL_DAYNAME().OPTIONAL_COMMA().DD().FLEX_DELIMITER_LOOSEDOT().MONTHNAME().FLEX_DELIMITER_LOOSEDOT().YYYY14_W_SPACE().OPTIONAL_TIMESTAMP_CAPTURE_GROUP()
25 .end()
26 .build();
27var MONTHNAME_DAY_YEAR = DateRegExBuilder_1.DateRegExBuilder.DateRegExBuilder()
28 .start()
29 .OPTIONAL_DAYNAME().OPTIONAL_COMMA().MONTHNAME().FLEX_DELIMITER_LOOSEDOT().DD().FLEX_DELIMITER_LOOSEDOT().YYYY14_W_SPACE().OPTIONAL_TIMESTAMP_CAPTURE_GROUP()
30 .end()
31 .build();
32var YEAR_MONTHDIG = DateRegExBuilder_1.DateRegExBuilder.DateRegExBuilder()
33 .start()
34 .OPTIONAL_DAYNAME().OPTIONAL_COMMA().YYYY14().FLEX_DELIMITER().MM_W_SPACE().OPTIONAL_TIMESTAMP_CAPTURE_GROUP()
35 .end()
36 .build();
37var MONTHDIG_YEAR = DateRegExBuilder_1.DateRegExBuilder.DateRegExBuilder()
38 .start()
39 .OPTIONAL_DAYNAME().OPTIONAL_COMMA().MM().FLEX_DELIMITER().YYYY14_W_SPACE().OPTIONAL_TIMESTAMP_CAPTURE_GROUP()
40 .end()
41 .build();
42var YEAR_MONTHNAME = DateRegExBuilder_1.DateRegExBuilder.DateRegExBuilder()
43 .start()
44 .OPTIONAL_DAYNAME().OPTIONAL_COMMA().YYYY14().FLEX_DELIMITER().MONTHNAME_W_SPACE().OPTIONAL_TIMESTAMP_CAPTURE_GROUP()
45 .end()
46 .build();
47var MONTHNAME_YEAR = DateRegExBuilder_1.DateRegExBuilder.DateRegExBuilder()
48 .start()
49 .OPTIONAL_DAYNAME().OPTIONAL_COMMA().MONTHNAME().FLEX_DELIMITER().YYYY2_OR_4_W_SPACE().OPTIONAL_TIMESTAMP_CAPTURE_GROUP()
50 .end()
51 .build();
52// For reference: https://regex101.com/r/47GARA/1/
53var TIMESTAMP = DateRegExBuilder_1.DateRegExBuilder.DateRegExBuilder()
54 .start()
55 .TIMESTAMP_UNITS_CAPTURE_GROUP()
56 .end()
57 .build();
58// The first year to use when calculating the number of days in a date
59var FIRST_YEAR = 1900;
60// The year 2000.
61var Y2K_YEAR = 2000;
62function isUndefined(x) {
63 return x === undefined;
64}
65function isDefined(x) {
66 return x !== undefined;
67}
68/**
69 * Matches a timestamp string, adding the units to the moment passed in.
70 * @param timestampString to parse. ok formats: "10am", "10:10", "10:10am", "10:10:10", "10:10:10am", etc.
71 * @param momentToMutate to mutate
72 * @returns {Moment} mutated and altered.
73 */
74function matchTimestampAndMutateMoment(timestampString, momentToMutate) {
75 var matches = timestampString.match(TIMESTAMP);
76 if (matches && matches[1] !== undefined) { // 10am
77 var hours = parseInt(matches[2]);
78 if (hours > 12) {
79 throw new Error();
80 }
81 momentToMutate.add(hours, 'hours');
82 }
83 else if (matches && matches[6] !== undefined) { // 10:10
84 var hours = parseInt(matches[7]);
85 var minutes = parseInt(matches[8]);
86 momentToMutate.add(hours, 'hours').add(minutes, 'minutes');
87 }
88 else if (matches && matches[11] !== undefined) { // 10:10am
89 var hours = parseInt(matches[13]);
90 var minutes = parseInt(matches[14]);
91 var pmTrue = (matches[16].toLowerCase() === "pm");
92 if (hours > 12) {
93 throw new Error();
94 }
95 if (pmTrue) {
96 // 12pm is just 0am, 4pm is 16, etc.
97 momentToMutate.set('hours', hours === 12 ? hours : 12 + hours);
98 }
99 else {
100 if (hours !== 12) {
101 momentToMutate.set('hours', hours);
102 }
103 }
104 momentToMutate.add(minutes, 'minutes');
105 }
106 else if (matches && matches[17] !== undefined) { // 10:10:10
107 var hours = parseInt(matches[19]);
108 var minutes = parseInt(matches[20]);
109 var seconds = parseInt(matches[21]);
110 momentToMutate.add(hours, 'hours').add(minutes, 'minutes').add(seconds, 'seconds');
111 }
112 else if (matches && matches[23] !== undefined) { // // 10:10:10am
113 var hours = parseInt(matches[25]);
114 var minutes = parseInt(matches[26]);
115 var seconds = parseInt(matches[27]);
116 var pmTrue = (matches[28].toLowerCase() === "pm");
117 if (hours > 12) {
118 throw new Error();
119 }
120 if (pmTrue) {
121 // 12pm is just 0am, 4pm is 16, etc.
122 momentToMutate.set('hours', hours === 12 ? hours : 12 + hours);
123 }
124 else {
125 if (hours !== 12) {
126 momentToMutate.set('hours', hours);
127 }
128 }
129 momentToMutate.add(minutes, 'minutes').add(seconds, 'seconds');
130 }
131 else {
132 throw new Error();
133 }
134 return momentToMutate;
135}
136/**
137 * Static class of helpers used to convert let ious types to each other.
138 */
139var TypeConverter = /** @class */ (function () {
140 function TypeConverter() {
141 }
142 /**
143 * Converts a datetime string to a moment object. Will return undefined if the string can't be converted.
144 * @param {string} timeString - string to parse and convert.
145 * @returns {moment.Moment}
146 */
147 TypeConverter.stringToMoment = function (timeString) {
148 var m = TypeConverter.parseStringToMoment(timeString);
149 if (m === undefined || !m.isValid()) {
150 return undefined;
151 }
152 return m;
153 };
154 /**
155 * Converts a time-formatted string to a number between 0 and 1, exclusive on 1.
156 * @param timeString
157 * @returns {number} representing time of day
158 */
159 TypeConverter.stringToTimeNumber = function (timeString) {
160 var m;
161 try {
162 m = matchTimestampAndMutateMoment(timeString, moment.utc([FIRST_YEAR]).startOf("year"));
163 }
164 catch (e) {
165 m = TypeConverter.parseStringToMoment(timeString);
166 if (m === undefined || !m.isValid()) {
167 throw new Error();
168 }
169 }
170 // If the parsing didn't work, try parsing as timestring alone
171 return (3600 * m.hours() + 60 * m.minutes() + m.seconds()) / 86400;
172 };
173 /**
174 * Parses a string returning a moment that is either valid, invalid or undefined.
175 * @param dateString to parse.
176 * @returns {moment}
177 */
178 TypeConverter.parseStringToMoment = function (dateString) {
179 var m;
180 /**
181 * Creates moment object from years, months and days.
182 * @param years of moment
183 * @param months of moment in number or string format (eg: January)
184 * @param days of moment
185 * @returns {Moment} created moment
186 */
187 function createMoment(years, months, days) {
188 var actualYear = years;
189 if (years >= 0 && years < 30) {
190 actualYear = Y2K_YEAR + years;
191 }
192 else if (years >= 30 && years < 100) {
193 actualYear = FIRST_YEAR + years;
194 }
195 var tmpMoment = moment.utc([actualYear]).startOf("year");
196 if (typeof months === "string") {
197 tmpMoment.month(months);
198 }
199 else {
200 tmpMoment.set("months", months);
201 }
202 // If we're specifying more days than there are in this month
203 if (days > tmpMoment.daysInMonth() - 1) {
204 throw new Error();
205 }
206 return tmpMoment.add(days, 'days');
207 }
208 // Check MONTHDIG_DAYDIG, MM(fd)DD, '01/06'
209 // NOTE: Must come before YEAR_MONTHDIG matching.
210 if (m === undefined) {
211 var matches = dateString.match(MONTHDIG_DAYDIG);
212 if (matches && matches.length >= 10) {
213 var months = parseInt(matches[1]) - 1; // Months are zero indexed.
214 var days = parseInt(matches[3]) - 1; // Days are zero indexed.
215 var tmpMoment = createMoment(moment.utc().get("years"), months, days);
216 if (matches[8] !== undefined) {
217 tmpMoment = matchTimestampAndMutateMoment(matches[8], tmpMoment);
218 }
219 m = tmpMoment;
220 }
221 }
222 // Check YEAR_MONTHDIG, YYYY(fd)MM, '1992/06'
223 // NOTE: Must come before YEAR_MONTHDIG_DAY matching.
224 if (m === undefined) {
225 var matches = dateString.match(YEAR_MONTHDIG);
226 if (matches && matches.length >= 6) {
227 var years = parseInt(matches[3]);
228 var months = parseInt(matches[5]) - 1; // Months are zero indexed.
229 var tmpMoment = createMoment(years, months, 0);
230 if (matches[6] !== undefined) {
231 tmpMoment = matchTimestampAndMutateMoment(matches[6], tmpMoment);
232 }
233 m = tmpMoment;
234 }
235 }
236 // Check YEAR_MONTHDIG_DAY, YYYY(fd)MM(fd)DD, "1992/06/24"
237 if (m === undefined) {
238 var matches = dateString.match(YEAR_MONTHDIG_DAY);
239 if (matches && matches.length >= 8) {
240 // Check delimiters. If they're not the same, throw error.
241 if (matches[4].replace(/\s*/g, '') !== matches[6].replace(/\s*/g, '')) {
242 throw new Error();
243 }
244 var years = parseInt(matches[3]);
245 var months = parseInt(matches[5]) - 1; // Months are zero indexed.
246 var days = parseInt(matches[7]) - 1; // Days are zero indexed.
247 var tmpMoment = createMoment(years, months, days);
248 if (matches.length >= 9 && matches[8] !== undefined) {
249 tmpMoment = matchTimestampAndMutateMoment(matches[8], tmpMoment);
250 }
251 m = tmpMoment;
252 }
253 }
254 // Check MONTHDIG_YEAR, MM(fd)YYYY, '06/1992'
255 // NOTE: Must come before MONTHDIG_DAY_YEAR matching.
256 if (m === undefined) {
257 var matches = dateString.match(MONTHDIG_YEAR);
258 if (matches && matches.length >= 6) {
259 var years = parseInt(matches[5]);
260 var months = parseInt(matches[3]) - 1; // Months are zero indexed.
261 var tmpMoment = createMoment(years, months, 0);
262 if (matches[6] !== undefined) {
263 tmpMoment = matchTimestampAndMutateMoment(matches[6], tmpMoment);
264 }
265 m = tmpMoment;
266 }
267 }
268 // Check MONTHDIG_DAY_YEAR, MM(fd)DD(fd)YYYY, "06/24/1992"
269 if (m === undefined) {
270 var matches = dateString.match(MONTHDIG_DAY_YEAR);
271 if (matches && matches.length >= 8) {
272 // Check delimiters. If they're not the same, throw error.
273 if (matches[4].replace(/\s*/g, '') !== matches[6].replace(/\s*/g, '')) {
274 throw new Error();
275 }
276 var years = parseInt(matches[7]);
277 var months = parseInt(matches[3]) - 1; // Months are zero indexed.
278 var days = parseInt(matches[5]) - 1; // Days are zero indexed.
279 var tmpMoment = createMoment(years, months, days);
280 if (matches.length >= 9 && matches[8] !== undefined) {
281 tmpMoment = matchTimestampAndMutateMoment(matches[8], tmpMoment);
282 }
283 m = tmpMoment;
284 }
285 }
286 // Check MONTHNAME_YEAR, Month(fd)YYYY, 'Aug 1992'
287 // NOTE: Needs to come before DAY_MONTHNAME_YEAR matching.
288 if (m === undefined) {
289 var matches = dateString.match(MONTHNAME_YEAR);
290 if (matches && matches.length >= 6) {
291 var years = parseInt(matches[5]);
292 var monthName = matches[3];
293 var tmpMoment = createMoment(years, monthName, 0);
294 if (matches[6] !== undefined) {
295 tmpMoment = matchTimestampAndMutateMoment(matches[6], tmpMoment);
296 }
297 m = tmpMoment;
298 }
299 }
300 // Check MONTHNAME_DAY_YEAR, Month(fd)DD(fd)YYYY, 'Aug 19 2020'
301 if (m === undefined) {
302 var matches = dateString.match(MONTHNAME_DAY_YEAR);
303 if (matches && matches.length >= 8) {
304 // Check delimiters. If they're not the same, throw error.
305 if (matches[4].replace(/\s*/g, '') !== matches[6].replace(/\s*/g, '')) {
306 throw new Error();
307 }
308 var years = parseInt(matches[7]);
309 var monthName = matches[3];
310 var days = parseInt(matches[5]) - 1; // Days are zero indexed.
311 var tmpMoment = createMoment(years, monthName, days);
312 if (matches.length >= 9 && matches[8] !== undefined) {
313 tmpMoment = matchTimestampAndMutateMoment(matches[8], tmpMoment);
314 }
315 m = tmpMoment;
316 }
317 }
318 // Check DAY_MONTHNAME_YEAR, DD(fd)Month(fd)YYYY, '24/July/1992'
319 if (m === undefined) {
320 var matches = dateString.match(DAY_MONTHNAME_YEAR);
321 if (matches && matches.length >= 8) {
322 var years = parseInt(matches[7]);
323 var monthName = matches[5];
324 var days = parseInt(matches[3]) - 1; // Days are zero indexed.
325 var firstDelimiter = matches[4].replace(/\s*/g, '');
326 var secondDelimiter = matches[6].replace(/\s*/g, '');
327 // Check delimiters. If they're not the same, and the first one isn't a space, throw error.
328 if (firstDelimiter !== secondDelimiter && firstDelimiter !== "") {
329 throw new Error();
330 }
331 var tmpMoment = createMoment(years, monthName, days);
332 if (matches.length >= 9 && matches[8] !== undefined) {
333 tmpMoment = matchTimestampAndMutateMoment(matches[8], tmpMoment);
334 }
335 m = tmpMoment;
336 }
337 }
338 // Check YEAR_MONTHNAME, YYYY(fd)Month, '1992/Aug'
339 if (m === undefined) {
340 var matches = dateString.match(YEAR_MONTHNAME);
341 if (matches && matches.length >= 6) {
342 var years = parseInt(matches[3]);
343 var monthName = matches[5];
344 var tmpMoment = createMoment(years, monthName, 0);
345 if (matches[6] !== undefined) {
346 tmpMoment = matchTimestampAndMutateMoment(matches[6], tmpMoment);
347 }
348 m = tmpMoment;
349 }
350 }
351 return m;
352 };
353 /**
354 * Parses a string as a date number. Throws error if parsing not possible.
355 * @param dateString to parse
356 * @returns {number} resulting date
357 */
358 TypeConverter.stringToDateNumber = function (dateString) {
359 // m will be set and valid or invalid, or will remain undefined
360 var m;
361 try {
362 m = TypeConverter.parseStringToMoment(dateString);
363 }
364 catch (e) {
365 throw new Errors_1.ValueError("DATEVALUE parameter '" + dateString + "' cannot be parsed to date/time.");
366 }
367 if (m === undefined || !m.isValid()) {
368 throw new Errors_1.ValueError("DATEVALUE parameter '" + dateString + "' cannot be parsed to date/time.");
369 }
370 return TypeConverter.momentToDayNumber(m.set('hours', 0).set('minutes', 0).set('seconds', 0));
371 };
372 /**
373 * Converts strings to numbers, returning undefined if string cannot be parsed to number. Examples: "100", "342424",
374 * "10%", "33.213131", "41.1231", "10e+1", "10E-1", "10.44E1", "-$9.29", "+$9.29", "1,000.1", "2000,000,000".
375 * For reference see: https://regex101.com/r/PwghnF/9/
376 * @param value to parse.
377 * @returns {number} or undefined
378 */
379 TypeConverter.stringToNumber = function (value) {
380 var NUMBER_REGEX = /^ *([\+/-])? *(\$)? *([\+/-])? *((\d+)?(,\d{3})?(,\d{3})?(,\d{3})?(,\d{3})?)? *(\.)? *(\d*)? *(e|E)? *([\+/-])? *(\d*)? *(%)? *$/;
381 var matches = value.match(NUMBER_REGEX);
382 if (matches !== null) {
383 var firstSign = matches[1];
384 var currency = matches[2];
385 var secondSign = matches[3];
386 var wholeNumberWithCommas = matches[4];
387 var decimalPoint = matches[10];
388 var decimalNumber = matches[11];
389 var sciNotation = matches[12];
390 var sciNotationSign = matches[13];
391 var sciNotationFactor = matches[14];
392 var percentageSign = matches[15];
393 // Number is not valid if it is a currency and in scientific notation.
394 if (isDefined(currency) && isDefined(sciNotation)) {
395 return;
396 }
397 // Number is not valid if there are two signs.
398 if (isDefined(firstSign) && isDefined(secondSign)) {
399 return;
400 }
401 // Number is not valid if we have 'sciNotation' but no 'sciNotationFactor'
402 if (isDefined(sciNotation) && isUndefined(sciNotationFactor)) {
403 return;
404 }
405 var activeSign = void 0;
406 if (isUndefined(firstSign) && isUndefined(secondSign)) {
407 activeSign = "+";
408 }
409 else if (!isUndefined(firstSign)) {
410 activeSign = firstSign;
411 }
412 else {
413 activeSign = secondSign;
414 }
415 var x = void 0;
416 if (isDefined(wholeNumberWithCommas)) {
417 if (isDefined(decimalNumber) && isDefined(decimalNumber)) {
418 x = parseFloat(activeSign + wholeNumberWithCommas.split(",").join("") + decimalPoint + decimalNumber);
419 }
420 else {
421 x = parseFloat(activeSign + wholeNumberWithCommas.split(",").join(""));
422 }
423 }
424 else {
425 x = parseFloat(activeSign + "0" + decimalPoint + decimalNumber);
426 }
427 if (isDefined(sciNotation) && isDefined(sciNotationFactor)) {
428 sciNotationSign = isDefined(sciNotationSign) ? sciNotationSign : "+";
429 // x + "e" + "-" + "10"
430 x = parseFloat(x.toString() + sciNotation.toString() + "" + sciNotationSign.toString() + sciNotationFactor.toString());
431 }
432 if (!isUndefined(percentageSign)) {
433 x = x * 0.01;
434 }
435 return x;
436 }
437 else {
438 try {
439 return TypeConverter.stringToDateNumber(value);
440 }
441 catch (_) {
442 return;
443 }
444 }
445 };
446 /**
447 * Converts any value to an inverted number or throws an error if it cannot coerce it to the number type
448 * @param value to convert
449 * @returns {number} to return. Will always return a number or throw an error. Never returns undefined.
450 */
451 TypeConverter.valueToInvertedNumber = function (value) {
452 return TypeConverter.valueToNumber(value) * (-1);
453 };
454 /**
455 * Converts any value to a number or throws an error if it cannot coerce it to the number type
456 * @param value to convert
457 * @returns {number} to return. Will always return a number or throw an error. Never returns undefined.
458 */
459 TypeConverter.valueToNumber = function (value) {
460 if (value instanceof Cell_1.Cell) {
461 if (value.isBlank()) {
462 return 0;
463 }
464 else {
465 if (value.hasError()) {
466 throw value.getError();
467 }
468 value = value.getValue();
469 }
470 }
471 if (typeof value === "number") {
472 return value;
473 }
474 else if (typeof value === "string") {
475 if (value === "") {
476 return 0;
477 }
478 var n = TypeConverter.stringToNumber(value);
479 if (n === undefined) {
480 throw new Errors_1.ValueError("Function expects number values, but is text and cannot be coerced to a number.");
481 }
482 return n;
483 }
484 else if (typeof value === "boolean") {
485 return value ? 1 : 0;
486 }
487 return 0;
488 };
489 /**
490 * Converts any value to a number, defaulting to 0 value in cases in which it cannot coerce it to a number type
491 * @param value to conver
492 * @returns {number} to return. Will always return a number or 0.
493 */
494 TypeConverter.valueToNumberGracefully = function (value) {
495 try {
496 return TypeConverter.valueToNumber(value);
497 }
498 catch (e) {
499 return 0;
500 }
501 };
502 /**
503 * Converts any value to a boolean or throws an error if it cannot coerce it to the boolean type.
504 * @param value to convert
505 * @returns {boolean} to return.
506 */
507 TypeConverter.valueToBoolean = function (value) {
508 if (value instanceof Cell_1.Cell) {
509 if (value.isBlank()) {
510 return false;
511 }
512 else {
513 if (value.hasError()) {
514 throw value.getError();
515 }
516 value = value.getValue();
517 }
518 }
519 if (typeof value === "number") {
520 return value !== 0;
521 }
522 else if (typeof value === "string") {
523 throw new Errors_1.ValueError("___ expects boolean values. But '" + value + "' is a text and cannot be coerced to a boolean.");
524 }
525 else if (typeof value === "boolean") {
526 return value;
527 }
528 };
529 /**
530 * Convert a value to string.
531 * @param value of any type, including array. array cannot be empty.
532 * @returns {string} string representation of value
533 */
534 TypeConverter.valueToString = function (value) {
535 if (value instanceof Cell_1.Cell) {
536 if (value.isBlank()) {
537 return "";
538 }
539 else {
540 if (value.hasError()) {
541 throw value.getError();
542 }
543 return value.getValue().toString();
544 }
545 }
546 else if (typeof value === "number") {
547 return value.toString();
548 }
549 else if (typeof value === "string") {
550 return value;
551 }
552 else if (typeof value === "boolean") {
553 return value ? "TRUE" : "FALSE";
554 }
555 };
556 /**
557 * Converts a value to a time number; a value between 0 and 1, exclusive on 1.
558 * @param value to convert
559 * @returns {number} representing a time value
560 */
561 TypeConverter.valueToTimestampNumber = function (value) {
562 if (value instanceof Cell_1.Cell) {
563 if (value.isBlank()) {
564 return 0;
565 }
566 else {
567 if (value.hasError()) {
568 throw value.getError();
569 }
570 return value.getValue();
571 }
572 }
573 else if (typeof value === "number") {
574 return value;
575 }
576 else if (typeof value === "string") {
577 if (value == "") {
578 return 0;
579 }
580 try {
581 return TypeConverter.stringToTimeNumber(value);
582 }
583 catch (e) {
584 if (TypeConverter.canCoerceToNumber(value)) {
585 return TypeConverter.valueToNumber(value);
586 }
587 throw new Errors_1.ValueError("___ expects number values. But '" + value + "' is a text and cannot be coerced to a number.");
588 }
589 }
590 else if (typeof value === "boolean") {
591 return 0; // value between 0 and 1, exclusive on 1.
592 }
593 return 0;
594 };
595 /**
596 * Returns true if string is number format.
597 * @param str to check
598 * @returns {boolean}
599 */
600 TypeConverter.isNumber = function (str) {
601 return str.match("\s*(\d+\.?\d*$)|(\.\d+$)|([0-9]{2}%$)|([0-9]{1,}$)") !== null;
602 };
603 /**
604 * Returns true if we can coerce it to the number type.
605 * @param value to coerce
606 * @returns {boolean} if could be coerced to a number
607 */
608 TypeConverter.canCoerceToNumber = function (value) {
609 if (typeof value === "number" || typeof value === "boolean" || value instanceof Cell_1.Cell) {
610 return true;
611 }
612 else if (typeof value === "string") {
613 return TypeConverter.isNumber(value);
614 }
615 return false;
616 };
617 /**
618 * Takes any input type and will throw a REF_ERROR or coerce it into a number.
619 * @param input to attempt to coerce into a number
620 * @returns {number} number representation of the input
621 */
622 TypeConverter.firstValueAsNumber = function (input) {
623 if (input instanceof Array) {
624 if (input.length === 0) {
625 throw new Errors_1.RefError("Reference does not exist.");
626 }
627 return TypeConverter.firstValueAsNumber(input[0]);
628 }
629 return TypeConverter.valueToNumber(input);
630 };
631 /**
632 * Takes any input type and will throw a REF_ERROR or coerce it into a string.
633 * @param input to attempt to coerce into a string
634 * @returns {number} number representation of the input
635 */
636 TypeConverter.firstValueAsString = function (input) {
637 if (input instanceof Array) {
638 if (input.length === 0) {
639 throw new Errors_1.RefError("Reference does not exist.");
640 }
641 return TypeConverter.firstValueAsString(input[0]);
642 }
643 return TypeConverter.valueToString(input);
644 };
645 /**
646 * Returns the first value that is not of the type array. Will throw RefError if any empty arrays are passed in.
647 * @param input to retrieve first value of
648 * @returns {any} any non-array value.
649 */
650 TypeConverter.firstValue = function (input) {
651 if (input instanceof Array) {
652 if (input.length === 0) {
653 throw new Errors_1.RefError("Reference does not exist.");
654 }
655 return TypeConverter.firstValue(input[0]);
656 }
657 return input;
658 };
659 /**
660 * Takes any input type and will throw a REF_ERROR or coerce it into a string.
661 * @param input to attempt to coerce into a string
662 * @returns {number} number representation of the input
663 */
664 TypeConverter.firstValueAsBoolean = function (input) {
665 if (input instanceof Array) {
666 if (input.length === 0) {
667 throw new Errors_1.RefError("Reference does not exist.");
668 }
669 return TypeConverter.firstValueAsBoolean(input[0]);
670 }
671 return TypeConverter.valueToBoolean(input);
672 };
673 /**
674 * Takes the input type and will throw a REF_ERROR or coerce it into a date number
675 * @param input input to attempt to coerce to a date number
676 * @param coerceBoolean should a boolean be converted
677 * @returns {number} representing a date
678 */
679 TypeConverter.firstValueAsDateNumber = function (input, coerceBoolean) {
680 coerceBoolean = coerceBoolean || false;
681 if (input instanceof Array) {
682 if (input.length === 0) {
683 throw new Errors_1.RefError("Reference does not exist.");
684 }
685 return TypeConverter.firstValueAsDateNumber(input[0], coerceBoolean || false);
686 }
687 return TypeConverter.valueToDateNumber(input, coerceBoolean);
688 };
689 /**
690 * Takes the input type and will throw a REF_ERROR or coerce it into a time number
691 * @param input input to attempt to coerce to a time number
692 * @returns {number} representing time of day
693 */
694 TypeConverter.firstValueAsTimestampNumber = function (input) {
695 if (input instanceof Array) {
696 if (input.length === 0) {
697 throw new Errors_1.RefError("Reference does not exist.");
698 }
699 return TypeConverter.firstValueAsTimestampNumber(input[0]);
700 }
701 return TypeConverter.valueToTimestampNumber(input);
702 };
703 /**
704 * Convert a value to date number if possible.
705 * @param value to convert
706 * @param coerceBoolean should a boolean be converted
707 * @returns {number} date
708 */
709 TypeConverter.valueToDateNumber = function (value, coerceBoolean) {
710 if (value instanceof Cell_1.Cell) {
711 if (value.isBlank()) {
712 return 0;
713 }
714 else {
715 if (value.hasError()) {
716 throw value.getError();
717 }
718 return value.getValue();
719 }
720 }
721 else if (typeof value === "number") {
722 return value;
723 }
724 else if (typeof value === "string") {
725 try {
726 return TypeConverter.stringToDateNumber(value);
727 }
728 catch (e) {
729 if (TypeConverter.canCoerceToNumber(value)) {
730 return TypeConverter.valueToNumber(value);
731 }
732 throw new Errors_1.ValueError("___ expects date values. But '" + value + "' is a text and cannot be coerced to a date.");
733 }
734 }
735 else if (typeof value === "boolean") {
736 if (coerceBoolean) {
737 return value ? 1 : 0;
738 }
739 throw new Errors_1.ValueError("___ expects date values. But '" + value + "' is a boolean and cannot be coerced to a date.");
740 }
741 };
742 /**
743 * Converts a moment to a date number.
744 * @param m to convert
745 * @returns {number} date
746 */
747 TypeConverter.momentToNumber = function (m) {
748 return m.diff(this.ORIGIN_MOMENT, "seconds") / this.SECONDS_IN_DAY;
749 };
750 /**
751 * Converts a moment to a date number, floored to the whole day date.
752 * @param m to convert
753 * @returns {number} date
754 */
755 TypeConverter.momentToDayNumber = function (m) {
756 return Math.floor(TypeConverter.momentToNumber(m));
757 };
758 /**
759 * Converts a number to moment.
760 * @param n to convert
761 * @returns {Moment} date
762 */
763 TypeConverter.numberToMoment = function (n) {
764 return moment.utc(TypeConverter.ORIGIN_MOMENT).add(n, "days");
765 };
766 /**
767 * Converts a number to moment while preserving the decimal part of the number.
768 * @param n to convert
769 * @returns {Moment} date
770 */
771 TypeConverter.decimalNumberToMoment = function (n) {
772 return moment.utc(TypeConverter.ORIGIN_MOMENT).add(n * TypeConverter.SECONDS_IN_DAY * 1000, "milliseconds");
773 };
774 /**
775 * Using timestamp units, create a time number between 0 and 1, exclusive on end.
776 * @param hours
777 * @param minutes
778 * @param seconds
779 * @returns {number} representing time of day between 0 and 1, exclusive on end.
780 */
781 TypeConverter.unitsToTimeNumber = function (hours, minutes, seconds) {
782 var v = (((hours % 24) * 60 * 60) + ((minutes) * 60) + (seconds)) / 86400;
783 return v % 1;
784 };
785 TypeConverter.ORIGIN_MOMENT = moment.utc([1899, 11, 30]).startOf("day");
786 TypeConverter.SECONDS_IN_DAY = 86400;
787 return TypeConverter;
788}());
789exports.TypeConverter = TypeConverter;
790/**
791 * Catches divide by zero situations and throws them as errors
792 * @param n number to check
793 * @returns {number} n as long as it's not zero.
794 */
795var checkForDevideByZero = function (n) {
796 n = +n; // Coerce to number.
797 if (!n) { // Matches +0, -0, NaN
798 throw new Errors_1.DivZeroError("Evaluation of function caused a divide by zero error.");
799 }
800 return n;
801};
802exports.checkForDevideByZero = checkForDevideByZero;