W3docs

Java LocalTime

Represent times without dates or time zones in Java with LocalTime.

Java LocalTime

LocalTime is the mirror of LocalDate: a time of day — hour, minute, second, nanosecond — with no date and no time zone. It represents the same wall-clock reading on every calendar day everywhere: LocalTime.of(9, 30) is half past nine on any day in any city.

That's the right type for a recurring time of day — store opening at 09:30, a daily cron-like job at 03:15, a meeting that starts at 14:00 regardless of the date. It is not the right type for "the moment the user clicked submit at 14:30 today" — that needs a date, and probably a zone. The two combined give you LocalDateTime, the topic of the next chapter.

Creating

LocalTime now = LocalTime.now();                              // current time in the JVM default zone
LocalTime nine = LocalTime.of(9, 0);                          // hour, minute
LocalTime nineThirty = LocalTime.of(9, 30, 15);               // hour, minute, second
LocalTime nineThirtyNanos = LocalTime.of(9, 30, 15, 500_000_000);   // + nanosecond (0 ..999_999_999)
LocalTime parsed = LocalTime.parse("09:30:15");               // ISO-8601 HH:mm[:ss[.nnnnnnnnn]]

The pre-built constants are useful for boundary conditions:

LocalTime.MIDNIGHT     // 00:00
LocalTime.NOON         // 12:00
LocalTime.MIN          // 00:00:00.000000000
LocalTime.MAX          // 23:59:59.999999999

MIN and MAX are particularly useful when combining with a LocalDate to span a whole day: LocalDateTime.of(date, LocalTime.MIN) is "midnight at the start of the date"; LocalTime.MAX is the last representable nanosecond of the day.

Resolution: nanoseconds

LocalTime is precise to the nanosecond — nine fields' worth of resolution (1 second = 1,000,000,000 ns). On most operating systems the actual clock resolution is millisecond (1,000,000 ns each) or microsecond (1,000 ns each); the extra precision is there so the type doesn't lose information when interacting with systems that have higher-resolution clocks.

Direct accessors:

time.getHour();           // 0-23
time.getMinute();         // 0-59
time.getSecond();         // 0-59
time.getNano();           // 0-999_999_999

There's no getMilli(); if you want milliseconds, divide the nano: time.getNano() / 1_000_000.

24-hour, no AM/PM

LocalTime is internally 24-hour. LocalTime.of(13, 0) is "1 PM" and there is no AM/PM in the type. Parsing strings with "AM"/"PM" requires a custom DateTimeFormatter (the Date Parsing chapter covers this) — the default parse is ISO-8601 24-hour only.

Arithmetic and modifications

Same fluent shape as LocalDate:

time.plusHours(2);
time.plusMinutes(30);
time.plusSeconds(45);
time.plusNanos(500_000);

time.withHour(14);                                            // replace one field
time.withMinute(0);

The wraparound behaviour: every plus/minus method on LocalTime wraps around midnight silently. LocalTime.of(23, 0).plusHours(2) is 01:00, not "tomorrow at 01:00" — there is no "tomorrow" in LocalTime. If you need to know whether a wraparound happened, use LocalDateTime or do the math yourself:

LocalTime late = LocalTime.of(23, 0);
LocalTime later = late.plusHours(2);                          // 01:00 — silently wrapped
// To detect wrap: compare the new value's getHour with what you expected, or use LocalDateTime.

This wraparound is documented and intentional, but it's a sharp edge if you forget. For "when does this shift end?" calculations that can cross midnight, the right type is LocalDateTime, not LocalTime.

Comparing

time.isBefore(other);
time.isAfter(other);
time.compareTo(other);
time.equals(other);

Lexical ordering by hour:minute:second:nano. LocalTime implements Comparable<LocalTime>, so you can sort a list of times or use it as a TreeMap key directly.

Distance

Duration.between and ChronoUnit.X.between both work:

Duration d = Duration.between(start, end);
long minutes = ChronoUnit.MINUTES.between(start, end);
long seconds = ChronoUnit.SECONDS.between(start, end);

The sign: positive when end is after start, negative otherwise. The same wraparound caveat applies — Duration.between(LocalTime.of(23, 0), LocalTime.of(1, 0)) is −22 hours, not +2 hours; the API treats 01:00 as earlier than 23:00 of the same notional day. For "the shift went past midnight," LocalDateTime-based arithmetic is the right tool.

Combining with LocalDate

You'll convert to a date-with-time often:

LocalDate date = LocalDate.of(2025, 11, 4);
LocalTime time = LocalTime.of(9, 30);

LocalDateTime dt = date.atTime(time);                         // 2025-11-04T09:30
LocalDateTime dt2 = time.atDate(date);                        // same thing

atTime / atDate are the bridge methods. The result is a LocalDateTime — still no zone, but now anchored to a calendar day. The next chapter takes that further.

A worked example: a tiny scheduling helper

The program below uses LocalTime for a daily-schedule kind of task: define an "office hours" window, check whether a given moment falls inside it, compute how long until the next opening, and demonstrate the midnight-wrap gotcha.

java— editable, runs on the server

What to take from the run:

  • LocalTime printed as 09:00, 17:30, 12:30 — the canonical ISO-8601 24-hour form. No AM/PM in the type. If you need to display "5:30 PM" to a user, the Date Formatting chapter has the formatter for that; the type itself doesn't know about it.
  • The "is the time inside the window" check used !isBefore(open) && !isAfter(close). That's the half-open-vs-closed-interval idiom — both endpoints are included. For "strictly inside," switch to the un-negated forms.
  • Duration.between(LocalTime.of(22, 0), LocalTime.of(2, 0)) returned PT-20H, not PT4H. LocalTime has no notion of "next day" — when end is earlier than start in clock terms, the duration goes negative. For a shift that crosses midnight, switch the inputs to LocalDateTime and let the dates resolve the ambiguity. This is the single biggest LocalTime footgun.
  • LocalTime.of(23, 30).plusHours(2) returned 01:30. The wraparound is silent — no exception, no flag, no carry into the date. If you need to know "did this wrap?", use LocalDateTime. If you genuinely want clock-arithmetic-mod-24 (a recurring schedule, say), the wraparound is the correct behaviour.
  • date.atTime(time) was the canonical bridge to LocalDateTime. The mirror time.atDate(date) produces the same result. You'll use these constantly when reading a time from one source and a date from another, then combining them into the single object the downstream API wants.

What's next

The next chapter, Java LocalDateTime, combines LocalDate and LocalTime into the third "local" type: a date and a time, still with no time zone attached. That's the natural unit for "this happened at 14:30 on November 4" when the zone is irrelevant or recorded separately.

Practice

Practice

What does `LocalTime.of(23, 0).plusHours(3)` return, and why?