4

I have an expression like "PT20.345S", "P2DT3H4M" etc as described here https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html#parse-java.lang.CharSequence-

How can I parse this, add it to the current time and get a java.util.Date object?

Neither works:

Date d1 = Date.from(LocalDateTime.now().plus(Duration.parse(_expression)));
Date d2 = Date.from(Duration.parse(_expression).addTo(LocalDateTime.now()));
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
fedd
  • 880
  • 12
  • 39
  • 1
    FYI: (A) Try to avoid the `java.util.Date` class. Supplanted entirely by the `java.time.Instant` class years ago. (B) Never use `LocalDateTime` when you mean an actual moment, a specific point on the timeline. Use `Instant`, `ZonedDateTime`, or `OffsetDateTime`. – Basil Bourque Jun 30 '18 at 23:50

2 Answers2

3
Duration amountToAdd = Duration.parse("PT20.345S");  // Represent a span of time. Here, about twenty and a third seconds.
Instant now = Instant.now() ;                        // Capture the current moment in UTC.
Instant otherMoment = now.plus(amountToAdd);         // Add the span-of-time to the current moment, for a moment in the future (or in the past if the duration is negative).
String output = otherMoment.toString():              // Generate a String in standard ISO 8601 format.

2018-06-30T19:34:47Z

Convert from modern java.time class to legacy class.

Date date1 = Date.from(otherMoment);
System.out.println(date1);

Running just now in Europe/Copenhagen time zone I got:

Sat Jun 30 21:34:47 CEST 2018

If I use your other example duration string, P2DT3H4M, I got:

Tue Jul 03 00:38:26 CEST 2018

Or if you’re into one-liners:

    Date date1 = Date.from(Instant.now().plus(Duration.parse("PT20.345S")));

The java.util.Date class is long outdated, so ideally you shouldn’t want to have one. If you need one anyway, typically for a legacy API that you cannot change or don’t want to change just now, you are thinking correctly when doing as much of the logic as possible using java.time, the modern Java date and time API, and converting to Date only in the end. Date’s closest cousin in the modern world is Instant, and direct conversions between Instant and Date exist, which is why I am using this class. An Instant is also lovely independent of zone offsets and time zones.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
0

In your code, the first solution should work if you convert the LocalDateTime to Instant with ZoneOffset (example UTC, or default of your system using ZoneOffset.systemDefault()) like below:

Date d1 = Date.from(LocalDateTime.now().plus(Duration.parse(_expression)).toInstant(OffsetDateTime.now().getOffset());

However, LocalDateTime is wrongly used in this case, because it does not represent a moment, is not a point on the timeline

From javadoc:

This class does not store or represent a time-zone. Instead, it is a description of the date, as used for birthdays, combined with the local time as seen on a wall clock. It cannot represent an instant on the time-line without additional information such as an offset or time-zone.

But, an Instant is a moment on the timeline in UTC

This class models a single instantaneous point on the time-line. This might be used to record event time-stamps in the application.

So, if you use an Instant, you know exactly what moment in time is being referred to, regardless of time zones. Since you are going to handle the business logic like adding amount of time to current time and convert to Date, this is a handy class to be used.

 Date date1 = Date.from(Instant.now().plus(Duration.parse("PT20.345S")));
Nguyen Phung
  • 129
  • 1
  • 7
  • Thank you! But why `ZoneOffset.systemDefault()` returns a ZoneId object? It's one of the reasons I am avoiding the new api... – fedd Jun 30 '18 at 18:12
  • The goal is to get the Date object plus the specified amount of time. If I use ZoneOffset.UTC, will I achieve it or I need to look how to get a ZoneOffset for the default time zone? – fedd Jun 30 '18 at 18:14
  • Firstly, sorry I didn't check `ZoneOffset.systemDefault()` return ZoneId instead. Then you may use `OffsetDateTime.now().getOffset()` to get the default system offset (Reference: https://stackoverflow.com/questions/41427384/how-to-get-default-zoneoffset-in-java8) – Nguyen Phung Jun 30 '18 at 18:18
  • sorry to unmark your advice, the other answer is closer: just take Instant instead of LocalDateTime, and then "add" returns Instant as well, which happily converts to Date – fedd Jun 30 '18 at 20:30
  • 1
    `LocalDateTime` is exactly the wrong class to be using here. It does *not* represent a moment, is *not* a point on the timeline. See the [correct Answer](https://stackoverflow.com/a/51117942/642706) by Ole V.V. – Basil Bourque Jun 30 '18 at 23:55
  • It’s okay @fedd – Nguyen Phung Jul 01 '18 at 02:51
  • Also thanks for your feedbacks @Basil, should I correct my answer and put an explaination why not to use ‘LocalDateTime’ in this case ? – Nguyen Phung Jul 01 '18 at 03:12
  • @NguyenPhung Sure. The Answer by Ole V.V. pretty much has it covered. So I suggest either fixing yours and take your own stab at explanation and example code, or delete. – Basil Bourque Jul 01 '18 at 05:38
  • 1
    @fedd This: `OffsetDateTime.now().getOffset()` has the same effect as this: `ZoneOffset.systemDefault()`. I suggest the second: shorter and your intent is more obvious to the reader. FYI: An offset-from-UTC is merely a number of hours-minutes-seconds, nothing more. A time zone is history of past, present, and future changes in the offset used by the people of a particular region. For example, most of the people in the west coast of North America, `America/Los_Angeles`, use an offset of `-08:00` (eight hours behind UTC) for half the year, and an offset of `-07:00` the other half of the year. – Basil Bourque Jul 01 '18 at 05:46