| /* |
| * JavaScript TimeSpan Library |
| * |
| * Copyright (c) 2010 Michael Stum, Charlie Robbins |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining |
| * a copy of this software and associated documentation files (the |
| * "Software"), to deal in the Software without restriction, including |
| * without limitation the rights to use, copy, modify, merge, publish, |
| * distribute, sublicense, and/or sell copies of the Software, and to |
| * permit persons to whom the Software is furnished to do so, subject to |
| * the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be |
| * included in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
| * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
| * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| |
| // |
| // ### Time constants |
| // |
| var msecPerSecond = 1000, |
| msecPerMinute = 60000, |
| msecPerHour = 3600000, |
| msecPerDay = 86400000; |
| |
| // |
| // ### Timespan Parsers |
| // |
| var timeSpanWithDays = /^(\d+):(\d+):(\d+):(\d+)(\.\d+)?/, |
| timeSpanNoDays = /^(\d+):(\d+):(\d+)(\.\d+)?/; |
| |
| // |
| // ### function TimeSpan (milliseconds, seconds, minutes, hours, days) |
| // #### @milliseconds {Number} Number of milliseconds for this instance. |
| // #### @seconds {Number} Number of seconds for this instance. |
| // #### @minutes {Number} Number of minutes for this instance. |
| // #### @hours {Number} Number of hours for this instance. |
| // #### @days {Number} Number of days for this instance. |
| // Constructor function for the `TimeSpan` object which represents a length |
| // of positive or negative milliseconds componentized into milliseconds, |
| // seconds, hours, and days. |
| // |
| var TimeSpan = exports.TimeSpan = function (milliseconds, seconds, minutes, hours, days) { |
| this.msecs = 0; |
| |
| if (isNumeric(days)) { |
| this.msecs += (days * msecPerDay); |
| } |
| |
| if (isNumeric(hours)) { |
| this.msecs += (hours * msecPerHour); |
| } |
| |
| if (isNumeric(minutes)) { |
| this.msecs += (minutes * msecPerMinute); |
| } |
| |
| if (isNumeric(seconds)) { |
| this.msecs += (seconds * msecPerSecond); |
| } |
| |
| if (isNumeric(milliseconds)) { |
| this.msecs += milliseconds; |
| } |
| }; |
| |
| // |
| // ## Factory methods |
| // Helper methods for creating new TimeSpan objects |
| // from various criteria: milliseconds, seconds, minutes, |
| // hours, days, strings and other `TimeSpan` instances. |
| // |
| |
| // |
| // ### function fromMilliseconds (milliseconds) |
| // #### @milliseconds {Number} Amount of milliseconds for the new TimeSpan instance. |
| // Creates a new `TimeSpan` instance with the specified `milliseconds`. |
| // |
| exports.fromMilliseconds = function (milliseconds) { |
| if (!isNumeric(milliseconds)) { return } |
| return new TimeSpan(milliseconds, 0, 0, 0, 0); |
| } |
| |
| // |
| // ### function fromSeconds (seconds) |
| // #### @milliseconds {Number} Amount of seconds for the new TimeSpan instance. |
| // Creates a new `TimeSpan` instance with the specified `seconds`. |
| // |
| exports.fromSeconds = function (seconds) { |
| if (!isNumeric(seconds)) { return } |
| return new TimeSpan(0, seconds, 0, 0, 0); |
| }; |
| |
| // |
| // ### function fromMinutes (milliseconds) |
| // #### @milliseconds {Number} Amount of minutes for the new TimeSpan instance. |
| // Creates a new `TimeSpan` instance with the specified `minutes`. |
| // |
| exports.fromMinutes = function (minutes) { |
| if (!isNumeric(minutes)) { return } |
| return new TimeSpan(0, 0, minutes, 0, 0); |
| }; |
| |
| // |
| // ### function fromHours (hours) |
| // #### @milliseconds {Number} Amount of hours for the new TimeSpan instance. |
| // Creates a new `TimeSpan` instance with the specified `hours`. |
| // |
| exports.fromHours = function (hours) { |
| if (!isNumeric(hours)) { return } |
| return new TimeSpan(0, 0, 0, hours, 0); |
| }; |
| |
| // |
| // ### function fromDays (days) |
| // #### @milliseconds {Number} Amount of days for the new TimeSpan instance. |
| // Creates a new `TimeSpan` instance with the specified `days`. |
| // |
| exports.fromDays = function (days) { |
| if (!isNumeric(days)) { return } |
| return new TimeSpan(0, 0, 0, 0, days); |
| }; |
| |
| // |
| // ### function parse (str) |
| // #### @str {string} Timespan string to parse. |
| // Creates a new `TimeSpan` instance from the specified |
| // string, `str`. |
| // |
| exports.parse = function (str) { |
| var match, milliseconds; |
| |
| function parseMilliseconds (value) { |
| return value ? parseFloat('0' + value) * 1000 : 0; |
| } |
| |
| // If we match against a full TimeSpan: |
| // [days]:[hours]:[minutes]:[seconds].[milliseconds]? |
| if ((match = str.match(timeSpanWithDays))) { |
| return new TimeSpan(parseMilliseconds(match[5]), match[4], match[3], match[2], match[1]); |
| } |
| |
| // If we match against a partial TimeSpan: |
| // [hours]:[minutes]:[seconds].[milliseconds]? |
| if ((match = str.match(timeSpanNoDays))) { |
| return new TimeSpan(parseMilliseconds(match[4]), match[3], match[2], match[1], 0); |
| } |
| |
| return null; |
| }; |
| |
| // |
| // List of default singular time modifiers and associated |
| // computation algoritm. Assumes in order, smallest to greatest |
| // performing carry forward additiona / subtraction for each |
| // Date-Time component. |
| // |
| var parsers = { |
| 'milliseconds': { |
| exp: /(\d+)milli(?:second)?[s]?/i, |
| compute: function (delta, computed) { |
| return _compute(delta, computed, { |
| current: 'milliseconds', |
| next: 'seconds', |
| max: 1000 |
| }); |
| } |
| }, |
| 'seconds': { |
| exp: /(\d+)second[s]?/i, |
| compute: function (delta, computed) { |
| return _compute(delta, computed, { |
| current: 'seconds', |
| next: 'minutes', |
| max: 60 |
| }); |
| } |
| }, |
| 'minutes': { |
| exp: /(\d+)minute[s]?/i, |
| compute: function (delta, computed) { |
| return _compute(delta, computed, { |
| current: 'minutes', |
| next: 'hours', |
| max: 60 |
| }); |
| } |
| }, |
| 'hours': { |
| exp: /(\d+)hour[s]?/i, |
| compute: function (delta, computed) { |
| return _compute(delta, computed, { |
| current: 'hours', |
| next: 'days', |
| max: 24 |
| }); |
| } |
| }, |
| 'days': { |
| exp: /(\d+)day[s]?/i, |
| compute: function (delta, computed) { |
| var days = monthDays(computed.months, computed.years), |
| sign = delta >= 0 ? 1 : -1, |
| opsign = delta >= 0 ? -1 : 1, |
| clean = 0; |
| |
| function update (months) { |
| if (months < 0) { |
| computed.years -= 1; |
| return 11; |
| } |
| else if (months > 11) { |
| computed.years += 1; |
| return 0 |
| } |
| |
| return months; |
| } |
| |
| if (delta) { |
| while (Math.abs(delta) >= days) { |
| computed.months += sign * 1; |
| computed.months = update(computed.months); |
| delta += opsign * days; |
| days = monthDays(computed.months, computed.years); |
| } |
| |
| computed.days += (opsign * delta); |
| } |
| |
| if (computed.days < 0) { clean = -1 } |
| else if (computed.days > months[computed.months]) { clean = 1 } |
| |
| if (clean === -1 || clean === 1) { |
| computed.months += clean; |
| computed.months = update(computed.months); |
| computed.days = months[computed.months] + computed.days; |
| } |
| |
| return computed; |
| } |
| }, |
| 'months': { |
| exp: /(\d+)month[s]?/i, |
| compute: function (delta, computed) { |
| var round = delta > 0 ? Math.floor : Math.ceil; |
| if (delta) { |
| computed.years += round.call(null, delta / 12); |
| computed.months += delta % 12; |
| } |
| |
| if (computed.months > 11) { |
| computed.years += Math.floor((computed.months + 1) / 12); |
| computed.months = ((computed.months + 1) % 12) - 1; |
| } |
| |
| return computed; |
| } |
| }, |
| 'years': { |
| exp: /(\d+)year[s]?/i, |
| compute: function (delta, computed) { |
| if (delta) { computed.years += delta; } |
| return computed; |
| } |
| } |
| }; |
| |
| // |
| // Compute the list of parser names for |
| // later use. |
| // |
| var parserNames = Object.keys(parsers); |
| |
| // |
| // ### function parseDate (str) |
| // #### @str {string} String to parse into a date |
| // Parses the specified liberal Date-Time string according to |
| // ISO8601 **and**: |
| // |
| // 1. `2010-04-03T12:34:15Z+12MINUTES` |
| // 2. `NOW-4HOURS` |
| // |
| // Valid modifiers for the more liberal Date-Time string(s): |
| // |
| // YEAR, YEARS |
| // MONTH, MONTHS |
| // DAY, DAYS |
| // HOUR, HOURS |
| // MINUTE, MINUTES |
| // SECOND, SECONDS |
| // MILLI, MILLIS, MILLISECOND, MILLISECONDS |
| // |
| exports.parseDate = function (str) { |
| var dateTime = Date.parse(str), |
| iso = '^([^Z]+)', |
| zulu = 'Z([\\+|\\-])?', |
| diff = {}, |
| computed, |
| modifiers, |
| sign; |
| |
| // |
| // If Date string supplied actually conforms |
| // to UTC Time (ISO8601), return a new Date. |
| // |
| if (!isNaN(dateTime)) { |
| return new Date(dateTime); |
| } |
| |
| // |
| // Create the `RegExp` for the end component |
| // of the target `str` to parse. |
| // |
| parserNames.forEach(function (group) { |
| zulu += '(\\d+[a-zA-Z]+)?'; |
| }); |
| |
| if (/^NOW/i.test(str)) { |
| // |
| // If the target `str` is a liberal `NOW-*`, |
| // then set the base `dateTime` appropriately. |
| // |
| dateTime = Date.now(); |
| zulu = zulu.replace(/Z/, 'NOW'); |
| } |
| else if (/^\-/.test(str) || /^\+/.test(str)) { |
| dateTime = Date.now(); |
| zulu = zulu.replace(/Z/, ''); |
| } |
| else { |
| // |
| // Parse the `ISO8601` component, and the end |
| // component from the target `str`. |
| // |
| dateTime = str.match(new RegExp(iso, 'i')); |
| dateTime = Date.parse(dateTime[1]); |
| } |
| |
| // |
| // If there was no match on either part then |
| // it must be a bad value. |
| // |
| if (!dateTime || !(modifiers = str.match(new RegExp(zulu, 'i')))) { |
| return null; |
| } |
| |
| // |
| // Create a new `Date` object from the `ISO8601` |
| // component of the target `str`. |
| // |
| dateTime = new Date(dateTime); |
| sign = modifiers[1] === '+' ? 1 : -1; |
| |
| // |
| // Create an Object-literal for consistently accessing |
| // the various components of the computed Date. |
| // |
| var computed = { |
| milliseconds: dateTime.getMilliseconds(), |
| seconds: dateTime.getSeconds(), |
| minutes: dateTime.getMinutes(), |
| hours: dateTime.getHours(), |
| days: dateTime.getDate(), |
| months: dateTime.getMonth(), |
| years: dateTime.getFullYear() |
| }; |
| |
| // |
| // Parse the individual component spans (months, years, etc) |
| // from the modifier strings that we parsed from the end |
| // of the target `str`. |
| // |
| modifiers.slice(2).filter(Boolean).forEach(function (modifier) { |
| parserNames.forEach(function (name) { |
| var match; |
| if (!(match = modifier.match(parsers[name].exp))) { |
| return; |
| } |
| |
| diff[name] = sign * parseInt(match[1], 10); |
| }) |
| }); |
| |
| // |
| // Compute the total `diff` by iteratively computing |
| // the partial components from smallest to largest. |
| // |
| parserNames.forEach(function (name) { |
| computed = parsers[name].compute(diff[name], computed); |
| }); |
| |
| return new Date( |
| computed.years, |
| computed.months, |
| computed.days, |
| computed.hours, |
| computed.minutes, |
| computed.seconds, |
| computed.milliseconds |
| ); |
| }; |
| |
| // |
| // ### function fromDates (start, end, abs) |
| // #### @start {Date} Start date of the `TimeSpan` instance to return |
| // #### @end {Date} End date of the `TimeSpan` instance to return |
| // #### @abs {boolean} Value indicating to return an absolute value |
| // Returns a new `TimeSpan` instance representing the difference between |
| // the `start` and `end` Dates. |
| // |
| exports.fromDates = function (start, end, abs) { |
| if (typeof start === 'string') { |
| start = exports.parseDate(start); |
| } |
| |
| if (typeof end === 'string') { |
| end = exports.parseDate(end); |
| } |
| |
| if (!(start instanceof Date && end instanceof Date)) { |
| return null; |
| } |
| |
| var differenceMsecs = end.valueOf() - start.valueOf(); |
| if (abs) { |
| differenceMsecs = Math.abs(differenceMsecs); |
| } |
| |
| return new TimeSpan(differenceMsecs, 0, 0, 0, 0); |
| }; |
| |
| // |
| // ## Module Helpers |
| // Module-level helpers for various utilities such as: |
| // instanceOf, parsability, and cloning. |
| // |
| |
| // |
| // ### function test (str) |
| // #### @str {string} String value to test if it is a TimeSpan |
| // Returns a value indicating if the specified string, `str`, |
| // is a parsable `TimeSpan` value. |
| // |
| exports.test = function (str) { |
| return timeSpanWithDays.test(str) || timeSpanNoDays.test(str); |
| }; |
| |
| // |
| // ### function instanceOf (timeSpan) |
| // #### @timeSpan {Object} Object to check TimeSpan quality. |
| // Returns a value indicating if the specified `timeSpan` is |
| // in fact a `TimeSpan` instance. |
| // |
| exports.instanceOf = function (timeSpan) { |
| return timeSpan instanceof TimeSpan; |
| }; |
| |
| // |
| // ### function clone (timeSpan) |
| // #### @timeSpan {TimeSpan} TimeSpan object to clone. |
| // Returns a new `TimeSpan` instance with the same value |
| // as the `timeSpan` object supplied. |
| // |
| exports.clone = function (timeSpan) { |
| if (!(timeSpan instanceof TimeSpan)) { return } |
| return exports.fromMilliseconds(timeSpan.totalMilliseconds()); |
| }; |
| |
| // |
| // ## Addition |
| // Methods for adding `TimeSpan` instances, |
| // milliseconds, seconds, hours, and days to other |
| // `TimeSpan` instances. |
| // |
| |
| // |
| // ### function add (timeSpan) |
| // #### @timeSpan {TimeSpan} TimeSpan to add to this instance |
| // Adds the specified `timeSpan` to this instance. |
| // |
| TimeSpan.prototype.add = function (timeSpan) { |
| if (!(timeSpan instanceof TimeSpan)) { return } |
| this.msecs += timeSpan.totalMilliseconds(); |
| }; |
| |
| // |
| // ### function addMilliseconds (milliseconds) |
| // #### @milliseconds {Number} Number of milliseconds to add. |
| // Adds the specified `milliseconds` to this instance. |
| // |
| TimeSpan.prototype.addMilliseconds = function (milliseconds) { |
| if (!isNumeric(milliseconds)) { return } |
| this.msecs += milliseconds; |
| }; |
| |
| // |
| // ### function addSeconds (seconds) |
| // #### @seconds {Number} Number of seconds to add. |
| // Adds the specified `seconds` to this instance. |
| // |
| TimeSpan.prototype.addSeconds = function (seconds) { |
| if (!isNumeric(seconds)) { return } |
| |
| this.msecs += (seconds * msecPerSecond); |
| }; |
| |
| // |
| // ### function addMinutes (minutes) |
| // #### @minutes {Number} Number of minutes to add. |
| // Adds the specified `minutes` to this instance. |
| // |
| TimeSpan.prototype.addMinutes = function (minutes) { |
| if (!isNumeric(minutes)) { return } |
| this.msecs += (minutes * msecPerMinute); |
| }; |
| |
| // |
| // ### function addHours (hours) |
| // #### @hours {Number} Number of hours to add. |
| // Adds the specified `hours` to this instance. |
| // |
| TimeSpan.prototype.addHours = function (hours) { |
| if (!isNumeric(hours)) { return } |
| this.msecs += (hours * msecPerHour); |
| }; |
| |
| // |
| // ### function addDays (days) |
| // #### @days {Number} Number of days to add. |
| // Adds the specified `days` to this instance. |
| // |
| TimeSpan.prototype.addDays = function (days) { |
| if (!isNumeric(days)) { return } |
| this.msecs += (days * msecPerDay); |
| }; |
| |
| // |
| // ## Subtraction |
| // Methods for subtracting `TimeSpan` instances, |
| // milliseconds, seconds, hours, and days from other |
| // `TimeSpan` instances. |
| // |
| |
| // |
| // ### function subtract (timeSpan) |
| // #### @timeSpan {TimeSpan} TimeSpan to subtract from this instance. |
| // Subtracts the specified `timeSpan` from this instance. |
| // |
| TimeSpan.prototype.subtract = function (timeSpan) { |
| if (!(timeSpan instanceof TimeSpan)) { return } |
| this.msecs -= timeSpan.totalMilliseconds(); |
| }; |
| |
| // |
| // ### function subtractMilliseconds (milliseconds) |
| // #### @milliseconds {Number} Number of milliseconds to subtract. |
| // Subtracts the specified `milliseconds` from this instance. |
| // |
| TimeSpan.prototype.subtractMilliseconds = function (milliseconds) { |
| if (!isNumeric(milliseconds)) { return } |
| this.msecs -= milliseconds; |
| }; |
| |
| // |
| // ### function subtractSeconds (seconds) |
| // #### @seconds {Number} Number of seconds to subtract. |
| // Subtracts the specified `seconds` from this instance. |
| // |
| TimeSpan.prototype.subtractSeconds = function (seconds) { |
| if (!isNumeric(seconds)) { return } |
| this.msecs -= (seconds * msecPerSecond); |
| }; |
| |
| // |
| // ### function subtractMinutes (minutes) |
| // #### @minutes {Number} Number of minutes to subtract. |
| // Subtracts the specified `minutes` from this instance. |
| // |
| TimeSpan.prototype.subtractMinutes = function (minutes) { |
| if (!isNumeric(minutes)) { return } |
| this.msecs -= (minutes * msecPerMinute); |
| }; |
| |
| // |
| // ### function subtractHours (hours) |
| // #### @hours {Number} Number of hours to subtract. |
| // Subtracts the specified `hours` from this instance. |
| // |
| TimeSpan.prototype.subtractHours = function (hours) { |
| if (!isNumeric(hours)) { return } |
| this.msecs -= (hours * msecPerHour); |
| }; |
| |
| // |
| // ### function subtractDays (days) |
| // #### @days {Number} Number of days to subtract. |
| // Subtracts the specified `days` from this instance. |
| // |
| TimeSpan.prototype.subtractDays = function (days) { |
| if (!isNumeric(days)) { return } |
| this.msecs -= (days * msecPerDay); |
| }; |
| |
| // |
| // ## Getters |
| // Methods for retrieving components of a `TimeSpan` |
| // instance: milliseconds, seconds, minutes, hours, and days. |
| // |
| |
| // |
| // ### function totalMilliseconds (roundDown) |
| // #### @roundDown {boolean} Value indicating if the value should be rounded down. |
| // Returns the total number of milliseconds for this instance, rounding down |
| // to the nearest integer if `roundDown` is set. |
| // |
| TimeSpan.prototype.totalMilliseconds = function (roundDown) { |
| var result = this.msecs; |
| if (roundDown === true) { |
| result = Math.floor(result); |
| } |
| |
| return result; |
| }; |
| |
| // |
| // ### function totalSeconds (roundDown) |
| // #### @roundDown {boolean} Value indicating if the value should be rounded down. |
| // Returns the total number of seconds for this instance, rounding down |
| // to the nearest integer if `roundDown` is set. |
| // |
| TimeSpan.prototype.totalSeconds = function (roundDown) { |
| var result = this.msecs / msecPerSecond; |
| if (roundDown === true) { |
| result = Math.floor(result); |
| } |
| |
| return result; |
| }; |
| |
| // |
| // ### function totalMinutes (roundDown) |
| // #### @roundDown {boolean} Value indicating if the value should be rounded down. |
| // Returns the total number of minutes for this instance, rounding down |
| // to the nearest integer if `roundDown` is set. |
| // |
| TimeSpan.prototype.totalMinutes = function (roundDown) { |
| var result = this.msecs / msecPerMinute; |
| if (roundDown === true) { |
| result = Math.floor(result); |
| } |
| |
| return result; |
| }; |
| |
| // |
| // ### function totalHours (roundDown) |
| // #### @roundDown {boolean} Value indicating if the value should be rounded down. |
| // Returns the total number of hours for this instance, rounding down |
| // to the nearest integer if `roundDown` is set. |
| // |
| TimeSpan.prototype.totalHours = function (roundDown) { |
| var result = this.msecs / msecPerHour; |
| if (roundDown === true) { |
| result = Math.floor(result); |
| } |
| |
| return result; |
| }; |
| |
| // |
| // ### function totalDays (roundDown) |
| // #### @roundDown {boolean} Value indicating if the value should be rounded down. |
| // Returns the total number of days for this instance, rounding down |
| // to the nearest integer if `roundDown` is set. |
| // |
| TimeSpan.prototype.totalDays = function (roundDown) { |
| var result = this.msecs / msecPerDay; |
| if (roundDown === true) { |
| result = Math.floor(result); |
| } |
| |
| return result; |
| }; |
| |
| // |
| // ### @milliseconds |
| // Returns the length of this `TimeSpan` instance in milliseconds. |
| // |
| TimeSpan.prototype.__defineGetter__('milliseconds', function () { |
| return this.msecs % 1000; |
| }); |
| |
| // |
| // ### @seconds |
| // Returns the length of this `TimeSpan` instance in seconds. |
| // |
| TimeSpan.prototype.__defineGetter__('seconds', function () { |
| return Math.floor(this.msecs / msecPerSecond) % 60; |
| }); |
| |
| // |
| // ### @minutes |
| // Returns the length of this `TimeSpan` instance in minutes. |
| // |
| TimeSpan.prototype.__defineGetter__('minutes', function () { |
| return Math.floor(this.msecs / msecPerMinute) % 60; |
| }); |
| |
| // |
| // ### @hours |
| // Returns the length of this `TimeSpan` instance in hours. |
| // |
| TimeSpan.prototype.__defineGetter__('hours', function () { |
| return Math.floor(this.msecs / msecPerHour) % 24; |
| }); |
| |
| // |
| // ### @days |
| // Returns the length of this `TimeSpan` instance in days. |
| // |
| TimeSpan.prototype.__defineGetter__('days', function () { |
| return Math.floor(this.msecs / msecPerDay); |
| }); |
| |
| // |
| // ## Instance Helpers |
| // Various help methods for performing utilities |
| // such as equality and serialization |
| // |
| |
| // |
| // ### function equals (timeSpan) |
| // #### @timeSpan {TimeSpan} TimeSpan instance to assert equal |
| // Returns a value indicating if the specified `timeSpan` is equal |
| // in milliseconds to this instance. |
| // |
| TimeSpan.prototype.equals = function (timeSpan) { |
| if (!(timeSpan instanceof TimeSpan)) { return } |
| return this.msecs === timeSpan.totalMilliseconds(); |
| }; |
| |
| // |
| // ### function toString () |
| // Returns a string representation of this `TimeSpan` |
| // instance according to current `format`. |
| // |
| TimeSpan.prototype.toString = function () { |
| if (!this.format) { return this._format() } |
| return this.format(this); |
| }; |
| |
| // |
| // ### @private function _format () |
| // Returns the default string representation of this instance. |
| // |
| TimeSpan.prototype._format = function () { |
| return [ |
| this.days, |
| this.hours, |
| this.minutes, |
| this.seconds + '.' + this.milliseconds |
| ].join(':') |
| }; |
| |
| // |
| // ### @private function isNumeric (input) |
| // #### @input {Number} Value to check numeric quality of. |
| // Returns a value indicating the numeric quality of the |
| // specified `input`. |
| // |
| function isNumeric (input) { |
| return input && !isNaN(parseFloat(input)) && isFinite(input); |
| }; |
| |
| // |
| // ### @private function _compute (delta, date, computed, options) |
| // #### @delta {Number} Channge in this component of the date |
| // #### @computed {Object} Currently computed date. |
| // #### @options {Object} Options for the computation |
| // Performs carry forward addition or subtraction for the |
| // `options.current` component of the `computed` date, carrying |
| // it forward to `options.next` depending on the maximum value, |
| // `options.max`. |
| // |
| function _compute (delta, computed, options) { |
| var current = options.current, |
| next = options.next, |
| max = options.max, |
| round = delta > 0 ? Math.floor : Math.ceil; |
| |
| if (delta) { |
| computed[next] += round.call(null, delta / max); |
| computed[current] += delta % max; |
| } |
| |
| if (Math.abs(computed[current]) >= max) { |
| computed[next] += round.call(null, computed[current] / max) |
| computed[current] = computed[current] % max; |
| } |
| |
| return computed; |
| } |
| |
| |
| // |
| // ### @private monthDays (month, year) |
| // #### @month {Number} Month to get days for. |
| // #### @year {Number} Year of the month to get days for. |
| // Returns the number of days in the specified `month` observing |
| // leap years. |
| // |
| var months = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; |
| function monthDays (month, year) { |
| if (((year % 100 !== 0 && year % 4 === 0) |
| || year % 400 === 0) && month === 1) { |
| return 29; |
| } |
| |
| return months[month]; |
| } |