postgres-interval 1.1.2 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts CHANGED
@@ -1,16 +1,18 @@
1
1
  declare namespace PostgresInterval {
2
2
  export interface IPostgresInterval {
3
- years?: number;
4
- months?: number;
5
- days?: number;
6
- hours?: number;
7
- minutes?: number;
8
- seconds?: number;
9
- milliseconds?: number;
3
+ years: number;
4
+ months: number;
5
+ days: number;
6
+ hours: number;
7
+ minutes: number;
8
+ seconds: number;
9
+ milliseconds: number;
10
10
 
11
11
  toPostgres(): string;
12
12
 
13
13
  toISO(): string;
14
+ toISOString(): string;
15
+ toISOStringShort(): string;
14
16
  }
15
17
  }
16
18
 
package/index.js CHANGED
@@ -1,18 +1,17 @@
1
1
  'use strict'
2
2
 
3
- var extend = require('xtend/mutable')
4
-
5
3
  module.exports = PostgresInterval
6
4
 
7
5
  function PostgresInterval (raw) {
8
6
  if (!(this instanceof PostgresInterval)) {
9
7
  return new PostgresInterval(raw)
10
8
  }
11
- extend(this, parse(raw))
9
+
10
+ Object.assign(this, parse(raw))
12
11
  }
13
- var properties = ['seconds', 'minutes', 'hours', 'days', 'months', 'years']
12
+ const properties = ['seconds', 'minutes', 'hours', 'days', 'months', 'years']
14
13
  PostgresInterval.prototype.toPostgres = function () {
15
- var filtered = properties.filter(this.hasOwnProperty, this)
14
+ const filtered = properties.filter(key => Object.prototype.hasOwnProperty.call(this, key) && this[key] !== 0)
16
15
 
17
16
  // In addition to `properties`, we need to account for fractions of seconds.
18
17
  if (this.milliseconds && filtered.indexOf('seconds') < 0) {
@@ -22,7 +21,7 @@ PostgresInterval.prototype.toPostgres = function () {
22
21
  if (filtered.length === 0) return '0'
23
22
  return filtered
24
23
  .map(function (property) {
25
- var value = this[property] || 0
24
+ let value = this[property] || 0
26
25
 
27
26
  // Account for fractional part of seconds,
28
27
  // remove trailing zeroes.
@@ -35,7 +34,7 @@ PostgresInterval.prototype.toPostgres = function () {
35
34
  .join(' ')
36
35
  }
37
36
 
38
- var propertiesISOEquivalent = {
37
+ const propertiesISOEquivalent = {
39
38
  years: 'Y',
40
39
  months: 'M',
41
40
  days: 'D',
@@ -43,22 +42,34 @@ var propertiesISOEquivalent = {
43
42
  minutes: 'M',
44
43
  seconds: 'S'
45
44
  }
46
- var dateProperties = ['years', 'months', 'days']
47
- var timeProperties = ['hours', 'minutes', 'seconds']
45
+ const dateProperties = ['years', 'months', 'days']
46
+ const timeProperties = ['hours', 'minutes', 'seconds']
48
47
  // according to ISO 8601
49
- PostgresInterval.prototype.toISO = function () {
50
- var datePart = dateProperties
48
+ PostgresInterval.prototype.toISOString = PostgresInterval.prototype.toISO = function () {
49
+ return toISOString.call(this, { short: false })
50
+ }
51
+
52
+ PostgresInterval.prototype.toISOStringShort = function () {
53
+ return toISOString.call(this, { short: true })
54
+ }
55
+
56
+ function toISOString ({ short = false }) {
57
+ const datePart = dateProperties
51
58
  .map(buildProperty, this)
52
59
  .join('')
53
60
 
54
- var timePart = timeProperties
61
+ const timePart = timeProperties
55
62
  .map(buildProperty, this)
56
63
  .join('')
57
64
 
58
- return 'P' + datePart + 'T' + timePart
65
+ if (!timePart.length && !datePart.length) return 'PT0S'
66
+
67
+ if (!timePart.length) return `P${datePart}`
68
+
69
+ return `P${datePart}T${timePart}`
59
70
 
60
71
  function buildProperty (property) {
61
- var value = this[property] || 0
72
+ let value = this[property] || 0
62
73
 
63
74
  // Account for fractional part of seconds,
64
75
  // remove trailing zeroes.
@@ -66,61 +77,80 @@ PostgresInterval.prototype.toISO = function () {
66
77
  value = (value + this.milliseconds / 1000).toFixed(6).replace(/0+$/, '')
67
78
  }
68
79
 
80
+ if (short && !value) return ''
81
+
69
82
  return value + propertiesISOEquivalent[property]
70
83
  }
71
-
72
84
  }
73
85
 
74
- var NUMBER = '([+-]?\\d+)'
75
- var YEAR = NUMBER + '\\s+years?'
76
- var MONTH = NUMBER + '\\s+mons?'
77
- var DAY = NUMBER + '\\s+days?'
78
- var TIME = '([+-])?([\\d]*):(\\d\\d):(\\d\\d)\.?(\\d{1,6})?'
79
- var INTERVAL = new RegExp([YEAR, MONTH, DAY, TIME].map(function (regexString) {
80
- return '(' + regexString + ')?'
86
+ const NUMBER = '([+-]?\\d+)'
87
+ const YEAR = `${NUMBER}\\s+years?`
88
+ const MONTH = `${NUMBER}\\s+mons?`
89
+ const DAY = `${NUMBER}\\s+days?`
90
+ // NOTE: PostgreSQL automatically overflows seconds into minutes and minutes
91
+ // into hours, so we can rely on minutes and seconds always being 2 digits
92
+ // (plus decimal for seconds). The overflow stops at hours - hours do not
93
+ // overflow into days, so could be arbitrarily long.
94
+ const TIME = '([+-])?(\\d+):(\\d\\d):(\\d\\d(?:\\.\\d{1,6})?)'
95
+ const INTERVAL = new RegExp(
96
+ '^\\s*' +
97
+ // All parts of an interval are optional
98
+ [YEAR, MONTH, DAY, TIME].map((str) => '(?:' + str + ')?').join('\\s*') +
99
+ '\\s*$'
100
+ )
101
+
102
+ // All intervals will have exactly these properties:
103
+ const ZERO_INTERVAL = Object.freeze({
104
+ years: 0,
105
+ months: 0,
106
+ days: 0,
107
+ hours: 0,
108
+ minutes: 0,
109
+ seconds: 0,
110
+ milliseconds: 0.0
81
111
  })
82
- .join('\\s*'))
83
-
84
- // Positions of values in regex match
85
- var positions = {
86
- years: 2,
87
- months: 4,
88
- days: 6,
89
- hours: 9,
90
- minutes: 10,
91
- seconds: 11,
92
- milliseconds: 12
93
- }
94
- // We can use negative time
95
- var negatives = ['hours', 'minutes', 'seconds', 'milliseconds']
96
-
97
- function parseMilliseconds (fraction) {
98
- // add omitted zeroes
99
- var microseconds = fraction + '000000'.slice(fraction.length)
100
- return parseInt(microseconds, 10) / 1000
101
- }
102
112
 
103
113
  function parse (interval) {
104
- if (!interval) return {}
105
- var matches = INTERVAL.exec(interval)
106
- var isNegative = matches[8] === '-'
107
- return Object.keys(positions)
108
- .reduce(function (parsed, property) {
109
- var position = positions[property]
110
- var value = matches[position]
111
- // no empty string
112
- if (!value) return parsed
113
- // milliseconds are actually microseconds (up to 6 digits)
114
- // with omitted trailing zeroes.
115
- value = property === 'milliseconds'
116
- ? parseMilliseconds(value)
117
- : parseInt(value, 10)
118
- // no zeros
119
- if (!value) return parsed
120
- if (isNegative && ~negatives.indexOf(property)) {
121
- value *= -1
122
- }
123
- parsed[property] = value
124
- return parsed
125
- }, {})
114
+ if (!interval) {
115
+ return ZERO_INTERVAL
116
+ }
117
+
118
+ const matches = INTERVAL.exec(interval) || []
119
+
120
+ const [
121
+ ,
122
+ yearsString,
123
+ monthsString,
124
+ daysString,
125
+ plusMinusTime,
126
+ hoursString,
127
+ minutesString,
128
+ secondsString
129
+ ] = matches
130
+
131
+ const timeMultiplier = plusMinusTime === '-' ? -1 : 1
132
+
133
+ const years = yearsString ? parseInt(yearsString, 10) : 0
134
+ const months = monthsString ? parseInt(monthsString, 10) : 0
135
+ const days = daysString ? parseInt(daysString, 10) : 0
136
+ const hours = hoursString ? timeMultiplier * parseInt(hoursString, 10) : 0
137
+ const minutes = minutesString
138
+ ? timeMultiplier * parseInt(minutesString, 10)
139
+ : 0
140
+ const secondsFloat = parseFloat(secondsString) || 0
141
+ // secondsFloat is guaranteed to be >= 0, so floor is safe
142
+ const absSeconds = Math.floor(secondsFloat)
143
+ const seconds = timeMultiplier * absSeconds
144
+ // Without the rounding, we end up with decimals like 455.99999999999994 instead of 456
145
+ const milliseconds = Math.round(timeMultiplier * (secondsFloat - absSeconds) * 1000000) / 1000
146
+ return {
147
+ years,
148
+ months,
149
+ days,
150
+ hours,
151
+ minutes,
152
+ seconds,
153
+ milliseconds
154
+ }
126
155
  }
156
+ PostgresInterval.parse = parse
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "postgres-interval",
3
3
  "main": "index.js",
4
- "version": "1.1.2",
4
+ "version": "3.0.0",
5
5
  "description": "Parse Postgres interval columns",
6
6
  "license": "MIT",
7
7
  "repository": "bendrucker/postgres-interval",
8
8
  "author": {
9
9
  "name": "Ben Drucker",
10
10
  "email": "bvdrucker@gmail.com",
11
- "url": "bendrucker.me"
11
+ "url": "https://www.bendrucker.me"
12
12
  },
13
13
  "engines": {
14
- "node": ">=0.10.0"
14
+ "node": ">=12"
15
15
  },
16
16
  "scripts": {
17
17
  "test": "standard && tape test.js"
@@ -21,12 +21,10 @@
21
21
  "interval",
22
22
  "parser"
23
23
  ],
24
- "dependencies": {
25
- "xtend": "^4.0.0"
26
- },
24
+ "dependencies": {},
27
25
  "devDependencies": {
28
- "tape": "^4.0.0",
29
- "standard": "^4.0.0"
26
+ "standard": "^16.0.0",
27
+ "tape": "^5.0.0"
30
28
  },
31
29
  "files": [
32
30
  "index.js",
package/readme.md CHANGED
@@ -1,12 +1,12 @@
1
- # postgres-interval [![Build Status](https://travis-ci.org/bendrucker/postgres-interval.svg?branch=master)](https://travis-ci.org/bendrucker/postgres-interval)
1
+ # postgres-interval [![tests](https://github.com/bendrucker/postgres-interval/workflows/tests/badge.svg)](https://github.com/bendrucker/postgres-interval/actions?query=workflow%3Atests)
2
2
 
3
3
  > Parse Postgres interval columns
4
4
 
5
5
 
6
6
  ## Install
7
7
 
8
- ```
9
- $ npm install --save postgres-interval
8
+ ```sh
9
+ npm install --save postgres-interval
10
10
  ```
11
11
 
12
12
 
@@ -18,10 +18,16 @@ var interval = parse('01:02:03')
18
18
  //=> {hours: 1, minutes: 2, seconds: 3}
19
19
  interval.toPostgres()
20
20
  // 3 seconds 2 minutes 1 hours
21
- interval.toISO()
21
+ interval.toISOString()
22
22
  // P0Y0M0DT1H2M3S
23
23
  ```
24
24
 
25
+ This package parses the default Postgres interval style. If you have changed [`intervalstyle`](https://www.postgresql.org/docs/current/runtime-config-client.html#GUC-INTERVALSTYLE), you will need to set it back to the default:
26
+
27
+ ```sql
28
+ set intervalstyle to default;
29
+ ```
30
+
25
31
  ## API
26
32
 
27
33
  #### `parse(pgInterval)` -> `interval`
@@ -37,9 +43,15 @@ A Postgres interval string.
37
43
 
38
44
  Returns an interval string. This allows the interval object to be passed into prepared statements.
39
45
 
40
- #### `interval.toISO()` -> `string`
46
+ #### `interval.toISOString()` -> `string`
47
+
48
+ Returns an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations) compliant string, for example `P0Y0M0DT0H9M0S`.
49
+
50
+ Also available as `interval.toISO()` for backwards compatibility.
51
+
52
+ #### `interval.toISOStringShort()` -> `string`
41
53
 
42
- Returns an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations) compliant string.
54
+ Returns an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations) compliant string shortened to minimum length, for example `PT9M`.
43
55
 
44
56
  ## License
45
57