Joda Time to Java Time Migration show different result while migration - java

Given the following code
public static void main(String[] args) {
org.joda.time.format.DateTimeFormatter _timestampFomatNYCJoda = org.joda.time.format.DateTimeFormat.forPattern("yyyyMMdd HHmmss.SSS").withZone(DateTimeZone.forID("America/New_York"));
DateTimeFormatter _timestampFomatNYC = DateTimeFormatter.ofPattern("yyyyMMdd HHmmss.SSS").withZone(ZoneId.of("America/New_York"));
LocalDateTime localDateTime = LocalDateTime.now();
org.joda.time.LocalDateTime jodaLocalDateTime = new org.joda.time.LocalDateTime();
System.out.println("System Time " + new Date());
System.out.println("Java Version " + localDateTime.format(_timestampFomatNYC));
System.out.println("Joda Version " + _timestampFomatNYCJoda.print(jodaLocalDateTime.toDateTime(DateTimeZone.UTC)));
}
Why does the Java Version and Joda Version dont match ? I am running this on IST clock.
Below is the output
System Time Fri Mar 27 17:01:33 IST 2020
Java Version 20200327 170133.933
Joda Version 20200327 130133.938

I can reproduce your results. I can also explain them. Joda-Time and java.time have been designed to behave differently in this case. Let’s look at them in turn.
Joda-Time
In Joda-Time DateTimeFormatter.withZone() gives you a formatter with an override zone, that is, a zone that will always be used for formatting dates and times. In other words, any date and time will be converted to this zone for printing. The documentation says:
When printing, this zone will be used in preference to the zone from
the datetime that would otherwise be used.
When you do new org.joda.time.LocalDateTime(), you are getting a LocalDateTime representing the current date and time in your default time zone. The Local in some class names means without time zone or offset from UTC. I figure that you must have got a value equal to 2020-03-27T17:01:33.938.
Apparently what happens when you format a LocalDateTime with a formatter with an override zone, is that the formatter assumes that your LocalDateTime is in UTC (which yours isn’t) and converts it from there, in your case to America/New_York time zone. Since summer time (DST) is in effect in New York, the offset is -04:00, so 17:01 becomes 13:01.
This is the wrong result. When the time is 17:01 in your time zone, it is not 17:01 UTC, so the conversion is based on a false premise. It is also not 13:01 in New York, so the converted result is telling a lie.
java.time
With java.time setting an override zone on a formatter works similarly for formatting, but with a difference that matters here: the override zone is only used when printing a date-time object that identifies an instant (a point in time). From the docs:
When formatting, if the temporal object contains an instant, then it
will be converted to a zoned date-time using the override zone.
Whether the temporal is an instant is determined by querying the
INSTANT_SECONDS field. If the input has a chronology then it will be
retained unless overridden. If the input does not have a chronology,
such as Instant, then the ISO chronology will be used.
… In all other cases, the override zone is added to the temporal,
replacing any previous zone, but without changing the date/time.
Again LocalDateTime.now() gives you the current date and time of day (a few milliseconds earlier than the query through Joda-Time), 2020-03-27T17:01:33.933. Local still means without offset or time zone.
Because your LocalDateTIme hasn’t got offset or time zone, it cannot identify an unambigous point in time, an instant. Therefore when formatting it neither the date nor the time of day is changed. And since your format pattern contains no time zone or offset, none is printed. So you just get the date and time in your time zone (not in New York), 20200327 170133.933.
To get the date and time in New York time zone
DateTimeFormatter timestampFormat
= DateTimeFormatter.ofPattern("yyyyMMdd HHmmss.SSS");
ZonedDateTime timeInNy = ZonedDateTime.now(ZoneId.of("America/New_York"));
System.out.println(timeInNy.format(timestampFormat));
When I ran this code just now, the output was:
20200327 122359.683
Documentation links
Joda-Time DateTimeFormatter.withZone()
java.time DateTimeFormatter.withZone()

Related

Offset is not set for few time zone in java

I would want to take the time and time zone from user and create an entry. Used below calendar API to do this, it is working for few time zone and not working few time zones
calendar.setTime(eventFormEntryBean.getStartDate());
TimeZone timeZone = TimeZone.getTimeZone("Europe/Amsterdam");
calendar.setTimeZone(timeZone);
Working timezone(at the end +xx:xx)
Pacific/Palau 2019-11-27T20:51:09.000+09:00
IST - 2019-11-20T22:00:00.000+05:30
Europe/Amsterdam - 2019-11-28T12:49:24.000+01:00
America/Los_Angeles - 2019-11-20T21:32:49.000-08:00
Not working time zone:-
Africa/Dakar - 2019-11-21T05:30:45.000Z
London(Europe/London) - 2019-11-21T12:08:42.000Z
For the above London and Africa/Dakar time zones do not have any indicator to distinguish the time zone, it simply specify “.000Z” at the end. Is there any attribute that we need to set in order to get full time zone?
what does that .000z means?
If you want to be able to write code that reflects the difference between offsets and time zones, leave java.util and switch to java.time (for Java 8+ and with a support library for Java 6 and 7).
Then you can do things like these:
public static void main(String[] args) {
/*
* the base of this example is a date time with an offset of +01:00
* (which is present in several zones, not just in Europe/Amsterdam!)
*/
String datetime = "2019-11-28T12:49:24.000+01:00";
// parse it to an offset-aware object
OffsetDateTime plusOneHourOffsetDateTime = OffsetDateTime.parse(datetime);
// print it to be sure ;-)
System.out.println(plusOneHourOffsetDateTime
.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
// convert it to a zone-aware date time object by providing the zone
ZonedDateTime europeAmsterdamZonedDateTime = plusOneHourOffsetDateTime
.atZoneSameInstant(ZoneId.of("Europe/Amsterdam"));
// print it
System.out.println(europeAmsterdamZonedDateTime
.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
// then take the same instant but use a different time zone
ZonedDateTime utcZonedDateTime = plusOneHourOffsetDateTime
.atZoneSameInstant(ZoneId.of("UTC"));
// print that, it adds a Z (indicating an offset of 00:00) and the time zone
// that was specified
System.out.println(utcZonedDateTime.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
// take a totally different time zone and do it again
ZonedDateTime pacificPalauZonedDateTime = plusOneHourOffsetDateTime
.atZoneSameInstant(ZoneId.of("Pacific/Palau"));
// print that one, too
System.out.println(pacificPalauZonedDateTime
.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
}
which outputs this
2019-11-28T12:49:24+01:00
2019-11-28T12:49:24+01:00[Europe/Amsterdam]
2019-11-28T11:49:24Z[UTC]
2019-11-28T20:49:24+09:00[Pacific/Palau]
EDIT
The reason for the DateTimeParseException mentioned in your comment is the date-time String, because it doesn't have a zone or an offset, which makes it unparseable by the default DateTimeFormatter used in OffsetDateTime.parse(String datetime).
If you have a String with date and time information but without a zone or an offset, you can parse it to a LocalDateTime first and create a ZonedDateTime from that:
public static void main(String[] args) {
// date time String without zone or offset information
String dateTimeString = "2019-11-30T19:35:06";
// create a LocalDateTime from the String
LocalDateTime ldt = LocalDateTime.parse(dateTimeString);
// then create a ZonedDateTime from the LocalDateTime adding a zone
ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault()); // system default here
// and print it
System.out.println(zdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
}
You misunderstood. Z means exactly the same as +00:00 and is the conventional and recommended way to write it in the ISO 8601 format that you are producing. So for all of your time zones you are getting the correct UTC offset (except possibly IST; that may stand for Irish summer time or Israel standard time, in which case your offset of +05:30 is wrong; don't rely on ambiguous three letter tome zone abbreviations).
The Calendar and TimeZone classes are both poorly designed and long outdated. I recommend that instead of using those you use java.time, the modern Java date and time API. You need ZoneId and ZonedDateTime. See the answer by deHaar.

How to convert UTC date to UTC OffsetDateTime in java 8?

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
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.
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.
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

How to check LocalDateTime instance is local or UTC in java?

I have want to find whether my LocalDateTime instance holds local date&time or UTC date&time (like DateTime.Kind property in C#)
LocalDateTime date1=LocalDateTime.now(); // it is local
LocalDateTime date2=LocalDateTime.now(ZoneId.of("UTC")); // it is UTC
Anything like (date1.getKind() == Kind.UTC || date1.getKind() == Kind.Local) in Java?
The LocalDateTime object itself doesn't store the timezone information - it has only the date and time related fields: day, month and year; hour, minute, seconds and nanoseconds. But the now method uses a timezone or an offset to get the correct values for those fields.
That's because the answer to the questions "What day is today?" and "What time is it?" is not as simple as we might think.
It's common to think that the answer is as simple as taking a look at our calendar/cell phone/whatever and seeing the current date/time. But the technically correct answer is: "It depends".
It depends, basically, on where you are. At this moment, each place in the world has its own local date and time. For example, in July 5th, 2017: while it was 14h (or 2 PM) in São Paulo, it was 6 PM in London and 5 PM in UTC, but in Tokyo it was 2 AM of the next day (July 6th).
Each region in the world has specific rules to determine what's their local time during history, and of course it affects their local date.
And the concept that maps a country/city/region to these rules is a timezone.
That's why the now method needs a timezone. The ZoneId object loads all the timezone data to check what's the current date and time in that zone and adjust the day/month/year/hour/minute/second/nanosecond values accordingly. The version that receives no parameters (LocalDateTime.now()) will use the system's default timezone, so the API always uses some timezone in the end.
The timezone (or the offset, such as ZoneOffset.UTC) is used to get the correct values for day, month, year, hour, minute, second and nanosecond, and then - in the case of LocalDateTime and any other classes that don't keep the zone - discarded.
So, the concept might be a little different from what you're thinking. If I do:
// ZoneOffset.UTC is equivalent to ZoneId.of("UTC")
LocalDateTime date = LocalDateTime.now(ZoneOffset.UTC);
What this code does is: "take the current date and time in UTC, and get just the date and time fields, discarding the timezone/offset information".
When I ran this code, the current date/time in UTC was 2017-09-25T12:15:43.570Z, so the LocalDateTime has the value equivalent to 2017-09-25T12:15:43.570 (without any timezone information, just the date and time fields). If I call now() without arguments, it'll use the JVM default timezone (in my case, it's America/Sao_Paulo), and the value will be 2017-09-25T09:15:43.570.
So, with a LocalDateTime you can get the values, but you can't know from which timezone those values came from, because it doesn't keep this information.
If you want a UTC date, you must use another classes, designed to keep this information:
Instant.now() - this will always get the current UTC instant
OffsetDateTime.now(ZoneOffset.UTC) - with this you can query for date and time fields (such as getDayOfMonth() or getHour())
ZonedDateTime.now(ZoneOffset.UTC) - for UTC, it's the same as OffsetDateTime, but if you use a different timezone, it handles all timezone specific data, such as Daylight Saving Time changes.
To check if such object is in UTC, one way is to use the getZone() method:
ZonedDateTime z = ZonedDateTime.now(ZoneOffset.UTC);
System.out.println(z.getZone().equals(ZoneOffset.UTC)); // true
But if you use equivalents like ZoneId.of("UTC"), the equals method return false. So you could also check if z.getZone().getId() is equals to Z or UTC. With OffsetDateTime, it's similar:
OffsetDateTime odt = OffsetDateTime.now(ZoneOffset.UTC);
System.out.println(odt.getOffset().equals(ZoneOffset.UTC)); // true
With Instant you don't need to check, because it's always in UTC.
You can check all the available types in Oracle's date/time tutorial.
Both ZonedDateTime and OffsetDateTime can be converted to a LocalDateTime using the toLocalDateTime() method:
// dt will have the current date and time in UTC
LocalDateTime dt = ZonedDateTime.now(ZoneOffset.UTC).toLocalDateTime();
// or
LocalDateTime dt = OffsetDateTime.now(ZoneOffset.UTC).toLocalDateTime();
With this, the dt variable will have all the date and time fields (day/month/year, hour/minute/second/nanosecond) that corresponds to the current date/time in UTC. But it won't keep any timezone/offset information, so the LocalDateTime object itself can't know from which timezone those values came from.
I realize this question is a bit old, but I am learning Java and found myself trying to do something very similar. After some reading I found I could do what you are asking with this:
public static void main (String args[]) {
LocalDateTime now = LocalDateTime.now(Clock.systemDefaultZone()); // The clock argument is not really needed here.
String pattern = "dd MMM yyyy HH:mm:ss"; // Setup your format for output
DateTimeFormatter dtf = DateTimeFormatter.ofPattern(pattern);
System.out.println("Local time is: " + dtf.format(now));
LocalDateTime utc = LocalDateTime.now(Clock.systemUTC()); // Define alternate timezone
System.out.println("GMT/UTC is: "+dtf.format(utc));
}

How to keep original timezone with JodaTime

I have date in String format I need to parse. The format is as following with timezone from all over the world :
String stringDate = "2016-04-29 12:16:49.222+04:30";
String pattern = "yyyy-MM-dd HH:mm:ss.SSSZ";
It seems that java.util.Date doesn't accept timezone with : separator. So I'm trying with Jodatime library :
DateTime formattedDate = DateTimeFormat.forPattern(pattern).parseDateTime(stringDate);
LocalDateTime formattedDate2 = DateTimeFormat.forPattern(pattern).parseLocalDateTime(stringDate);
MutableDateTime formattedDate3 = DateTimeFormat.forPattern(pattern).parseMutableDateTime(stringDate);
System.out.println(formattedDate);
System.out.println(formattedDate2);
System.out.println(formattedDate3);
These lines output :
2016-04-29T09:46:49.222+02:00
2016-04-29T12:16:49.222
2016-04-29T09:46:49.222+02:00
As far as I understand the formatter modify output timezone to comply on mine (I'm in Paris, UTC+2), but I want the output keep its original timezone. Is it possible to do it with Jodatime library? Or should I change for another?
Edit :
Actually I need to get a Date object on which the timezone offset would be 270 (the timezone offset of the stringDate : 4 hour and 30 minutes) in place of 120 (my local timezone offset):
System.out.println(formattedDate.toDate().getTimezoneOffset()); // I expect 270 but I get 120
What you missed is DateTimeFormatter#withOffsetParsed:
Returns a new formatter that will create a datetime with a time zone equal to that of the offset of the parsed string.
Otherwise the formatter will parse it into your local time zone (surprising, I know).
#Test
public void preserveTimeZone() {
String stringDate = "2016-04-29 12:16:49.222+04:30";
String pattern = "yyyy-MM-dd HH:mm:ss.SSSZ";
DateTime dt = DateTimeFormat.forPattern(pattern).withOffsetParsed().parseDateTime(stringDate);
System.out.println(dt); // prints "2016-04-29T12:16:49.222+04:30"
}
As for your edit - java.util.Date does not hold time zone information and the deprecated getTimezoneOffset() method only
Returns the offset, measured in minutes, for the local time zone relative to UTC that is appropriate for the time represented by this Date object.
So you'd better use Joda Time or java.time classes to handle time zones properly.
When I run the same code that you have posted, I end up with
2016-04-29T02:46:49.222-05:00
2016-04-29T12:16:49.222
2016-04-29T02:46:49.222-05:00
which if you will notice, has different hour values AND time-zone values. However, if you look at their millis:
System.out.println(formattedDate.getMillis());
System.out.println(formattedDate2.toDateTime().getMillis());
System.out.println(formattedDate3.getMillis());
you'll see the output
1461916009222
1461950209222
1461916009222
So they have the same epoch time, but are printed out differently. This is due to the mechanism of toString() on DateTime objects, and how they are to be interpreted.
DateTime and LocalDateTime(MutableDateTime is just a mutable version of DateTime) deal with the same epoch time in different ways. LocalDateTime will always assume that epoch time is UTC time(per the javadoc for LocalDateTime), while DateTime will assume that epoch is represented in the time zone of the Chronology which it holds(per the javadoc again). If the TimeZone is not specified at construction time, then the Chronology will assume that you want the timezone of your default Locale, which is set by the JVM. In your case, the default Locale is Paris France, while mine is St. Louis USA. Paris currently holds a +2:00 time zone offset, while St. Louis has -5:00, leading to the different time zone representations when we print it.
To get even more annoying, those offsets can change over time. If I come back in 6 months and try to answer this again, my values will show -6:00 (stupid Daylight savings time!)
The important thing to remember is that these two dates have the same epoch time: we are talking about the same instant in time, we are just representing that time differently when we print it out.
If you want to use a different time zone for representing the output of the parse result, then you can set the DateTimeZone during formatting using DateTimeFormat.withZone() or DateTimeFormat.withLocale:
DateTimeFormatter sdf = DateTimeFormat.forPattern(pattern).withZone(DateTimeZone.forOffsetHoursMinutes(4,30));
System.out.println(formattedDate.getMillis());
System.out.println(formattedDate2.toDateTime().getMillis());
System.out.println(formattedDate3.getMillis());
which will print
2016-04-29 12:16:49.222+0430
2016-04-29 12:16:49.222
2016-04-29 12:16:49.222+0430
notice that the LocalDateTime version still prints out without the TimeZone. That's kind of the feature of LocalDateTime: it is represented without having to deal with all this business.
So that is why your printing values look weird. To further your question about getting a java.util.Date object from the parsed DateTime object: toDate will give you a java.util.Date which represents the same epoch time. However, java.util.Date behaves similarly to DateTime, in that unless otherwise stated, it will use the TimeZone of the default Locale. If you know the Locale ahead of time, then you can use the toDate(Locale) method to ensure you use that Locale's TimeZone offset.
It gets a lot harder if you don't know the TimeZone ahead of time; in the past, I've had to hand-parse the TimeZone hour and minute offsets to determine the proper TimeZone to use. In this exact case that's not too difficult, since the last 6 characters are extremely well-formed and regular(unless, of course, they aren't :)).

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.

Categories