index.js
  1  /**
  2   * Helpers.
  3   */
  4  
  5  var s = 1000;
  6  var m = s * 60;
  7  var h = m * 60;
  8  var d = h * 24;
  9  var y = d * 365.25;
 10  
 11  /**
 12   * Parse or format the given `val`.
 13   *
 14   * Options:
 15   *
 16   *  - `long` verbose formatting [false]
 17   *
 18   * @param {String|Number} val
 19   * @param {Object} [options]
 20   * @throws {Error} throw an error if val is not a non-empty string or a number
 21   * @return {String|Number}
 22   * @api public
 23   */
 24  
 25  module.exports = function(val, options) {
 26    options = options || {};
 27    var type = typeof val;
 28    if (type === 'string' && val.length > 0) {
 29      return parse(val);
 30    } else if (type === 'number' && isNaN(val) === false) {
 31      return options.long ? fmtLong(val) : fmtShort(val);
 32    }
 33    throw new Error(
 34      'val is not a non-empty string or a valid number. val=' +
 35        JSON.stringify(val)
 36    );
 37  };
 38  
 39  /**
 40   * Parse the given `str` and return milliseconds.
 41   *
 42   * @param {String} str
 43   * @return {Number}
 44   * @api private
 45   */
 46  
 47  function parse(str) {
 48    str = String(str);
 49    if (str.length > 100) {
 50      return;
 51    }
 52    var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(
 53      str
 54    );
 55    if (!match) {
 56      return;
 57    }
 58    var n = parseFloat(match[1]);
 59    var type = (match[2] || 'ms').toLowerCase();
 60    switch (type) {
 61      case 'years':
 62      case 'year':
 63      case 'yrs':
 64      case 'yr':
 65      case 'y':
 66        return n * y;
 67      case 'days':
 68      case 'day':
 69      case 'd':
 70        return n * d;
 71      case 'hours':
 72      case 'hour':
 73      case 'hrs':
 74      case 'hr':
 75      case 'h':
 76        return n * h;
 77      case 'minutes':
 78      case 'minute':
 79      case 'mins':
 80      case 'min':
 81      case 'm':
 82        return n * m;
 83      case 'seconds':
 84      case 'second':
 85      case 'secs':
 86      case 'sec':
 87      case 's':
 88        return n * s;
 89      case 'milliseconds':
 90      case 'millisecond':
 91      case 'msecs':
 92      case 'msec':
 93      case 'ms':
 94        return n;
 95      default:
 96        return undefined;
 97    }
 98  }
 99  
100  /**
101   * Short format for `ms`.
102   *
103   * @param {Number} ms
104   * @return {String}
105   * @api private
106   */
107  
108  function fmtShort(ms) {
109    if (ms >= d) {
110      return Math.round(ms / d) + 'd';
111    }
112    if (ms >= h) {
113      return Math.round(ms / h) + 'h';
114    }
115    if (ms >= m) {
116      return Math.round(ms / m) + 'm';
117    }
118    if (ms >= s) {
119      return Math.round(ms / s) + 's';
120    }
121    return ms + 'ms';
122  }
123  
124  /**
125   * Long format for `ms`.
126   *
127   * @param {Number} ms
128   * @return {String}
129   * @api private
130   */
131  
132  function fmtLong(ms) {
133    return plural(ms, d, 'day') ||
134      plural(ms, h, 'hour') ||
135      plural(ms, m, 'minute') ||
136      plural(ms, s, 'second') ||
137      ms + ' ms';
138  }
139  
140  /**
141   * Pluralization helper.
142   */
143  
144  function plural(ms, n, name) {
145    if (ms < n) {
146      return;
147    }
148    if (ms < n * 1.5) {
149      return Math.floor(ms / n) + ' ' + name;
150    }
151    return Math.ceil(ms / n) + ' ' + name + 's';
152  }