A client's API that I am integrating with returns a time with the time zone as a short id (3 letter id). e.g.
9:00 MST
13:30 EDT
17:00 MDT
I realize this is bad practice (especially with the daylight savings), and short ids should be avoided, but unfortunately I have no way to get the client to change their api. Is there a clean way to convert these short ids to a property ZoneId? ZoneId.SHORT_IDS looks like the closest, but it doesn't include daylights savings. Worst case, I think I can just make my own map of short ids to ZoneIds since it's limited to the US
Time with zone makes no sense
13:30 EDT
A time-of-day plus a time zone makes no sense. Without the context of a date, there is no real meaning here.
The SQL-92 standard declares a TIME WITH TIME ZONE type, but neglects to define its meaning. I am not alone in being mystified by the SQL committee’s intention. And also, importantly, that data type is a misnomer: the SQL standard means offset when they say “time zone”.
Java offers an OffsetTime class. I presume this class exists merely for mapping through JDBC to that screwy SQL type of TIME WITH TIME ZONE. The Javadoc offers no real explanation as to the meaning of this class.
So what is your goal? I suggest you edit your Question to indicate what kind of processing you intend to do with such inputs. Perhaps we can guide you from there.
Pseudo time zones are not unique
You asked:
Is there a clean way to convert these short ids to a property ZoneId?
No.
These 2-4 character pseudo time zones are not defined, are not standardized, and are not unique.
CST — Do you mean Central Standard Time in the Americas? Or do you mean China Standard Time? Or Cuba Standard Time?
IST — Do you mean India Standard Time? Or Ireland Standard Time?
PST — Pacific Standard Time or Pitcairn Standard Time?
BST — Bangladesh Standard Time, Bougainville Standard Time, or British Summer Time?
AMST — Amazon Summer Time or Armenia Summer Time?
So, no you cannot cleanly determine a time zone from a pseudo zone. You can only guess. If you know for certain the domain of possible values you expect to receive, and you are certain as to their intended actual time zone, then you can create your own mapping.
But even then, there is no class in Java nor data type in SQL to represent a date with time zone. You would have to convert that time zone to an offset from UTC. And for that conversion, you would need a date to determine a moment to determine the offset in use at that moment by the people of that zone… which leads us back in a circle to point at the top of this Answer: A time with only a zone (or offset), but without the context of a date, makes no sense.
Reject senseless data
You said:
but unfortunately I have no way to get the client to change their api
Providing a time-of-day with an ambiguous pseudo time zone is like providing a money amount tagged with "dollar" while asking to convert to "francs".
We don't know if "dollar" means Canada Dollar (CAD), United States Dollar (USD), or a Australian dollar (AUD), or any of the twenty currencies with that name.
We don't know if franc means the franc CFA in West Africa, the Swiss franc, or some other currency with that name.
We don't have a date on which to look up a conversion rate.
So what would you do with such a request? What can you do? You would have to either ask for much more explicit definitions, or you would have to refuse the request.
Related
I have a web application that returns a booking time based on the country where the event was performed.
Eg: If the booking was created in India at 02-JUNE-2020 1700 IST,then time returned is:
2020-06-02T17:00:00+0530
If the booking was created in Thailand at 02-JUNE-2020 1700 Thai Time,then time returned is:
2020-06-02T17:00:00+0700
Now I have to store all this in a system in UK time,so the data would be:
for India,in UK system: 2020-06-02T12:30:00+0100
for Thailand,in UK system: 2020-06-02T11:00:00+0100
I know I can use the zone indicator of +0530 to convert to milliseconds offset by using
TimeZone.getAvailableIDs(milliseconds);
and find the corresponding timezone to do a reverse integration.
But is there an easy way to translate the IST to UK time directly in java ?
+0530 is not actually indicative of any particular time zone. A time zone could be, say, Europe/Amsterdam. This is +0100 in winter and +0200 in summer, and the zone Europe/Paris has the exact same offset at the exact same dates. Whilst unlikely, it is entirely possible that 5 years from now this is no longer the case. Note that +0100 does not accurately describe Europe/Amsterdam (it'd be wrong in summer), and cannot disambiguate between amsterdam and paris, which is why it's not good enough, generally. If this is just what you've been given and you can't change it, yea, getAvailableIDs is one way to at least attempt to convert +0530 into a zone, but note that usually you get many answers, so I don't know how you'd figure out how to pick the 'right' zone. Consider changing the system so that you get this timezone, the full ID, as part of the input instead.
Let's say you have obtained the zone, somehow.
Given 2020-06-02T17:00:00+0530 - you can translate this to the exact moment in time that the event being described by this timestamp has/will occur. That's presumably important; if you want an alarm to go off at that time anywhere on the planet you can now make that happen. That you store this 'in UK time' is just an implementation detail, that doesn't matter: You're storing the instant in time this event occurs, and not the way the human-oriented system that created this timestamp would refer to it (which is, presumably: '5 in the evening, on the second of june in 2020, in india').
But, you indicate a need to convert back from this 'exact instant in time' back to the zoned time.
Why?
If the answer is: So that it is familiar to the human, because the human I will end up printing this string to is definitely in india, you potentially have some issues if you go with the not-human-relevant zone ID of '+0530'; you optimally want to go with the more human-relevant zone ID of 'Asia/Kolkata', for example.
Okay, and now in java please!
an instant in time is best represented with an instance of java.time.Instant. This has no timezone info; it just marks a moment in time. (internally it stores as UTC, but that is an implementation detail. these things are timezoneless).
Once you have an Instant, and you have a TimeZone, you can do:
Instant x = ....; // obtain an instance somehow.
ZoneId zone = ZoneId.of("Asia/Kolkata"); // get a zone somehow.
ZonedDateTime zdt = x.atZone(zone);
You can print a zdt, for example with a java.time.format.DateTimeFormatter instance, and it'll render it as somebody in india would prefer.
If you have instead stored, say, a string containing the text 2020-06-02T12:30:00+0100, you can go from there to an instant rather easily, and then you can .atZone your way back to indian time.
No, I'm not talking about zone offsets --- those can vary during the year for a region based on e.g. DST. I'm talking about the actual time zones maintained by IANA. I understand these are not supported by ISO 8601, correct?
What are platforms doing to support identifying time zones in ISO 8601-like string representations? I notice that the latest Java date/time library is using an extended ISO 8601 format for this, e.g. 2011-12-03T10:15:30+01:00[Europe/Paris]. (See DateTimeFormatter API.)
Is there some converging convention (e.g. with other languages and platforms) for extending ISO 8601 to support time zone designation?
Update:
There's now a draft IETF proposal to extend RFC3339 with the time zone identifier in square brackets, among other things: https://datatracker.ietf.org/doc/draft-ietf-sedate-datetime-extended/
Original Answer:
I understand these are not supported by ISO 8601, correct?
Correct. ISO-8601 does not concern itself with time zone identifiers. IANA/Olson TZ names are not a "standard". They are just the most reliable thing we have. (Some may consider them the de facto standard.)
What are platforms doing to support this?
Support what exactly? This part of your question is unclear. If you mean to support IANA time zones, well that's all over the place. Some platforms have them built-in, and some rely on libraries. If you mean to support a string representation of an ISO-8601 date-time-offset + time zone ID, some platforms have this and some do not. You'll have to be more specific if you want to know more.
I notice that the latest Java date/time library is using an extended ISO 8601 format for this, e.g. 2011-12-03T10:15:30+01:00[Europe/Paris]. (See DateTimeFormatter API.)
I think you are talking about DateTimeFormatter.ISO_ZONED_DATE_TIME. The docs say specifically:
The ISO-like date-time formatter...
...extends the ISO-8601 extended offset date-time format to add the time-zone. The section in square brackets is not part of the ISO-8601 standard.
So this is Java's specific format, not a standard.
Is there some converging convention (e.g. with other languages and platforms) for extending ISO 8601 to support time zone designation?
As far as I know, there is currently no standard that covers the combining of an ISO8601 timestamp and an IANA time zone identifier into a single format. One could represent it many different ways, including:
2011-12-03T10:15:30+01:00[Europe/Paris] (this is the default in Java 8)
2011-12-03T10:15:30+01:00(Europe/Paris)
2011-12-03T10:15:30+01:00 Europe/Paris
2011-12-03T10:15:30+01:00 - Europe/Paris
2011-12-03T10:15:30+01:00/Europe/Paris
2011-12-03T10:15:30+01:00|Europe/Paris
2011-12-03T10:15:30 Europe/Paris (+01) (this is the default in Noda Time)
If what you're looking for is a way to include a ZonedDateTime or similar data in an API in a standardized manner, my personal recommendation would be to pass the time zone name in a separate field. That way, each portion of data is as good as it can be. For example in JSON:
{
"timestamp": "2011-12-03T10:15:30+01:00",
"timezone": "Europe/Paris"
}
The Answer by Matt Johnson is spot-on correct. I'll just add a few thoughts.
Time zone versus offset-from-UTC
An offset-from-UTC is merely a number of hours, minutes, and seconds ahead/behind UTC. Alone, this does make a date-time into a specific moment on the timeline. But it is not nearly as informative as including the official time zone name as well.
While there is no standard yet for including the time zone name, I do hope others follow the lead of the java.time classes in appending in square brackets the name of the time zone. This format seems sensible to me as it would be simple to truncate the square-bracket portion to be backward-compatible with non-savvy software.
For example:2011-12-03T10:15:30+01:00[Europe/Paris]. If the data were only 2011-12-03T10:15:30+01:00, we would be able to identify the moment on the timeline, but would not be able to adjust other moments into the same frame of mind as we would not know what rules of adjustment to apply. Zones such as Europe/Zagreb, Africa/Brazzaville, Arctic/Longyearbyen, and Europe/Isle_of_Man all share the offset of +01:00, but they may well have other adjustments in force differing from those of Europe/Paris. So if you were to try to add three days to the value 2011-12-03T10:15:30+01:00, you really cannot faithfully compute the result because you do not know what adjustments may need to apply such as DST cutovers that may be occurring during those three days.
A time zone defines the set of rules for handling anomalies such as Daylight Saving Time (DST). Politicians around the world enjoy making adjustments to their time zones, or even re-defining them. So these rules change frequently. Think of a time zone as a collection of offsets over time, many periods of time in history wherein each period had a particular offset in use in that particular region.
You can think of a time zone as a collection of offset-from-UTC values. In America/Los_Angeles part of this year is 8 hours behind UTC, and part of the year will be 7 hours behind UTC. That makes 2 points of data collected as part of that time zone.
Another example, in previous years, Turkey spent part of each year 2 hours ahead of UTC and part of each year 3 hours ahead. In 2016, that changed to indefinitely staying 3 hours ahead. So, multiple points of data in the time zone Europe/Istanbul.
Just use UTC
Personally I do not see much value in even using values such as 2011-12-03T10:15:30+01:00. Without a time zone, you might just as well use UTC alone. In this case, 2011-12-03T09:15:30Z (9 AM instead of 10 AM).
Generally the best practice is to use UTC when storing and exchanging date-time values. Think of UTC as the One-True-Time, with zoned or offset values being mere variations.
I want to get the timezone shortcut like EST (for eastern standard), PST (pacific), and so on based on the UTC offset. I realize it's not a simple problem and there can be more than one location based on a particular offset, but that's okay.
I'm trying to get it using Util Calendar object but I don't seem to get a string but rather just the offset.
public String foo(int offset)
{
....
return TimeZoneShortcut;
}
Thanks in advance.
The answer by user2580516 is correct. I can add a bit more.
Avoid Three-Letter Codes
The three-letter time zone IDs are neither standardized nor unique. Avoid them.
For example, IST is used to mean India Standard Time or Irish Standard Time. There are many such collisions.
Time Zone Names
Instead of 3-letter codes, use proper time zone names. Examples: "Europe/Paris", "America/Montreal", and "Asia/Kolkata".
There does not seem to be an official standard for time zone names. That surprises me; hopefully I'm wrong and someone can fill me in. At any rate, a commonly used list is take from the tz database (formerly known as the Olson database), as listed in this Wikipedia page.
The excellent date-time library, Joda-Time, has a method to generate a list of its currently known time zone names.
The time zone names change over time, some are added, and their rules change too. All that is determined by politicians and bureaucrats, so changes are last-minute and not always sensible. So you should take care to keep your date-time library up-to-date, or at least update its contained time zone database.
Impossible Question – Cannot Determine Time Zone
A time zone is more than just an numerical offset from UTC/GMT. A time zone also contains the set of rules for Daylight Saving Time (DST) and other anomalies.
So you cannot infer a time zone from an offset. You can guess, but you cannot be sure.
For example, take the offset of +01:00. Is that "Europe/Paris" or "Africa/Lagos"? Both have an offset of one hour ahead of UTC. So does it matter which you use? Yes… France observes Daylight Saving Time but Nigeria does not. Assigning the wrong time zone means your date-time calculations will be wrong.
Another twist… Perhaps that +01:00 was recorded in London during the summer time. In summer, London observes DST and moves its clocks 1 hour ahead. While standard time there is +00:00 (on UTC/GMT), DST moves them one hour ahead of that.
Yet another twist… Even if you say "just pick one", which one? For +00:00 in just standard time, there are at least 2 three-letter codes (CET and MET) and 37 named time zones crossing two continents.
Perhaps you are thinking, "I can use the date to figure out if DST was in effect". Nope, DST starts and ends on different dates in various time zones sharing the same offset. Furthermore, some countries (time zones) are sensible enough to not fool with DST.
So regarding your question being "not a simple problem … but that's okay" is wrong. It's not a problem, it's impossible. Like the question, "Given a birthday, determine an individual person". You can determine that a person or time zone is not correct, but you cannot determine which is correct.
Record Time Zone With Time
If knowing the time zone (its locality and rules) is important to you, you must record the zone information along with the date-time. This may mean an extra field in your database for example.
Java 8 brings a new java.time.8 package, inspired by Joda-Time, defined by JSR 310. The designers have come to realize the importance of the time zone as a part of a date-time value. As a result, their designs include:
The main date-time class starts with the word "Zoned" to stress that the class includes time zone info: ZonedDateTime
Their toString implementation on the ZonedDateTime class extends the ISO 8601 format by appending the name of the time zone in brackets. Instead of:2014-02-14T20:51:55.427-08:00it outputs2014-02-14T20:51:55.427-08:00[America/Los_Angeles]
Use TimeZone.getAvailableIDs(), and select one (maybe the first) that has only three letters. You'll have to adjust the offset to numeric milliseconds to pass into that function. Use of the three letter IDs is deprecated, but it sounds like you are okay with that.
When storing time zone for a given date, is there a particular advantage offered by persisting the time zone's ID string (e.g. Joda's DateTimeZone.getId()) versus saving the local time offset from UTC (e.g. Joda's DateTimeZone.getOffset())?
Although Joda's DateTimeZone and the JDK's TimeZone appear to share ID strings, it seems that saving the offset would be more language agnostic.
The answer depends on you're use case. The offset is language agnostic, but might need to be interpreted back to the ID for a user to pick from. Most users don't know what they're offset is.
On the other hand, if you're users can be reasonably expected to know their offset, then you don't need to interpret.
It's more of a design decision then it is a best practice issue.
Storing with the offset only you will not know about daylight savings etc. Example from the doc: "Using this system, America/Los_Angeles is expressed as UTC-08:00, or UTC-07:00 in the summer"
I am trying to get current time in specific time zones. I tried following code.
Calendar j = new GregorianCalendar(TimeZone.getTimeZone("US/Mountain"));
j.setTimeInMillis(Calendar.getInstance().getTimeInMillis());
System.out.println(j.get(Calendar.HOUR_OF_DAY)+":"+j.get(Calendar.MINUTE));
TimeZone tz = TimeZone.getTimeZone("US/Mountain");
System.out.println("tz.getRawOffset()"+tz.getRawOffset()/3600);
System.out.println(tz.getDSTSavings());
System.out.println
(tz.inDaylightTime(new Date()));
The answer I got is surprising -
16:57 - wrong by 1 hr
tz.getRawOffset()-7000
3600000
true - why daylight saving is true for Arizona?
How to get the correct wall clock time of Phoenix or any other city in US?
Arizona is in the Mountain timezone, but doesn't observe DST. If you specify the timezone "US/Mountain", then the computer will apply the rules used by most states in the Mountain time zone, which include observing daylight savings time. To get the rules for Arizona (which don't include DST), you want the timezone "US/Arizona" (or "America/Phoenix"). In the Navajo nation, you want the timezone named "Navajo".
To save yourself some of the trouble, always try to use the names from "America/*" where you can pick the name of a city that has the same timezone rules as the place you're interested in.
To get the correct time in the correct timezone for any given city in the world, you simply have to familiarize yourself with the names in the Olson timezone database and their meanings. While you usually think of the term "time zone" to mean the time of day in the middle of the winter (when everybody observes standard time), in the Olson database a timezone name represents the entire history of daylight savings time rules and timezone rules for a particular region.
As an example, even though Indiana now observes Eastern time and observes DST (except for a few counties right near Chicago which are on Central time like Chicago), before 2006 they didn't observe DST. A timezone named "US/Indiana" (or "America/Indianapolis") was created to cover this region, an even today, you would still want to use the timezone "America/Indianapolis" when talking about Indiana, so that queries about dates and times before 2006 could be answered correctly.
Most of Arizona does not observe Daylight Savings Time, but the Navajo Nation inside Arizona does observe DST.
I am providing the modern answer. Don’t use the classes Calendar, GregorianCalendar, TimeZone and Date. They are all poorly designed and fortunately all long outdated.
java.time
It’s simple when you know how:
ZonedDateTime arizonaExceptNavajo = ZonedDateTime.now(ZoneId.of("America/Phoenix"));
System.out.println(arizonaExceptNavajo);
When I ran this code just now, the output was:
2019-10-23T04:07:23.034-07:00[America/Phoenix]
The US/Mountain time zone ID is deprecated. It’s a link to America/Denver, and America/Denver does use summer time (daylight saving time, DST). Modern time zone IDs have the form region/city where region is either a continent like America or an ocean like Pacific.
As others have said, summer time is used on one place in Arizona, the Navajo Nation. The Navajo time zone ID mentioned is deprecated too. Use America/Denver:
ZonedDateTime navajoNation = ZonedDateTime.now(ZoneId.of("America/Denver"));
System.out.println(navajoNation);
2019-10-23T05:07:23.037-06:00[America/Denver]
Since summer time is still in effect, the time of day is one hour ahead compared to the one for Phoenix above.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Navajo Nation on Wikipedia.
The zone information can be obtained directly for many major cities worldwide. This includes Phoenix, which has the zone identifier "America/Phoenix".
You can find the list of available zone identifiers using the method TimeZone.getAvailableIDs() or by manually inspecting the contents of the JRE lib/zi directory.
As other posters have noted, the "US/Arizona" zone information is distinct from "US/Mountain".