Timezone Support
Generate recurring dates in specific IANA timezones. Every standard timezone identifier is supported — from America/New_York to Asia/Kolkata to UTC.
Quick Start
Add TIMEZONE to any config object. Dates are still returned in your FORMAT, but the recurrence is resolved in that timezone's calendar day.
import { generateRecurringDates } from "recurring-dates";
const result = generateRecurringDates({
STARTS_ON: "01-03-2025",
ENDS_ON: "31-03-2025",
FREQUENCY: "W",
WEEK_DAYS: ["MON", "WED", "FRI"],
TIMEZONE: "America/New_York", // <-- IANA timezone
});
console.log(result.text); // "Every week on Monday, Wednesday and Friday"
console.log(result.dates); // ["03-03-2025", "05-03-2025", ...]Timezone Utilities
A set of helper functions is exported from the package for common timezone operations.
isValidTimezoneisValidTimezone(timezone: string): booleanReturns true if the string is a valid IANA timezone identifier. Handles null / undefined safely.
Example
import { isValidTimezone } from "recurring-dates";
isValidTimezone("America/New_York"); // true
isValidTimezone("Europe/London"); // true
isValidTimezone("Invalid/Zone"); // false
isValidTimezone(null); // false
isValidTimezone(undefined); // falsegetUserTimezonegetUserTimezone(): stringReturns the user's current system timezone using the Intl API. Useful for automatically defaulting to the user's local timezone.
Example
import { getUserTimezone, generateRecurringDates } from "recurring-dates";
const userTz = getUserTimezone();
// e.g. "America/New_York", "Asia/Tokyo", "Europe/London"
// Auto-detect timezone for the user
const result = generateRecurringDates({
STARTS_ON: "01-01-2025",
ENDS_ON: "31-01-2025",
FREQUENCY: "D",
TIMEZONE: getUserTimezone(), // automatic
});getTimezoneOffsetgetTimezoneOffset(timezone: string): numberReturns the current UTC offset in hours for the timezone. Returns a decimal for half-hour offsets (e.g. 5.5 for Asia/Kolkata).
Example
import { getTimezoneOffset } from "recurring-dates";
getTimezoneOffset("UTC"); // 0
getTimezoneOffset("America/New_York"); // -5 (EST) or -4 (EDT)
getTimezoneOffset("Asia/Kolkata"); // 5.5
getTimezoneOffset("Asia/Tokyo"); // 9getTimezoneOffsetStringgetTimezoneOffsetString(timezone: string): stringReturns the offset as a formatted string suitable for display (e.g. "+05:30", "-08:00").
Example
import { getTimezoneOffsetString } from "recurring-dates";
getTimezoneOffsetString("UTC"); // "+00:00"
getTimezoneOffsetString("Asia/Kolkata"); // "+05:30"
getTimezoneOffsetString("America/New_York"); // "-05:00" or "-04:00"formatDateInTimezoneformatDateInTimezone(date: Date, timezone: string, options?: Intl.DateTimeFormatOptions): stringFormats a JavaScript Date object for display in a specific timezone using the Intl API.
Example
import { formatDateInTimezone } from "recurring-dates";
const date = new Date("2025-03-05T00:00:00Z");
formatDateInTimezone(date, "UTC"); // "03/05/2025"
formatDateInTimezone(date, "Asia/Tokyo"); // "03/05/2025"
formatDateInTimezone(date, "America/New_York"); // "03/04/2025" (previous day!)getTimezoneListgetTimezoneList(): Array<{ timezone: string; offset: string; offset_hours: number }>Returns the full list of supported timezones with their current UTC offsets. Useful for building a timezone picker UI.
Example
import { getTimezoneList } from "recurring-dates";
const zones = getTimezoneList();
// [
// { timezone: "America/New_York", offset: "-05:00", offset_hours: -5 },
// { timezone: "Europe/London", offset: "+00:00", offset_hours: 0 },
// { timezone: "Asia/Kolkata", offset: "+05:30", offset_hours: 5.5 },
// { timezone: "Asia/Tokyo", offset: "+09:00", offset_hours: 9 },
// ...
// ]
// Build a select dropdown
const options = zones.map((z) => ({
value: z.timezone,
label: `${z.timezone} (${z.offset})`,
}));SUPPORTED_TIMEZONESSUPPORTED_TIMEZONES: string[]Array of 40+ commonly used IANA timezone identifiers covering all major world regions.
Example
import { SUPPORTED_TIMEZONES } from "recurring-dates";
// Americas
"America/New_York", "America/Chicago", "America/Denver",
"America/Los_Angeles", "America/Toronto", "America/Sao_Paulo",
// Europe
"Europe/London", "Europe/Paris", "Europe/Berlin", "Europe/Moscow",
// Asia
"Asia/Dubai", "Asia/Kolkata", "Asia/Bangkok", "Asia/Singapore",
"Asia/Tokyo", "Asia/Seoul", "Asia/Shanghai",
// Australia & Pacific
"Australia/Sydney", "Australia/Melbourne", "Pacific/Auckland",
// Africa & UTC
"Africa/Cairo", "Africa/Johannesburg", "UTC", ...Real-World Examples
Global Team Meetings
Generate weekly standups at the same local time for each regional team:
import { generateRecurringDates } from "recurring-dates";
const regions = [
{ name: "US East", timezone: "America/New_York" },
{ name: "UK", timezone: "Europe/London" },
{ name: "India", timezone: "Asia/Kolkata" },
{ name: "Japan", timezone: "Asia/Tokyo" },
];
regions.forEach(({ name, timezone }) => {
const meetings = generateRecurringDates({
STARTS_ON: "01-03-2025",
ENDS_ON: "31-03-2025",
FREQUENCY: "W",
WEEK_DAYS: ["MON", "WED"],
TIMEZONE: timezone,
});
console.log(`${name}: ${meetings.dates.length} standups`);
});
// US East: 8 standups
// UK: 8 standups
// India: 8 standups
// Japan: 8 standupsAuto-Detect User Timezone (React)
Use getUserTimezone() so schedules always reflect the visitor's local timezone without any manual selection:
import { useRecurringDates, getUserTimezone } from "recurring-dates";
export function LocalSchedule({ start, end }) {
const userTz = getUserTimezone(); // e.g., "Asia/Tokyo"
const { dates, text } = useRecurringDates({
STARTS_ON: start,
ENDS_ON: end,
FREQUENCY: "W",
WEEK_DAYS: ["TUE", "THU"],
TIMEZONE: userTz,
FORMAT: "YYYY-MM-DD",
});
return (
<section>
<p className="caption">{text} — in {userTz}</p>
<ul>
{dates.map((d) => <li key={d}>{d}</li>)}
</ul>
</section>
);
}Monthly Billing Per Customer Timezone
Send invoices on the 1st of each month in the customer's local timezone so they always arrive at the right time:
import { generateRecurringDates } from "recurring-dates";
function getBillingSchedule(customerTimezone: string) {
return generateRecurringDates({
STARTS_ON: "01-01-2025",
ENDS_ON: "31-12-2025",
FREQUENCY: "M",
MONTH_DATES: [1],
TIMEZONE: customerTimezone,
FORMAT: "YYYY-MM-DD",
});
}
// Australian customer — first of month in AEST
const { dates } = getBillingSchedule("Australia/Sydney");
// ["2025-01-01", "2025-02-01", ..., "2025-12-01"]Staggered Maintenance Windows
Schedule monthly maintenance at 2 AM local time for each server region so customers are never all affected at once:
import { generateRecurringDates } from "recurring-dates";
const regions = {
"us-east-1": "America/New_York",
"eu-west-1": "Europe/London",
"ap-north-1": "Asia/Tokyo",
};
const schedule = Object.entries(regions).map(([region, tz]) => ({
region,
maintenance: generateRecurringDates({
STARTS_ON: "01-01-2025",
ENDS_ON: "31-12-2025",
FREQUENCY: "M",
MONTH_DATES: [28], // last few days of month
TIMEZONE: tz,
FORMAT: "YYYY-MM-DD",
}).dates,
}));Validation & Error Handling
When an invalid timezone is supplied the function returns the standard error shape — no exceptions thrown.
const result = generateRecurringDates({
STARTS_ON: "01-01-2025",
ENDS_ON: "31-01-2025",
FREQUENCY: "D",
TIMEZONE: "Not/ATimezone",
});
console.log(result);
// {
// dates: [],
// error: "Invalid timezone: 'Not/ATimezone'. Use a valid IANA timezone
// identifier (e.g., 'America/New_York', 'Europe/London', 'Asia/Tokyo')"
// }
// Omitting TIMEZONE (or passing null/undefined) is perfectly fine —
// the library falls back to local calendar day resolution.
const ok = generateRecurringDates({
STARTS_ON: "01-01-2025",
ENDS_ON: "31-01-2025",
FREQUENCY: "D",
// TIMEZONE not set — no error
});Supported Timezones
Any valid IANA timezone identifier works. The table below shows the 40+ timezones exported as SUPPORTED_TIMEZONES.
| Region | Timezone | Offset |
|---|---|---|
| Americas | America/New_York | UTC-5 / UTC-4 |
| Americas | America/Chicago | UTC-6 / UTC-5 |
| Americas | America/Denver | UTC-7 / UTC-6 |
| Americas | America/Los_Angeles | UTC-8 / UTC-7 |
| Americas | America/Anchorage | UTC-9 / UTC-8 |
| Americas | Pacific/Honolulu | UTC-10 |
| Americas | America/Toronto | UTC-5 / UTC-4 |
| Americas | America/Mexico_City | UTC-6 / UTC-5 |
| Americas | America/Sao_Paulo | UTC-3 |
| Americas | America/Buenos_Aires | UTC-3 |
| Europe | Europe/London | UTC+0 / UTC+1 |
| Europe | Europe/Paris | UTC+1 / UTC+2 |
| Europe | Europe/Berlin | UTC+1 / UTC+2 |
| Europe | Europe/Madrid | UTC+1 / UTC+2 |
| Europe | Europe/Rome | UTC+1 / UTC+2 |
| Europe | Europe/Moscow | UTC+3 |
| Europe | Europe/Istanbul | UTC+3 |
| Asia | Asia/Dubai | UTC+4 |
| Asia | Asia/Kolkata | UTC+5:30 |
| Asia | Asia/Bangkok | UTC+7 |
| Asia | Asia/Singapore | UTC+8 |
| Asia | Asia/Shanghai | UTC+8 |
| Asia | Asia/Tokyo | UTC+9 |
| Asia | Asia/Seoul | UTC+9 |
| Oceania | Australia/Sydney | UTC+10 / UTC+11 |
| Oceania | Australia/Perth | UTC+8 |
| Oceania | Pacific/Auckland | UTC+12 / UTC+13 |
| Africa | Africa/Cairo | UTC+2 / UTC+3 |
| Africa | Africa/Johannesburg | UTC+2 |
| Africa | Africa/Lagos | UTC+1 |
| UTC | UTC | UTC+0 |
Config Key Used
TIMEZONE — optional string. Accepts any valid IANA timezone identifier. Defaults to local time when omitted or null.