Timeleap parses duration strings and shifts UTC timestamps forward or backward. It validates durations, advances a timestamp to the next or previous point, and builds cumulative backoff schedules. Fixed units (second to week) resolve to millisecond math, while calendar units (month, year) shift the UTC date and overflow to the next month when the day does not exist (for example Jan 31 plus 1 month lands on Mar 2 in a leap year or Mar 3 otherwise).
| Condition | Timeleap API |
|---|---|
| Validate duration input from a form, API request, or config file | parse |
| Set token, session, or cache TTL expiry from a duration string | next |
| Schedule the next run for a recurring job or cron-style task | next |
| Derive a subscription renewal date across months and years | next |
| Compute a rate-limit reset time from a fixed window | next |
| Skip weekends or blocked hours when picking the next valid slot | next |
| Roll a timestamp backward to a previous billing or reporting cycle | prev |
| Build a time range by pairing a backward start with a forward end | prev |
| Compute retry timestamps for failed calls using exponential growth | backoff |
| Generate a series of staged reminders or notifications before an event | backoff |
See USECASE.md for detailed scenarios and end-to-end examples.
Note
Prerequisites: For npm use Node.js (e.g. nodejs.org).
npm:
npm install @neabyte/timeleapCDN (jsDelivr/unpkg/esm.sh):
<script type="module">
import timeleap from 'https://cdn.jsdelivr.net/npm/@neabyte/timeleap/dist/index.mjs'
</script>Or via esm.sh:
<script type="module">
import timeleap from 'https://esm.sh/@neabyte/timeleap'
</script>Or via importmap:
<script type="importmap">
{
"imports": {
"@neabyte/timeleap": "https://cdn.jsdelivr.net/npm/@neabyte/timeleap/dist/index.mjs"
}
}
</script>
<script type="module">
import timeleap from '@neabyte/timeleap'
</script>import timeleap from '@neabyte/timeleap'
// Pick a fixed UTC base
const base = Date.UTC(2077, 0, 1)
// Validate a duration string
timeleap.parse('1 hour') // true
// Shift forward to the next point
timeleap.next({
time: base,
duration: '1 hour'
}) // 3376688400000
// Shift backward to the previous point
timeleap.prev({
time: base,
duration: '1 hour'
}) // 3376681200000
// Build a cumulative backoff schedule
timeleap.backoff({
time: base,
duration: '1 hour',
type: 'exponential',
limit: 3
})
// [3376688400000, 3376695600000, 3376710000000]
// Shift forward but skip weekends
timeleap.next({
time: base,
duration: '1 day',
skip: { day: ['saturday', 'sunday'] }
})
// 3376944000000Every unit accepts singular, plural, and short forms, with optional spacing:
timeleap.parse('1h') // short
timeleap.parse('1 h') // short with space
timeleap.parse('2 days') // plural with space
timeleap.parse('3weeks') // plural no space
timeleap.parse('2 months') // plural with space
timeleap.parse('10minutes') // plural no space
timeleap.parse('1x') // false, invalid unitimport timeleap from '@neabyte/timeleap'
// Pick a fixed UTC base
const base = Date.UTC(2077, 0, 1)
const iso = (time: number): string => new Date(time).toISOString()
// Shift and build a schedule
const next = timeleap.next({
time: base,
duration: '2 months'
})
const prev = timeleap.prev({
time: base,
duration: '2 months'
})
const schedule = timeleap.backoff({
time: base,
duration: '1 day',
type: 'linear',
limit: 3
})
// Print each result as ISO
console.log(`Base: ${iso(base)}`)
console.log(`Next: ${iso(next)}`)
console.log(`Prev: ${iso(prev)}`)
console.log(`Schedule: ${schedule.map(iso).join(', ')}`)Base: 2077-01-01T00:00:00.000Z
Next: 2077-03-01T00:00:00.000Z
Prev: 2076-11-01T00:00:00.000Z
Schedule: 2077-01-02T00:00:00.000Z, 2077-01-04T00:00:00.000Z, 2077-01-07T00:00:00.000Z
timeleap.backoff(input)input<TimeleapBackoff>Base time, duration, curve type, and limit.type<TimeleapCurve>Growth curve for the schedule.limit<number>Number of points, an integer between1and1024.
- Returns:
number[]- cumulative UTC timestamps.
timeleap.next(input)input<TimeleapShift>Base time, duration, and optional skip.skip<TimeleapSkip>(Optional) Time dimensions to avoid landing on.
- Returns:
number- UTC timestamp one duration ahead.
timeleap.parse(duration)duration<string>The duration string to validate.- Returns:
boolean-truewhen the string is a valid duration.
timeleap.prev(input)input<TimeleapShift>Base time, duration, and optional skip.skip<TimeleapSkip>(Optional) Time dimensions to avoid landing on.
- Returns:
number- UTC timestamp one duration behind.
When the shifted result lands on a blocked point, it steps by the smallest blocked dimension in the shift direction until it is clear.
timeleap.next({
time: base,
duration: '1 hour',
skip: {
minute: [0, 30], // 0..59
hour: [0, 1, 2, 3, 4, 5], // 0..23
day: ['saturday', 'sunday'], // 0..6 or weekday name
week: [1, 5], // week of month, 1..5
month: ['january', 'december'] // 1..12 or month name
}
})- Every field is optional, all checks run in UTC.
- Names are case-insensitive,
dayandmonthalso accept numbers. - Throws
RangeErrorwhen a value is out of range or the rules block a whole dimension (every minute, hour, day, week, or month).
deno task builddeno task testThis project is licensed under the MIT license. See the LICENSE file for details.