Java: get pacific timezone without DST - java

I need to use Pacific timezone in my code that doesn't incorporate DST. I know that America/Los_Angeles takes DST into account. When I try to use PST, the time API throws this exception:
java.time.zone.ZoneRulesException: Unknown time-zone ID: PST
Is there a solution for this?

"PST" isn't a time zone ID.
It sounds like you don't really want to use the Pacific time zone so much as a constant UTC offset of -8 hours. You can do that with:
ZoneOffset offset = ZoneOffset.ofHours(-8);

Options include:
Use a ZoneOffset as already explained in Jon Skeet’s answer. Since ZoneOffset is a subclass of ZoneId, you can use it everywhere a ZoneId is required.
Use ZoneId.of("Pacific/Pitcairn"). This time zone is at offset -08:00 all year. Pitcairn Standard Time is abbreviated PST too.
For the sake of completeness ZoneId.of("Etc/GMT+8") gives you a zone ID that is not a ZoneOffset but is equivalent to the ZoneOffset (+8 is no typo, the sign is intentionally inverted).
Link: List of tz database time zones

Related

Does having ZoneId and ZoneOffset at the same time make sense?

I'm a little bit confused of possibility in Java to have ZoneId and ZoneOffset in date at the same time
For example, does the date 2020-01-01'T'00:00:00+01:00[UTC-8] make sense? What time is it in UTC?
Is it +01:00 offset from UTC-8 timezone, so it's the same as 2020-01-01'T'00:00:00[UTC-7]?
No 2020-01-01'T'00:00:00+01:00[UTC-8] is a self contradiction. I doubt that there’s a way to construct a ZonedDateTime having this value. Or only by defining your own time zone that has an ID of UTC-8 and an offset of +01:00 at that point in time.
A ZonedDateTime has got both time zone and offset.
I am unsure whether it’s relevant to your question, but one way to study whether we can make a point in time out of a self-contradictory string could be:
System.out.println(ZonedDateTime.parse("2020-01-01T00:00:00+01:00[Pacific/Pitcairn]"));
Output on Java 11:
2019-12-31T15:00-08:00[Pacific/Pitcairn]
Pitcairn is at offset -08:00 all year. It seems that ZonedDateTime has chosen to trust the offset +01:00, so the time is the same as 2019-12-31T23:00 in UTC. It has then converted this time to the offset that it finds right for Pacific/Pitcairn time zone according to the time zone database, giving 15:00 in that time zone.
Edit:
What time is it in UTC? Is it +01:00 offset from UTC-8 timezone,
so it's the same as 2020-01-01'T'00:00:00[UTC-7]?
No, it is not the same at all. 2020-01-01'T'00:00:00[UTC-7] would be the same point in time as 2020-01-01T07:00:00 in UTC, but as I said, we got 2019-12-31T23:00 in UTC, which is 8 hours earlier than what you suggested. The -08:00 are not used to interpret the point in time, they are only used for conversion.
PS There aren’t any single quotes in 2020-01-01T00:00:00+01:00[UTC-8]. They are only in a format pattern that you would use for printing and/or parsing such a string.

Why GMT and UTC timezones don't have same rules

Why does below line print false? i think it should print true.
TimeZone.getTimeZone("UTC+5:30").hasSameRules(TimeZone.getTimeZone("GMT+5:30")
The answer is in the JavaDoc of TimeZone#getTimeZone:
the ID for a TimeZone, either an abbreviation such as "PST", a full name such as "America/Los_Angeles", or a custom ID such as "GMT-8:00"
Returns:
the specified TimeZone, or the GMT zone if the given ID cannot be understood.
And (from the class documentation)
[...] The syntax of a custom time zone ID is:
CustomID:
GMT Sign Hours : Minutes
GMT Sign Hours Minutes
GMT Sign Hours
The ID "UTC+5:30" is not a valid TimeZone ID (as per the specification of the method/class) and is interpreted as "GMT" zone, which is clearly distinct from the "GMT+5:30" zone.
Since you are located in India, you should use
ZoneId india = ZoneId.of("Asia/Kolkata");
Two messages:
The TimeZone class has design problems and is long outdated. The same goes for its friends like Calender and SimpleDateFormat. So don’t use them. Instead use java.time, the modern Java date and time API. Its replacement for TimeZone is the ZoneId class (and ZoneOffset for an offset from UTC, but don’t use that as a time zone, it isn’t one, see the next item).
Don’t use an offset from either UTC or GMT as a time zone. It works in your particular case, of course, but it may leave the reader wondering why you chose +05:30, which Asia/Kolkata clearly conveys. Also Asia/Kolkata is future-proof in case at some point in time the politicians change the UTC offset for India or introduce summer time (DST). While this is unlikely for India, it happens all the time in other places in the world, so It’s better to make it a habit to use the region/city format always.
For just one of the many design advantages of the modern API, try the modern version of your code:
ZoneId.of("UTC+5:30").getRules().equals(ZoneId.of("GMT+5:30").getRules())
This throws: java.time.DateTimeException: Invalid ID for offset-based ZoneId: UTC+5:30. Now you know from the outset what’s wrong: UTC+5:30 is not a valid time zone ID.
Link: Oracle tutorial: Date Time explaining how to use java.time.

What is the default time zone in Joda DateTimeFormatter if time zone is not specified?

I'm not familiar with Joda DateTimeFormatter, so I'm wondering if there is no time zone specified for DateTimeFormatter, what will be the default time zone? For example I have:
DateTimeFormatter stdFormatter = DateTimeFormat.forPattern("MM/dd/yyyy");
DateTime today = stdFormatter.parseDateTime("07/20/2017");
In this case, what would be the time zone of today? Is it gonna be 2017-07-20 00:00:00 UTC by default? Thank you!
Referring to the DateTime documentation here, the DateTime internally stores the value as milliseconds past 1970-01-01T00:00:00Z, where Z is the UTC zone.
The way it is output depends on how you decide to format it (i.e. if you want to print the DateTime in a different time zone, you can use the Joda libraries to do so).
In this case, DateTimeFormat.forPattern uses the JVM default locale, which is determined by Locale.getDefault(), whatever that may be for you. So, your DateTime will contain the time at UTC for '07/20/2017 00:00:00 {YOUR TIME ZONE}'.
Say your time zone is PDT (i.e. UTC-7). Then "07/20/2017 00:00:00 PDT" == "07/20/2017 07:00:00 UTC". Your DateTime object will store that UTC time.

Convert Time from one time zone to another using Java 8 Time

I am trying to convert Date with GMT +5:30 to EST with java 8 ZonedDateTime.
String inputDate = "2015/04/30 13:00";
DateTimeFormatter sourceFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm", Locale.US);
LocalDateTime local = LocalDateTime.parse(inputDate, sourceFormatter);
// local : 2015-04-30T13:00
//Combining this local date-time with a time-zone to create a ZonedDateTime.
ZonedDateTime zoned = local.atZone(TimeZone.getTimeZone("GMT+5:30").toZoneId());
// zoned : 2015-04-30T13:00+05:30[GMT+05:30]
ZonedDateTime zonedUS = zoned.withZoneSameInstant(TimeZone.getTimeZone("GMT-5:00").toZoneId());
// zonedUS : 2015-04-30T02:30-05:00[GMT-05:00]
I am expecting 3:30 AM EST but what I am getting is 2:30 AM EST as 1 PM IST= 3:30AM EST. What am I missing?
It seems that whatever service you found was being over-helpful in interpreting what you meant and assumed North American Eastern Daylight Time (EDT) when you specified EST (Eastern Standard Time). Most, not all of the places using EST as standard time are using daylight saving time and hence were on EDT or offset UTC-04:00 on the date you use, April 30, 2015.
If it makes sense in your situation, you should always prefer to give time zone in the region/city format, as Asia/Kolkata and America/New_York. If you intended Eastern Time as in New York or Montréal, one may say that your “time zone” of GMT-5:00 was wrong and the cause of your unexpected result.
So your code becomes for example:
String inputDate = "2015/04/30 13:00";
DateTimeFormatter sourceFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm", Locale.US);
LocalDateTime local = LocalDateTime.parse(inputDate, sourceFormatter);
// local : 2015-04-30T13:00
//Combining this local date-time with a time-zone to create a ZonedDateTime.
ZonedDateTime zoned = local.atZone(ZoneId.of("Asia/Kolkata"));
// zoned : 2015-04-30T13:00+05:30[Asia/Kolkata]
ZonedDateTime zonedUS = zoned.withZoneSameInstant(ZoneId.of("America/Montreal"));
// zonedUS : 2015-04-30T03:30-04:00[America/Montreal]
I have made one other change: When using the modern classes from java.time, there is no point in also using the outdated TimeZone class, so I have taken that out. The code is slightly simpler, and more importantly, ZoneId.of(String) includes validation of your time zone string so you will discover any spelling error in the time zone name (like when I just happened to type a ( instead of the / in Asia/Kolkata — such happens all the time).
Most of the above has already been said in comments by Jon Skeet and others. I thought it deserved to go into an answer so it’s plain to see that the question has been answered.
Though the question is old, felt like I could add more to the accepted answer.
A ZonedDateTime is different from an OffsetDateTime.
I would prefer to use ZonedDateTime when I'm getting a time for a specific location like "Asia/Kolkata", "Asia/Shanghai", "US/Pacific" (this time zone will change depending on the day of the year because of Daylight savings).
To illustrate with an example,
var pacific = ZonedDateTime.of(2020,11,01,1,59,0,0,ZoneId.of("US/Pacific"))
var afterAnHour = pacific.plusHours(1)
This will give me a time of
2020-November-01 01:59:00.000 AM -07:00[US/Pacific]
And if i add an hour to it, it will give me a time of
2020-November-01 01:59:00.000 AM -08:00[US/Pacific]
You can see that the hour component is same even after adding an hour to the time. This is because the daylight savings time has kicked in and the time zone is shifted from -07:00 to -08:00.
Now if i use an OffsetDateTime look what happens.
var offsetNow = OffsetDateTime.of(2020,11,01,1,59,0,0,ZoneOffset.of("-07:00"))
var offsetAfterAnHour = offsetNow.plusHours(1)
The offsetNow will be,
2020-November-01 01:59:00.000 -07:00
And adding an hour to it will be,
2020-November-01 02:59:00.000 -07:00
you can see that the hour component has become 2 after adding an hour.
The key point is a ZonedDateTime uses ZoneRules to calculate important properties like Daylight savings time so that it can adjust the time zone accordingly.
While the OffsetDateTime will not change the zone offset for anything.

Time Zones in Java / GWT (Client-side)

[Client-side GWT class]
I have a Date Object...
Date dataObject = DateTimeFormat.getFormat("yyyy-MM-dd'T'HH:mm:ss.SSS")
.parse("2009-10-12T00:00:00.000);
This works fine. However when I do a:
dateObject.getTime();
It returns a UNIX Time milliseconds using a GMT with daylight savings, therefore making it a UNIX Time I cannot use. I need it in UTC. How do I do this?
Currently I'm parsing a date and it is giving me back:
'Thu Apr 16 08:46:20 GMT+100 2009' # '1239867980191'
However the date I'm passing in is 1 hour less than this time (7:46 and not 8:46!).
How do I pass in the fact it's UTC? Or if it can't use UTC (which would be ridiculous), how do I use GMT without the daylight savings?
Your last edit makes things clearer.
Basically, you are confused, and you already get what you want.
1239867980191 milliseconds since the Epoch translates to Thursday, April 16th, 2009, at 7:46:20.191 in the GMT time zone. The very same instant translates to the same day, but 8:46:20.191 in the GMT+01 time zone. If your input string specified "7:46:20.191" and you indeed got 1239867980191 from Date.getTime() then congratulations, the parsing code understood your "7:46:20.191" as to be interpreted in the GMT time zone, and did it properly.
If afterwards you get "8:46:20" when printing, this is only because you use the GMT+01 time zone for displaying that instant. Note that the string contains GMT+100 precisely to notify you that it uses that time zone for display purposes. The instant which the Date instance represents is nonetheless exactly the instant you wish it to contain. Remember that a Date instance represents an instant in time, for which no notion of time zone applies: time zones are used to convert instants into calendar elements (days, hours...) and back.
To convert a Date to a displayable string, use DateTimeFormat.format(Date, TimeZone) which lets you specify which time zone you want to use for that string.
Since the Calendar class is not supported in GWT, maybe something hackish like this will work:
final String timezone = "GMT-07:00";
DateTimeFormat dtf = DateTimeFormat.getFormat("yyyy-MM-dd'T'HH:mm:ssZZZZ");
long unix = dtf.parse("2009-10-12T00:00:00" + timezone).getTime();
This way you can provide the correct timezone info - though, that should be the default behaviour.
It is the other way round. A Date instance holds the time in milliseconds since the Epoch, using the UTC time scale (i.e. leap seconds are ignored). This is what Date.getTime() returns and that's what you want.
The culprit here is the parser, which interprets the date you give as a string in your local time zone. If you want DateTimeFormat to interpret the string as a date-and-time given in the UTC time zone, append an explicit time zone to the parsed string:
DateTimeFormat.getFormat("yyyy-MM-dd'T'HH:mm:ssZZZZ")
.parse("2009-10-12T00:00:00.000" + " GMT");
(The above assumes that I understood GWT documentation properly; I have not tried.)
Just to be clear in my notations: for all practical purposes, there is no difference between "GMT" and "UTC", and there is no daylight saving in the GMT time zone. Other time zones are often defined as "GMT plus or minus some offset" and the offset may change between summer and winter. For instance, the time zone in New York is somewhat equivalent to "GMT-04" in summer and "GMT-05" in winter.
I keep seeing formats with ZZZZ being suggested... but why?
"yyyy-MM-dd'T'HH:mm:ss.SSSZ" would match
"2009-10-12T00:00:00.000-0000"
The last part being the offset from UTC; California (to use someone else's example time) would be -0800, -0700 in summer.
As a side note, GMT is also always -0000. That's why Britain's summer time zone is BST (British Summer Time, +0100).
Try the Calendar object.
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
Date dataObject = DateTimeFormat.getFormat("yyyy-MM-dd'T'HH:mm:ss.SSS")
.parse("2009-10-12T00:00:00.000);
cal.setTime(dataObject);
cal.getTimeInMillis();
According to the API, getTimeInMillis() returns "the current time as UTC milliseconds from the epoch."
EDIT: as _bravado pointed out, the Calendar API is currently not available for GWT (Issue 603). While this would get the appropriate time in a Java application, it isn't going to work here. There is information in the group about using GMT.
EDIT: Missing a closing bracket on the the Calendar.getInstance() call

Categories