'use strict' var extend = require('xtend/mutable') module.exports = PostgresInterval function PostgresInterval (raw) { if (!(this instanceof PostgresInterval)) { return new PostgresInterval(raw) } extend(this, parse(raw)) } var properties = ['seconds', 'minutes', 'hours', 'days', 'months', 'years'] PostgresInterval.prototype.toPostgres = function () { var filtered = properties.filter(this.hasOwnProperty, this) // In addition to `properties`, we need to account for fractions of seconds. if (this.milliseconds && filtered.indexOf('seconds') < 0) { filtered.push('seconds') } if (filtered.length === 0) return '0' return filtered .map(function (property) { var value = this[property] || 0 // Account for fractional part of seconds, // remove trailing zeroes. if (property === 'seconds' && this.milliseconds) { value = (value + this.milliseconds / 1000).toFixed(6).replace(/\.?0+$/, '') } return value + ' ' + property }, this) .join(' ') } var propertiesISOEquivalent = { years: 'Y', months: 'M', days: 'D', hours: 'H', minutes: 'M', seconds: 'S' } var dateProperties = ['years', 'months', 'days'] var timeProperties = ['hours', 'minutes', 'seconds'] // according to ISO 8601 PostgresInterval.prototype.toISOString = PostgresInterval.prototype.toISO = function () { var datePart = dateProperties .map(buildProperty, this) .join('') var timePart = timeProperties .map(buildProperty, this) .join('') return 'P' + datePart + 'T' + timePart function buildProperty (property) { var value = this[property] || 0 // Account for fractional part of seconds, // remove trailing zeroes. if (property === 'seconds' && this.milliseconds) { value = (value + this.milliseconds / 1000).toFixed(6).replace(/0+$/, '') } return value + propertiesISOEquivalent[property] } } var NUMBER = '([+-]?\\d+)' var YEAR = NUMBER + '\\s+years?' var MONTH = NUMBER + '\\s+mons?' var DAY = NUMBER + '\\s+days?' var TIME = '([+-])?([\\d]*):(\\d\\d):(\\d\\d)\\.?(\\d{1,6})?' var INTERVAL = new RegExp([YEAR, MONTH, DAY, TIME].map(function (regexString) { return '(' + regexString + ')?' }) .join('\\s*')) // Positions of values in regex match var positions = { years: 2, months: 4, days: 6, hours: 9, minutes: 10, seconds: 11, milliseconds: 12 } // We can use negative time var negatives = ['hours', 'minutes', 'seconds', 'milliseconds'] function parseMilliseconds (fraction) { // add omitted zeroes var microseconds = fraction + '000000'.slice(fraction.length) return parseInt(microseconds, 10) / 1000 } function parse (interval) { if (!interval) return {} var matches = INTERVAL.exec(interval) var isNegative = matches[8] === '-' return Object.keys(positions) .reduce(function (parsed, property) { var position = positions[property] var value = matches[position] // no empty string if (!value) return parsed // milliseconds are actually microseconds (up to 6 digits) // with omitted trailing zeroes. value = property === 'milliseconds' ? parseMilliseconds(value) : parseInt(value, 10) // no zeros if (!value) return parsed if (isNegative && ~negatives.indexOf(property)) { value *= -1 } parsed[property] = value return parsed }, {}) }