The elapsed time since midnight in Java is the time of day counted from 00:00, and LocalTime returns it in one call, toSecondOfDay() for seconds or getLong(ChronoField.MILLI_OF_DAY) for milliseconds. For the current value, we read LocalTime.now(zone) first, because midnight comes at a different moment in every time zone.
We need the time since midnight for daily counters and quotas that reset at midnight, for time-of-day rules such as happy hours, and for systems that store a time as a number, such as a sensor log or a CSV export with a seconds-of-day column.
The following example converts a fixed time of day and the current time in London.
LocalTime time = LocalTime.of(14, 30, 15, 250_000_000);
int seconds = time.toSecondOfDay(); // 52215
long millis = time.getLong(ChronoField.MILLI_OF_DAY); // 52215250
long nanos = time.toNanoOfDay(); // 52215250000000
int londonSeconds = LocalTime.now(ZoneId.of("Europe/London")).toSecondOfDay(); // seconds since midnight in London at this moment
The toSecondOfDay() method drops the fraction of the second. We also cover the other ways to get the same numbers, the time zone question, the two days a year when the local time differs from the real elapsed time, and the time left until the next midnight.
1. Get Elapsed Time Since Midnight
A LocalTime holds only the time of day, so the time since midnight is the value itself in a single unit. The class has two direct methods, toSecondOfDay() and toNanoOfDay(), and the ChronoField constants read the value in other units, such as MILLI_OF_DAY and MINUTE_OF_DAY.
The approach with ChronoUnit.between() and LocalTime.MIDNIGHT gives the same results and reads like the question we ask. A Duration is useful when we pass the value on or format it later.
LocalTime time = LocalTime.of(14, 30, 15, 250_000_000);
long secondsBetween = ChronoUnit.SECONDS.between(LocalTime.MIDNIGHT, time); // 52215
long millisBetween = ChronoUnit.MILLIS.between(LocalTime.MIDNIGHT, time); // 52215250
int minuteOfDay = time.get(ChronoField.MINUTE_OF_DAY); // 870
Duration sinceMidnight = Duration.between(LocalTime.MIDNIGHT, time); // PT14H30M15.25S
double hours = sinceMidnight.toMillis() / 3_600_000.0; // 14.504236111111112
The table lists the options. All of them truncate, so 14:30:15.250 is 52215 seconds and not 52216.
| Unit | Shortest call | Alternative |
|---|---|---|
| Seconds | time.toSecondOfDay() | ChronoUnit.SECONDS.between(LocalTime.MIDNIGHT, time) |
| Milliseconds | time.getLong(ChronoField.MILLI_OF_DAY) | ChronoUnit.MILLIS.between(LocalTime.MIDNIGHT, time) |
| Minutes | time.get(ChronoField.MINUTE_OF_DAY) | ChronoUnit.MINUTES.between(LocalTime.MIDNIGHT, time) |
| Nanoseconds | time.toNanoOfDay() | ChronoUnit.NANOS.between(LocalTime.MIDNIGHT, time) |
| As a Duration | Duration.between(LocalTime.MIDNIGHT, time) | Duration.ofNanos(time.toNanoOfDay()) |
The reverse direction works too. LocalTime.ofSecondOfDay(52215) returns 14:30:15, which helps when a file or a device stores the time as seconds of the day. More on the class is in the LocalTime guide.
2. Elapsed Time Since Midnight in a Specified Time Zone
By default, LocalTime.now() reads the system clock in the JVM default zone. A server in a UTC data center and a user in New York see different values for the same moment, so we pass the zone that the business rule belongs to, or ZoneOffset.UTC for UTC midnight.
To show real numbers, the next example uses a fixed Clock set to 08:15:30 UTC on October 10, 2026, and reads it in three zones.
Clock clock = Clock.fixed(Instant.parse("2026-10-10T08:15:30Z"), ZoneOffset.UTC);
int utcSeconds = LocalTime.now(clock).toSecondOfDay(); // 29730
int newYorkSeconds = LocalTime.now(clock.withZone(ZoneId.of("America/New_York"))).toSecondOfDay(); // 15330
int kolkataSeconds = LocalTime.now(clock.withZone(ZoneId.of("Asia/Kolkata"))).toSecondOfDay(); // 49530
long utcMillisNow = LocalTime.now(ZoneOffset.UTC).getLong(ChronoField.MILLI_OF_DAY); // milliseconds since UTC midnight at this moment
In New York the clock shows 04:15:30 and in Kolkata 13:45:30, so the same moment is 15330 and 49530 seconds after the local midnight. A Clock parameter also makes the code testable, as the article on the current date and time explains.
3. Elapsed Time on Daylight Saving Days
The LocalTime methods count what the wall clock shows, not the time that has passed. On the day a zone switches to daylight saving time, the clock skips an hour, so at 10:00 only 9 hours have passed since midnight. On the day it switches back, 11 hours have passed.

When the real elapsed time matters, for example to pay for parked hours or to rate-limit by real seconds, we compute a Duration between the start of the day and the current moment as ZonedDateTime values. The method LocalDate.atStartOfDay(zone) returns the first valid time of the day in that zone.
ZoneId berlin = ZoneId.of("Europe/Berlin");
ZonedDateTime springForward = ZonedDateTime.of(2026, 3, 29, 10, 0, 0, 0, berlin);
int wallClockSeconds = springForward.toLocalTime().toSecondOfDay(); // 36000
long realSeconds = Duration.between(springForward.toLocalDate().atStartOfDay(berlin), springForward).toSeconds(); // 32400
ZonedDateTime fallBack = ZonedDateTime.of(2026, 10, 25, 10, 0, 0, 0, berlin);
long realFallBack = Duration.between(fallBack.toLocalDate().atStartOfDay(berlin), fallBack).toSeconds(); // 39600
In a few zones, midnight itself does not exist on the day of the change, so LocalTime.MIDNIGHT is not the start of that day. Chile, for example, moves its clocks from 00:00 to 01:00. The atStartOfDay() method handles that case, which is why we use it instead of combining the date with LocalTime.MIDNIGHT ourselves.
ZoneId santiago = ZoneId.of("America/Santiago");
ZonedDateTime dayStart = LocalDate.of(2026, 9, 6).atStartOfDay(santiago); // 2026-09-06T01:00-03:00[America/Santiago]
ZonedDateTime noon = ZonedDateTime.of(2026, 9, 6, 12, 0, 0, 0, santiago);
long santiagoSeconds = Duration.between(dayStart, noon).toSeconds(); // 39600
4. Using LocalDateTime or ZonedDateTime
If the app already has a LocalDateTime or a ZonedDateTime, we do not need a separate LocalTime. We get the start of the same day from the value itself and measure from there, so both ends come from one clock reading.
LocalDateTime orderTime = LocalDateTime.of(2026, 10, 10, 19, 5, 40);
long orderSeconds = ChronoUnit.SECONDS.between(orderTime.toLocalDate().atStartOfDay(), orderTime); // 68740
LocalDateTime dayBegin = orderTime.truncatedTo(ChronoUnit.DAYS); // 2026-10-10T00:00
ZonedDateTime zoned = ZonedDateTime.of(2026, 10, 10, 19, 5, 40, 0, ZoneId.of("Asia/Tokyo"));
long zonedSeconds = ChronoUnit.SECONDS.between(zoned.toLocalDate().atStartOfDay(zoned.getZone()), zoned); // 68740
Older code often calls LocalDate.now() for the date and ZonedDateTime.now() for the time in two separate statements. When the code runs a few milliseconds before midnight, the two calls can land on different days, and the result jumps by a full day. Reading the clock once and deriving both values from that reading avoids the problem.
ZonedDateTime current = ZonedDateTime.now(ZoneId.of("Europe/Paris")); // one reading of the clock
long safeSeconds = Duration.between(current.toLocalDate().atStartOfDay(current.getZone()), current).toSeconds(); // real seconds since midnight in Paris
5. Time Remaining Until the Next Midnight
The opposite question, the time left until midnight, also comes up often. A public API that resets each customer’s daily quota at midnight UTC returns the remaining seconds in a Retry-After header, and a leaderboard cache that is rebuilt every night expires at the next local midnight.
static Duration untilNextMidnight(ZonedDateTime now) {
ZonedDateTime nextMidnight = now.toLocalDate().plusDays(1).atStartOfDay(now.getZone());
return Duration.between(now, nextMidnight);
}
ZonedDateTime requestTime = ZonedDateTime.of(2026, 10, 10, 20, 15, 30, 0, ZoneOffset.UTC);
Duration remaining = untilNextMidnight(requestTime); // PT3H44M30S
long retryAfter = remaining.toSeconds(); // 13470
int simpleRemaining = 86_400 - requestTime.toLocalTime().toSecondOfDay(); // 13470
The shortcut 86400 – toSecondOfDay() is right for UTC and for fixed offsets. For zones with daylight saving time, untilNextMidnight() also returns the right value on the 23-hour and 25-hour days. To show the remaining time as 03:44:30, we format the Duration as in formatting milliseconds to hh:mm:ss.
6. Time Since Midnight FAQs
The UTC case and the reverse conversion from a number to a LocalTime need a few extra lines of code.
6.1. How do we get the milliseconds since midnight in UTC?
We read the time of day in UTC and take the MILLI_OF_DAY field. The epoch value from System.currentTimeMillis() % 86_400_000 gives the same number, because UTC days have no daylight saving changes and Java ignores leap seconds.
long viaLocalTime = LocalTime.now(ZoneOffset.UTC).getLong(ChronoField.MILLI_OF_DAY); // milliseconds since UTC midnight
long viaEpoch = System.currentTimeMillis() % 86_400_000L; // the same value from the epoch
6.2. Is LocalTime.MIDNIGHT the same as LocalTime.MIN?
Yes. Both constants are 00:00. LocalTime.MAX is 23:59:59.999999999, the last moment of the day, and LocalTime.NOON is 12:00.
6.3. How do we convert seconds since midnight back to a time?
We call LocalTime.ofSecondOfDay(seconds), or LocalTime.ofNanoOfDay(nanos) for nanoseconds. Both accept only values inside one day, so 86400 seconds throws a DateTimeException.
LocalTime fromSeconds = LocalTime.ofSecondOfDay(52215); // 14:30:15
LocalTime tooLarge = LocalTime.ofSecondOfDay(86_400); // DateTimeException: Invalid value for SecondOfDay (valid values 0 - 86399): 86400
6.4. What is the difference between time since midnight and System.currentTimeMillis()?
System.currentTimeMillis() counts from the epoch, January 1, 1970 UTC, so it grows across days. The time since midnight starts again at zero every day in the chosen zone. For the epoch value, see the article on the current timestamp in Java.
7. Conclusion
LocalTime gives the elapsed time since midnight with toSecondOfDay(), MILLI_OF_DAY or toNanoOfDay(), and ChronoUnit.between() with LocalTime.MIDNIGHT returns the same numbers. We pass a zone to now(), because midnight depends on the place.
The LocalTime methods count the wall clock. For the real elapsed time on daylight saving days, and in zones where midnight is skipped, we measure a Duration from atStartOfDay(zone). For related tasks, such as the start and end of a day, see the Java date and time guides.
8. References
- LocalTime (Java SE 25 API)
- ChronoField (Java SE 25 API)
- LocalDate.atStartOfDay() (Java SE 25 API)
- IANA Time Zone Database
Happy Learning !!