Kazi Rahamatullah
Kazi Rahamatullah
AboutProjectsBlogContact
UsesBooks
ResumeView CV
Kazi Rahamatullah

© Copyright 2026 Kazi Rahamatullah

AboutProjectsBlogBooksUses
Twitter/XGitHubProduct HuntCodeSandbox
Back to Blog
JavaScriptDateTimeInterview PreparationFrontend Development

JavaScript Date & Time

Master JavaScript dates — Unix epoch, UTC vs local time, creating and parsing Date objects, getters and setters, arithmetic, common pitfalls, and production best practices with ISO 8601.

Jul 3, 202612 min read
JavaScript Date & Time

Introduction

Dates look simple until you ship them. A user in Dhaka, a server in Virginia, and a database storing "local time" without a zone will eventually produce bugs that only appear on the last Sunday in October.

JavaScript stores every Date internally as milliseconds since the Unix epoch (1 January 1970, 00:00:00 UTC). What you see in the console is almost always converted to the local time zone of the runtime — browser, Node.js, or test runner.

This guide covers:

  1. Core concepts — epoch, timestamps, UTC vs local
  2. Key terminology — UTC, GMT, time zones, and milliseconds
  3. Creating dates — constructors, Date.now(), and ISO 8601
  4. Reading & writing — getters, setters, and UTC equivalents
  5. Parsing & validation — safe parsing and invalid-date checks
  6. Arithmetic — adding time and measuring duration
  7. Common pitfalls — month index, DST, and parsing quirks
  8. Best practices — storage, display, and when to reach for a library

Each section includes runnable examples for the browser console and Node.js.

Note

The native Date API is fine for basic tasks. For time zones, recurring events, or locale-aware formatting at scale, pair this knowledge with Intl, or libraries like date-fns and Luxon — covered in the best practices section.

Course Reference

Quick index

#Section
1Core concepts
2Key terminology
3Creating Date objects
4Getters & setters
5Parsing & validation
6Date arithmetic
7Common pitfalls
8Best practices

1. Core concepts

IdeaDetail
Unix epoch1 January 1970, 00:00:00 UTC — the zero point for timestamps
Internal storageA Date holds milliseconds since epoch as a Number (UTC-based)
DisplaytoString(), getHours(), etc. convert to the local time zone
Time zone complexityOffsets, DST, and political changes make manual math error-prone
javascript
const now = new Date()
console.log(now.getTime()) // 1730000000000 — UTC milliseconds (example)
console.log(now.toString()) // local representation, e.g. "Fri Jul 03 2026 ..."
console.log(now.toISOString()) // always UTC: "2026-07-03T09:52:00.000Z"

Think of a Date as a UTC instant on a timeline. Local getters project that instant onto your machine's time zone; UTC getters read the instant directly.

Tip

When debugging, log toISOString() for a canonical UTC view and getTimezoneOffset() to see how many minutes local time differs from UTC.

javascript
const d = new Date()
console.log(d.toISOString()) // UTC
console.log(d.getTimezoneOffset()) // e.g. -360 for UTC+6 (Dhaka)

Back to index


2. Key terminology

Epoch time

The universal starting point for computer clocks: 1 January 1970, 00:00:00 UTC. Every timestamp counts forward from that moment.

Timestamp

A numeric count of time units since the epoch. JavaScript uses milliseconds; many Unix tools use seconds.

javascript
const ms = Date.now()
const seconds = Math.floor(ms / 1000)
 
console.log(ms) // 1730000000000
console.log(seconds) // 1730000000

Warning

Mixing seconds and milliseconds is a classic production bug. APIs and JWT exp claims often use seconds; Date.now() returns milliseconds. Always confirm the unit before converting.

UTC and GMT

UTC (Coordinated Universal Time) is the modern standard. GMT is often used interchangeably in casual speech. Neither observes daylight saving — they are stable baselines for storage and APIs.

Time zones

A region's offset from UTC, e.g. UTC+6 for Bangladesh Standard Time. Offsets can change with DST (not applicable in Bangladesh, but relevant for US/EU users).

javascript
// Intl gives correct zone-aware formatting without manual offset math
const formatter = new Intl.DateTimeFormat('en-BD', {
  timeZone: 'Asia/Dhaka',
  dateStyle: 'full',
  timeStyle: 'long',
})
 
console.log(formatter.format(new Date()))
// "Friday, 3 July 2026 at 3:52:00 pm GMT+6"

Back to index


3. Creating Date objects

Current date and time

javascript
const now = new Date() // current instant
const timestamp = Date.now() // same instant as number — preferred for performance

Performance

Date.now() avoids allocating a Date object when you only need a timestamp — useful in loops, logging, and performance measurements.

From a timestamp (milliseconds)

javascript
const d = new Date(1_700_000_000_000)
console.log(d.toISOString()) // "2023-11-14T22:13:20.000Z"

From an ISO 8601 string (recommended)

javascript
const d = new Date('2026-07-03T09:30:00.000Z')
console.log(d.getUTCFullYear()) // 2026
console.log(d.getUTCMonth()) // 6 (July — see pitfalls)
console.log(d.getUTCDate()) // 3

ISO format: YYYY-MM-DDTHH:mm:ss.sssZ — the trailing Z means UTC.

From components (local time)

javascript
// year, monthIndex, day, hours, minutes, seconds, ms
const birthday = new Date(1995, 6, 15) // 15 July 1995 — local midnight
console.log(birthday.getFullYear()) // 1995
console.log(birthday.getMonth()) // 6
console.log(birthday.getDate()) // 15

Note

The component constructor creates a date in local time. The same numbers passed to Date.UTC() produce a UTC instant — different results if your zone is not UTC.

javascript
const local = new Date(2026, 0, 1) // 1 Jan 2026, 00:00 local
const utcMs = Date.UTC(2026, 0, 1) // 1 Jan 2026, 00:00 UTC as milliseconds
const utc = new Date(utcMs)
 
console.log(local.toString())
console.log(utc.toISOString())

Static helpers

javascript
Date.now() // current timestamp (ms)
Date.parse('2026-07-03') // parse to timestamp — implementation-dependent for non-ISO strings
Date.UTC(2026, 6, 3) // UTC ms for 3 July 2026 00:00:00 UTC

Back to index


4. Getters & setters

Local getters

javascript
const d = new Date('2026-07-03T14:30:00.000Z')
 
console.log(d.getFullYear()) // local year
console.log(d.getMonth()) // 0–11
console.log(d.getDate()) // day of month 1–31
console.log(d.getDay()) // day of week 0–6 (Sun–Sat)
console.log(d.getHours())
console.log(d.getMinutes())
console.log(d.getSeconds())
console.log(d.getMilliseconds())

UTC getters

Use UTC methods when the source data is UTC (APIs, databases) and you want stable values regardless of the user's zone:

javascript
const d = new Date('2026-07-03T14:30:00.000Z')
 
console.log(d.getUTCFullYear()) // 2026
console.log(d.getUTCMonth()) // 6
console.log(d.getUTCDate()) // 3
console.log(d.getUTCHours()) // 14
console.log(d.getUTCMinutes()) // 30

Setters (mutate in place)

javascript
const d = new Date()
d.setFullYear(2027)
d.setMonth(11) // December
d.setDate(25)
d.setHours(0, 0, 0, 0) // midnight local
 
console.log(d.toString())

UTC setters mirror local ones: setUTCFullYear, setUTCMonth, setUTCDate, setUTCHours, etc.

Tip

Date objects are mutable. Calling setMonth changes the same object. Clone before mutating if you need the original:

javascript
const original = new Date()
const copy = new Date(original.getTime())
copy.setDate(copy.getDate() + 7)

Useful output methods

javascript
const d = new Date('2026-07-03T09:00:00.000Z')
 
d.toISOString() // "2026-07-03T09:00:00.000Z" — best for APIs/DB
d.toJSON() // same as toISOString()
d.toDateString() // "Fri Jul 03 2026"
d.toTimeString() // local time with zone name
d.toLocaleDateString('en-BD') // locale-aware display
d.toLocaleString('en-BD', { timeZone: 'Asia/Dhaka' })

Back to index


5. Parsing & validation

Prefer ISO strings

javascript
const good = new Date('2026-07-03T10:00:00Z')
const alsoGood = new Date('2026-07-03') // parsed as UTC midnight in modern engines

Avoid ambiguous formats

javascript
// ❌ Ambiguous — US vs EU date order
new Date('03/07/2026') // could be 3 Jul or 7 Mar depending on engine/locale
 
// ✅ Unambiguous
new Date('2026-07-03')

Warning

Date.parse() and the Date string constructor are implementation-dependent for non-ISO formats. Never rely on new Date('07/03/2026') in production without explicit parsing rules.

Detect invalid dates

javascript
function isValidDate(value) {
  const d = value instanceof Date ? value : new Date(value)
  return !Number.isNaN(d.getTime())
}
 
console.log(isValidDate('2026-07-03')) // true
console.log(isValidDate('not a date')) // false
console.log(isValidDate(new Date('invalid'))) // false

Safe parsing helper

javascript
function parseISODate(isoString) {
  const d = new Date(isoString)
  if (Number.isNaN(d.getTime())) {
    throw new RangeError(`Invalid ISO date: ${isoString}`)
  }
  return d
}
 
const published = parseISODate('2026-07-03T09:00:00.000Z')

Back to index


6. Date arithmetic

Work in milliseconds for addition and subtraction — avoid manually adding days to getDate() across month boundaries.

Constants

javascript
const SECOND = 1000
const MINUTE = 60 * SECOND
const HOUR = 60 * MINUTE
const DAY = 24 * HOUR

Add duration

javascript
function addDays(date, days) {
  return new Date(date.getTime() + days * DAY)
}
 
const today = new Date('2026-07-03T12:00:00Z')
const nextWeek = addDays(today, 7)
console.log(nextWeek.toISOString()) // 2026-07-10T12:00:00.000Z

Difference between two dates

javascript
function diffInDays(a, b) {
  const ms = Math.abs(b.getTime() - a.getTime())
  return Math.floor(ms / DAY)
}
 
const start = new Date('2026-01-01')
const end = new Date('2026-07-03')
console.log(diffInDays(start, end)) // 183

Human-readable duration

javascript
function formatDuration(ms) {
  const totalSeconds = Math.floor(ms / 1000)
  const hours = Math.floor(totalSeconds / 3600)
  const minutes = Math.floor((totalSeconds % 3600) / 60)
  const seconds = totalSeconds % 60
  return `${hours}h ${minutes}m ${seconds}s`
}
 
const started = new Date('2026-07-03T08:00:00Z')
const finished = new Date('2026-07-03T10:45:30Z')
console.log(formatDuration(finished - started)) // "2h 45m 30s"

Performance

Subtracting two Date objects coerces them to numbers (timestamps) automatically: dateB - dateA returns milliseconds. Clean and fast for duration math.

Back to index


7. Common pitfalls

Month indexing (0–11)

January is 0, December is 11. This trips up almost every developer at least once.

javascript
const wrong = new Date(2026, 7, 1) // 1 August 2026 — not July!
const right = new Date(2026, 6, 1) // 1 July 2026
 
console.log(wrong.getMonth()) // 7
console.log(right.getMonth()) // 6

Warning

getMonth() returns 0–11, but getDate() returns 1–31. Mixing these ranges is a top source of off-by-one date bugs in forms and calendars.

setMonth overflow

javascript
const d = new Date(2026, 0, 31) // 31 January
d.setMonth(1) // "February 31" rolls to March
console.log(d.toDateString()) // "Tue Mar 03 2026" in most engines

Always validate day-of-month when incrementing months, or use a library.

Daylight saving time (DST)

In zones with DST, some local times do not exist (spring forward) or occur twice (fall back). Manual offset math breaks.

javascript
// ❌ Fragile — assumes every day has 24 hours in local time
const tomorrow = new Date()
tomorrow.setDate(tomorrow.getDate() + 1)
 
// ✅ Prefer UTC ms arithmetic for fixed durations
const in24Hours = new Date(Date.now() + 24 * 60 * 60 * 1000)

Note

For "same clock time tomorrow" (e.g. 9:00 AM daily reminder), you need time-zone-aware logic — native Date alone is not enough. Use Intl or Luxon / date-fns-tz.

Comparing dates

javascript
const a = new Date('2026-07-03')
const b = new Date('2026-07-03')
 
console.log(a === b) // false — different object references
console.log(a.getTime() === b.getTime()) // true — same instant
console.log(a <= b) // true — coercion to number works

Tip

Compare instants with getTime() or numeric coercion. For calendar-day equality in a zone, compare formatted date parts or use a library's isSameDay helper.

Back to index


8. Best practices

Store UTC, display local

LayerRecommendation
DatabaseTIMESTAMPTZ or ISO 8601 UTC strings
APIAlways serialize with toISOString()
UIFormat with Intl or locale helpers for the user
javascript
// API response handler
function serializeForApi(date) {
  return date.toISOString()
}
 
// UI display
function displayLocal(isoUtc) {
  return new Intl.DateTimeFormat(undefined, {
    dateStyle: 'medium',
    timeStyle: 'short',
  }).format(new Date(isoUtc))
}
 
displayLocal('2026-07-03T09:00:00.000Z')
// e.g. "3 Jul 2026, 3:00 pm" in Asia/Dhaka

Use ISO 8601 for wire format

javascript
const payload = {
  title: 'New post',
  publishedAt: new Date().toISOString(),
}
JSON.stringify(payload)
// {"title":"New post","publishedAt":"2026-07-03T09:52:00.000Z"}

When to use a library

TaskNative DateLibrary (date-fns / Luxon)
Current timestamp✅✅
ISO parse/format✅✅
Add days in UTC✅✅
Named time zones⚠️ Intl✅
Recurring rules (RRULE)❌✅
Business days / holidays❌✅
javascript
// date-fns example
import { addDays, format, parseISO, isValid } from 'date-fns'
 
const base = parseISO('2026-07-03')
const next = addDays(base, 7)
console.log(format(next, 'yyyy-MM-dd')) // "2026-07-10"
console.log(isValid(parseISO('bad'))) // false

Performance

Import individual functions from date-fns (import { addDays } from 'date-fns/addDays') to keep bundles small. Luxon is heavier but excellent for time-zone math.

Tip

Production checklist

  • Store and transmit UTC (ISO 8601 with Z)
  • Never trust ambiguous new Date(string) input — validate format
  • Remember month is 0-indexed in constructors and getters
  • Confirm seconds vs milliseconds from external APIs
  • Use Intl.DateTimeFormat for user-facing labels
  • Reach for date-fns / Luxon for zones, recurrence, and calendars
  • Compare instants with getTime(), not === on objects

Interview Answer

"How does JavaScript store dates internally?"

A Date object wraps a single Number: milliseconds since 1 January 1970 UTC. Getters like getHours() apply the runtime's local time zone; getUTCHours() reads the UTC projection. For APIs and databases, serialize with toISOString() and parse ISO strings on input. Avoid non-ISO string parsing and manual DST math.

Back to index


Summary

JavaScript dates are UTC instants dressed in local clothing. Create them with ISO strings or timestamps, read them with the right getter (local vs UTC), mutate carefully, and do math in milliseconds. The month index, DST, and ambiguous parsers cause most real-world bugs — store UTC, show local, and escalate to Intl or date-fns when time zones get serious.

Related reads: JavaScript Strings for formatting patterns, and JavaScript Code Snippets for a reusable date-formatting utility.

Share this article

XLinkedInFacebook
Kazi Rahamatullah

Written by

Kazi Rahamatullah

FullStack Developer

X / TwitterGitHubLinkedIn

Subscribe to my newsletter

Stay up to date and get notified when I share new contents.

No spam ever, unsubscribe anytime