0

I have a timestamp (ts1) stored in a long, expressed in nanos since epoch. I want to be able to compare it with another timestamp (ts2), which I know is for today 8am

The aim is to know if ts1 is after or before ts2.

I have ts2 as below:

LocalTime eight = LocalTime.of(8, 0);
LocalDate today = LocalDate.now(ZoneId.of("Europe/Milan"));
LocalDateTime ts2 = LocalDateTime.of(today, time);

How can I compare ts2 with ts1 which is a long?

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
mandhan
  • 3
  • 2
  • you can use [compareto](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/LocalDateTime.html#compareTo(java.time.chrono.ChronoLocalDateTime)) `ts2.compareTo(LocalDateTime.ofInstant(timestamp, ZoneId.of("Europe/Milan")));` or [isbefore](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/LocalDateTime.html#isBefore(java.time.chrono.ChronoLocalDateTime)) or [isAfter](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/LocalDateTime.html#isAfter(java.time.chrono.ChronoLocalDateTime)) –  Mar 15 '23 at 19:12
  • Such a comparison does not make sense in itself since the former is a point in time and the latter a date and time of day. Neither contains a time zone, but we would have needed one to interpret the two in relation to each other. You may want `ZonedDateTime.now(ZoneId.of("Europe/Rome")) .with(LocalTime.of(8, 0)) .toInstant() .isBefore(Instant.ofEpochSecond(0, ts1))`. – Ole V.V. Mar 15 '23 at 20:42

3 Answers3

2

Comparing the current date & time in Milan with 8 AM today in Milan.

timestamp (ts1) stored in a long, expressed in nanos since epoch

Assuming you mean the epoch reference of first moment of 1970 in UTC, 1970-01-01T00:00Z, derive a Instant.

Instant instant = Instant.ofEpochSecond( 0 , myNanos ) ;

another timestamp (ts2), which I know is for today 8am

Determining "today" requires a time zone. For any given moment, both the date and time-of-day vary around the globe by time zone.

By the way, your Question’s time zone Europe/Milan does not exist. I don't know much about Italy, but I assume you really want Europe/Rome.

ZoneId z = ZoneId.of( "Europe/Rome" ) ;
ZonedDateTime nowInMilan = ZonedDateTime.now( z ) ;

We can move to another moment using the with method. Pass a LocalTime object for 8 AM. All parts of time-of-day will be replaced: hour, minute, second, & fractional second.

ZonedDateTime eightAmTodayInMilan = nowInMilan.with( LocalTime.of( 8 , 0 ) ) ;

compare it

You can compare moments using the isAfter, isBefore, and isEqual methods.

boolean nowIsAfter8AmInMilan = nowInMilan.isAfter( eightAmTodayInMilan ) ;

Alternatively, you could calculate elapsed time as a Duration. Then check if negative or positive.

Duration elapsed = Duration.between( eightAmTodayInMilan  , nowInMilan  ) ;
boolean nowIsBefore8AmInMilan = elapsed.isNegative() ;

LocalDateTime cannot represent a moment

The LocalDateTime represents a date with a time-of-day, no more, no less. This class purposely lacks any concept of time zone or offset-from-UTC.

Without the context of a zone/offset, an instance of LocalDateTime is inherently ambiguous. We don't know if noon on the 23rd of this January means noon in Tokyo, noon in Toulouse, or noon in Toledo Ohio US — three very different moments, each several hours apart.

For the problem stated in this Question, I would not involve LocalDateTime.

The Y2262 problem

I would suggest you find another way to represent a moment as a count of nanoseconds.

The main reason is that such a count is inherently ambiguous and error-prone. Since no human can read such a number as a date and time, logging and debugging are complicated.

Also, beware of the Y2262 problem. Assuming your epoch reference is first moment of 1970 in UTC, then counting nanos limits your future 2262-04-11T23:47:16.854775807Z. That is sufficient for current business purposes, but what if your app is still running in a few centuries?

Instant.EPOCH.plus( Duration.ofNanos( Long.MAX_VALUE ) )

Generally best to exchange date-time values as text in standard ISO 8601 format. Those formats are designed to be readable both by machine and by humans across cultures.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • Interesting, I was looking for something like `with()` but didn't realize LocalTime was a TemporalAdjuster. – shmosel Mar 15 '23 at 20:46
  • @OleV.V. Doesn't work for me either in Java 8, but I figured I need to update my timezone data. – shmosel Mar 15 '23 at 20:47
  • @OleV.V. I like that middle one, `ofEpochSecond` — clever, concise, and clear. Now in the Answer. – Basil Bourque Mar 15 '23 at 20:53
  • @shmosel It’s not that often needed, but there are occasions like this where it is really nifty that each date-time object in java.time also works as a temporal adjuster. It’s nothing special for `LocalTime`, it’s general for the classes. – Ole V.V. Mar 15 '23 at 20:58
1

LocalDate and LocalDateTime are time zone agnostic. They only accept one in the now() factory to resolve the date in the specified zone, but then the time zone is discarded, so there's no way to convert it back to a particular timestamp. If you construct a ZonedDateTime instead, you can then adjust the time and extract the timestamp like this:

long ts2 = ZonedDateTime.now(ZoneId.of("Europe/Milan"))
        .withHour(8)
        .truncatedTo(ChronoUnit.HOURS)
        .toInstant()
        .toEpochMilli();
shmosel
  • 49,289
  • 6
  • 73
  • 138
0

You could use something like this

    Long ts1 = 123l;
    // Convert ts1 to an Instant
    long nanosInSecond = 1000000000L;
    Instant instantTs1 = Instant.ofEpochSecond(ts1 / nanosInSecond, ts1 % nanosInSecond);

    // Create ts2 from LocalDateTime
    LocalTime eight = LocalTime.of(8, 0);
    String zoneId = "Europe/Milan";
    LocalDate today = LocalDate.now(ZoneId.of(zoneId));
    LocalDateTime ts2 = LocalDateTime.of(today, eight);
    Instant instantTs2 = ts2.atZone(ZoneId.of(zoneId)).toInstant();

    
    if (instantTs1.isBefore(instantTs2)) {
        System.out.println("ts1 before ts2");
    } else if (instantTs1.isAfter(instantTs2)) {
        System.out.println("ts1 after ts2");
    } else {
        System.out.println("equal");
    }
JavaMan
  • 1,142
  • 12
  • 22
  • Not bad. The conversion from nanos using explicit division and modulo is too manual for my taste. You may just do `Instant.ofEpochSecond(0, ts1)`, the method will perform the operations for you, and you don’t need the `nanosInSecond` constant. And a dumb detail, indicate a long with an uppercase `L` since a lower case `l` as in `123l` is terribly easy to misread for digit `1`. – Ole V.V. Mar 16 '23 at 05:54