@trap_stevo/timetide
v0.0.6
Published
Master the flow of time with the ultimate solution for precision scheduling, real-time synchronization, and temporal control. Designed for developers who demand flexibility and power, this system blends intuitive time parsing, dynamic countdowns, and pers
Maintainers
Readme
⏱️ @trap_stevo/timetide
Master the flow of time with the ultimate solution for precision scheduling, real-time synchronization, and temporal control.
Designed for developers who demand flexibility and power, TimeTide blends intuitive time parsing, dynamic countdowns, persistent task handling, natural-language date interpretation, and nanosecond-to-millennia resolution—all into a seamless, elegant toolkit.
Schedule events using human-readable commands, manage durations with clarity, and broadcast tick events to dashboards or distributed systems. Whether you build responsive interfaces, backend automation, or temporal workflows, TimeTide equips you to orchestrate time itself—with legendary elegance and unmatched control.
🚀 Features
- ⏱️ Human-readable durations —
"30m","1.5h","2w","10y","3gen","1mil" - 📅 Natural-language dates —
"next Friday","in 3 days","end of day","tomorrow" - 🔁 Repeating & persistent timers — recurring jobs that can survive restarts
- 📦 Named timers — pause, resume, extend, reduce, reschedule, chain follow-ups
- 📡 Tick streams — global ticks for dashboards, schedulers, analytics
- 🎯 Threshold comparison helpers —
exceedsDuration,exceedsTime - 🧠 Advanced parsing helpers —
normalizeDateInput,parseDateTime - 🧮 High-precision units — nanoseconds → millennia
- ⚙️ Configurable persistence — store timers anywhere
⚙️ Initialization
const TimeTide = require("@trap_stevo/timetide");
TimeTide.initializeTimers({
persistPath : "./.timers.json",
disablePersistence : false
});Initialization Options
| Key | Type | Description | Default |
|-----|------|-------------|---------|
| persistPath | string | Where persistent timers are stored | "./.timers.json" |
| disablePersistence | boolean | Disable reading/writing named timers | false |
| onLoadTimersError | function | Called if persisted state cannot be loaded | undefined |
📡 Scheduling API (One-Time & Named Tasks)
Methods
| Method | Description | Persisted? |
|--------|-------------|------------|
| schedule(name, dur, cb, meta?) | Named timer with persistence | ✔ |
| schedule(dur, cb) | Simple one-time timer | ❌ |
| countdown(dur, cb) | Alias of schedule(dur, cb) | ❌ |
| then(name, cb) | Chain a follow-up after a named timer fires | ✔ |
| clear(idOrName) | Cancel timer by ID or name | ✔ |
| extend(name, dur) | Add time to a named timer | ✔ |
| reduce(name, dur) | Subtract time from a named timer | ✔ |
| reschedule(name, dur) | Replace remaining time with new duration | ✔ |
| pause(name) | Freeze a named timer and store remaining time | ✔ |
| resume(name) | Resume a paused named timer | ✔ |
| pauseAll() | Pause all named timers | ✔ |
| resumeAll() | Resume all named timers | ✔ |
Example — Named, persistent timer
TimeTide.schedule("backupJob", "45m", () => {
console.log("Backup finished!");
}, { priority : "high" });
TimeTide.then("backupJob", () => {
console.log("Follow-up job triggered.");
});🔁 Recurring Tasks
Non-persistent recurring tasks
| Method | Description |
|--------|-------------|
| onEvery(interval, cb) | Run a callback every interval |
| repeat(interval, cb) | Alias of onEvery |
const id = TimeTide.onEvery("10s", () => {
console.log("Heartbeat");
});
// Later
TimeTide.clear(id);Persistent recurring tasks (cron-like loop)
function hourly()
{
console.log("Hourly job fired");
}
function scheduleHourly()
{
TimeTide.schedule("hourlyJob", "1h", () => {
hourly();
scheduleHourly();
});
}
scheduleHourly();- Recurs forever
- Can be paused / resumed via
pause("hourlyJob")/resume("hourlyJob") - Keeps the same named timer
Example — Auto-grading loop (safer pattern)
function autoGrade()
{
console.log("Auto-grade fired at", new Date().toISOString());
}
function chainAutoGrade()
{
TimeTide.schedule("auto-grade", "1m", () => {
try
{
autoGrade();
}
finally
{
// Always re-schedule, even if autoGrade throws
chainAutoGrade();
}
});
}
chainAutoGrade();📡 Tick Events
| Method | Description |
|--------|-------------|
| startTick(interval) | Create a global ticker emitting events |
| stopTick(interval) | Stop a global ticker |
| events.on("tick:interval", cb) | Subscribe to a specific tick label |
| events.on("tick", cb) | Subscribe to all ticks, receives label |
TimeTide.startTick("1s");
TimeTide.events.on("tick:1s", () => {
console.log("Second tick");
});
TimeTide.events.on("tick", (label) => {
// label could be "1s", "5m", "1000ms", etc.
});Stop:
TimeTide.stopTick("1s");⏳ Countdown & Expiration Helpers
| Method | Description |
|--------|-------------|
| countdown(dur, cb) | One-shot countdown (alias of schedule(dur, cb)) |
| expired(date) | true if date is earlier than now |
| getTimeUntil(date) | Milliseconds until given date |
| addTime(date, dur) | Returns new Date: date + duration |
| subtractTime(date, dur) | Returns new Date: date - duration |
Example — Time until a deadline
const deadline = "2025-12-01T17:00:00Z";
const msUntil = TimeTide.getTimeUntil(deadline);
console.log("Remaining:", TimeTide.toReadable(msUntil));
if (TimeTide.expired(deadline))
{
console.log("Deadline already passed");
}⚖️ Duration & Time Comparison
exceedsDuration(timeInput, threshold, options?)
Generic threshold comparison between a time and a reference.
Parameters
| Name | Type | Description |
|------|------|-------------|
| timeInput | Date \| number \| string | Target time to compare |
| threshold | number \| string | Milliseconds or duration string ("30m", "1h", "3d", "5y", "2gen", etc.) |
| options.reference | Date \| number \| string | Time to compare against (default: now) |
Return
{
valid : boolean, // false if inputs could not be parsed
exceeded : boolean, // true if |target - reference| > threshold
diffMs : number | null, // absolute difference in milliseconds
thresholdMs : number | null
}Example
const result = TimeTide.exceedsDuration("in 90m", "1h");
if (result.valid && result.exceeded)
{
console.log("Over 1 hour difference");
}
else if (result.valid)
{
console.log("Within 1 hour:", TimeTide.toReadable(result.diffMs));
}exceedsTime(timeInput, threshold, options?)
Alias of exceedsDuration.
const result = TimeTide.exceedsTime(lastPingAt, "30m", { reference : Date.now() });
if (result.exceeded)
{
console.log("Last ping is older than 30 minutes");
}Example — Eligibility gate within 1 hour
function inspectEligibility(gradeAtInput)
{
// gradeAtInput can be Date, ms, numeric string, ISO string, or natural language
const result = TimeTide.exceedsDuration(gradeAtInput, "1h", { reference : Date.now() });
if (!result.valid)
{
return { eligible : false, reason : "Invalid gradeAt input" };
}
if (result.exceeded)
{
return {
eligible : true,
reason : "Grade time exceeded 1 hour threshold"
};
}
return {
eligible : false,
reason : `Still within 1 hour window (${TimeTide.toReadable(result.diffMs)} difference)`
};
}🧠 Time Parsing, Conversion & Utilities
All time utilities are provided directly on TimeTide.
| Method | Description |
|--------|-------------|
| parseDuration(str) | Parse "1h 30m" → ms |
| toMilliseconds(str) | Duration string → milliseconds |
| toSeconds(str) | Duration string → seconds |
| toReadable(ms) | Convert ms to "2h 3m 10s" style string |
| toETA(seconds) | Convert seconds (numeric) to a readable ETA string |
| parseDate(str) | Parse natural-language / ISO date into Date |
| normalizeDateInput(input) | Normalize Date / ms / numeric / string input into Date or null |
| parseDateTime(input, unit) | Parse date-like input into a specific time unit |
| validDate(input) | Returns true if input can be parsed into a valid date |
parseDateTime(input, unit = "ms")
Convert any supported date-like input into the desired time unit using a unit + alias map that mirrors the duration model.
Supported input types
Date- millisecond
number - numeric timestamp string (e.g.
"1763754901019") - ISO date string
- natural language (
"in 1 hour","tomorrow","next Friday 6pm", etc.)
Supported units
| Unit | Aliases | Output |
|------|---------|--------|
| ms | millisecond, milliseconds | Milliseconds since epoch |
| s | sec, second, seconds | Whole seconds since epoch |
| m | min, minute, minutes | Whole minutes since epoch |
| h | hr, hour, hours | Whole hours since epoch |
| d | day, days | Whole days since epoch |
| w | week, weeks | Whole weeks since epoch |
| mo | month, months | Approx. months since epoch |
| y | yr, year, years | Approx. years since epoch |
| dec | decade, decades | Approx. decades since epoch |
| cen | century, centuries | Approx. centuries since epoch |
| gen | generation, generations | Approx. generations since epoch |
| mil | millennium, millennia | Approx. millennia since epoch |
| iso | — | ISO string |
| date | — | Date object |
Internally, parseDateTime:
- Normalizes the input into a
Date - Converts that
Dateto milliseconds (ms) - Uses a factor map + alias map to divide into the requested unit
- Returns a rounded-down integer for numeric units
Examples
// 1) Millisecond timestamp from ISO string
const ts = TimeTide.parseDateTime("2025-11-21T20:46:59.495Z", "ms");
// 2) Unix-style seconds from natural language
const unix = TimeTide.parseDateTime("next Friday", "seconds"); // "seconds" → "s"
// 3) Days since epoch
const days = TimeTide.parseDateTime("today", "days");
// 4) ISO from natural language
const iso = TimeTide.parseDateTime("in 2 weeks", "iso");
// 5) Using a numeric ms timestamp input and converting to days
const createdAtMs = Date.now() - TimeTide.toMilliseconds("3d 2h");
const ageDays = TimeTide.parseDateTime(createdAtMs, "days");
// ⭐ 6) Raw millisecond timestamp directly from DB / storage
const dbTs = 1763754901019;
const normalized = TimeTide.parseDateTime(dbTs, "ms");
console.log(normalized); // → 1763754901019
// 7) Gating based on a future point in time
const gradeAt = TimeTide.parseDateTime("in 1 hour", "ms");
const remainingMs = gradeAt - Date.now();
console.log("Remaining until grade:", TimeTide.toReadable(remainingMs));🔥 Supported Duration Units
TimeTide supports mixed & stacked units in parseDuration / toMilliseconds:
"1h 30m"
"2d 4h 20m 5s"
"1.5h"
"3gen 5y 2mo"
"10y 2w 3d 4h 5m 6s"Unit Table
| Unit | Tokens | Approximate ms |
|------|--------|----------------|
| Nanoseconds | ns, nanoseconds | 1e-6 |
| Milliseconds | ms, milliseconds | 1 |
| Seconds | s, seconds | 1000 |
| Minutes | m, minutes | 60000 |
| Hours | h, hours | 3600000 |
| Days | d, days | 86400000 |
| Weeks | w, weeks | 604800000 |
| Months | mo, months | 2629800000 |
| Years | y, years | 31557600000 |
| Decades | dec, decades | 315576000000 |
| Centuries | cen, centuries | 3155760000000 |
| Generations | gen, generations | 788940000000 |
| Millennia | mil, millennia | 31557600000000 |
🌙 Natural-Language Date Phrases
Examples of phrases parseDate / parseDateTime can understand include:
"now","today","tonight""end of day","midnight","noon""yesterday","tomorrow""in 3 hours","in 5 days","in 2 weeks""10 minutes ago","3 days ago""next Monday","last Friday","this Friday""this month","next month","last year""next weekend""January 6, 2025 13:45""2025-01-06T13:45:00Z"
📦 Installation
npm install @trap_stevo/timetide⚡ Quick Start (Step-by-Step)
1) Initialize TimeTide
const TimeTide = require("@trap_stevo/timetide");
TimeTide.initializeTimers({
persistPath : "./.timers.json",
disablePersistence : false
});2) Schedule a simple one-time task
TimeTide.schedule("10s", () => {
console.log("⏰ Fired after 10 seconds");
});3) Create a named, persistent task
TimeTide.schedule("daily-report", "24h", () => {
console.log("📊 Daily report generated");
}, { type : "report", priority : "high" });
TimeTide.then("daily-report", () => {
console.log("✅ Daily report follow-up");
});If your app restarts and you call initializeTimers again with the same persistPath, daily-report will be restored (as long as it hasn’t fired yet and its callback is registered again).
4) Create a recurring heartbeat
const heartbeatID = TimeTide.onEvery("5s", () => {
console.log("💓 Heartbeat:", new Date().toISOString());
});
// Later:
TimeTide.clear(heartbeatID);5) Use natural-language dates and duration comparison
// "next Friday 18:00" as a millisecond timestamp
const launchAtMs = TimeTide.parseDateTime("next Friday 6pm", "ms");
const remainingMs = launchAtMs - Date.now();
console.log("Time until launch:", TimeTide.toReadable(remainingMs));
// Check if something is more than 1 hour away from now
const result = TimeTide.exceedsDuration("in 90m", "1h");
if (result.valid && result.exceeded)
{
console.log("⏳ More than 1 hour difference");
}🗂️ Inspect Active Timers
const timers = TimeTide.getTimers();
console.log(timers);
/*
{
backupJob : {
createdAt : Date,
remainingMs : 120000,
readable : "2m",
meta : { priority : "high" }
}
}
*/🧼 Cleanup Pattern
const id = TimeTide.onEvery("5s", () => {
console.log("tick");
});
TimeTide.startTick("1m");
function cleanup()
{
TimeTide.clear(id);
TimeTide.stopTick("1m");
}
process.on("SIGINT", cleanup);
process.on("SIGTERM", cleanup);📜 License
See LICENSE.md
⏱️ Control Time. Shape Systems.
From millisecond-precision scheduling to natural-language parsing, from duration comparison to recurring, persistent, restart-safe timers—TimeTide gives you complete command over time itself.
