pomeranian-durations
TypeScript icon, indicating that this package has built-in type declarations

1.12.0 • Public • Published

Pomeranian Durations 🐶

An immutable duration library based on the ISO-8601 format for durations.

npm install pomeranian-durations --save
yarn add pomeranian-durations

Helpers

add

add | sum | addMicroseconds | addMilliseconds | addSeconds | addMinutes | addHours | addDays | addWeeks | addMonths | addYears | addToDate

Helpers to add to a duration.

add('PT3S', 'PT1S') // => 'PT4S'
sum(['PT1M', 'PT2M', 'PT3M']) // => 'PT6M'
addMicroseconds(1, 'PT1S') // => 'PT1.000001S'
addMilliseconds(1, 'PT1S') // => 'PT1.001S'
addSeconds(1, 'PT1S') // => 'PT2S'
addMinutes(1, 'PT1M') // => 'PT2M'
addHours(1, 'PT1M') // => 'PT1H1M'
addDays(1, 'P1D') // => 'P2D'
addWeeks(1, 'P1W') // => 'P2W'
addMonths(1, 'P1M') // => 'P2M'
addYears(1, 'P1Y') // => 'P2Y'
addToDate('PT1S', new Date('2000-01-01T00:00:00Z')) // => new Date('2000-01-01T00:00:01Z')
addToDate('PT1H', new Date('2000-01-01T00:00:00Z')) // => new Date('2000-01-01T01:00:00Z')
addToDate('P1M', new Date('2000-01-01T00:00:00Z')) // => new Date('2000-02-01T00:00:00Z')

ceil

ceil | ceilSeconds | ceilMinutes | ceilHours | ceilDays | ceilWeeks | ceilMonths | ceilYears

Helpers to ceil an ISO8601 duration to a particular granularity.

ceil('PT2s', 'PT5S') // => 'PT6S'
ceil('PT3s', 'PT5S') // => 'PT6S'
ceil('PT4s', 'PT5S') // => 'PT8S'
ceilSeconds('PT1.1S') // => 'PT2S'
ceilMinutes('PT1.1M') // => 'PT2M'
ceilHours('PT1.1H') // => 'PT2H'
ceilDays('P1.1D') // => 'P2D'
ceilWeeks('P1.1W') // => 'P2W'
ceilMonths('P1.1M') // => 'P2M'
ceilYears('P1.1Y') // => 'P2Y'

compare

gte | gt | lt | lte | eq

Helpers to compare 2 iso durations with each other. Only time parts can be compared as otherwise the comparison might be wrong. When any of the functions is partially applied the arguments are automatically swapped so one can write the following:

const isStillBigger = pipe(
  add('PT10S'),
  gte('PT1M'),
)
isStillBigger('PT50S') // => true
isStillBigger('PT49S') // => false
gte('PT2S', 'PT2S') // => true
gte('PT3S', 'PT2S') // => true
gte('PT3S')('PT2S') // => false
gt('PT2S', 'PT2S') // => false
gt('PT3S', 'PT2S') // => true
gt('PT3S')('PT2S') // => false
lt('PT2S', 'PT2S') // => false
lt('PT3S', 'PT2S') // => false
lt('PT3S')('PT2S') // => true
lte('PT2S', 'PT2S') // => true
lte('PT3S', 'PT2S') // => false
lte('PT3S')('PT2S') // => true
eq('PT2S', 'PT2S') // => true

conversions

asMicroseconds | asMilliseconds | asSeconds | asMinutes | asHours | asDecimalMicroseconds | asDecimalMilliseconds | asDecimalSeconds | asDecimalMinutes | asDecimalHours

Helpers to convert between different units.

asMicroseconds('PT2s') // => 2000000
asMilliseconds('PT2s') // => 2000
asSeconds('PT2s') // => 2
asMinutes('PT1h1m') // => 61
asHours('PT60m') // => 1
asDecimalMicroseconds('PT1m1s') // => 0.000061
asDecimalMilliseconds('PT1m1.1s') // => 0.0611
asDecimalSeconds('PT1m1s') // => 61
asDecimalMinutes('PT1m1s') // => 1.0166666666666666
asDecimalHours('PT1m1s') // => 0.016944444444444443

find

findSeconds | findMinutes | findHours | findDays | findWeeks | findMonths | findYears

Helpers for finding particular units in a given ISO8601 duration

findSeconds('PT1S') // => 1
findSeconds('PT1M') // => undefined
findMinutes('PT1M') // => 1
findMinutes('P1Y') // => undefined
findHours('PT1H') // => 1
findHours('PT1M') // => undefined
findDays('P1D') // => 1
findDays('PT1M') // => undefined
findWeeks('P1W') // => 1
findWeeks('PT1M') // => undefined
findMonths('P1M') // => 1
findMonths('PT1s') // => undefined
findYears('P1Y') // => 1
findYears('PT1M') // => undefined

floor

floor | floorSeconds | floorMinutes | floorHours | floorDays | floorWeeks | floorMonths | floorYears

Helpers to floor an ISO8601 duration to a particular granularity.

floor('PT2s', 'PT5S') // => 'PT4S'
floor('PT3s', 'PT5S') // => 'PT3S'
floor('PT4s', 'PT5S') // => 'PT4S'
floorSeconds('PT1.1S') // => 'PT1S'
floorMinutes('PT1.1M') // => 'PT1M'
floorHours('PT1.1H') // => 'PT1H'
floorDays('P1.1D') // => 'P1D'
floorWeeks('P1.1W') // => 'P1W'
floorMonths('P1.1M') // => 'P1M'
floorYears('P1.1Y') // => 'P1Y'

format

format

Helpers to format an iso duration. Available tokens are:

Token Unit Result example
%Y years 0, 1, ..., 112
%YY years 00, 01, ..., 112
%M' months 0, 1, ..., 112
%MM months 00, 01, ..., 112
%W weeks 0, 1, ..., 112
%WW weeks 00, 01, ..., 112
%D days 0, 1, ..., 112
%DD days 00, 01, ..., 112
%h hours 0, 1, ..., 112
%hh hours 00, 01, ..., 112
%m minutes 0, 1, ..., 112
%mm minutes 00, 01, ..., 112
%s seconds 0, 1, ..., 112
%ss seconds 00, 01, ..., 112
format('%hh:%mm:%ss', 'PT1M2S') // => '00:01:02'

from-sql

fromPostgres | fromPostgresVerbose

Helpers to convert from an SQL Time interval to an ISO8601 duration. More information can be found here http://www.postgresqltutorial.com/postgresql-interval/

fromPostgres('1 mons 01:02:03') // => 'P1MT1H2M3S'
fromPostgresVerbose('1 mons 3 secs 1 day') // => 'P1M1DT3S'

from

fromMicroseconds | fromMilliseconds | fromSeconds | fromMinutes | fromHours | fromDays | fromWeeks | fromMonths | fromYears

Helpers to convert an integer to an ISO8601 duration.

fromMicroseconds(1) // => 'PT0.000001S'
fromMilliseconds(1) // => 'PT0.001S'
fromSeconds(1) // => 'PT1S'
fromMinutes(1) // => 'PT1M'
fromHours(1) // => 'PT1H'
fromDays(1) // => 'P1D'
fromWeeks(1) // => 'P1W'
fromMonths(1) // => 'P1M'
fromYears(1) // => 'P1Y'

in

inSeconds | inMinutes | inHours

Helpers to convert an ISO8601 duration to a different unit. Date components (years, months, weeks, days) can't be converted to other unites without date and timezone information. Because of that converting date components into other units isn't supported right now. To do precise arithmetic operations it is recommended to avoid years, months, weeks and days completely when using durations. For more information have a look at http://www.ostyn.com/standards/scorm/samples/ISOTimeForSCORM.htm

inSeconds('PT1M') // => 'PT60S'
inMinutes('PT1H') // => 'PT60M'
inHours('PT60M') // => 'PT1H'

math

absolute | invert

Generic helpers to do math operations on durations.

absolute('PT-1S') // => 'PT1S'
absolute('PT1S') // => 'PT1S'
invert('PT-1M') // => 'PT1M'
invert('P-1DT1S') // => 'P1DT-1S'

normalize

normalizeTime

Helpers to normalize an ISO8601 duration. (eg. 61 seconds => 1 minute 1 second)

normalizeTime('P1DT1234S') // => 'P1DT20M34S'
normalizeTime('PT1S') // => 'PT1S'

remove

removeSeconds | removeMinutes | removeHours | removeDays | removeWeeks | removeMonths | removeYears | removeTimeUnits | removeDateUnits

Helpers to remove certain units from an ISO8601 string.

removeSeconds('PT1M1S') // => 'PT1M'
removeMinutes('PT1M1S') // => 'PT1S'
removeHours('PT1H1M') // => 'PT1M'
removeDays('P1DT1M') // => 'PT1M'
removeWeeks('P1WT1M') // => 'PT1M'
removeMonths('P1MT1M') // => 'PT1M'
removeYears('P1YT1M') // => 'PT1M'
removeTimeUnits('P1DT1M') // => 'P1D'
removeDateUnits('P1DT1M') // => 'PT1M'

sort

sortAsc | sortAscBy | sortDesc | sortDescBy

Helpers to sort durations. Attention durations with multiple date parts can only be compared using an approximation, so the result might be incorrect! (eg. on some days the following is true: 'PT23H1M' > 'PT1D'). If you're using the same units in all given durations that is not an issue.

['PT2S', 'PT1S'].sort(sortAsc) // ['PT1S', 'PT2S']
[{ randomKey: 'PT2S' }, { randomKey: 'PT1S' }].sort(sortAscBy('randomKey'))
  // => [{ randomKey: 'PT1S' }, { randomKey: 'PT2S' }]
['PT1S', 'PT2S'].sort(sortDesc) // ['PT2S', 'PT1S']
[{ randomKey: 'PT2S' }, { randomKey: 'PT1S' }].sort(sortDescBy('randomKey'))
  // => [{ randomKey: 'PT2S' }, { randomKey: 'PT1S' }]

subtract

subtract | subtractMilliseconds | subtractMicroseconds | subtractSeconds | subtractMinutes | subtractHours | subtractDays | subtractWeeks | subtractMonths | subtractYears | subtractFromDate

Helpers to subtract from a duration.

subtract('PT2M', 'PT1M') // => 'PT1M'
subtractMilliseconds(1, 'PT2M') // => 'PT2M-0.001S'
subtractMicroseconds(1, 'PT2M') // => 'PT2M-0.000001S'
subtractSeconds(1, 'PT2S') // => 'PT1S'
subtractMinutes(1, 'PT2M') // => 'PT1M'
subtractHours(1, 'PT2H') // => 'PT1H'
subtractDays(1, 'P2D') // => 'P1D'
subtractWeeks(1, 'P2W') // => 'P1W'
subtractMonths(1, 'P2M') // => 'P1M'
subtractYears(1, 'P2Y') // => 'P1Y'
subtractFromDate('PT1S', new Date('2000-01-01T00:00:00Z')) // => new Date('1999-12-31T23:59:59Z')

validate

isValid | isInvalid | whenInvalid | whenInvalidDuration

Helpers for validating ISO8601 durations.

isValid('PT1S') // => true
isValid('invalid') // => false
isInvalid('invalid') // => true
isInvalid('PT1S') // => false
const add10 = compose(
  add(10),
  whenInvalid(() => { throw new Error('Invalid duration') }),
);

add10('invalid') // error: 'Invalid duration'
const convertToHours = compose(
  asHours,
  whenInvalidDuration(null),
);

convertToHours('PT10H') // 10
convertToHours('Blub') // null

to-sql

toPostgresVerbose | toPostgres | toSql

toPostgresVerbose('P2DT3M') // => '@ 2 days 3 mins'
toPostgres('P2DT1M') // => '2 days 00:01:00'
toSql('P1Y2DT1M') // => '1-0 2 0:01:00'

transformations

toIso | toFragments | unitNamesAsc | unitNamesDesc

toIso({ seconds: 1, hours: 2 }) // => 'PT2H1S'
toFragments('PT1H1S') // => ({ seconds: 1, minutes: 0, hours: 1, days: 0, weeks: 0, months: 0, years: 0 })
unitNamesAsc('PT1H1S') // => ['seconds', 'hours']
unitNamesDesc('PT1H1S') // => ['hours', 'seconds']

Handling parsing errors

By default this library returns 'Invalid Duration' when it can't parse the format. As every application wants to handle those kinds of errors differently developers can use functional composition to create their own verison of pomeranian durations.

const errorHandler = () => 'An error occured, our team is already on it.'
const addSchoolHour = pipe(
  addHours(1.5),
  whenInvalid(errorHandler)
);

addSchoolHour('PT1H') // => PT2.5H
addSchoolHour('Invalid duration') // => 'An error occured, our team is already on it.'

Upgrade to version 1.0.0

  • Swapped arguments of add* and subtract* functions, as they're now curried.
// version 0.*
addSeconds('PT1S', 1) // => PT2S

// version 1.*
addSeconds(1, 'PT1S') // => PT2S
addSeconds(1)('PT1S') // => PT2S
  • Wrapper got removed (please use regular functional composition instead)
  • Pomeranian durations return 'Invalid Duration' when it receives an invalid duration. It's up to the developer how this should be handled. This library provides a helper whenInvalid which can be used to handle those cases.
import { addSeconds as _addSeconds, whenInvalid } from 'pomeranian-durations';

const addSeconds1 = pipe(
  whenInvalid(() => 'PT0S'),
  _addSeconds(10),
); // => PT10S

const addSeconds2 = pipe(
  _addSeconds(10),
  whenInvalid(() => 'PT0S'),
); // => PT0S

Precision Issues

Because date components (years, months, weeks, days) can't be converted to other unites without date and timezone information, pomeranian-durations doesn't support them yet. To do precise arithmetic operations it is recommended to avoid years, months, weeks and days completely when using durations.

For more information have a look at http://www.ostyn.com/standards/scorm/samples/ISOTimeForSCORM.htm

Contributing

All contributions are very welcome. If you need help with the setup or if this library is missing some features just create an issue or ping me on Twitter (@webpapaya). I'm happy to help you out.

Package Sidebar

Install

npm i pomeranian-durations

Weekly Downloads

309

Version

1.12.0

License

ISC

Unpacked Size

517 kB

Total Files

40

Last publish

Collaborators

  • webpapaya