Java LocalTime Class: Current Time, Formatting and Methods
Call LocalTime.of(23, 30).plusMinutes(45) and you get neither an exception nor "24:15" — you get 00:15. A LocalTime lives inside a single 24-hour cycle, so after 23:59:59.999999999 the clock simply starts again at midnight. And the original object stays untouched: the method returns a new one.
The LocalTime class in Java (java.time.LocalTime) is an immutable, thread-safe class that represents a time of day without a date and without a time zone: hours, minutes, seconds and nanoseconds, for example 13:45:30.123456789. It was added in Java 8 as part of the java.time API and is the right choice whenever you need a Java time without date — schedules, opening hours, alarms, or any value like "at 9 a.m." where the day does not matter. To get the current time in Java, call LocalTime.now().
How to Create a LocalTime: of() and parse()
A LocalTime is created with static factory methods — the class has no public constructor. LocalTime.of(hour, minute) builds a time to the minute, of(hour, minute, second) to the second, of(hour, minute, second, nanoOfSecond) with full precision, and LocalTime.parse("08:15:30") reads it from a string.
import java.time.LocalTime;
public class LocalTimeExample1 {
public static void main(String[] args) {
// Hours (0-23) and minutes (0-59)
LocalTime fiveThirty = LocalTime.of(5, 30);
// Hours, minutes and seconds (0-59)
LocalTime noon = LocalTime.of(12, 0, 0);
// Hours, minutes, seconds and nanoseconds (0-999,999,999)
LocalTime almostMidnight = LocalTime.of(23, 59, 59, 999_999_999);
// From an ISO-8601 string
LocalTime fromString = LocalTime.parse("08:15:30");
System.out.println(fiveThirty);
System.out.println(noon);
System.out.println(almostMidnight);
System.out.println(fromString);
}
} 05:30
12:00
23:59:59.999999999
08:15:30 Look closely at the output: toString() omits zero seconds and nanoseconds, so LocalTime.of(12, 0, 0) prints as 12:00. Fractions of a second are printed in groups of three digits: LocalTime.of(10, 0, 0, 1_000_000) prints 10:00:00.001.
999999 nanoseconds is not 999 milliseconds
The fourth argument of of() is nanoseconds. LocalTime.of(23, 59, 59, 999999) gives 23:59:59.000999999 — less than one millisecond after 23:59:59. For "the last moment before midnight" write 999_999_999 or simply use the constant LocalTime.MAX.
of() validates its arguments immediately. An invalid value does not "roll over" into the next hour — it throws. A string passed to parse() without a formatter must be in ISO-8601 form with two-digit hours and minutes:
LocalTime.of(24, 0); // DateTimeException: Invalid value for HourOfDay (valid values 0 - 23): 24
LocalTime.of(10, 60); // DateTimeException: Invalid value for MinuteOfHour (valid values 0 - 59): 60
LocalTime.parse("9:30"); // DateTimeParseException: Text '9:30' could not be parsed at index 0
LocalTime.parse("25:00"); // DateTimeParseException: Text '25:00' could not be parsed: Invalid value for HourOfDay (valid values 0 - 23): 25 Strings in other formats ("9:30", "07:05 PM") are parsed with a pattern — see LocalTime Format. You can also build a time from the number of seconds since midnight — see toSecondOfDay() and ofSecondOfDay().
Get the Current Time in Java: LocalTime.now()
To get the current time in Java, call LocalTime.now(). It returns the time of day from the system clock in the JVM's default time zone, including fractions of a second; the individual parts are available through getHour(), getMinute(), getSecond() and getNano().
import java.time.LocalTime;
public class LocalTimeExample2 {
public static void main(String[] args) {
LocalTime now = LocalTime.now();
int hour = now.getHour();
int minute = now.getMinute();
int second = now.getSecond();
int nanosecond = now.getNano();
System.out.println("Now: " + now);
System.out.println("Hour: " + hour);
System.out.println("Minute: " + minute);
System.out.println("Second: " + second);
System.out.println("Nanosecond: " + nanosecond);
}
} Now: 14:07:52.381204 (depends on when you run it)
Hour: 14
Minute: 7
Second: 52
Nanosecond: 381204000 The precision of now() depends on the platform: Java 8 usually gives milliseconds, Java 9 and later typically microseconds. That is why the current time is shown to users formatted or trimmed with truncatedTo(). If you need both the date and the time, use LocalDateTime.now() instead.
now() has two more overloads. LocalTime.now(ZoneId.of("Asia/Tokyo")) returns the current time in the given time zone, and LocalTime.now(clock) reads the time from a Clock object: in production that is the system clock, in tests a fixed clock, which makes the result reproducible:
import java.time.Clock;
import java.time.Instant;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
public class LocalTimeNowExample {
public static void main(String[] args) {
Instant moment = Instant.parse("2024-03-15T10:00:00Z");
System.out.println(LocalTime.now(Clock.fixed(moment, ZoneOffset.UTC)));
System.out.println(LocalTime.now(Clock.fixed(moment, ZoneId.of("Asia/Tokyo"))));
}
} 10:00
19:00 Constants MIN, MAX, MIDNIGHT and NOON
LocalTime has four ready-made constants: MIN and MIDNIGHT are the start of the day, 00:00 (equal values), NOON is 12:00, and MAX is the last nanosecond of the day, 23:59:59.999999999. There is no "24:00" value in LocalTime.
| Constant | Value | When to use it |
|---|---|---|
LocalTime.MIN | 00:00 | Lower bound of a range, "start of day" |
LocalTime.MIDNIGHT | 00:00 | Same value, but reads better when you mean midnight |
LocalTime.NOON | 12:00 | Splitting the day into before and after noon |
LocalTime.MAX | 23:59:59.999999999 | Upper bound of a range, "end of day" |
Reading Hours, Minutes and Seconds: get() and ChronoField
Besides getHour() and getMinute(), LocalTime has the generic methods get(TemporalField) and getLong(TemporalField) that take ChronoField constants. They give you other views of the same time: the hour on a 12-hour clock, the minute or second of the day, milliseconds.
import java.time.LocalTime;
import java.time.temporal.ChronoField;
public class LocalTimeExample3 {
public static void main(String[] args) {
LocalTime time = LocalTime.of(13, 45, 30, 123_456_789);
int hourAMPM = time.get(ChronoField.HOUR_OF_AMPM); // 0-11
int amPm = time.get(ChronoField.AMPM_OF_DAY); // 0 - AM, 1 - PM
int hourDay = time.get(ChronoField.HOUR_OF_DAY); // 0-23
int minuteDay = time.get(ChronoField.MINUTE_OF_DAY); // 0-1439
int minuteHour = time.get(ChronoField.MINUTE_OF_HOUR); // 0-59
int secondDay = time.get(ChronoField.SECOND_OF_DAY); // 0-86399
int secondMinute = time.get(ChronoField.SECOND_OF_MINUTE); // 0-59
int milli = time.get(ChronoField.MILLI_OF_SECOND); // 0-999
long nanoDay = time.getLong(ChronoField.NANO_OF_DAY); // 0-86399999999999
int nanoSecond = time.get(ChronoField.NANO_OF_SECOND); // 0-999999999
System.out.println("Hour (AM/PM): " + hourAMPM);
System.out.println("AM (0) or PM (1): " + amPm);
System.out.println("Hour (0-23): " + hourDay);
System.out.println("Minute of day: " + minuteDay);
System.out.println("Minute: " + minuteHour);
System.out.println("Second of day: " + secondDay);
System.out.println("Second: " + secondMinute);
System.out.println("Millisecond: " + milli);
System.out.println("Nano of day: " + nanoDay);
System.out.println("Nano of second: " + nanoSecond);
}
} Hour (AM/PM): 1
AM (0) or PM (1): 1
Hour (0-23): 13
Minute of day: 825
Minute: 45
Second of day: 49530
Second: 30
Millisecond: 123
Nano of day: 49530123456789
Nano of second: 123456789 NANO_OF_DAY requires getLong(): values up to 86,399,999,999,999 do not fit into an int, so time.get(ChronoField.NANO_OF_DAY) throws UnsupportedTemporalTypeException: Invalid field 'NanoOfDay' for get() method, use getLong() instead. Fields a time simply does not have also throw: time.get(ChronoField.EPOCH_DAY) fails with UnsupportedTemporalTypeException: Unsupported field: EpochDay — without a date there is no day number.
Comparing Times: isBefore(), isAfter(), equals()
Two LocalTime values are compared with isBefore() and isAfter() (strictly earlier and strictly later) and checked for equality with equals(). The == operator is wrong here because it compares references. compareTo() returns a negative number, zero or a positive number, so LocalTime is Comparable and can be sorted.
import java.time.LocalTime;
public class CompareLocalTimeExample {
public static void main(String[] args) {
LocalTime fiveThirty = LocalTime.of(5, 30);
LocalTime noon = LocalTime.of(12, 0, 0);
LocalTime almostMidnight = LocalTime.of(23, 59, 59, 999_999_999);
System.out.println("fiveThirty.isAfter(noon)? " + fiveThirty.isAfter(noon));
System.out.println("fiveThirty.isBefore(noon)? " + fiveThirty.isBefore(noon));
System.out.println("noon.equals(almostMidnight)? " + noon.equals(almostMidnight));
System.out.println("fiveThirty.compareTo(noon): " + fiveThirty.compareTo(noon));
System.out.println("noon.compareTo(noon): " + noon.compareTo(noon));
}
} fiveThirty.isAfter(noon)? false
fiveThirty.isBefore(noon)? true
noon.equals(almostMidnight)? false
fiveThirty.compareTo(noon): -1
noon.compareTo(noon): 0 A common real-world task is checking whether a time falls within a range. For a working day from 9:00 to 18:00, "not before the start and strictly before the end" looks like this:
LocalTime now = LocalTime.of(14, 30);
boolean isWorking = !now.isBefore(LocalTime.of(9, 0)) && now.isBefore(LocalTime.of(18, 0));
System.out.println(isWorking); // true When the range crosses midnight — say, quiet hours from 22:00 to 6:00 — the usual "between" check breaks, because the start is later than the end. Here a time matches if it is after the start or before the end:
LocalTime from = LocalTime.of(22, 0);
LocalTime to = LocalTime.of(6, 0);
LocalTime check = LocalTime.of(2, 0);
boolean isQuietTime = check.isAfter(from) || check.isBefore(to);
System.out.println(isQuietTime); // true equals() takes every field into account, including nanoseconds: LocalTime.of(12, 0).equals(LocalTime.of(12, 0, 0)) is true, but 12:00:00 and 12:00:00.000000001 are not equal.
Changing a Field with with()
withHour(), withMinute(), withSecond() and withNano() replace one part of the time and return a new object; the generic with(TemporalField, long) does the same through ChronoField. The other fields keep their values, and the original LocalTime never changes.
import java.time.LocalTime;
import java.time.temporal.ChronoField;
public class WithLocalTimeExample {
public static void main(String[] args) {
LocalTime noon = LocalTime.of(12, 0, 0);
LocalTime ten = noon.with(ChronoField.HOUR_OF_DAY, 10);
LocalTime eight = noon.withHour(8);
LocalTime twelveThirty = noon.withMinute(30);
LocalTime thirtyTwoSeconds = noon.withSecond(32);
// Calls can be chained
LocalTime secondsNano = noon.withSecond(20).withNano(999_999);
System.out.println("ten: " + ten);
System.out.println("eight: " + eight);
System.out.println("twelveThirty: " + twelveThirty);
System.out.println("thirtyTwoSeconds: " + thirtyTwoSeconds);
System.out.println("secondsNano: " + secondsNano);
System.out.println("noon is unchanged: " + noon);
}
} ten: 10:00
eight: 08:00
twelveThirty: 12:30
thirtyTwoSeconds: 12:00:32
secondsNano: 12:00:20.000999999
noon is unchanged: 12:00 Out-of-range values are rejected: noon.withHour(25) throws DateTimeException: Invalid value for HourOfDay (valid values 0 - 23): 25.
Adding and Subtracting Time: plus() and minus()
To add or subtract time, use plusHours(), plusMinutes(), plusSeconds(), plusNanos() and their counterparts minusHours(), minusMinutes(), minusSeconds(), minusNanos(). The generic plus(long, TemporalUnit) and plus(TemporalAmount) accept a ChronoUnit or a Duration. Every call returns a new object.
import java.time.Duration;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
public class PlusMinusLocalTimeExample {
public static void main(String[] args) {
LocalTime fiveThirty = LocalTime.of(5, 30);
// Adding
LocalTime sixThirty = fiveThirty.plusHours(1);
LocalTime fiveForty = fiveThirty.plusMinutes(10);
LocalTime plusSeconds = fiveThirty.plusSeconds(14);
LocalTime plusNanos = fiveThirty.plusNanos(99_999_999);
LocalTime sevenThirty = fiveThirty.plus(2, ChronoUnit.HOURS);
LocalTime seven = fiveThirty.plus(Duration.ofMinutes(90));
// Subtracting
LocalTime fourThirty = fiveThirty.minusHours(1);
LocalTime fiveTen = fiveThirty.minusMinutes(20);
LocalTime minusSeconds = fiveThirty.minusSeconds(2);
LocalTime minusNanos = fiveThirty.minusNanos(1);
LocalTime fiveTwenty = fiveThirty.minus(10, ChronoUnit.MINUTES);
System.out.println("sixThirty: " + sixThirty);
System.out.println("fiveForty: " + fiveForty);
System.out.println("plusSeconds: " + plusSeconds);
System.out.println("plusNanos: " + plusNanos);
System.out.println("sevenThirty: " + sevenThirty);
System.out.println("seven: " + seven);
System.out.println("fourThirty: " + fourThirty);
System.out.println("fiveTen: " + fiveTen);
System.out.println("minusSeconds: " + minusSeconds);
System.out.println("minusNanos: " + minusNanos);
System.out.println("fiveTwenty: " + fiveTwenty);
}
} sixThirty: 06:30
fiveForty: 05:40
plusSeconds: 05:30:14
plusNanos: 05:30:00.099999999
sevenThirty: 07:30
seven: 07:00
fourThirty: 04:30
fiveTen: 05:10
minusSeconds: 05:29:58
minusNanos: 05:29:59.999999999
fiveTwenty: 05:20 The key behaviour is wrap-around at the day boundary. A LocalTime has no date, so going past 23:59:59.999999999 continues from zero, and going below 00:00 continues from the end of the day. No exception is thrown.
What does this code print?
System.out.println(LocalTime.of(23, 30).plusMinutes(45));
System.out.println(LocalTime.of(0, 15).minusMinutes(30));
System.out.println(LocalTime.of(10, 0).plusHours(25));
System.out.println(LocalTime.of(10, 0).plusHours(24)); 00:15
23:45
11:00
10:00 Adding exactly 24 hours returns the original time, and adding 25 hours is the same as adding one. To know that you actually crossed midnight, you need a date — that is, a LocalDateTime. Trying to add days to a LocalTime, for example plus(1, ChronoUnit.DAYS), fails with UnsupportedTemporalTypeException: Unsupported unit: Days.
LocalTime Format: DateTimeFormatter
To format a LocalTime in Java, call time.format(DateTimeFormatter.ofPattern("HH:mm:ss")); the reverse is LocalTime.parse(text, formatter). In the pattern, HH is the hour 00–23, hh is the hour 01–12, mm minutes, ss seconds, SSS milliseconds and a the AM/PM marker. The full list of pattern letters belongs to the DateTimeFormatter lesson of this module.
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class FormatLocalTimeExample {
public static void main(String[] args) {
LocalTime time = LocalTime.of(13, 45, 30, 123_456_789);
System.out.println(time.format(DateTimeFormatter.ofPattern("HH:mm:ss")));
System.out.println(time.format(DateTimeFormatter.ofPattern("HH:mm:ss.SSS")));
System.out.println(time.format(DateTimeFormatter.ofPattern("hh:mm a", Locale.ENGLISH)));
System.out.println(time.format(DateTimeFormatter.ISO_LOCAL_TIME));
// The reverse: String -> LocalTime
LocalTime evening = LocalTime.parse("07:05 PM", DateTimeFormatter.ofPattern("hh:mm a", Locale.ENGLISH));
LocalTime morning = LocalTime.parse("9:30", DateTimeFormatter.ofPattern("H:mm"));
System.out.println(evening);
System.out.println(morning);
}
} 13:45:30
13:45:30.123
01:45 PM
13:45:30.123456789
19:05
09:30 LocalTime.parse("9:30") cannot read the string "9:30", because the default ISO format requires 09:30. If hours come without a leading zero, use the pattern H:mm, as in the example above.
HH vs hh, and a Locale for AM/PM
The pattern hh:mm without the a marker cannot tell 01:45 from 13:45, so use HH for a 24-hour clock. The a marker prints "AM/PM" or a localized equivalent depending on the default locale of the JVM, so pass a Locale explicitly when you need predictable output — and when parsing strings like "07:05 PM".
A date pattern does not work with LocalTime: time.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) throws UnsupportedTemporalTypeException: Unsupported field: YearOfEra, because the object has no date.
Dropping Fractions of a Second: truncatedTo()
truncatedTo(ChronoUnit) zeroes every field smaller than the given unit and returns a new time. Use it when fractions of a second get in the way: in output, in comparisons and when writing to a database.
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
public class TruncatedToExample {
public static void main(String[] args) {
LocalTime time = LocalTime.of(13, 45, 30, 123_456_789);
System.out.println(time.truncatedTo(ChronoUnit.MILLIS));
System.out.println(time.truncatedTo(ChronoUnit.SECONDS));
System.out.println(time.truncatedTo(ChronoUnit.MINUTES));
System.out.println(time.truncatedTo(ChronoUnit.HOURS));
System.out.println(time.truncatedTo(ChronoUnit.HALF_DAYS));
}
} 13:45:30.123
13:45:30
13:45
13:00
12:00 Units longer than a day are not supported: truncatedTo(ChronoUnit.MONTHS) throws UnsupportedTemporalTypeException: Unit is too large to be used for truncation. A typical use case: two now() values are almost never equal because of the fractions, but after truncatedTo(ChronoUnit.MINUTES) you can compare them "to the minute".
toSecondOfDay() and ofSecondOfDay()
toSecondOfDay() returns the number of seconds since midnight (0 to 86399), and LocalTime.ofSecondOfDay(long) converts it back. The nanosecond pair is toNanoOfDay() and ofNanoOfDay(). These methods are handy when a time is stored as a single number.
import java.time.LocalTime;
public class SecondOfDayExample {
public static void main(String[] args) {
System.out.println(LocalTime.of(1, 1, 1).toSecondOfDay());
System.out.println(LocalTime.MAX.toSecondOfDay());
System.out.println(LocalTime.MAX.toNanoOfDay());
System.out.println(LocalTime.ofSecondOfDay(3661));
System.out.println(LocalTime.ofNanoOfDay(1_000_000_000L));
}
} 3661
86399
86399999999999
01:01:01
00:00:01 The value 86400 (exactly one day) is invalid: LocalTime.ofSecondOfDay(86400) throws DateTimeException: Invalid value for SecondOfDay (valid values 0 - 86399): 86400. toSecondOfDay() discards fractions of a second, which is why LocalTime.MAX gives 86399, not 86400.
Time Between Two LocalTime Values
The difference between two times is calculated with ChronoUnit.MINUTES.between(start, end) (or HOURS, SECONDS), with the equivalent start.until(end, ChronoUnit.MINUTES), or with Duration.between(start, end), which returns an object holding hours, minutes and seconds. Duration itself is covered in its own lesson of this module.
import java.time.Duration;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
public class BetweenLocalTimeExample {
public static void main(String[] args) {
LocalTime start = LocalTime.of(9, 15);
LocalTime end = LocalTime.of(17, 45);
System.out.println(ChronoUnit.MINUTES.between(start, end));
System.out.println(ChronoUnit.HOURS.between(start, end));
System.out.println(Duration.between(start, end));
// End before start - the result is negative
System.out.println(Duration.between(end, start));
}
} 510
8
PT8H30M
PT-8H-30M A LocalTime has no date, so it cannot know that a shift started yesterday. For a night shift, ChronoUnit.MINUTES.between(LocalTime.of(22, 0), LocalTime.of(6, 0)) returns -960, not the expected 480 minutes. If the difference is negative, add a day: Duration.between(from, to).plusHours(24) gives PT8H. For reliable calculations that cross midnight, use LocalDateTime.
LocalTime vs LocalDateTime, ZonedDateTime and Instant
LocalTime stores only the time of day, LocalDateTime adds a date, ZonedDateTime adds a time zone on top of that, and Instant is an exact point on the UTC timeline. "When does the shop open" is a LocalTime, "when is the meeting" is a LocalDateTime or ZonedDateTime, and "when did the event happen" is an Instant.
| Class | What it stores | Example value | When to choose it |
|---|---|---|---|
LocalTime | Time of day | 10:30 | Schedules, opening hours, alarms |
LocalDate | Date only | 2024-03-15 | Birthdays, due dates |
LocalDateTime | Date and time, no zone | 2024-03-15T10:30 | Events within one time zone |
ZonedDateTime | Date, time and zone | 2024-03-15T10:30+03:00[Europe/Moscow] | Meetings and flights across cities |
Instant | Point on the UTC timeline | 2024-03-15T07:30:00Z | Timestamps, logs, database storage |
A LocalTime becomes a LocalDateTime with atDate(LocalDate) and goes back with toLocalTime(). To get an Instant, you also need a zone:
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
public class LocalTimeConvertExample {
public static void main(String[] args) {
LocalTime time = LocalTime.of(10, 30);
ZoneId moscow = ZoneId.of("Europe/Moscow");
LocalDateTime dateTime = time.atDate(LocalDate.of(2024, 3, 15)); // LocalTime + LocalDate
Instant instant = dateTime.atZone(moscow).toInstant(); // + zone -> instant
LocalTime inMoscow = Instant.parse("2024-03-15T10:00:00Z")
.atZone(moscow).toLocalTime(); // Instant -> LocalTime
System.out.println(dateTime);
System.out.println(dateTime.toLocalTime());
System.out.println(instant);
System.out.println(inMoscow);
}
} 2024-03-15T10:30
10:30
2024-03-15T07:30:00Z
13:00 LocalTime.now() vs System.nanoTime()
LocalTime.now() answers "what time is it?", while System.nanoTime() answers "how much time has passed?". nanoTime() returns a long number of nanoseconds from an arbitrary origin: the number itself has nothing to do with the time of day and only makes sense as the difference between two calls. Use it to measure how long code runs, and LocalTime to show the time to a user.
long start = System.nanoTime();
// ... code being measured ...
long elapsedNanos = System.nanoTime() - start;
System.out.println("Elapsed, ms: " + elapsedNanos / 1_000_000); nanoTime() is not affected by changes to the system clock (manual adjustment, NTP synchronization), so intervals do not "jump". The difference between two LocalTime.now() calls can even turn out negative if the clock was set back or the measurement spanned midnight. If you need a moment in time as an object rather than a number, use Instant.now().
LocalTime Methods Cheat Sheet
LocalTime methods fall into five groups: creating, reading fields, comparing, getting a modified copy and converting. Because the class is immutable, every method that seems to "change" the time actually returns a new object.
| Method | What it does | Edge case / error |
|---|---|---|
now(), now(ZoneId), now(Clock) | Current time of day | Depends on the JVM time zone; pass a Clock in tests |
of(h, m), of(h, m, s), of(h, m, s, n) | Time from its parts | of(24, 0) — DateTimeException |
parse(text), parse(text, formatter) | Parses a string | "9:30" without the H:mm pattern — DateTimeParseException |
getHour(), getMinute(), getSecond(), getNano(), get(ChronoField) | Parts of the time | get(ChronoField.EPOCH_DAY) — UnsupportedTemporalTypeException |
isBefore(), isAfter(), equals(), compareTo() | Comparison | A range across midnight needs an "or" condition |
withHour(), withMinute(), withSecond(), withNano(), with() | Replaces one field | withHour(25) — DateTimeException |
plusHours(), minusMinutes() and similar, plus(), minus() | Adds or subtracts time | Wraps around past midnight; DAYS — exception |
format(formatter) | String by pattern | Date pattern (yyyy) — exception |
truncatedTo(unit) | Zeroes fields below the unit | MONTHS and larger — exception |
toSecondOfDay(), ofSecondOfDay(), toNanoOfDay(), ofNanoOfDay() | Time as a number since midnight and back | ofSecondOfDay(86400) — DateTimeException |
atDate(LocalDate) | Turns it into a LocalDateTime | Still no time zone |
Where Developers Get Tripped Up
Most LocalTime bugs come down to three causes: forgetting to assign the result of an immutable operation, using the wrong pattern, and expecting a time to know its date.
| Mistake | What happens | How to fix it |
|---|---|---|
time.plusHours(1); without assignment | The time does not change — the result is lost | time = time.plusHours(1); |
time1 == time2 | Compares references, not values | time1.equals(time2) |
LocalTime.of(23, 59, 59, 999999) | Gives 23:59:59.000999999 | 999_999_999 or LocalTime.MAX |
Pattern hh:mm for a 24-hour time | 13:45 is printed as 01:45 | Pattern HH:mm |
Range 22:00–06:00 as isAfter(from) && isBefore(to) | Always false | isAfter(from) || isBefore(to) |
Measuring code speed with LocalTime.now() | Imprecise, affected by system clock changes | System.nanoTime() |
Frequently Asked Questions
How do I convert a LocalTime from one time zone to another?
LocalTime does not store a zone, so the conversion goes through ZonedDateTime: LocalDate.now().atTime(time).atZone(ZoneId.of("Europe/London")).withZoneSameInstant(ZoneId.of("Asia/Tokyo")).toLocalTime(). The date is required because a zone's offset can depend on it (daylight saving time). If LocalTime.now() shows a different time than your wall clock, check the JVM zone with ZoneId.systemDefault().
How do I save a LocalTime to a database with JDBC?
Since JDBC 4.2 you pass the object directly: ps.setObject(1, time) and rs.getObject("start_time", LocalTime.class); the column type is TIME. The old route via java.sql.Time.valueOf(time) still works but drops fractions of a second.
How do I sort a list of LocalTime values and find the latest one?
LocalTime implements Comparable, so Collections.sort(list) orders times from earliest to latest: [18:00, 09:30, 12:00] becomes [09:30, 12:00, 18:00]. Collections.max(list) returns the latest time and Collections.min(list) the earliest. How the comparison works is explained in Comparing Times.
How do I unit-test code that calls LocalTime.now()?
Do not call LocalTime.now() directly in business logic: inject a Clock into the class and call LocalTime.now(clock). Use Clock.systemDefaultZone() in production and Clock.fixed(...) in tests, so the result no longer depends on when the test runs. See the example in Get the Current Time in Java.
Comments