Best Practices
Building reliable applications that handle dates and times correctly is not easy!
It works on your machine, but you run into issues when you deploy it to machines in different timezones and with users all around the world. These guidelines will help you avoid common pitfalls and build maintainable calendar applications.
Core Principles
1. Always Specify Timezones Explicitly
I can not stress this enough. Always set timezone explicitly!
Never rely on the system timezone for any date operations as it creates unpredictable behavior between environments.
The Date()
object is not your friend as it's not timezone aware.
Read more about this in Why NOT to use Date.
const date = new Date("2024-01-01");
// Could be anything 0, 1, 23, etc. depending on browser or server timezone
console.log(date.getHours());
User experience tip: If the user have not selected Country or Timezone in our app, default to the browsers local timezone, but never rely on it. This is how most calendar applications like Outlook handle timezone selection.
2. Store as Timestamps, Display with Calendar Dates
Timestamps are the perfect storage for date and time as they are timezone agnostic performant and serializable.
Use timezone aware calendar library like Datezone
when you want to display and do calendar math.
Read more about why you should use timestamps in Why use Timestamps.
import { addDays, calendarToTimestamp, format } from "datezone";
// 💡 User settings, store in a database, local storage or similar
// Do not rely on browsers timezone.
const userLocale = "en-US";
const userTimezone = "America/New_York";
// ✅ Store Dates as timestamp in your state, (url, local storage or database)
const calendarEvent = {
endTime: Date.now(),
id: "123",
name: "Team Meeting",
// Calendar date: 2025-03-08 5PM EST
startTime: calendarToTimestamp(2025, 3, 8, 17, 0, 0, 0, userTimezone),
};
// ✅ Whenever you you want to display a date or do calendar operations,
// set timezone explicitly, (For example the user's preferred timezone)
calendarEvent.startTime = addDays(calendarEvent.startTime, 1, userTimezone);
const formattedDate = format(calendarEvent.startTime, "MMM dd, yyyy HH:mm", {
locale: userLocale,
timeZone: userTimezone,
});
console.log(formattedDate);
3. Never Use simple arithmetic for Calendar Operations
Don't use simple math on timestamps for date operations. Daylight Savings Time (DST) transitions make this unreliable.
// Demonstrates how naive duration calculations ignore the "skipped" hour when Daylight Saving Time starts in New York (America/New_York).
// 1:59 AM EST (UTC-5) — just before the clocks spring forward
const before = new Date("2024-03-10T01:59:00-05:00");
// 3:00 AM EDT (UTC-4) — the next valid local time after the DST jump
const after = new Date("2024-03-10T03:00:00-04:00");
// How many minutes does JavaScript think have passed?
const diffMinutes = (after.getTime() - before.getTime()) / 60_000;
console.log("diffMinutes", diffMinutes); // 👉 1 minute (!)
Read more about DST and common pitfalls in how DST works
DST gotcha: During DST transitions, some days are 23 or 25 hours long. Simple arithmetic might give you the wrong calendar date twice a year.
Database, Storage and API Design
Database Storage Details: Postgres & MySQL
PostgreSQL:
- Store timestamps as either
TIMESTAMP
or as aBIGINT
(milliseconds since epoch, UTC). - Do not use
TIMESTAMP WITH TIME ZONE
(TIMESTAMPTZ
) for storage. Always store dates in UTC, regardless of user location. This makes it easy to compare dates and run statistical queries across users.
Note: Using TIMESTAMP
(without time zone) for storage is a strongly opinionated choice. I believe Timezone handling should always be managed in your business logic layer, not in the database. This keeps your data unambiguous and avoids subtle bugs caused by implicit timezone conversions in SQL engines. Let your application code handle all timezone conversions and calendar math.
- The popular
pg
npm package will return an ISO string forTIMESTAMP
columns and anumber
forBIGINT
columns. Usedatezone
'sfromIsoString()
orDate.parse().getTime()
to convert ISO strings to timestamps.
MySQL:
- Use the
TIMESTAMP
column type and set the connection option{ dateStrings: true }
in your MySQL driver. This ensures you get string values instead of JavaScriptDate
objects, which avoids subtle bugs and extra allocations. - As with Postgres, always store and interpret values as UTC.
import { createConnection } from "mysql2";
const _connection = createConnection({
database: "test",
dateStrings: true, // Set to true to return ISO Strings instead of Date objects,
host: "localhost",
user: "root",
});
- Storing all dates in UTC (as numbers or ISO strings) ensures consistent, unambiguous comparisons and makes analytics/statistical queries much simpler.
- Timezone conversion and related bugs should be in your business layer in code. Not in the Database.
In API's, use Timestamps or ISO Strings
When using an API, always use timestamps or ISO strings for dates.
Date object is not supported in JSON. Don't use it in API's.
Read more about this in Why NOT to use Date.
Summary
Following these best practices will help you build robust, maintainable applications with reliable date and time handling:
- Always specify timezones explicitly
- Store timestamps, display with timezone context
- Never use arithmetic for calendar operations
- Store as TIMESTAMP or BIGINT in Database
- In API's use Timestamps or ISO Strings
These practices, combined with datezone
's timezone-aware calendar functions, will eliminate the vast majority of date-related bugs in your applications.