93 comments

[ 3.9 ms ] story [ 199 ms ] thread
How do I convert a Date to a different time zone? You don’t – because a Date doesn’t have a time zone.

There's something about this, and the way we think about dates, which makes it very hard for some people to understand. Some people get it immediately, others never will. Not wanting to sound superior (I think the Date object seems almost designed to foster this confusion), but I've lost count of the number of times I've spent a long time explaining this, and then at the end of it all the person still says something like, "OK, well we'll just use this GMT Date then."

I think it is a matter of perspective. Say we take Planck time to be the atomic unit of time. Then really any larger unit of time can be viewed as interval of time with some error bounds. One you reach time units of on the scale of "days", time zones definitely impact this interval. Saying something happen(s/ed) on a particular day is not precise because a day is just a particular interval of time that varies based upon your time zone.

This is why if you've ever submitted a paper to a conference, the deadline will nearly always be given with an hour level of precision and a time zone. Because just saying submissions are due on day X is not well defined in the modern world.

Edited: Not sure why this is getting downvoted. Do you people seriously believe that 4/23 is the same period of time in NYC as it is in London?

From the HN guidelines:

Please resist commenting about being downvoted. It never does any good, and it makes boring reading.

Can we get an addendum to please resist quoting the guidelines? That is definitely boring reading.
> Do you people seriously believe that 4/23 is the same period of time in NYC as it is in London?

Honestly, it sounds like you didn't read the article and/or you misunderstood grandparent comment.

The Date class in Java does not represent a calendar date or period of time like "April 23th 2017". It represents an instant in time. It is precise to the millisecond and it does not depend on a specific time zone -- although it can be represented in different time zones. If you give the same (Java) Date value to a bunch of of people around the world and tell them to hook up to your live chat at that date and time, they will be able to attend at that time with no further information (such as timezone).

I think trurl's comment actually serves to highlight how awful java.util.Date's design is, and how important names are in programming. trurl was responding to a comment about Date not having a timezones, on an article that goes in depth as to how Date just represents an instant in time and not a date on a calendar, and he still misunderstood what java.util.Date's represent. Imagine how the legions of programmers new to Java make the same mistake.
I don't see anything in trurl's comment that makes any reference to java.util.Date. A more charitable reading would be to assume he is discussing the abstract concept of a date as it was mentioned upthread.
> Some people get it immediately, others never will.

Member of the second camp reporting in. The default toString() behavior of java.util.Date threw me off for a very, very long time.

Whether a Date includes the time zone is an arbitrary convention, and unfortunately some people intuitively gravitate towards one or the other.

You can say that a Date doesn't include a time zone, but then a Date no longer actually refers to a concrete time -- it needs to be combined with a time zone first. This differs from how conventional language deals with dates, which is why people don't like it. And also the point of the article -- Java's Date class is misnamed and confusing.

No, that’s exactly the parent’s point. Date always refers to a single concrete point in time, a time zone is only needed when parsing the date or formatting it for output.
(comment deleted)
An offset from a certain point in time without a time zone might be better described as an abstract point in time? Given such Date-s A, B, C you could order them (with potential overlap) and talk about how far apart they were) - but would need to convert via the help of a timezone to get a concrete point in time?
It appears that you still think of a date without a time zone as relative, which isn’t right (IMHO). A Unix date is an absolute mark on the axis of time, time zones are just different “views” on this time.
What really helped me get this is the distinction between GMT and UTC.

UTC is a universal method to describe time-instant. However, UTC is not a timezone. GMT is a timezone, specifically, it is the timezone GMT+0. Representing a time-instant in GMT for universality is wrong, representing a date (i.e. the kind of thing humans use to denote appointments in their daily life) in UTC is also wrong. Use the construct that matches the domain.

(comment deleted)
C++'s equivalent is std::chrono::time_point, which seems like a much better name. A "point in time" is not obviously something a time zone would be attached to.
For me, the moment the penny dropped was the realisation that "Timezone" is a presentation layer issue. i.e. it's not something you should concern yourself with until you're rendering it to be read by a human. Otherwise just using some well-defined simple convention such as epoch-time is fine throughout your application. There are exceptions such as doing date-calculations and such, but I extend my rule of thumb such that these kinds of things should be converting from epoch time (e.g. to `Calendar`) for the calculation and then back again.
I agree, I find it a well working convention to use UTC times for everything in an app, and convert to local time zone only at the time of display when formatting the date as a string.
Unless you are dealing with recurring events that happen at a specific time of day, in which case you need to know the local timezone to avoid off-by-one-hour errors if daylight savings kicks in.
Yes, I'm sure the use cases I've had to deal with have been very simple to start with.
That works for some things, but not for others. If I set my alarm clock to 7 AM local time, you convert that to UTC, then I travel one time zone over and you wake me up at 8 AM local time, I'm going to be annoyed.
That works, but note that it's different from using java.util.Date, which is not in UTC or any other time zone.
Not quite though. If you have a calendar app, and someone has an appointment for 1 Jan 2018 14:00 LTM (LTM being a zone I just made up). Now suppose than in the next year, the LTM timezone changes. This sometimes happens to ease international commerce.

In the case of such a change, I'd argue the time-instant should change and the timezone representation should be constant.

Excellent corner case. Here it comes down to how you model the storage of your appointments. Since the issue is framed within a calendar the "instant" itself is not of primary concern, but the position within that calendar.

This is also how the likes of "chron" model scheduling of tasks. I found this so hard to wrap my head around at first precisely because I was used to dealing with time "as a programmer" from the inside out. Of course when you're scheduling a repeating task that you want to happen "at midnight" or "on a Monday morning" it makes more sense to specify in this top-down manner.

I might categorise such things as "a presentation layer" concern, but as you point out, in these cases your moment in time is stored as a calendar reference.

TLDR: It sucks balls use Jodatime

Edit: I was wrong my bad

I have been using the Java 8 Time API which seems to have been based on Jodatime, and one dependency less
> Java 8 Time API which seems to have been based on Jodatime

JSR-310 was developed in large parts by the author of Joda, more than based on Joda it's also based on lessons learned from Joda.

Only if you are using a version before Java 8
Represent !

Some of the back end systems are still on 1.2.

Something about "no money for licenses and upgrades" on items that use Java.

Not too many in-house developers, aka zero, want to touch that core system.

> Note that from Java SE 8 onwards, users are asked to migrate to java.time (JSR-310) - a core part of the JDK which replaces this project.

http://www.joda.org/joda-time/

So basically a joda-like sane api was included as a JSR, making joda obsolete?

Are there any such plans for .NET and NodaTime? They have a more "everything is a package"-philosophy these days, but sensible dates and times really ought to be right in the corlib, especially since the bad dates and time apis are already there.

I've mentioned a few times to MS folks that I'd be very happy for the mscorlib team to make Noda Time redundant. They haven't bitten yet though.
(comment deleted)
Several years ago I answered a question on StackOverflow "what's wrong with the date and time api in Java"

http://stackoverflow.com/questions/1969442/whats-wrong-with-...

Considering how long ago it was answered, it took a /long/ time for the Java 8 replacement to come on board and give some sanity back to Java date computations.

It took a long time for Java to fix anything before Java 8.

The Sun-Oracle changeover sort of crippled change in the Java language for almost a decade.

Java 8 was released 5 years after the Sun Oracle acquisition. What do you mean by decade?
I think the article gets at this, but the biggest "eureka" I had was when I realized that java.util.Date is just sugar around a long value (ms since the epoch). Thus, wherever you use a Date, if you would NOT consider using a long there, then Date is the wrong choice. E.g. timestamp for log entry records - yeah, makes sense. Days on a schedule app - definitely wrong choice.

Of course, as the article and other commenters have said, just use jodatime or the Java 8 equivalent.

The basic problem with this then being that all conversions between the two concepts are implicit. Dates are points on calendars and there is complexity to deal with and assumptions to be made when converting to points in time.
What hn_throwaway_99 is saying is that there aren't two concepts. For java.util.Date there's only one concept, which is that it wraps a long number of milliseconds since the epoch. So it has nothing to do with dates on a calendar, despite its name.
Or just use a long...
Yeah. Have fun working out date arithmetic, leap seconds and time zones on your own. What could possibly go wrong.
No, that would be stupid - but you can't use a Date for that either.
You can; there are getters and setters for the different components. And because Date is lenient by default, you can do eg.

    date.setMonth(date.getMonth() + 1);
and it will do the Right Thing even if the month is December.
The `get` and `set` methods on `Date` have been deprecated since 1.1... In favor of `Calendar`, which is all `Date` is using under the hood to perform such calculations anyway.
True that, somehow totally slipped from my mind. To my defense, the last few times I've used java.util.Date it has been with GWT which doesn't provide anything better at least out of the box.
No worries. The whole thing was a tire-fire from the get-go, and I certainly don't blame you.
It should be encouraged to use long. Not only it performs better, but it is also simpler to understand and use compared to obscurity of java.util.Date.

I've written all of date arithmetic using only long values and I can share the code if anyone is interested

Before Java 8 and Jodatime, it was better to use java.util.Date than a long, for type-checking and self-documentation.

If a function accepts a java.util.Date and you pass an id of some database object by mistake, you'll get a compile error. If the function accepts a long you won't.

If a function accepts a java.util.Date and the parameter is called "start" you know it accepts an instant in time, if it accepts a long you might think it could be something else.

Yes, the class is terribly named, but before there was Java 8 and Jodatime, it was better to use it than a long.

It is not always a good idea to box primitives so that you can have compiler point out mistakes. Yes, it is good for developer once or twice, but end user will suffer performance hog forever.
Modern JVMs are pretty good at optimizing away the overhead of boxing primitives. So don't throw away the advantages of type checking unless you have hard evidence of an actual performance problem.
This is a trade-off between "should the program work" or "should the program be fast". The users would prefer a slow program that works, over a program that crashes or corrupts data.
But java.util.Date is mutable where a long isn't, and that's a problem. People can mutate a Date from under your feet if they manage to get a reference to it. This has a lot of far reaching implications, but essentially, unless you protectively copy all values of type Date you get or give, you can't trust your own internal state. You can't write a legal equals or hashCode implementation that uses a date field. The list goes on.

Don't do java.util.Date.

And rather importantly, with a dedicated type you're a bit less likely to encounter and off-by-a-thousand error when there's confusion about millis vs. seconds since Unix epoch.
I'll never understand why they thought 0-based months made sense, but then used 1-based days and 1900-based years. Even if C did it. Almost feel like I can hear the committee:

"Meh it's only a Date class, no one will really use it, will they? Billy already committed the code for 0-based months. Who here does not think of January as month zero?"

feet nervously scuffing carpet

"Right, settled! Moving on, company picnic is on the 1st of 0 next year (96)".

The java.util.Date API is pretty clearly modeled off of POSIX time, and therefore retains compatibility with POSIX's wrong decisions. I suspect that POSIX used 0-based months so they could index into some const char *monthNames[]; variable; since years and days are shown directly, there's no need to force them to be 0-based (not that day-of-week and day-of-year are 0-based as well).
I didn't realize that came from POSIX or was an attempt at complying with it. Yes the array indice makes sense (although exposing that externally and not just adjusting the value internally seems like a poor decision). Not sure why Java felt the need to carry that behaviour across, I can't recall anyone clammering for POSIX compliant date handling.
The funny part is that JavaScript copied this behaviour from Java for compatibility (in fact most of JavaScript's Date's behaviour is a direct copy of Java's).

I didn't know that this is one case where Java copied from C, which means JS copied it from Java and Java copied it from C (which itself seems to have inherited it via POSIX), all in the name of API compatibility but confusing the majority of programmers in each language every time.

    > I'll never understand why they thought 0-based
    > months made sense, but then used 1-based days
    > and 1900-based years. Even if C did it[...]
Java, Perl etc. all copied this pattern from localtime() in C. In all of these languages it's idiomatic to write something like:

    printf("%s %d, %d\n", mnames[mon], mday, year);
To print out e.g. "January 6, 78". The reason month days aren't zero based is obvious, usually when you use them you aren't going to have go through a lookup table, whereas nobody calls January "0" or "1", so you'll always need a lookup table.

Indexing months by 1 would mean declaring the "mnames" array would be the un-idoimatic:

    char *mname[] = { NULL, "Jan", ... };
Instead of:

    char *mname[] = { "Jan", ... };
Or you'd need to use the latter form and do lookups by mnames[mon - 1] instead of mnames[mon].

The reason you have to add 1900 to the year is that when C was made it was standard for software to use two-digit years, and if you told some of C's designers back in the 70s that we'd still be using & discussing these APIs in 2017 they'd probably have laughed at you.

Of course sometimes you need to use month indexes themselves, e.g. if you wanted to print out 1978-01-06 in a log file:

    printf("%d-%d-%d...\n", year + 1900, mon + 1, mday, ...);
But such are the trade-offs of low-level API design. I guess C's designers took the inherent idiomaticness of zero-based indexes & expectation that the common case would to print out month names over such use-cases.
> whereas nobody calls January "0" or "1"

Eh, in many locales it's idiomatic to refer to months by number, not by name, especially in the "short form" of a date. So you end up having to add one to the month number. But of course when they made C they didn't think about internationalization either...

> Or you'd need to use the latter form and do lookups by mnames[mon - 1] instead of mnames[mon].

All of which should be hidden behind your API anyway. It was a terrible choice, because it forces the client to deal with weird things just so that you can squeeze a single decrement out of your code.

This confused me with Foundation's Date class as well. I think it's really important to have abstractions that are really clear. Like for example I had to represent Open Hours for business and wanted a way to represent 4pm on Monday. That's surprisingly hard (in Swift and Java) because the APIs want you to realize the complexity of Time. But I don't care. I want an abstraction that says "Yup it's Monday at 4pm in Santa Clara". I think better Date and Time libraries that deal with more user facing things would be great.
`NSDate` used to support creation from a "natural language" phrase: https://developer.apple.com/reference/foundation/nsdate/1415...

It was deprecated recently, however.

Even further back, there was an NSCalendarDate class that was supposed to represent the human concept of a date. IIRC they got rid of it because they felt like it muddled presentational concerns with data structure concerns. It was pretty convenient, though.
Yep, I had to do a bit of fuddling around with DateComponents and it was painful.
> That's surprisingly hard (in Swift and Java) because the APIs want you to realize the complexity of Time

That's because time is hard. Time feels easy because we use it constantly in every day tasks. And if you just ignore those complexities, you get buggy code that costs your company money.

Time feels easy because we humans constantly make implicit and contextual assumptions about meaning. Computers are terrible at both implicit and contextual assumptions.
A relic of 90s programming. Perhaps we can take a moment to realise we are not just doomed to reinvent things that were done properly in the 70s. Looking back there were plenty of things that were just poorly thought out and this was one of them.
My very first job out of university involved the Y2K bug. Believe me, things weren't done properly in the 1970s (note the 4 figure date there. The scars are real...)
It doesn’t have a calendar system.

Hopefully this won't come off as mere quibbling about terminology, but to me it makes more sense to say that it does have a calendar system, it's just that it isn't any of the ones humans use. When I write "April 23, 2017 6:04pm CDT", the calendar system is Gregorian. If I were to write the integer value underlying a java.util.Date, I would argue the calendar system is Unix Epoch.

Both are systems that pick out arbitrary points in history (year 1 AD or midnight January 1, 1970) to base numbers off of. Both have rules on how you manipulate those numbers and what values mean.

If someone persists in asking what the time zone is for a java.util.Date, I would probably tell them that, more or less, it has its own time zone. This isn't quite true, but it's close because time zones are basically parameters for parameterized calendar types, so a time zone kind of implies a calendar.

Anyway, the point is that thinking of it as its own calendar system hopefully makes it clear that it stands on its own.

So this isn't super exact in the common usage of calendars, in part because calendars are usually implicitly fixed to a timezone (hence Google Calendar asking to change your calendar's timezone).

For example, December 25th might be Christmas, but you cannot define two Dates that "delineate" Christmas in the world, because the start time is different dependent on the time zone.

While this is kinda pedantic to Earth calendars, it's also the kind of problem that most commonly causes issues in our programming.

You can do "if date < christmas", but it most likely won't do what you want it to do if your program is meant to run in many timezones.

For example, December 25th might be Christmas

Except in countries where "Christmas" tends to refer December 24th and December 25th is "Christmas Day".

Indeed - in this case you're talking about locale rather than timezone.
So how do computers represent instants? They typically store an amount of time before or after a particular instant which is effectively an origin. Many systems use the Unix epoch, which is the instant represented in the Gregorian calendar in UTC as midnight at the start of January 1st 1970. That doesn’t mean the epoch is inherently “in” UTC – the Unix epoch could equally well be defined as “the instant at which it was 7pm on December 31st 1969 in New York”.

A thousand times yes. I see many developers confused by this. The Unix epoch is not inherently specific to UTC, it just happens to have a round date when described in that time zone. Seconds (or milliseconds) since the Unix epoch is a unit without timezone.

You say "it just happens" as if it's a coincidence. It was a deliberate choice.
And timezones are a completely orthogonal issue if you note that epoch is defined as "midnight at the start of January 1st 1970" in UTC.
The misleading name is the worst misfeature. I've had to explain to at least half a dozen coworkers that Date is not a date, but a timestamp. Some people just refuse to believe it, perhaps because they trust the Java designers more than me, as you normally should.

Often they persist in thinking "it's just a more detailed date, 17:08, April 23 is still April 23".

Then I have to explain that every Date object is on two different real dates. That's a big struggle for many.

One similar thing that's always bothered me conventionally. Why is it `System.arraycopy()` and not `System.arrayCopy()`?
actually, why not System.copyArray(). arraycopy() suggests that the method returns a copy of the array ( sorted() vs sort() )
Tangential, but one of the best sources I've seen on issues with times and dates is Eric Naggum's "The Long, Painful History of Time".

http://naggum.no/lugm-time.html

Notably, besides outlining the complexity of the domain, the article also serves as explanation of decisions made by author in implementing local-time, a popular date/time library for Common Lisp.

While the content is spot on, I disagree about the title - it's more of an introduction than an 'all about'. You understand it best if you already know how to (not) use java.util.Date...
For many years my advice to other java devs has been to avoid using java.util.Date and Oracle date columns where possible - for all the reasons given in the article, but generally because they don't always do what you expect.

The reason is that these dates encapsulates one concept of date (a point in time), and devs often try to use it for another (the legal date).

A legal date is fixed. It also has no time zone, but in a different way - it will not look different when viewed from another timezone. I was born on the opposite side of the world to where I live. The DOB on my passport didn't change, as I moved around the world.

Too often the user inputs a legal date. Software translates this to a point in time so it can be stored in one of these dates, the TZ info is lost, and before you know it the software is displaying a date other than that input. A bug is filed, a dev investigates and hacks code to add one hour, the bug is closed, and another opened when we pass into daylight saving.

It doesn't have to be like this - even MS Excel knows to just use a day count.

I am not using Java but I am right now making a booking system. I am storing local time (with Offset) and the timezone identifier such as "Europe/London".

How does using Day Count help?

Sorry for being oblique.

A day count is just the number of days since some arbitary point. Say today is 42849, tomorrow is 42850, etc.. If the user enters the 24th April 2017, you store 42849. When you read it back 42849 is always displayed as 24th April 2017.

There is no magic - you could just store 3 integers, or a formatted string say "20170424". Anything that doesn't involve timezones.

Perhaps, with your programming language you don't get the same issues, but to use java.util.Date you have to parse this into a number of milliseconds since 1 Jan 1970. You might round it down to midnight at the start of the day - or the end. You might select midnight according to local time (with or without daylight saving), you might use UTC, or you might unwittingly leave it to the machine. To persist this to a DB you might use some ORM mapping like Hibernate - which without careful configuration may also make some assumptions about timezone, and Oracle might do the same. By the time the value is read back, your date may not be the same one entered - and this may not show up until you pass into daylight saving.

Not at all, I was genuinely curious. Thanks for the detailed response, I understand what you were getting at.