index.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. 'use strict'
  2. var extend = require('xtend/mutable')
  3. module.exports = PostgresInterval
  4. function PostgresInterval (raw) {
  5. if (!(this instanceof PostgresInterval)) {
  6. return new PostgresInterval(raw)
  7. }
  8. extend(this, parse(raw))
  9. }
  10. var properties = ['seconds', 'minutes', 'hours', 'days', 'months', 'years']
  11. PostgresInterval.prototype.toPostgres = function () {
  12. var filtered = properties.filter(this.hasOwnProperty, this)
  13. // In addition to `properties`, we need to account for fractions of seconds.
  14. if (this.milliseconds && filtered.indexOf('seconds') < 0) {
  15. filtered.push('seconds')
  16. }
  17. if (filtered.length === 0) return '0'
  18. return filtered
  19. .map(function (property) {
  20. var value = this[property] || 0
  21. // Account for fractional part of seconds,
  22. // remove trailing zeroes.
  23. if (property === 'seconds' && this.milliseconds) {
  24. value = (value + this.milliseconds / 1000).toFixed(6).replace(/\.?0+$/, '')
  25. }
  26. return value + ' ' + property
  27. }, this)
  28. .join(' ')
  29. }
  30. var propertiesISOEquivalent = {
  31. years: 'Y',
  32. months: 'M',
  33. days: 'D',
  34. hours: 'H',
  35. minutes: 'M',
  36. seconds: 'S'
  37. }
  38. var dateProperties = ['years', 'months', 'days']
  39. var timeProperties = ['hours', 'minutes', 'seconds']
  40. // according to ISO 8601
  41. PostgresInterval.prototype.toISOString = PostgresInterval.prototype.toISO = function () {
  42. var datePart = dateProperties
  43. .map(buildProperty, this)
  44. .join('')
  45. var timePart = timeProperties
  46. .map(buildProperty, this)
  47. .join('')
  48. return 'P' + datePart + 'T' + timePart
  49. function buildProperty (property) {
  50. var value = this[property] || 0
  51. // Account for fractional part of seconds,
  52. // remove trailing zeroes.
  53. if (property === 'seconds' && this.milliseconds) {
  54. value = (value + this.milliseconds / 1000).toFixed(6).replace(/0+$/, '')
  55. }
  56. return value + propertiesISOEquivalent[property]
  57. }
  58. }
  59. var NUMBER = '([+-]?\\d+)'
  60. var YEAR = NUMBER + '\\s+years?'
  61. var MONTH = NUMBER + '\\s+mons?'
  62. var DAY = NUMBER + '\\s+days?'
  63. var TIME = '([+-])?([\\d]*):(\\d\\d):(\\d\\d)\\.?(\\d{1,6})?'
  64. var INTERVAL = new RegExp([YEAR, MONTH, DAY, TIME].map(function (regexString) {
  65. return '(' + regexString + ')?'
  66. })
  67. .join('\\s*'))
  68. // Positions of values in regex match
  69. var positions = {
  70. years: 2,
  71. months: 4,
  72. days: 6,
  73. hours: 9,
  74. minutes: 10,
  75. seconds: 11,
  76. milliseconds: 12
  77. }
  78. // We can use negative time
  79. var negatives = ['hours', 'minutes', 'seconds', 'milliseconds']
  80. function parseMilliseconds (fraction) {
  81. // add omitted zeroes
  82. var microseconds = fraction + '000000'.slice(fraction.length)
  83. return parseInt(microseconds, 10) / 1000
  84. }
  85. function parse (interval) {
  86. if (!interval) return {}
  87. var matches = INTERVAL.exec(interval)
  88. var isNegative = matches[8] === '-'
  89. return Object.keys(positions)
  90. .reduce(function (parsed, property) {
  91. var position = positions[property]
  92. var value = matches[position]
  93. // no empty string
  94. if (!value) return parsed
  95. // milliseconds are actually microseconds (up to 6 digits)
  96. // with omitted trailing zeroes.
  97. value = property === 'milliseconds'
  98. ? parseMilliseconds(value)
  99. : parseInt(value, 10)
  100. // no zeros
  101. if (!value) return parsed
  102. if (isNegative && ~negatives.indexOf(property)) {
  103. value *= -1
  104. }
  105. parsed[property] = value
  106. return parsed
  107. }, {})
  108. }