node-comfortv2.0.0

nc.time

Dates and durations: formatting, relative time, calendar math, time zones and scheduling, in any language Intl knows. The default language is English so results are the same on every machine; change it with setLocale() or per call with { locale }. Time zones work the same way with setTimezone() or { timeZone }.

const { time } = require("@ix-xs/node-comfort");
import { setLocale } from "@ix-xs/node-comfort/time";
time.setLocale("fr").setTimezone("Europe/Paris");
time.relative(Date.now() - 3600e3);        // "il y a 1 heure"
time.format(new Date(), "dddd D MMMM YYYY"); // "lundi 15 janvier 2024"
time.cron("0 9 * * 1-5", sendReport);        // weekdays at 9:00
GuideExplanations and examples for nc.time.
Read the guide →

Functions

nc.time.setLocale()

setLocale(locale: string): typeof nc.time

Sets the default language for relative, calendar, formatDuration and format.

Parameters

NameTypeDescription
localestringLike "fr", "en-GB", "pt-BR" or "ja".

Returns typeof import("./Time"): The module, so calls chain.

Example

time.setLocale("fr");
time.relative(Date.now() - 60e3); // "il y a 1 minute"

nc.time.setTimezone()

setTimezone(timeZone: string | undefined): typeof nc.time

Sets the default time zone for format, calendar, isSameDay, cron... Pass undefined to go back to the system zone.

Parameters

NameTypeDescription
timeZonestring | undefinedLike "America/New_York".

Returns typeof import("./Time"): The module, so calls chain.

Throws RangeError If the zone doesn't exist.

Example

time.setTimezone("Europe/Paris");

nc.time.getConfig()

getConfig(): { locale: string; timeZone: string | undefined; }

The current defaults.

Returns { locale: string, timeZone: string | undefined }

Example

time.getConfig(); // { locale: "en", timeZone: undefined }

nc.time.timezones()

timezones(): string[]

Every time zone Node knows, sorted.

Returns string[]

Example

time.timezones().includes("Europe/Paris"); // true

nc.time.offset()

offset(timeZone?: string, date?: DateInput): number

A zone's UTC offset at a given date, in minutes. Daylight saving included.

Parameters

NameTypeDescription
timeZoneoptionalstringDefaults to the global zone, then the system's.
dateoptionalDateInputDefaults to now.

Returns number

Example

time.offset("Europe/Paris", "2024-07-01"); // 120
time.offset("Europe/Paris", "2024-01-01"); // 60

nc.time.parseDuration()

parseDuration(input: string | number): number | null

Turns a duration into milliseconds. Understands "1h30m", "2 days", "500ms", clock times like "01:30:00", ISO durations like "PT1H30M", and plain numbers. Months and years are refused because their length varies; use add() for those.

Parameters

NameType
inputstring | number

Returns number | null: null when it isn't a duration.

Example

time.parseDuration("1h30m");    // 5400000
time.parseDuration("01:30:00"); // 5400000
time.parseDuration("banana");   // null

nc.time.formatDuration()

formatDuration(ms: number, options?: FormatDurationOptions): string

Writes a duration for humans.

Parameters

NameType
msnumber
optionsoptionalFormatDurationOptions

Returns string

Example

time.formatDuration(5_400_000);                               // "1h 30m"
time.formatDuration(90_000, { long: true });                  // "1 minute 30 seconds"
time.formatDuration(90_000, { long: true, locale: "fr" });    // "1 minute 30 secondes"
time.formatDuration(93_784_000, { units: 2 });                // "1d 2h"
time.formatDuration(5_405_000, { clock: true });              // "01:30:05"

nc.time.relative()

relative(date: DateInput, from?: DateInput, options?: RelativeOptions): string

Describes a date relative to now: "3 hours ago", "in 2 days", in any language.

Parameters

NameTypeDescription
dateDateInput
fromoptionalDateInputCompare with this date instead of now.
optionsoptionalRelativeOptions

Returns string

Example

time.relative(Date.now() - 3_600_000);                                  // "1 hour ago"
time.relative(Date.now() - 86_400_000, undefined, { numeric: "auto" }); // "yesterday"
time.relative(Date.now() - 3_600_000, undefined, { locale: "fr" });     // "il y a 1 heure"

nc.time.calendar()

calendar(date: DateInput, options?: CalendarOptions): string

Describes a date like a chat app does: "Today at 2:30 PM", "Yesterday at 9:05 AM", a weekday for the coming days, and the full date beyond that.

Parameters

NameType
dateDateInput
optionsoptionalCalendarOptions

Returns string

Example

time.calendar(new Date());                     // "Today at 2:30 PM"
time.calendar(yesterday, { locale: "fr" });    // "Hier à 09:05"

nc.time.format()

format(date: DateInput, pattern?: string, options?: FormatDateOptions): string

Formats a date with tokens, in any language and time zone.

TokenOutputTokenOutput
YYYY / YY2024 / 24HH / H09 / 9 (24h)
Qquarter, 1-4hh / h09 / 9 (12h)
MMMM / MMMJanuary / Janmm / m05 / 5
MM / M01 / 1ss / s07 / 7
DD / D05 / 5SSS042 (ms)
dddd / dddMonday / MonA / aPM / pm
dweekday, 0 is SundayZ / ZZ+02:00 / +0200
W / WWISO weekX / xUnix seconds / ms

Text in brackets stays as is: "[Today is] dddd".

Parameters

NameTypeDescription
dateDateInput
patternoptionalstringDefault: "YYYY-MM-DD HH:mm:ss"
optionsoptionalFormatDateOptions

Returns string: Or "Invalid Date".

Example

time.format(new Date(), "YYYY-MM-DD HH:mm:ss");                  // "2024-01-15 14:30:05"
time.format(Date.now(), "dddd D MMMM YYYY", { locale: "fr" });    // "lundi 15 janvier 2024"
time.format(Date.now(), "HH:mm Z", { timeZone: "Asia/Tokyo" });   // "22:30 +09:00"

nc.time.toISODate()

toISODate(date?: DateInput, options?: TimeZoneOptions): string

The date as YYYY-MM-DD, the format <input type="date"> and most APIs expect.

Parameters

NameTypeDescription
dateoptionalDateInputDefaults to now.
optionsoptionalTimeZoneOptions

Returns string

Example

time.toISODate(new Date(2024, 0, 5)); // "2024-01-05"

nc.time.add()

add(date: DateInput, amount: string | number, unit?: CalendarUnit): Date

Adds time to a date and returns a new Date. Months and years follow the calendar: January 31 plus one month is the end of February.

Parameters

NameTypeDescription
dateDateInput
amountnumber | stringA number with unit, or a duration like "1h30m".
unitoptionalCalendarUnitDefault: "ms"

Returns Date

Example

time.add(new Date(), "2h30m");
time.add(new Date(), 3, "days");
time.add(new Date(2024, 0, 31), 1, "month"); // 2024-02-29

nc.time.subtract()

subtract(date: DateInput, amount: string | number, unit?: CalendarUnit): Date

Subtracts time from a date and returns a new Date.

Parameters

NameTypeDescription
dateDateInput
amountnumber | string
unitoptionalCalendarUnitDefault: "ms"

Returns Date

Example

time.subtract(new Date(), 7, "days");
time.subtract(new Date(), "90m");

nc.time.diff()

diff(a: DateInput, b: DateInput, unit?: CalendarUnit): number

a - b in a unit. Fixed units can give fractions; months and years are whole calendar months and years.

Parameters

NameTypeDescription
aDateInput
bDateInput
unitoptionalCalendarUnitDefault: "ms"

Returns number

Example

time.diff("2024-01-03", "2024-01-01", "days");   // 2
time.diff("2024-03-15", "2024-01-20", "months"); // 1

nc.time.startOf()

startOf(date: DateInput, unit: StartOfUnit, options?: WeekOptions): Date

A new Date at the start of the day, week, month... in local time.

Parameters

NameType
dateDateInput
unitStartOfUnit
optionsoptionalWeekOptions

Returns Date

Example

time.startOf(new Date(), "day");                         // today at 00:00
time.startOf(new Date(), "week");                        // Monday at 00:00
time.startOf(new Date(), "week", { weekStartsOn: 0 });   // Sunday at 00:00

nc.time.endOf()

endOf(date: DateInput, unit: StartOfUnit, options?: WeekOptions): Date

A new Date at the last millisecond of the day, week, month...

Parameters

NameType
dateDateInput
unitStartOfUnit
optionsoptionalWeekOptions

Returns Date

Example

time.endOf(new Date(), "month"); // last day of the month, 23:59:59.999

nc.time.isBefore()

isBefore(a: DateInput, b: DateInput): boolean

Is a before b?

Parameters

Returns boolean

nc.time.isAfter()

isAfter(a: DateInput, b: DateInput): boolean

Is a after b?

Parameters

Returns boolean

Example

if (time.isAfter(expiresAt, Date.now())) grantAccess();

nc.time.isBetween()

isBetween(date: DateInput, start: DateInput, end: DateInput): boolean

Is the date between start and end, both included?

Parameters

NameType
dateDateInput
startDateInput
endDateInput

Returns boolean

nc.time.isSameDay()

isSameDay(a: DateInput, b: DateInput, options?: TimeZoneOptions): boolean

Are both dates on the same calendar day?

Parameters

NameType
aDateInput
bDateInput
optionsoptionalTimeZoneOptions

Returns boolean

Example

time.isSameDay(a, b);                            // local time
time.isSameDay(a, b, { timeZone: "Asia/Tokyo" }); // as seen in Tokyo

nc.time.isToday()

isToday(date: DateInput, options?: TimeZoneOptions): boolean

Is the date today?

Parameters

NameType
dateDateInput
optionsoptionalTimeZoneOptions

Returns boolean

nc.time.isYesterday()

isYesterday(date: DateInput, options?: TimeZoneOptions): boolean

Was the date yesterday?

Parameters

NameType
dateDateInput
optionsoptionalTimeZoneOptions

Returns boolean

nc.time.isTomorrow()

isTomorrow(date: DateInput, options?: TimeZoneOptions): boolean

Is the date tomorrow?

Parameters

NameType
dateDateInput
optionsoptionalTimeZoneOptions

Returns boolean

nc.time.isWeekend()

isWeekend(date: DateInput, options?: TimeZoneOptions): boolean

Is it a Saturday or a Sunday?

Parameters

NameType
dateDateInput
optionsoptionalTimeZoneOptions

Returns boolean

nc.time.isLeapYear()

isLeapYear(yearOrDate: DateInput): boolean

Is it a leap year?

Parameters

NameTypeDescription
yearOrDatenumber | DateInputA year (1000 to 9999) or a date.

Returns boolean

Example

time.isLeapYear(2024); // true
time.isLeapYear(1900); // false

nc.time.isValid()

isValid(value: unknown): boolean

Is it a real date, or something that parses to one?

Parameters

NameType
valueunknown

Returns boolean

Example

time.isValid("2024-02-29"); // true
time.isValid("2024-02-30"); // false

nc.time.daysInMonth()

daysInMonth(dateOrYear: DateInput, month?: number): number

Number of days in a month.

Parameters

NameTypeDescription
dateOrYearDateInput | numberA date, or a year when you pass month.
monthoptionalnumber1 to 12.

Returns number

Example

time.daysInMonth(new Date(2024, 1)); // 29
time.daysInMonth(2023, 2);           // 28

nc.time.dayOfYear()

dayOfYear(date?: DateInput): number

Day of the year, from 1 to 366.

Parameters

NameTypeDescription
dateoptionalDateInputDefaults to now.

Returns number

nc.time.weekOfYear()

weekOfYear(date?: DateInput, options?: { utc?: boolean; } | undefined): number

ISO week number, from 1 to 53. Weeks start on Monday, and week 1 holds the year's first Thursday.

Parameters

NameTypeDescription
dateoptionalDateInputDefaults to now.
optionsoptional{ utc?: boolean }Read the date in UTC.

Returns number

Example

time.weekOfYear(new Date(2021, 0, 3)); // 53, the last week of 2020

nc.time.min()

min(dates: Iterable<DateInput>): Date | undefined

The earliest date.

Parameters

NameType
datesIterable<DateInput>

Returns Date | undefined

nc.time.max()

max(dates: Iterable<DateInput>): Date | undefined

The latest date.

Parameters

NameType
datesIterable<DateInput>

Returns Date | undefined

nc.time.unix()

unix(): number

The current Unix timestamp, in seconds.

Returns number

nc.time.stopwatch()

stopwatch(): Stopwatch

Starts a precise stopwatch.

Returns Stopwatch

Example

const sw = time.stopwatch();
await loadConfig(); sw.lap("config");
await connectDb();  sw.lap("db");
console.log(sw.stop(true)); // "182.4ms"

nc.time.measure()

measure<T>(fn: () => T): Promise<{ result: Awaited<T>; duration: number; }>

Runs a function, sync or async, and tells you how long it took.

Parameters

NameType
fn() => T

Returns Promise<{ result: Awaited<T>, duration: number }>: duration is in milliseconds.

Example

const { result, duration } = await time.measure(() => db.query(sql));
nc.info(`Query took ${duration.toFixed(1)}ms`);

nc.time.every()

every(interval: string | number, task: () => unknown, options?: EveryOptions): ScheduledTask

Runs a task at a regular interval. Unlike setInterval, a slow run delays the next one instead of piling up, timing doesn't drift, and errors are caught.

Parameters

NameTypeDescription
intervalnumber | stringIn ms, or a duration like "30s".
task() => unknownCan be async.
optionsoptionalEveryOptions

Returns ScheduledTask

Throws RangeError If the interval isn't a positive duration.

Example

const job = time.every("5m", syncInventory, { immediate: true, onError: nc.error });
job.next(); // when it runs next
job.stop();

nc.time.nextRun()

nextRun(expression: string, options?: CronOptions): Date | undefined

The next date matching a cron expression, without scheduling anything.

Expressions have 5 fields (minute hour day month weekday), or 6 with seconds first. You can use *, lists (1,15), ranges (1-5), steps (*\/15), names (jan, mon-fri) and @daily, @hourly, @weekly, @monthly, @yearly. As in classic cron, when both the day and the weekday are set, either one matching is enough.

Parameters

NameType
expressionstring
optionsoptionalCronOptions

Returns Date | undefined: undefined if nothing matches within 8 years.

Throws SyntaxError If the expression is invalid.

Example

time.nextRun("0 9 * * mon-fri");                              // next weekday at 09:00
time.nextRun("0 0 1 * *", { timeZone: "America/New_York" });  // next 1st of the month

nc.time.cron()

cron(expression: string, task: () => unknown, options?: CronOptions): ScheduledTask

Runs a task on a cron schedule, in any time zone. Daylight-saving changes are handled: a skipped time is skipped, a repeated one runs once. See nextRun() for the syntax.

Parameters

NameTypeDescription
expressionstring
task() => unknownCan be async.
optionsoptionalCronOptions

Returns ScheduledTask

Throws SyntaxError If the expression is invalid.

Example

const job = time.cron("0 9 * * 1-5", sendDailyReport, { timeZone: "Europe/Paris" });
time.cron("*\/15 * * * *", refreshCache, { onError: nc.error });
job.stop();

Types

Import any of them in TypeScript with import type { CalendarOptions } from "@ix-xs/node-comfort", or in JavaScript with import("@ix-xs/node-comfort").CalendarOptions.

CalendarOptions

Options for calendar().

PropertyTypeDescription
localeoptionalstringDefaults to the global locale.
timeZoneoptionalstringDefaults to the global zone, then the system's.
nowoptionalDateInputWhat "now" is. Defaults to the current time.

CalendarUnit

A fixed unit, or months and years, whose length varies.

type CalendarUnit = DurationUnit | "M" | "month" | "months" | "y" | "year" | "years"

CronOptions

Options for cron() and nextRun().

PropertyTypeDescription
timeZoneoptionalstringZone in which the expression is read, like "Europe/Paris". Defaults to the global zone, then the system's.
fromoptionalDateInputnextRun() only: search after this date. Defaults to now.
unrefoptionalbooleanLet the process exit even if the schedule is active.
overlapoptionalbooleanStart a run even if the previous one hasn't finished. By default it's skipped.
onErroroptional((error: unknown) => void)Called when the task fails.

CronSpec

Parsed cron fields.

PropertyTypeDescription
secondsSet<number>
minutesSet<number>
hoursSet<number>
daysSet<number>
monthsSet<number>
weekdaysSet<number>
anyDayboolean
anyWeekdayboolean

DateInput

A date: a Date, a timestamp in ms, or a string new Date() understands (ISO 8601 is safest).

type DateInput = Date | number | string

DurationUnit

A unit with a fixed length.

type DurationUnit = "ms" | "millisecond" | "milliseconds" | "s" | "sec" | "second" | "seconds" | "m" | "min" | "minute" | "minutes" | "h" | "hour" | "hours" | "d" | "day" | "days" | "w" | "week" | "weeks"

EveryOptions

Options for every().

PropertyTypeDescription
immediateoptionalbooleanAlso run once right away.
unrefoptionalbooleanLet the process exit even if the schedule is active.
onErroroptional((error: unknown) => void)Called when the task fails. Without it, errors surface as unhandled errors.

FormatDateOptions

Options for format().

PropertyTypeDescription
localeoptionalstringLanguage of month and day names. Defaults to the global locale.
timeZoneoptionalstringShow the date in this zone, like "Asia/Tokyo". Defaults to the global zone, then the system's.

FormatDurationOptions

Options for formatDuration().

PropertyTypeDescription
longoptionalbooleanFull, translated words: "1 hour 30 minutes" instead of "1h 30m".
unitsoptionalnumberShow at most this many units, largest first: units: 2 turns "1d 3h 5m" into "1d 3h".
clockoptionalbooleanClock style, like "01:30:05".
millisecondsoptionalbooleanShow milliseconds. Defaults to true.
localeoptionalstringLanguage for long. Defaults to the global locale.

RelativeOptions

Options for relative().

PropertyTypeDescription
localeoptionalstringDefaults to the global locale.
numericoptional"always" | "auto""auto" says "yesterday" instead of "1 day ago". Defaults to "always".
styleoptional"long" | "short" | "narrow""short" gives "in 3 mo.". Defaults to "long".

ScheduledTask

A schedule returned by every() and cron().

PropertyTypeDescription
stop() => voidStops the schedule. A run in progress finishes.
next() => DateWhen the next run is planned.
isRunning() => booleanWhether a run is in progress.
runs() => numberHow many runs have started.

StartOfUnit

A unit for startOf() and endOf().

type StartOfUnit = "year" | "quarter" | "month" | "week" | "isoWeek" | "day" | "hour" | "minute" | "second"

Stopwatch

Returned by stopwatch().

PropertyTypeDescription
elapsed() => numberMilliseconds since the start.
stop(format?: boolean) => string | numberThe elapsed time; stop(true) formats it, like "1.5s".
lap(label?: string) => numberRecords a lap and returns the ms since the previous one.
laps() => { label: string; ms: number; total: number; }[]Every lap so far.
reset() => voidStarts over.

TimeZoneOptions

Time zone setting.

PropertyTypeDescription
timeZoneoptionalstringZone used to decide which day it is. Defaults to the global zone, then the system's.

WeekOptions

Week settings.

PropertyTypeDescription
weekStartsOnoptional0 | 4 | 6 | 2 | 1 | 3 | 5First day of the week: 0 for Sunday, 1 for Monday. Defaults to 1.
node-comfort v2.0.0View the source