skip to content

Date & Time API

The java.time API and what it replaced: the core temporal types, durations and periods, zones and daylight saving, formatting and parsing, and immutability. Date handling questions are common because so much production code gets time zones wrong.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

page 1 of 2

What is the spring-forward gap in DST, and what happens in java.time when you create a ZonedDateTime at a local time that does not exist?

level: juniorimportance: must knowfreq 62%

answer

  1. Spring-forward = clock jumps, an hour vanishes
  2. Gap local times do not exist as an instant
  3. java.time shifts forward by the gap, never throws
  4. Detect via ZoneRules.getTransition(...).isGap()
  5. 02:30 Berlin -> 03:30 +02:00

basics

~20 s

In spring, clocks jump forward (e.g. 02:00 to 03:00), so an hour of local time never happens. If you ask java.time for a ZonedDateTime in that missing hour, it pushes the time forward by the gap (the missing hour) instead of failing.

solid answer

~40 s

Daylight Saving Time means clocks shift twice a year. In spring-forward, the clock skips an hour: at 02:00 it jumps to 03:00, so local times like 02:30 do not exist on that day. When you build a ZonedDateTime for a non-existent local time, java.time does not throw. By default it resolves the gap by shifting the instant forward by the length of the gap (usually one hour) and applying the later offset, so 02:30 becomes 03:30 in the new offset. This is documented behavior of ZonedDateTime.of and ofLocal: in a gap the local time is adjusted forward; in an overlap it picks the earlier offset. Knowing this prevents off-by-one-hour bugs when scheduling near the spring transition.

code

java · 7 lines
java
ZoneId berlin = ZoneId.of("Europe/Berlin");
LocalDateTime missing = LocalDateTime.parse("2024-03-31T02:30");
ZonedDateTime z = missing.atZone(berlin);
System.out.println(z); // 2024-03-31T03:30+02:00[Europe/Berlin]

var t = berlin.getRules().getTransition(missing);
System.out.println(t != null && t.isGap()); // true

go deeper

for a junior

Knows DST exists and that spring loses an hour; can state java.time shifts the time forward rather than throwing.

for a middle

Can show the exact transformation (02:30 -> 03:30 with the later offset) and detect a gap with ZoneRules.getTransition.

for a senior

Reasons about the scheduling consequences (a local-time job firing late on transition day) and chooses to reject/log vs accept the shift.

for a principal

Sets org-wide policy: store instants/UTC, schedule in UTC where possible, and define how gap inputs are validated across services to avoid silent shifts.

**Background terms.** - **Local time / `LocalDateTime`**: a date-and-time with no time zone, e.g. `2024-03-31T02:30`. It is just "wall-clock" numbers; it does not by itself name a moment on the global timeline. - **Time zone / `ZoneId`**: a named region (e.g. `Europe/Berlin`) whose rules say which UTC **offset** applies at any moment. - **Offset / `ZoneOffset`**: the fixed difference from UTC, e.g. `+01:00` (winter) or `+02:00` (summer). - **Instant**: an exact point on the global timeline (UTC seconds since 1970). A `ZonedDateTime` = local time + zone, which together resolve to one instant. - **DST (Daylight Saving Time)**: many zones move their clocks forward in spring and back in autumn to shift daylight. The forward move is **spring-forward**; the backward move is **fall-back**. **The spring-forward gap.** When a zone springs forward, say at `02:00` the clock instantly becomes `03:00`, the wall-clock times `02:00:00`–`02:59:59` **never occur** that day. This missing interval is the **gap** (typically one hour). A `LocalDateTime` inside the gap is a perfectly valid date-time object, but it corresponds to **no real instant** in that zone. **What java.time does.** `ZonedDateTime.of(localDateTime, zone)` (and `LocalDateTime.atZone(zone)`, which calls the same logic) must turn the local time into an instant. The contract for a gap is: **shift the local time forward by the size of the gap and use the offset after the transition**. So `2024-03-31T02:30` in `Europe/Berlin` becomes `2024-03-31T03:30+02:00`, not an exception, not a null. The instant produced is the moment one hour later than the naive reading would suggest. The rules come from `ZoneRules.getTransition(localDateTime)`, which returns a `ZoneOffsetTransition`; for a gap, `isGap()` is true and the resolution adds `transition.getDuration()`. **Why it never throws.** java.time deliberately favors a total, predictable function over exceptions: every `LocalDateTime` maps to exactly one `ZonedDateTime`. The cost is that a silently-shifted value can surprise you, hence the bugs around scheduling. **How to detect a gap yourself.** Use `zone.getRules().getTransition(local)`; if it returns a non-null transition with `isGap() == true`, your local time was invalid and got moved. You can then decide to reject the input, log it, or accept the shift. **Practical impact.** A daily 02:30 job scheduled by local time will, on the spring-forward day, actually fire at 03:30 (the gap pushed it). Alarm/cron-style code that assumes "02:30 exists every day" is wrong twice a year for affected zones.

  • How can you tell, in code, that a given LocalDateTime fell into a spring-forward gap?
    Call zone.getRules().getTransition(localDateTime). If it returns a non-null ZoneOffsetTransition whose isGap() is true, the local time does not exist and resolution shifts it forward by getDuration().
  • Does this gap behavior affect Instant or epoch-millis arithmetic?
    No. Instant and epoch time are pure UTC points with no DST. The gap only matters when converting a wall-clock LocalDateTime to/from a zoned time.

saying these in an interview costs you the question

  • Claiming java.time throws an exception for a gap time (it does not by default)
  • Thinking a gap LocalDateTime is invalid to construct (only its zone resolution is special)
  • Assuming the gap is always exactly one hour (most are, but the duration comes from the rules)
  • Believing UTC or Instant are affected by DST (they are not; only wall-clock zones)

context

open as a page

What is the difference between Duration and Period in java.time, and when would you use each?

level: juniorimportance: must knowfreq 78%

basics

~10 s

Duration measures an exact amount of time in seconds and nanoseconds (good for hours/minutes/seconds). Period measures an amount of dates in years, months and days. Use Duration for machine time, Period for calendar time.

open as a page

Why is DateTimeFormatter preferred over SimpleDateFormat, and what makes it safe to share across threads?

level: juniorimportance: must knowfreq 70%

basics

~10 s

DateTimeFormatter is immutable and thread-safe, so one instance can be shared everywhere. The old SimpleDateFormat keeps mutable state during parsing, so sharing it between threads corrupts results.

open as a page

Why is java.util.Date discouraged in modern Java code, and what do you use instead?

level: juniorimportance: must knowfreq 70%

basics

~10 s

java.util.Date is mutable, not thread-safe, mixes a date and a time together, and has confusing deprecated methods. Use the java.time package (LocalDate, LocalDateTime, Instant) instead, which is clearer and immutable.

open as a page

What are LocalDate, LocalTime, and LocalDateTime, and when would you use each?

level: juniorimportance: must knowfreq 80%

basics

~10 s

LocalDate is a date only (year-month-day), LocalTime is a time only (hour-minute-second), and LocalDateTime is both together. None of them carries a time zone. Use whichever matches the data you have.

open as a page

How do you convert between epoch milliseconds (a long) and Instant, and what are the precision pitfalls?

level: juniorimportance: must knowfreq 65%

basics

~10 s

Use Instant.ofEpochMilli(long) to go from milliseconds to an Instant, and instant.toEpochMilli() to go back. The long counts milliseconds since 1970-01-01 UTC. toEpochMilli() drops sub-millisecond nanos.

open as a page

Why can't you convert a LocalDateTime directly to an Instant, and how do you do it correctly?

level: juniorimportance: must knowfreq 70%

basics

~20 s

A LocalDateTime has no time zone, so it doesn't point to a real moment. You must attach a zone (or offset) first: localDateTime.atZone(zone).toInstant(), and the same local time maps to different Instants in different zones.

open as a page

Why does `date.plusDays(1);` on its own line have no effect, and how do you fix it?

level: juniorimportance: must knowfreq 85%

basics

~20 s

All java.time types are immutable, so plusDays builds and returns a NEW date instead of changing the old one. If you ignore the returned value, nothing changes. Fix it by assigning the result: date = date.plusDays(1).

open as a page

What is java.time.Instant, and what does it represent?

level: juniorimportance: must knowfreq 70%

basics

~10 s

Instant is a single point in time on the global timeline, measured as seconds and nanoseconds from the epoch (1970-01-01 00:00 UTC). It carries no time zone, so it's an unambiguous machine timestamp.

open as a page

What is the fall-back overlap in DST, how does java.time resolve an ambiguous local time by default, and how do you choose the other offset?

level: middleimportance: must knowfreq 58%

basics

~20 s

In autumn, clocks go back so one hour of local time happens twice. A time like 02:30 is ambiguous. By default java.time picks the earlier of the two offsets. You can force the other one with withLaterOffsetAtOverlap() (or back with withEarlierOffsetAtOverlap()).

open as a page

Explain the classic DateTimeFormatter pattern pitfalls: yyyy vs YYYY, MM vs MMM, and hh vs HH.

level: middleimportance: must knowfreq 78%

basics

~20 s

Lowercase yyyy is the calendar year; uppercase YYYY is the week-based year and can be wrong near New Year. MM is a two-digit month number, MMM is the month name. hh is 12-hour (needs AM/PM), HH is 24-hour.

open as a page

How do you convert between legacy java.util.Date and the modern java.time API?

level: middleimportance: must knowfreq 60%

basics

~20 s

Use date.toInstant() to turn a Date into an Instant, and Date.from(instant) to go back. To get a LocalDateTime you must add a time zone: date.toInstant().atZone(zoneId). To convert a LocalDate you choose a zone and take an Instant from it.

open as a page

How do you create, access, and combine LocalDate/LocalTime/LocalDateTime, given that they are immutable?

level: middleimportance: must knowfreq 65%

basics

~20 s

Create them with factory methods like LocalDate.of(...), LocalTime.now(), or by parsing a string. Read parts with getters such as getYear() or getHour(). They never change in place — methods like plusDays or atTime return a brand-new object you must assign.

open as a page

How do you convert between the legacy java.util.Date / Calendar and the modern java.time types, and what subtleties matter?

level: middleimportance: must knowfreq 60%

basics

~10 s

Use the bridge methods added in Java 8: date.toInstant() and Date.from(instant); calendar.toInstant() and GregorianCalendar.from(zonedDateTime). A java.util.Date is really an instant (UTC), so it maps cleanly to Instant, not to LocalDateTime.

open as a page

How do you convert between Instant and zoned/offset values, and what is preserved or lost?

level: middleimportance: must knowfreq 60%

basics

~20 s

Use instant.atZone(zoneId) for a ZonedDateTime and instant.atOffset(offset) for an OffsetDateTime; call .toInstant() to go back. Going to a zone/offset adds human fields without changing the moment; going back to Instant drops the zone but keeps the exact point.

open as a page

What is the difference between ZoneId and ZoneOffset, and when do you use each?

level: middleimportance: must knowfreq 65%

basics

~10 s

ZoneOffset is just a fixed difference from UTC, like +02:00. ZoneId is a named region, like Europe/Paris, that knows the full rules including daylight saving, so its offset can change through the year.

open as a page

Across a DST boundary, why can adding a Duration of 24 hours give a different result than adding a Period of 1 day, and which should you use?

level: seniorimportance: must knowfreq 54%

basics

~20 s

Duration counts exact seconds, so 24 hours is always 24 hours of real time. Period counts calendar units, so 1 day means the same wall-clock time tomorrow. On a DST-change day the real day is 23 or 25 hours, so the two answers differ by an hour. Use Period for calendar dates, Duration for elapsed time.

open as a page

Why is sharing a single SimpleDateFormat across threads dangerous, and what is the fix?

level: seniorimportance: must knowfreq 58%

basics

~10 s

SimpleDateFormat is mutable and keeps internal state while formatting, so two threads using one instance corrupt each other, producing wrong or garbled dates. Fix it by using the immutable, thread-safe java.time DateTimeFormatter instead.

open as a page

What are the predefined ISO_* DateTimeFormatter constants, and when would you use them instead of a custom pattern?

level: juniorimportance: should knowfreq 55%

basics

~10 s

Constants like DateTimeFormatter.ISO_LOCAL_DATE or ISO_INSTANT produce/parse standard ISO-8601 text (e.g. 2026-06-20). Use them for machine-to-machine data so you don't reinvent a pattern.

open as a page

What are the main pitfalls of java.util.Calendar, including its month numbering?

level: juniorimportance: should knowfreq 55%

basics

~10 s

Calendar months are 0-based, so January is 0 and December is 11, which causes off-by-one bugs. Calendar is also mutable, verbose, and easy to misuse. Prefer java.time's LocalDate, where months are 1-based.

open as a page

How are months and days-of-week numbered in java.time, and why does it matter?

level: juniorimportance: should knowfreq 55%

basics

~20 s

In java.time months are 1-based: January is 1 and December is 12. Days of week are also 1-based with MONDAY = 1 and SUNDAY = 7. This is different from the old Calendar class, where January was 0.

open as a page

What is a TemporalAdjuster in java.time, and how do you apply one to a date?

level: juniorimportance: should knowfreq 45%

basics

~20 s

A TemporalAdjuster is a small object that knows how to move a date to a related date, like the last day of the month. You apply it with date.with(adjuster), which returns a new date and leaves the original unchanged.

open as a page

What happens when you call Duration.between() on two LocalDate values, and what should you use instead?

level: middleimportance: should knowfreq 62%

basics

~10 s

It throws an exception (UnsupportedTemporalTypeException) because a LocalDate has no time-of-day, and Duration needs seconds. Use Period.between() for years/months/days, or ChronoUnit.DAYS.between() to get a number of days.

open as a page

What is ChronoUnit.between() and how does it differ from Period.between() and Duration.between()?

level: middleimportance: should knowfreq 55%

basics

~10 s

ChronoUnit.between(start, end) returns a single number (long) in one unit you choose, like DAYS or HOURS. Period.between returns a years/months/days object; Duration.between returns a seconds/nanos object.

open as a page

How do you construct, combine, and normalize Duration and Period amounts, and what are the gotchas?

level: middleimportance: should knowfreq 45%

basics

~20 s

Build them with factories like Duration.ofMinutes(90) or Period.of(1,2,10). Combine with plus/minus (they're immutable, so use the return value). Duration auto-carries seconds into minutes/hours, but Period does NOT carry days into months — use normalized() for years/months only.

open as a page

How does Locale affect DateTimeFormatter output, and how do you produce locale-appropriate dates for users?

level: middleimportance: should knowfreq 48%

basics

~10 s

Locale controls language and conventions: month/day names, AM/PM text, and field order. Set it with withLocale(...) or pass it to ofPattern/ofLocalizedDate so users see dates in their own language and style.

open as a page

Explain the difference between next(DayOfWeek), nextOrSame(DayOfWeek), and dayOfWeekInMonth in TemporalAdjusters.

level: middleimportance: should knowfreq 40%

basics

~20 s

next(MONDAY) gives the next Monday strictly after the date, even if the date itself is Monday. nextOrSame(MONDAY) returns the same date if it is already Monday, otherwise the next one. dayOfWeekInMonth(n, MONDAY) gives the n-th Monday of that month.

open as a page

What does truncatedTo do on a temporal value, and how does it differ from a TemporalAdjuster?

level: middleimportance: should knowfreq 42%

basics

~10 s

truncatedTo cuts off everything smaller than the unit you give it. For example, time.truncatedTo(ChronoUnit.MINUTES) zeroes out seconds and nanoseconds. It does not move the date around like an adjuster does; it just drops sub-units.

open as a page

How do Instant and ZonedDateTime relate, and how do you convert between them and reinterpret a moment in another zone?

level: middleimportance: should knowfreq 55%

basics

~10 s

An Instant is one UTC moment; a ZonedDateTime is that moment plus a zone (so it has a wall-clock reading). Convert with instant.atZone(zone) and zdt.toInstant(). To view the same instant elsewhere, use zdt.withZoneSameInstant(otherZone).

open as a page

How does the fluent API of java.time let you build a derived date/time, and what makes chaining safe?

level: middleimportance: should knowfreq 60%

basics

~10 s

Each plus/minus/with method returns a new java.time object, so you can chain calls like date.plusMonths(1).withDayOfMonth(1). Every step returns a fresh value, and the original is never touched, so chaining is safe.

open as a page

showing 1–30 of 43