🐶
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
- ceil
- compare
- conversions
- find
- floor
- format
- from-sql
- from
- in
- math
- normalize
- remove
- sort
- subtract
- validate
- to-sql
- transformations
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
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
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
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
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.