2

I have a utility written in my application that takes in a timezone and returns me a date (java.util.Date) as follows -

// Inside MyDateHelper.java
public static java.util.Date getTimeForTimeZone(String timeZoneId)
{
    final Date currentDate = Calendar.getInstance(TimeZone.getTimeZone(timeZoneId)).getTime();
    return currentDate;
}

I researched around the internet to find that Date does not have any timeZone information in it. Rather it is a wrapper around a long value starting from the epoch of 1970. So, my question is if I have to get the current Date-Time instant a transaction has occurred to store in a date field (of type java.util.Date), Is this utility of any use?

For all practical purposes are the two lines of code achieving the same?

transactionEntry.setTime(MyDateHelper.getTimeForTimeZone("UTC+7:00"));

OR

transactionEntry.setTime(new Date());

I also checked out the new Java 8 Time API, but for my purpose I think doing this is also an overkill and achieves nothing more new Date() -

Date date4 = new Date();
LocalDateTime localDate = date4.toInstant().atZone(ZoneId.of("UTC+07:00")).toLocalDateTime();
final ZonedDateTime dateWithZone = localDate.atZone(ZoneId.of("UTC+07:00"));
Date dateOutput = Date.from(dateWithZone.toInstant());
transactionEntry.setTime(dateOutput);

The whole purpose is to capture the time a transaction occurred with the time zone, but I can only store the time as a java.util.Date. No matter in which timezone the transaction occurred, the Date will be able to capture the instant of time correctly, right? Can someone confirm that this understanding is correct?

Anurag
  • 367
  • 1
  • 2
  • 15

3 Answers3

5

The Answer by Ole V.V. Is correct. I’ll add just a bit of discussion.

There is only one timeline experienced by everyone on Earth simultaneously (ignoring Einstein et al.). By convention we track that time in UTC.

Learn to think of UTC as The One True Time. All other offsets and zones are but a mere variation of UTC. When on the job as a programmer or sysadmin, forget about your own parochial time zone. Keep a second click on your desk or wall tracking time in UTC. Do all your logging, tracing, data-storage, and data-exchange in UTC.

The basic class for tracking a moment in UTC is java.time.Instant. Objects of this class are always in UTC by definition. This class supplants java.util.Date, which also represents a moment in UTC, but has many design flaws and should never be used. Instant has a resolution of nanoseconds, finer than the milliseconds of Date.

For more flexibility, use the OffsetDateTime class.

OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;

And back again.

Instant instant = odt.toInstant() ;

An offset-from-UTC is merely a number of hours-minutes-seconds. A time zone is much more. A time zone is a history of past, present, and future changes to the offset used by the people of a particular region.

To use a time zone, apply a ZoneId to get a ZonedDateTime. This class supplants the terrible Calendar and GregorianCalendar classes.

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

Going back again.

Instant instant = zdt.toInstant() ;

This zdt, odt, and instant all represent the very same simultaneous moment, the same point on the timeline.

Lastly, stop using Date and Calendar. Sun, Oracle, and the JCP community all gave up on those classes when they adopted JSR 310, and so should you. Those legacy classes really are a bloody awful mess.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • Thank you for your answer and confirmation! We use a proprietary software which is up to date with the Java 8, and yet uses the Date instead of Instant on all it's ORM classes. I wonder if they still do it for consistency with dates in the database. – Anurag Oct 08 '18 at 09:13
4

First suggestion:

    Instant transactionTime = Instant.now();

You are correct that there is no point in going through the time in your time zone, neither in a ZonedDateTime nor in a Calendar, when what you need is a timestamp, a moment in time. java.time.Instant is exactly that, a point in time without time zone (and is therefore the most direct replacement for a Date). You should probably want to use java.time, the modern Java date and time API, when you can. Date, Calendar and TimeZone are not only long outdated, they also have a range of design problems with them. The modern classes are so much nicer to work with.

If your transaction entry class internally uses java.util.Date and you don’t want to upgrade its internal logic right now, you can still add an overloaded setTime method that accepts an Instant.

public void setTime(Instant time) {
    // Delegate to the old method that accepts a java.util.Date
    setTime(Date.from(time));
}

A possible next step could be to mark the method that accepts a Date deprecated.

Second suggestion, if you insist on a Date I would still prefer:

    Date transactionTime = Date.from(Instant.now());

It does the same as new Date(), but tells the reader much more directly that you obtain the current moment. I can’t see how this could not be an advantage.

As an aside, if you did want to use your time zone, you should use a proper time zone ID like Antarctica/Davis rather than an offset. Again it tells your reader more about your intentions and is future-proof in case summer time (DST) is introduced some time or the offset changes for other reasons.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • Thanks! Your answer reconfirms my understanding and the answer made before you. Yes, I agree, Instant should replace Date wherever a timestamp is required for the event to occur but I'm afraid I am not able to change the base code. I think for all pusposes of showing the correct time with timezone for a particular place, I should use a formatter to set the TimeZone and the dates will get printed correctly, yes? – Anurag Oct 07 '18 at 13:30
  • 1
    Using the old-fashioned classes this is correct. Using the modern ones conversion to the user's time zone and formatting are two distinct operations, but yes, you will typically do both for showing the correct time for a particular place. – Ole V.V. Oct 07 '18 at 15:34
3

but I can only store the time as a java.util.Date.

Then the time zone does not matter,

Calendar calendar = Calendar.getInstance();

calendar.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
Date america = calendar.getTime();

calendar.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
Date shanghai = calendar.getTime();

System.out.println(america.equals(shanghai)); // true

You can just use:

transactionEntry.setTime(new Date());

This page will help you to understant Instant/Date, LocalDateTime, ZonedDateTime.

I think you might re-think the design. Why only java.util.Date is allowd?

From the description, it seems that you do care about the time zone where transaction occurs. While java.util.Date just do not support it. Which means you can store an accurate value(millis since epoch) to java.util.Date, but this value can not be represented by accurate date time, since the time zone has been lost.

You might need use java.time.ZonedDateTime to achieve it.

xingbin
  • 27,410
  • 9
  • 53
  • 103
  • Thanks for the confirmation! I ran the piece of code you mentioned and yes it prints out both dates as equal. – Anurag Oct 07 '18 at 13:27
  • 1
    @Anurag Yes, that's what I'm tring to say: Even if you pass ZoneId, it does not make difference – xingbin Oct 07 '18 at 13:28
  • 1
    No, never use `LocalDateTime` when tracking a specific moment. By definition, that class cannot represent a moment. – Basil Bourque Oct 07 '18 at 18:54