You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
125 lines
3.3 KiB
125 lines
3.3 KiB
'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 |
|
}, {}) |
|
}
|
|
|