2

How can I convert the date object which is already in UTC to an OffsetDateTime Object in UTC itself in Java? This logic should be written on a microservice where the timezone can be entirely different. So .now() and other things are ruled out, I guess. Also, I don't want to pass Timezone as params anywhere.

Sample code:

public OffsetDateTime convertFrom(Date source) {
    LOGGER.info("source: " + source.toString());
    LOGGER.info("instant: " + source.toInstant().toString());
    LOGGER.info("response: " + source.toInstant().atOffset(ZoneOffset.UTC).toString());
    return source.toInstant().atOffset(ZoneOffset.UTC);
}

and the output I get is:

  • source: 2018-07-11 15:45:13.0

  • instant: 2018-07-11T19:45:13Z

  • response: 2018-07-11T19:45:13Z

I want my output return to be 2018-07-11 15:45:13Z for input 2018-07-11 15:45:13.0

Draken
  • 3,134
  • 13
  • 34
  • 54
  • 1
    `now` is always the present time no matter which timezone you are in. – Peter Lawrey Jul 13 '18 at 13:22
  • If you set the default timezone to what ever you need, everything will parse, and be printed in that timezone unless you specify otherwise. – Peter Lawrey Jul 13 '18 at 13:23
  • @PeterLawrey This code has to be on different server with different timezone. Date object will be passed as params from another server in UTC. How can i use now in my code? – Manoj Selvaraj Jul 13 '18 at 13:25
  • 1
    The date object: what is that? A String? A Date? An Instant? A LocalDate? Post code. – JB Nizet Jul 13 '18 at 13:28
  • @JBNizet Edited the question. Please check – Manoj Selvaraj Jul 13 '18 at 13:31
  • @ManojSelvaraj The Date object is displayed using the default timezone of the machine which is hopefully correct (it's internal representation is GMT but that's not how it is displayed usually). Otherwise you can format it using any timezone you wish. – Peter Lawrey Jul 13 '18 at 13:34
  • @PeterLawrey Date Object will be from third party server – Manoj Selvaraj Jul 13 '18 at 13:43
  • @ManojSelvaraj where the Date object comes from and Timmons the server was in when it created or what the time zone of the receiver is doesn't change the time. – Peter Lawrey Jul 13 '18 at 14:00
  • You probably rather want [ZonedDateTime](https://docs.oracle.com/javase/8/docs/api/java/time/ZonedDateTime.html) taking into account day time savings. – Joop Eggen Jul 13 '18 at 14:11

3 Answers3

3

tl;dr

A java.util.Date and a Instant both represent a moment in UTC. Other time zones and offsets are irrelevant.

Instant instant = myJavaUtilDate.toInstant() 

How can I convert the date object which is already in UTC to an OffsetDateTime Object in UTC itself in Java?

You don’t need OffsetDateTime. Use Instant as shown above.

Use ZonedDateTime, not OffsetDateTime

You do not need OffsetDateTime. An offset-from-UTC is merely a number of hours and minutes. Nothing more, nothing less. In contrast, a time zone is a history of the past, present, and future changes to the offset used by the people of a particular region. So a time zone, if known, is always preferable to a mere offset. So use ZonedDateTime rather than OffsetDateTime wherever possible.

Use OffsetDateTime only when given an offset-from-UTC, such as +02:00, without the context of a specific time zone, such as Europe/Paris.

Convert Date to Instant

If given a java.util.Date, concert to the modern class (Instant) that replaced that troublesome old class. Both represent a moment in UTC as a count from the same epoch reference of first moment of 1970 in UTC. The modern class resolves to nanoseconds rather than milliseconds. To convert, call new methods added to the old class.

Instant instant = myJavaUtilDate.toInstant() ;

Remember that both java.util.Date and Instant always represent a moment in UTC.

Capture current moment, “now”

Capture the current moment in UTC.

Instant instant = Instant.now() ;

now() and other things are ruled out, I guess.

No, you can always capture the current moment by calling Instant.now() on any machine at any time. The JVM’s current default time zone is irrelevant as Instant is always in UTC.

Adjust from UTC into another time zone. Same moment, same point on the timeline, different wall-clock time. <— That is the most important concept to comprehend in this discussion!

ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = instant.atZone() ;

As a shortcut, you can skip the Instant when capturing current moment.

ZonedDateTime zdt = ZonedDateTime.now( z ) ;

Move back to UTC by extracting a Instant object.

Instant instant = zdt.toInstant() ; 

Tip: Focus on UTC

Usually best to have most of your work in UTC. When storing, logging, debugging, or exchanging moments, use UTC. Forget about your own parochial time zone while on the job as a programmer or sysadmin; learn to think in UTC. Keep a second click in your office set to UTC.

Avoid flipping between time zones all the time. Stick with UTC. Adjust to a time zone only when presenting to the user or when business logic demands.

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

It is already working as intended, the problem is that Date.toString is "helpfully" converting the internal timestamp to your local timezone. Using Date.toGMTString would result in the exact same timestamp for each of the values.

If the resulting timestamp is wrong then the problem lies in the creation of the Date instance. Using the constructor like new Date(2018, 7, 11, 15, 45, 11) would result in that date being calculated for the system timezone, not UTC. To create it for UTC there is Date.UTC but all these APIs have been deprecated since Java 1.1 because they are so confusing.

Kiskae
  • 24,655
  • 2
  • 77
  • 74
0
public static OffsetDateTime convertFrom(Date source) {
    if (source instanceof Timestamp) {
        return ((Timestamp) source).toLocalDateTime()
                .atOffset(ZoneOffset.UTC);
    }
    return source.toInstant().atOffset(ZoneOffset.UTC);
}

The object that was passed to your method was a java.sql.Timestamp, not a Date. We can see this fact from the way it was printed: 2018-07-11 15:45:13.0 is the return value from Timestamp.toString(). The Timestamp class is implemented as a subclass of Date, but this doesn’t mean that we can nor should handle it as a Date. The documentation warns us:

Due to the differences between the Timestamp class and the java.util.Date class mentioned above, it is recommended that code not view Timestamp values generically as an instance of java.util.Date. The inheritance relationship between Timestamp and java.util.Date really denotes implementation inheritance, and not type inheritance.

In the implementation above I have assumed that you cannot mitigate the possibility of getting a Timestamp argument, so I am handling the possibility the best I can. The code is still fragile, though, because sometimes a Timestamp denotes a point in time (I should say that this is the point), at other times it denotes a date and hour of day. Granted that the Timestamp does not hold a time zone in it, the two are not the same. I understand that your sample Timestamp denotes a date and time of 2018-07-11 15:45:13.0, and you want this interpreted in UTC. My code does that (your code in the question, on the other hand, correctly handles the situation where the Timestamp denotes a point in time). Also, even though no time zone is passed in my code, its behaviour still depends on the time zone setting of your JVM.

When I pass a Timestamp of 2018-07-11 15:45:13.0 to my method above, it returns an OffsetDateTime of 2018-07-11T15:45:13Z.

The double nature of Timestamp is unfortunate and confusing, and the only real solution would be if you could avoid that class completely. The Date class too is poorly designed, and both are outdated and replaced by java.time, the modern Java date and time API. If you cannot avoid the old classes in your code, I certainly understand your desire to convert to the modern OffsetDateTime first thing. If on the other hand I understand correctly that the date and time comes through JSON, you may be able to parse it on your side without any of the old date and time classes, which would be a good solution to your problem. And under all circumstances, if your real goal is to represent the point in time in a time zone neutral way, I agree with Basil Bourque in preferring an Instant over an OffsetDateTime in UTC.

Link: Documentation of java.sql.Timestamp

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161