4

In Java or Scala, what is the best way to find the number of seconds between 2 LocalTimes?

If I have 11:20:00.000Z and 11:21:00.500Z I would want the result to be 61 seconds, not 60 seconds.

B. Smith
  • 1,063
  • 4
  • 14
  • 23
  • 1
    Isn't this just a rounding problem (from 60.5 seconds)? – Thilo Mar 21 '18 at 00:03
  • https://stackoverflow.com/questions/28353725/java-subtract-localtime discusses how to do this for minutes. – RStrad Mar 21 '18 at 00:06
  • @RStrad These solutions all round down, as do most date/time libraries I've looked at. – B. Smith Mar 21 '18 at 00:08
  • Why do you think it should round to 61? 60.5 should round to 60 as the nearest even. – Joe C Mar 21 '18 at 00:10
  • 1
    @Joe C Because my use case requires that. For example, if I wanted the length of a flight in seconds, and I decided I wanted to round up to the nearest second. – B. Smith Mar 21 '18 at 00:14
  • 1
    @Thilo Yes it is a rounding problem. Almost every date/time library seems to round down instead of up. – B. Smith Mar 21 '18 at 00:15
  • 1
    Your times seem to be `OffsetTime`s rather than `LocalTime`s. As long as both have the same offset, it doesn’t matter. Could they have different offsets? That would complicate things, but a solution could still be found. – Ole V.V. Mar 21 '18 at 05:25
  • What rounding do you want? Always up? And does that mean a ceiling or away from 0? – Ole V.V. Mar 21 '18 at 05:37

5 Answers5

3

Give a try to LocalTime#until

LocalTime past = LocalTime.now().minusSeconds(4);
LocalTime now = LocalTime.now();

System.out.println("4 == " + past.until(now, ChronoUnit.SECONDS));

You can find more about the method in doc of LocalTime#until.

Mark
  • 5,994
  • 5
  • 42
  • 55
2

You can calculate the time difference in millis and round the result in seconds using math.round()

import java.time.{LocalTime, OffsetTime}
import java.time.temporal.ChronoUnit.MILLIS

val t1 = LocalTime.parse("11:20:00.000")
val t2 = LocalTime.parse("11:21:00.500")

math.round(MILLIS.between(t1, t2) / 1000.0)
// res1: Long = 61

[UPDATE]

The above timezone-ignored calculation works fine given that the times to be compared are in the same timezone. In case different timezones are involved, OffsetTime as suggested in @studx's answer should be used instead:

val t1 = OffsetTime.parse("11:20:00.000Z")      // UTC +00:00
val t2 = OffsetTime.parse("11:21:00.500-01:00") // UTC -01:00

math.round(MILLIS.between(t1, t2) / 1000.0)
// res2: Long = 3661
Leo C
  • 22,006
  • 3
  • 26
  • 39
1

You want to use LocalTime, but the inputs (11:20:00.000Z) have the Z in the end, which is the UTC designator. If the times are in UTC, it's not wise to ignore this info - assuming that other inputs may have another offsets.

So I would not ignore the Z and use the proper type:

OffsetTime t1 = OffsetTime.parse("11:20:00.000Z");
OffsetTime t2 = OffsetTime.parse("11:21:00.500Z");

Both inputs have the same offset, so using LocalTime will work as well. But if there are inputs with different offsets, then OffsetTime is the right type to use.

Then you get the difference in milliseconds and round it to get the rounded seconds:

long millis = ChronoUnit.MILLIS.between(t1, t2);
long seconds = Math.round(millis / 1000.0); // 61
studx
  • 39
  • 3
0

DateFormatter, LocalTime and Duration in java.time

val time1 = "11:20:00.000Z"
val time2 = "11:21:00.500Z"

import java.time._
import java.time.format._
val format = DateTimeFormatter.ofPattern("HH:mm:ss.SSS")
val parsedTime1 = LocalTime.parse(time1.replace("Z", ""), format)
val parsedTime2 = LocalTime.parse(time2.replace("Z", ""), format)
val diff = Duration.between(parsedTime1, parsedTime2)
val roundedSeconds = math.round((diff.getSeconds.toDouble+(diff.getNano.toDouble/1000000000.0)))
println(roundedSeconds)
//61 is printed

using SimpleDateFormat in java.text

val time1 = "11:20:00.000Z"
val time2 = "11:21:00.500Z"

import java.text._
val format = new SimpleDateFormat("HH:mm:ss.SSSZ")
val parsedTime1 = format.parse(time1.replaceAll("Z$", "+0000"))
val parsedTime2 = format.parse(time2.replaceAll("Z$", "+0000"))
val diff = math round (parsedTime2.getTime - parsedTime1.getTime)/1000.0
println(diff)
//61 is printed
Ramesh Maharjan
  • 41,071
  • 6
  • 69
  • 97
  • 1
    Please don’t teach the young ones to use the long outdated and notoriously troublesome `SimpleDateFormat`. Today we have so much better in [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). Which it seems the asker is probably already using. It is also so much nicer to work with. – Ole V.V. Mar 21 '18 at 05:22
  • 1
    Thanks @OleV.V. I am also learning myself as well. :) updated my answer :) – Ramesh Maharjan Mar 21 '18 at 07:21
  • Nice. Thanks. With java.time you can do without the explicit formatter, just `LocalTime.parse(time1.replace("Z", ""))` (I believe). Or maybe even more elegant `OffsetTime.parse(time1).toLocalTime()` (using Java syntax). – Ole V.V. Mar 21 '18 at 07:42
  • yeah I just wanted to add extra information for other readers otherwise there wouldn't be any difference between my and Leo's answer @OleV.V. – Ramesh Maharjan Mar 21 '18 at 07:46
0

tl;dr

ChronoUnit.SECONDS.between(                                                // Calculate elapsed time as a total number of seconds.
    LocalTime.parse( "11:20:00.000" ).truncatedTo( ChronoUnit.SECONDS ) ,  // Parse string as a `LocalTime` time-of-day, then lop off any fractional second.
    LocalTime.parse( "11:21:00.500" ).truncatedTo( ChronoUnit.SECONDS )    // Lop off the half-second present in this `LocalTime` object. 
)

60

Truncate

If you want to ignore the fractional seconds, truncate, just lop them off.

LocalTime start = LocalTime.parse( "11:20:00.000" ).truncatedTo( ChronoUnit.SECONDS ) ;
LocalTime stop = LocalTime.parse( "11:21:00.500" ).truncatedTo( ChronoUnit.SECONDS ) ;  // Lop off the extra half-second we don't care about.

Calculate the elapsed time as a total number of seconds using the ChronoUnit enum.

long seconds = ChronoUnit.SECONDS.between( start , stop ) ;

60

By the way, your Question asks about LocalTime, which has no concept of time zone or offset-from-UTC. Yet your example times have a Z on the end which is short for Zulu and means UTC. So those examples cannot be LocalTime values.

If you really have date-time values in UTC, represent them as Instant objects rather than as LocalTime. And perform the same logic: truncate, and calculate with SECONDS.between.


About java.time

The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.

Where to obtain the java.time classes?

The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154