NC Logo UseToolSuite
Time & Date 📖 Pillar Guide

Time & Date in Programming: The Complete Developer Guide

Master Unix timestamps, timezone handling, cron expressions, and date formatting. Learn to avoid the most common time-related bugs in JavaScript, Python, and beyond.

Necmeddin Cunedioglu Necmeddin Cunedioglu

Practice what you learn

Unix Timestamp Converter

Try it free →

Time & Date in Programming: The Complete Developer Guide

Time handling is one of the most bug-prone areas in software development. Timezone offsets shift with Daylight Saving Time, Unix timestamps differ between seconds and milliseconds across languages, and cron syntax varies between platforms. This guide covers everything you need to handle time correctly.

Unix Timestamps Explained

A Unix timestamp is the number of seconds (or milliseconds) elapsed since January 1, 1970, 00:00:00 UTC — the Unix epoch.

Seconds vs. Milliseconds

This is the #1 source of timestamp bugs:

LanguageFunctionUnitDigits
JavaScriptDate.now()Milliseconds13
Pythontime.time()Seconds (float)10
PHPtime()Seconds10
JavaSystem.currentTimeMillis()Milliseconds13
Gotime.Now().Unix()Seconds10
RubyTime.now.to_iSeconds10

If your API returns 1711324800 (10 digits), it’s seconds. If it returns 1711324800000 (13 digits), it’s milliseconds. Confusing the two produces dates in the year 1970 or 56000+.

Convert timestamps instantly with our Unix Timestamp Converter.

Negative Timestamps

Dates before January 1, 1970 have negative timestamps:

  • 0 → January 1, 1970 00:00:00 UTC
  • -86400 → December 31, 1969 00:00:00 UTC
  • 2147483647 → January 19, 2038 03:14:07 UTC (32-bit max)

The Year 2038 Problem

32-bit signed integers overflow on January 19, 2038 at 03:14:07 UTC. Systems storing timestamps as 32-bit integers will interpret dates after this as December 1901. Modern 64-bit systems are not affected, but embedded systems, legacy databases, and IoT firmware may be vulnerable.

Timezones — The Hard Part

Timezones are not fixed offsets. They change with Daylight Saving Time, political decisions, and historical adjustments.

UTC vs. GMT vs. Offsets

  • UTC (Coordinated Universal Time) — The global reference time. Never changes with DST.
  • GMT (Greenwich Mean Time) — Historically equivalent to UTC, but technically a timezone.
  • UTC+03:00 — A fixed offset. Does not tell you which timezone.

The IANA Timezone Database

Always use IANA timezone identifiers (e.g., America/New_York, Europe/Istanbul) instead of abbreviations like “EST” or “PST”:

  • EST can mean Eastern Standard Time (UTC-5) or Eastern Standard Time in Australia (UTC+10).
  • America/New_York is unambiguous and automatically handles DST transitions.

Common Timezone Bugs

Bug 1: Storing local time instead of UTC

// ❌ Wrong — stores local time, breaks across timezones
const date = new Date().toISOString(); // This is UTC, actually correct
const local = new Date().toString(); // This includes timezone — don't store this

Rule: Always store timestamps in UTC. Convert to local time only at display time.

Bug 2: DST transition gaps

When clocks spring forward (e.g., 2:00 AM → 3:00 AM), times between 2:00-2:59 AM do not exist. When clocks fall back, times between 1:00-1:59 AM occur twice.

// March 8, 2026 — US DST spring forward
// 2:30 AM does not exist in America/New_York

Bug 3: Assuming UTC offset is constant

// ❌ Istanbul is UTC+3 in summer AND winter (since 2016)
// ❌ But New York switches between UTC-5 (EST) and UTC-4 (EDT)
// Never hardcode offsets — use the IANA database

Date Formatting — ISO 8601

ISO 8601 is the international standard for date and time representation:

FormatExampleUse case
Date only2026-03-17Database date fields
Date + Time (UTC)2026-03-17T14:30:00ZAPI responses, logs
Date + Time + Offset2026-03-17T14:30:00+03:00User-facing timestamps
DurationP1Y2M3DT4H5M6STime intervals

The Z suffix means UTC (Zulu time). Always prefer ISO 8601 in APIs for unambiguous parsing across all languages and platforms.

JavaScript Date Formatting

// ISO 8601
new Date().toISOString(); // "2026-03-17T14:30:00.000Z"

// Localized display
new Date().toLocaleDateString('en-US', {
  year: 'numeric', month: 'long', day: 'numeric',
  timeZone: 'America/New_York'
}); // "March 17, 2026"

// Intl.DateTimeFormat for full control
new Intl.DateTimeFormat('tr-TR', {
  dateStyle: 'full', timeStyle: 'short',
  timeZone: 'Europe/Istanbul'
}).format(new Date()); // "17 Mart 2026 Salı 17:30"

Cron Expressions

Cron expressions schedule recurring tasks. The standard format uses 5 fields:

┌───────── minute (0-59)
│ ┌───────── hour (0-23)
│ │ ┌───────── day of month (1-31)
│ │ │ ┌───────── month (1-12)
│ │ │ │ ┌───────── day of week (0-7, 0 and 7 = Sunday)
│ │ │ │ │
* * * * *

Common Patterns

ExpressionMeaning
* * * * *Every minute
0 * * * *Every hour (at minute 0)
0 0 * * *Every day at midnight
0 9 * * 1-5Weekdays at 9:00 AM
*/15 * * * *Every 15 minutes
0 0 1 * *First day of every month
0 */6 * * *Every 6 hours
0 0 * * 0Every Sunday at midnight

Special Characters

  • * — Every value
  • , — List: 1,3,5 = Monday, Wednesday, Friday
  • - — Range: 1-5 = Monday through Friday
  • / — Step: */15 = every 15th value
  • ? — No specific value (some implementations)

Cron and DST

Cron jobs scheduled during DST transitions can skip or run twice:

  • Spring forward (2:00 AM → 3:00 AM): Jobs at 2:30 AM are skipped
  • Fall back (3:00 AM → 2:00 AM): Jobs at 2:30 AM may run twice

Solution: Run critical cron jobs in UTC: TZ=UTC 0 2 * * *

Parse any cron expression with our Cron Expression Parser.

Best Practices

Storage

  1. Always store UTC — Convert to local time only at display time.
  2. Use ISO 8601 strings or Unix timestamps — never store formatted strings like “March 17, 2026”.
  3. Store timezone separately — If you need to know the user’s timezone, store the IANA identifier alongside the UTC timestamp.

APIs

  1. Return ISO 8601 with Z suffix"2026-03-17T14:30:00Z" is unambiguous.
  2. Accept flexible input — Parse multiple formats, but always normalize to UTC internally.
  3. Document your timestamp format — Specify whether you use seconds or milliseconds.

Scheduling

  1. Use UTC for cron — Avoid DST-related skips and duplicates.
  2. Test edge cases — February 29, DST transitions, year boundaries, the year 2038.
  3. Use NTP — Ensure your server clocks are synchronized via Network Time Protocol.

Language-Specific Tips

JavaScript

  • Date.now() returns milliseconds — divide by 1000 for seconds.
  • new Date(string) parsing is implementation-dependent — always use ISO 8601 format.
  • Use Intl.DateTimeFormat for localized display, not string concatenation.
  • Consider Temporal API (Stage 3 proposal) for future-proof date handling.

Python

  • datetime.utcnow() is deprecated in Python 3.12+ — use datetime.now(timezone.utc).
  • time.time() returns seconds as a float — the decimal part is sub-second precision.
  • Use zoneinfo module (Python 3.9+) instead of pytz for timezone handling.

SQL

  • Use TIMESTAMP WITH TIME ZONE (PostgreSQL) or DATETIME (MySQL) — never VARCHAR.
  • NOW() returns server-local time — use NOW() AT TIME ZONE 'UTC' or UTC_TIMESTAMP().

Further Reading

Necmeddin Cunedioglu
Necmeddin Cunedioglu Author

Software developer and the creator of UseToolSuite. I write about the tools and techniques I use daily as a developer — practical guides based on real experience, not theory.