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