I'm using java.util.Date. The expression:
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2016-10-16 00:00:00").toString()
is returning "Sun Oct 16 01:00:00 BRST 2016" (a wrong date) for this particular date, but a correct response on most other dates.
I also tried a format string taken from Oracle docs: "yyyy-MM-dd'T'HH:mm:ss.SSSZ" and a specific date: "2016-10-16T00:00:00.000-0300" and got the same "error" (I suppose), one hour ahead:
Sun Oct 16 01:00:00 BRST 2016
That usually happens due to Daylight Saving Time (DST) (also known as "summer time"). Based on the date/time, and on the output you've got (Sun Oct 16 01:00:00 BRST 2016), I suppose it's Brazil's DST (BRST is the abbreviation for Brazilian's summer time).
SimpleDateFormat uses the JVM's default timezone (if you don't specify one), so probably your default zone is America/Sao_Paulo or Brazil/East (you can check that by calling TimeZone.getDefault().getID()).
In America/Sao_Paulo timezone, DST started at October 16th 2016: at midnight, clocks shifted 1 hour forward from midnight to 1 AM (and the offset changed from -03:00 to -02:00). So all local times between 00:00 and 00:59 didn't exist in this timezone (you can also think that clocks changed from 23:59:59.999999999 directly to 01:00).
That's why this specific date in midnight (which didn't exist in this timezone) is automatically shifted to the next valid moment (1 AM). But if I set a specific timezone in the formatter, this won't happen:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// set formatter to use UTC (instead of JVM default timezone)
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
// parse it as midnight (no shift to 01:00)
Date date = sdf.parse("2016-10-16 00:00:00");
In this case, I'm using UTC, which has no DST effects. But remind that the date created above will be equivalent to midnight in UTC (which is the same instant as October 15th 2016 at 9 PM in Brazil (the day before) - maybe it's not what you want).
Be aware of that before changing the timezone: if you want a specific instant (a precise point in time), changing the timezone will affect the final result. If you just want to consider the date/time values and don't care about in what timezone it is (treating the value as it's a "local date/time"), just set the formatter to use UTC, to avoid DST effects (an ugly workaround, IMO, but just because the java.util.Date API doesn't have specific types for local date/times).
But anyway, this is not an error. It's the expected behaviour (DST and timezones have lots of strange and non-intuitive behaviours, but that's the way it is).
Java new Date/Time API
The old classes (Date, Calendar and SimpleDateFormat) have lots of problems and design issues, and they're being replaced by the new APIs.
If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.
If you're using Java 6 or 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, you'll also need the ThreeTenABP (more on how to use it here).
The code below works for both.
The only difference is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same.
This new API has lots of new types that best suit different use-cases. In your case, if you want just the date and time fields and don't care about timezones, you can use a LocalDateTime. To parse it, just use a DateTimeFormatter:
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime dt = LocalDateTime.parse("2016-10-16 00:00:00", fmt);
System.out.println(dt); // 2016-10-16T00:00
This will ignore DST effects, because a LocalDateTime has no timezone information.
But if you want to consider the timezone, you can convert this to a ZonedDateTime, using a ZoneId to get the timezone:
// convert to a timezone
ZonedDateTime z = dt.atZone(ZoneId.of("America/Sao_Paulo"));
System.out.println(z); // 2016-10-16T01:00-02:00[America/Sao_Paulo]
In this case, note that the time was adjusted to 1 AM, because I converted to America/Sao_Paulo timezone, so the DST effects were considered, as already explained above.
With this new API we can look closer at what's happening in this specific timezone, for this particular date/time. First I'll create a ZonedDateTime that corresponds to October 15th 2016, at 23:59:59 in America/Sao_Paulo timezone, and then I'll add 1 second to it:
// October 15th 2016, at 23:59:59 in Sao Paulo timezone
ZonedDateTime z = ZonedDateTime.of(2016, 10, 15, 23, 59, 59, 0, ZoneId.of("America/Sao_Paulo"));
System.out.println(z); // 2016-10-15T23:59:59-03:00[America/Sao_Paulo]
System.out.println(z.plusSeconds(1)); // 2016-10-16T01:00-02:00[America/Sao_Paulo]
Note that the original date is in offset -03:00 (3 hours behind UTC, which is the standard offset for America/Sao_Paulo timezone). One second later, it should be midnight, but due to DST change, the clock shifts directly to 1 AM, and the offset changes to -02:00.
Even if I try to directly create October 16th 2016 at midnight in this timezone, the value will be corrected, because this local time doesn't exist in this timezone, due to DST shift:
// Try to create October 16th 2016, at midnight in Sao Paulo timezone
ZonedDateTime z = ZonedDateTime.of(2016, 10, 16, 0, 0, 0, 0, ZoneId.of("America/Sao_Paulo"));
System.out.println(z); // 2016-10-16T01:00-02:00[America/Sao_Paulo]
So, it's not an error. October 16th 2016 at midnight in America/Sao_Paulo timezone doesn't exist due to a DST change, and the API's automatically corrects this to the next valid moment (which is, in this case, 1 AM).
The API uses IANA timezones names (always in the format Region/City, like America/Sao_Paulo or Europe/Berlin).
Avoid using the 3-letter abbreviations (like CST or PST) because they are ambiguous and not standard.
You can get a list of available timezones (and choose the one that fits best your system) by calling ZoneId.getAvailableZoneIds().
You can also use the system's default timezone with ZoneId.systemDefault(), but this can be changed without notice, even at runtime, so it's better to explicity use a specific one.
Your problem is probably the timezone, so you could use:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
Here is the original answer.
Related
I am confused with Timezone conversions in Java. I have a few cases which I will list out below.
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// sdf.setTimeZone(TimeZone.getTimeZone("Asia/kolkata"));
Date date1 = sdf.parse("2021-01-31");
System.out.println(date1); //. O/P - Sun Jan 31 00:00:00 IST 2021
Now lets uncomment the Timezone part and see the time difference
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setTimeZone(TimeZone.getTimeZone("Asia/kolkata"));
Date date1 = sdf.parse("2021-01-31");
System.out.println(date1); // O/P - Sun Jan 31 05:30:00 IST 2021
Now lets set the TimeZone to IST and see the time difference
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setTimeZone(TimeZone.getTimeZone("IST"));
Date date1 = sdf.parse("2021-01-31");
System.out.println(date1); // O/P - Sun Jan 31 00:00:00 IST 2021
Now lets set the TimeZone to UTC and see the time difference
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date1 = sdf.parse("2021-01-31");
System.out.println(date1); // O/P - Sun Jan 31 05:30:00 IST 2021
Can anybody please explain me why this shift in time is happening (+- 5:30) when I change the Timezone?
For IST and Asia/Kolkata, time should have remain same because they are same Timezone, but why the shift?
Why When using the UTC Timezone, time gets increased by 5:30 hours? What I understand is IST is 5:30 hrs ahead of UTC, so cnverting to UTC should have decreased the time by 5:30 hrs
Why even after converting to UTC, my time displays IST 2021?
I still have confusion here.
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date1 = sdf.parse("2021-01-31");
System.out.println(date1.getTime()); // 1612051200000
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
sdf1.setTimeZone(TimeZone.getTimeZone("Asia/Kolkata"));
Date date2 = sdf1.parse("2021-01-31");
System.out.println(date2.getTime()); // 1612031400000
Why instant of time in UTC is greater than instant of time in Asia/Kolkata ?
Here are some things for you to note:
When a Date is printed, it will be formatted in your computer's local timezone (that's what Date.toString does). Presumably, your computer is in the Asia/Kolkata timezone, so the output is always displayed as a date & time in that timezone.
A Date represents a point in time (i.e. an instant). It is not a tuple of year, month, day, hour, minute, seconds and timezone
Since there are no time in your input string, the time 00:00:00 is used for the time when parsing.
Just a date and a time is not enough to produce a point in time. You also need a timezone to specify a point in time. Since there is no timezone in your input string, the local timezone of your computer is used, or if you have set it, sdf.getTimeZone().
Although a timezone is used in parsing the date, the timezone is not part of the Date object.
Can anybody please explain me why this shift in time is happening (+- 5:30) when I change the Timezone?
When you use the "IST" timezone (first and third code snippet), sdf gets the following pieces of information:
Date: 2021-01-31
Time: 00:00:00
TimeZone: Asia/Kolkata
With these pieces of information, it can produce a point in time, represented by a number of milliseconds since the Java Epoch - 1970-01-01 00:00:00 UTC. This is the Date object. Then you print the Date object, which gets formatted to your local timezone. Your local timezone just so happens to be the same as the one that sdf is provided with, so you see Sun Jan 31 00:00:00 IST 2021.
When you use UTC (second and fourth code snippets), these information are provided to sdf:
Date: 2021-01-31
Time: 00:00:00
TimeZone: UTC
That represents a different point in time than 2021-01-31T00:00:00 in Kolkata. How different? 2021-01-31T00:00:00 in UTC is exactly 5 and a half hours later than 2021-01-31T00:00:00 in Kolkata. Recall that to convert a UTC time to Kolkata, you add 5 and a half hours.
For IST and Asia/Kolkata, time should have remain same because they are same Timezone, but why the shift?
Because you have misspelled Asia/Kolkata. The first "K" in "Kolkata" should be capitalised. Unknown zone IDs are treated as UTC by the TimeZone class. This is why you should move to the new java.time classes. ZoneId throws an exception if you supply it with an unknown zone ID.
Why When using the UTC Timezone, time gets increased by 5:30 hours? What I understand is IST is 5:30 hrs ahead of UTC, so converting to UTC should have decreased the time by 5:30 hrs
You are thinking about formatting dates, not parsing, because remember that the timezone is not part of Date, but part of SimpleDateFormat. Your code does not format Date, only parses them. Without formatting, Dates are always printed in your local timezone.
To see your desired behaviour using SimpleDateFormat, you'd first parse the date string once, and then format it using SimpleDateFormats with different timezones.
Really though, you should change to java.time. Using that API, your zone changing code could be written like so:
ZonedDateTime zdt = LocalDate.parse("2021-01-31")
.atStartOfDay()
.atZone(ZoneId.of("Asia/Kolkata"));
System.out.println(zdt);
ZonedDateTime utcDateTime = zdt.withZoneSameInstant(ZoneId.of("UTC"));
System.out.println(utcDateTime);
// output:
// 2021-01-31T00:00+05:30[Asia/Kolkata]
// 2021-01-30T18:30Z[UTC]
java time
I recommend you use java.time, the modern Java date and time API, for your date work
LocalDate date = LocalDate.parse("2021-01-31");
System.out.println(date);
Output is:
2021-01-31
A LocalDate is a date without time of day and without time zone or UTC offset, so using it frees you completely from all time zone trouble. Furthermore we don’t need any explicit formatter. Your string is in ISO 8601 format, and LocalDate parses the most common ISO 8601 variant as its default. As you can see, it also prints the same ISO 8601 format back when we print it, implicitly calling its toString method.
What went wrong in your code?
The SimpleDateFormat, TimeZone and Date classes that you are using are poorly designed and long outdated. No wonder that their behaviour confuses you.
I am assuming that Asia/Kolkata (or Asia/Colombo or Asia/Calcutta) is the default time zone of your JVM. In your first example the SimpleDateFormat is using your default time zone and is parsing the string into the first moment of the day in that time zone.
In your second example, as Elavya has spotted so well, you have got a lower case k in Asia/kolkata which causes TimeZone not to recognize the intended time zone. And this is where TimeZone excels in bad design: it just tacitly gives you GMT instead. Next the Date class is poorly designed too and still prints the time in the default time zone of the JVM, giving the illusion that the Date object contains a time zone. This has confused very many. The start of the day in GMT is the same point in time as 05:30:00 IST, so this is what you get.
In your third and fourth example, even though the three letter time zone abbreviations are deprecated, IST (contrary to what Eklavya said) is interpreted as Asia/Kolkata and UTC as Etc/UTC. Even though as Eklavya also said, IST is ambiguous.
So in short:
The change happens because the start of the day is a different point in time in different time zones.
Because of your typo in Asia/kolkata. Time zone IDs are case sensitive.
You are not converting to UTC. You are parsing in UTC thereby converting from UTC, and Date.toString() further converts to Asia/Kolkata (IST) as the output also says.
Because the Date object hasn’t got a time zone and because Date.toString() grabs the default time zone of your JVM and uses it for rendering the string to be returned.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Wikipedia article: ISO 8601
All about java.util.Date
Java doc for getTimeZone
ID - 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". Note that the support of abbreviations is for JDK 1.1.x
compatibility only and full names should be used.
TimeZone abbreviation is not supported. So you can't use IST
And in TimeZone Doc for Three-letter time zone IDs
For compatibility with JDK 1.1.x, some other three-letter time zone
IDs (such as "PST", "CTT", "AST") are also supported. However, their
use is deprecated because the same abbreviation is often used for
multiple time zones (for example, "CST" could be U.S. "Central
Standard Time" and "China Standard Time"), and the Java platform can
then only recognize one of them.
Problem is IST abbreviation is used for multiple time zones like Irish Standard Time, Isreal Standrad Time, Indian Standard Time. And you mistyped Asia/Kolkata as Asia/kolkata.
So, the GMT zone will return if the given ID cannot be understood from TimeZone.getTimeZone()
As an addition to the accepted answer, for the last part of your question;
Why instant of time in UTC is greater than instant of time in Asia/Kolkata in below code?
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date1 = sdf.parse("2021-01-31");
System.out.println(date1.getTime()); // 1612051200000
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
sdf1.setTimeZone(TimeZone.getTimeZone("Asia/Kolkata"));
Date date2 = sdf1.parse("2021-01-31");
System.out.println(date2.getTime()); // 1612031400000
First, you have a point T in time regardless of timezone. In our example T=2021-01-31 00:00:00.
When we set timezone as UTC and print the time using java.util.Date.getTime() method, it will print milliseconds since the Unix epoch, which occurred at midnight January 1st 1970, UTC. So it will print 1612051200000. As you see the epoch and our date has the same timezone which is UTC. So the time is printed directly, no adjustment necessary for timezone.
Now, when we set timezone as Asia/Kolkata, during SimpleDateFormat.parse, timezone information will be added to date. That means +5:30h(19800000ms) will be added to time T. Therefore our time T is increased by 19800000ms. However T must be pointing to the same point in time. How do we fix that? It is fixed on SimpleDateFormat.parse method by subtracting 19800000ms from the time 1612051200000ms so that getTime() method will now show 1612031400000ms so that our actual time T will still show the same point in time(which is 1612051200000ms) because in this date object we have an extra 19800000ms which comes from timezone.
I had the below issue During daylight change CST-CDT reset.
Am getting the Input from Was8.5 server 2018-03-11-05.00 (UTC-5) as expected, but when it comes to WAS7 server, the below method returns Sun Mar 10 00.00.00 CST 2018 instead of Sun Mar 11 00.00.00 CDT 2018
/*
* Converts XMLGregorianCalendar to java.util.Date
*/
public static Date toDate(XMLGregorianCalendar calendar){
if(calendar == null) {
return null;
}
return calendar.toGregorianCalendar().getTime();
}
I know the server date/timezone reset didn’t take place properly, but in case if I want to get right Time when CST to CDT change or vise versa. How can I rewrite the code to convert XMLGregorianCalendar to java.util.Date in Java?
Something like,
If incoming request was CST(UTC-6), the toDate(XMLGregorianCalendar calendar) returns CDT (UTC-5). then I want toDate() should return CST (UTC-6).
the same way,
If incoming request was CDT(UTC-5), the toDate(XMLGregorianCalendar calendar) returns CST(UTC-6). then i want toDate() should return CDT(UTC-5).
java.util.Date doesn't have a timezone. It just have a long value that represents the number of milliseconds since unix epoch.
What you see (Sun Mar 10 00.00.00 CST 2018) is the result of toString() method, and it uses the JVM default timezone to convert the long value to a date and time in that timezone. See this article for more details:
https://codeblog.jonskeet.uk/2017/04/23/all-about-java-util-date/
Anyway, one way to really know what's happening is to check this long value:
long millis = calendar.toGregorianCalendar().getTimeInMillis();
And then you can print this value in UTC:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss XXX");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(sdf.format(new Date(millis)));
Or, if you use Java 8:
System.out.println(Instant.ofEpochMilli(millis));
This will tell you the UTC instant that the Date corresponds to, so you can debug your code a little better than relying on Date::toString() method, which is confusing and misleading.
Regarding your main issue, I've tried to reproduce (I'm using Java 8 because it's easier to manipulate than using Date). First I created a date/time corresponding to 2018-03-11 in UTC-05:00, and I assumed the time to be midnight:
// March 11th 2018, midnight, UTC-05:00
OffsetDateTime odt = OffsetDateTime.parse("2018-03-11T00:00-05:00");
Then I converted this to America/Chicago timezone, which is a zone that uses CST/CDT:
// get the same instant in Central Time
ZonedDateTime zdt = odt.atZoneSameInstant(ZoneId.of("America/Chicago"));
Then I printed this:
// print the date/time with timezone abbreviation
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm xxx z", Locale.US);
System.out.println(zdt.format(fmt)); // 2018-03-10 23:00 -06:00 CST
Note that the result is 2018-03-10 23:00 -06:00 CST: March 10th in UTC-06:00.
That's because in 2018, Daylight Saving Time starts only at 2 AM of March 11th. At midnight, DST has not started yet, so the offset is still UTC-06:00.
Anyway, your conversion code is correct, because Date just represents a point in time (a count of elapsed time since epoch) and doesn't have timezone attached to it. Perhaps the problem lies somewhere, and checking the millis value might help you to understand what's going on (my guess is that XmlGregorianCalendar sets the time to midnight when it's not present, which would explain the result of Sun Mar 10 00.00.00 CST 2018).
If that helps, the exact UTC instant where DST transition occurs (March 11th 2018 at 2 AM in UTC-06:00) corresponds to the millis value 1520755200000. If your dates in March 2018 have a value lower than that, it means they're before DST starts, and they'll be in CST.
My first suggestion is that you don’t need what you are asking for. As I see it, you’ve got a date and a UTC offset, and I don’t really see that the offset adds any useful information. Just take the date. I believe what has happened was that a point in time after the transition to summer time on March 11 was stripped of the time-of-day, but the UTC offset was kept for whatever reason or maybe for no reason at all. When giving the time at start of day (00:00), the offset disagrees with your time zone of America/Chicago (or Central Time Zone, but the ID in region/city format is unambiguous and recommended).
And don’t use java.util.Date for your date. That class is long outdated. Today we have so much better in java.time, the modern Java date and time API. Furthermore its LocalDate class is better suited for a date without time-of-day because this is exactly what it is, while a Date is really a point a in time, that is, a whole different story. Depending on taste conversion from XMLGregorianCalendar can happen in two ways.
The direct way
return LocalDate.of(calendar.getYear(), calendar.getMonth(), calendar.getDay());
With your XMLGregorianCalendar of 2018-03-11-05:00 the result is a LocalDate of 2018-03-11.
The indirect way via GregorianCalendar and ZonedDateTime:
return calendar.toGregorianCalendar().toZonedDateTime().toLocalDate();
The result is the same. The advantage of the latter is you don’t need to concern yourself with the individual fields of year, month and day-of-month. Among other things this means you don’t risk putting them in the wrong order.
If you do insist on keeping the time zone or UTC offset, at least take the offset. Sun Mar 11 00.00.00 CDT 2018 doesn’t make sense because March 11 at 00:00 hours DST was not yet in effect (it began at 02:00). Such a non-existing time will just confuse everyone. Convert your calendar object to OffsetDateTime:
return calendar.toGregorianCalendar().toZonedDateTime().toOffsetDateTime();
Result: 2018-03-11T00:00-05:00. This point in time exists.:-)
Since your calendar comes from a foreign system, you will probably want to validate it since any field may be undefined and return DatatypeConstants.FIELD_UNDEFINED. When using LocalDate.of(), you may decide that its argument validation is enough since it will object to DatatypeConstants.FIELD_UNDEFINED being passed as an argument. toGregorianCalendar() on the other hand will tacitly use default values, so when using it I would consider validation indispensable.
What went wrong in your code?
I ran your code, and similarly to iolus (see the other answer) I got Sat Mar 10 23:00:00 CST 2018. This the correct point in time. As iolus also explained, this is Date.toString rendering the point in time this way. The Date object itself doesn’t have a time zone or UTC offset in it. So I should say that your code was correct. It was just you getting confused by the toString method. Many have been before you, and the good solution is to avoid the Date class completely. Also I would think that your observations have nothing to do with any difference between WAS 7 and WAS 8.5.
I read many posts at SO and tested most of them. None of them is working for me. Here is my code:
DateTimeZone fromTimeZone = DateTimeZone.forID("America/New_York");
DateTimeZone toTimeZone = DateTimeZone.forID("US/Central");
Date now = new Date();
DateTime dateTime = new DateTime(now, fromTimeZone);
DateTime newDateTime = dateTime.withZone(toTimeZone);
System.out.println(dateTime.toDate() + "--" + newDateTime.toDate());
Here is what I got in print:
Tue Aug 22 13:08:13 EDT 2017--Tue Aug 22 13:08:13 EDT 2017
I am hoping to display "Tue Aug 22 12:08:13 CDT 2017" for the second time zone.
A java.util.Date doesn't have timezone information. Joda's DateTime has, but it's wrapped into a Chronology to translate this instant to "human readable" date/time fields.
But in the end, both objects just represent points (instants) in the time-line.
Just check the values of dateTime.getMillis(), newDateTime.getMillis(), dateTime.toDate().getTime() and newDateTime.toDate().getTime(). They will all be exactly the same, and this value represents the number of milliseconds since epoch (1970-01-01T00:00Z).
The timezone passed to the DateTime object just affects the output of toString() (when this milliseconds value is "translated" to a local date and time), but it doesn't change the milliseconds value itself. So if you do:
DateTime dateTime = new DateTime(now, fromTimeZone);
System.out.println(dateTime);
It will print the date and time that's equivalent to the milliseconds value, but converted to the fromTimeZone (America/New_York):
2017-08-22T13:33:08.345-04:00
The withZone method just sets to a different timezone, but keeps the same milliseconds value:
DateTime newDateTime = dateTime.withZone(toTimeZone);
System.out.println(newDateTime);
The code above keeps the instant (the milliseconds value), but prints the equivalent date and time in the toTimeZone (US/Central):
2017-08-22T12:33:08.345-05:00
The .toDate() method returns a java.util.Date, which just contains the same milliseconds value, and no timezone information. Then, System.out.println implicity calls Date::toString() method, and this converts the milliseconds value to the JVM's default timezone. In this case both will be:
Tue Aug 22 13:33:08 EDT 2017
Because both dates represent the same instant (the same number of milliseconds since epoch).
If you want to get a String that contains the date in a specific format, you can use a org.joda.time.format.DateTimeFormatter:
DateTimeFormatter fmt = DateTimeFormat.forPattern("EEE MMM dd HH:mm:ss z yyyy").withLocale(Locale.ENGLISH);
System.out.println(fmt.print(new DateTime(DateTimeZone.forID("US/Central"))));
There's no need to convert dates objects, because actually no conversion is really happening: all methods above don't change the milliseconds value.
Also note that I used a java.util.Locale to make sure the month and day of week are in English. If you don't specify a locale, the JVM default will be used, and it's not guaranteed to always be English (and it can also be changed, even at runtime, so it's better to always specify it).
Then I get the current date and set the timezone to be used when printing it. Note that you can get a DateTime directly, there's no need to create a java.util.Date.
The output will be:
Tue Aug 22 12:33:08 CDT 2017
To get exactly the same output you want (with both dates), you can do:
DateTimeFormatter fmt = DateTimeFormat.forPattern("EEE MMM dd HH:mm:ss z yyyy").withLocale(Locale.ENGLISH);
DateTime nowNy = new DateTime(DateTimeZone.forID("America/New_York"));
DateTime nowCentral = nowNy.withZone(DateTimeZone.forID("US/Central"));
System.out.println(fmt.print(nowNy) + "--" + fmt.print(nowCentral));
The output will be:
Tue Aug 22 13:33:08 EDT 2017--Tue Aug 22 12:33:08 CDT 2017
Java new Date/Time API
Joda-Time is in maintainance mode and being replaced by the new APIs, so I don't recommend start a new project with it. Even in joda's website it says: "Note that Joda-Time is considered to be a largely “finished” project. No major enhancements are planned. If using Java SE 8, please migrate to java.time (JSR-310)." (if you don't want to or can't migrate from Joda to another API, you can desconsider this section).
If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.
If you're using Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it here).
The code below works for both.
The only difference is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same.
The relevant classes are DateTimeFormatter (to format the date to a String in a specific format), ZonedDateTime (which represents a date and time in a specific timezone) and a ZoneId (which represents a timezone):
// formatter - use English locale for month and day of week
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss z yyyy", Locale.ENGLISH);
// current date/time in New York timezone
ZonedDateTime nowNy = ZonedDateTime.now(ZoneId.of("America/New_York"));
// convert to another timezone (US/Central)
ZonedDateTime nowCentral = nowNy.withZoneSameInstant(ZoneId.of("US/Central"));
// format dates
System.out.println(fmt.format(nowNy) + "--" + fmt.format(nowCentral));
The output is the same as above.
I have a date string in CST (24 Hours) and I want to convert it to GMT (12 Hours).
I have a java method like below which works fine when my system time is Kolkata time. (System where I run the java method)
But when my system is in Shanghai time, the GMT time comes incorrect.
String inputDate = "01-19-2017 06:01 CST";
SimpleDateFormat inputFormatter = new SimpleDateFormat("MM-dd-yyyy hh:mm Z");
parsedInput = inputFormatter.parse(inputDate);
// parsedInput -> Tue Jan 19 06:01:00 CST 2017 -> When system time is Shanghai time
// parsedInput -> Thu Jan 19 17:31:00 IST 2017 -> When system time is Kolkata time
SimpleDateFormat formatter = new SimpleDateFormat("MM-dd-yyyy hh:mm a Z");
TimeZone gmt = TimeZone.getTimeZone("GMT");
formatter.setTimeZone(gmt);
String formattedDate = formatter.format(parsedInput);
// formattedDate -> 01-18-2017 10:01 PM +0000 -> When system time is Shanghai time (Incorrect)
// formattedDate -> 01-19-2017 12:01 PM +0000 -> When system time is Kolkata time
Avoid pseudo time zones
The 3-4 letter time zone abbreviations such as CST are not actual time zones. They are not standardized. They are not even unique!
Your CST might be “China Standard Time” or might be “Central Standard Time” in the Americas, or might be something else. There is no way to know which one.
Another example: IST might mean “India Standard Time” or “Ireland Standard Time” or others.
Deciphering these pseudo-zones reliably is impossible. The Joda-Time library has a wise policy of refusing to even try. Unfortunately the java.time classes make a guess when parsing, but the result may not be the zone you expect.
So never use these useless pseudo-zones. Use a proper time zone name in the format of continent/region such as America/Chicago, Asia/Kolkata, Pacific/Auckland.
Avoid legacy date-time classes
You are using the troublesome old date-time classes that are now legacy, supplanted by the java.time classes.
Workaround
If you know all your inputs are intended for the same time zone, then lop off the pseudo-zone and process as a LocalDateTime, apply the intended zone as a ZoneId to produce a ZonedDateDate. From that you can extract an Instant for UTC time.
String input = "01-19-2017 06:01".replace( " " , "T" ) ; // Insert a “T” to comply with standard ISO 8601 format used by default in java.time.
LocalDateTime ldt = LocalDateTime.parse( input ); // Lacks any concept of time zone or offset-from-UTC. So *not* an actual moment on the timeline.
ZoneId z = ZoneId.of( "America/Chicago" ); // Assuming “CST” meant this zone in North America.
ZonedDateTime zdt = ldt.atZone( z ); // Assign a time zone to determine an actual moment on the timeline.
Instant instant = zdt.toInstant(); // Extract a value in UTC.
You may choose to see that same moment through the lens of the wall-clock time in India.
ZonedDateTime zdtKolkata = zdt.withZoneSameInstant( ZoneId.of( "Asia/Kolkata" ) ) ;
Never rely on default zone
Your JVM’s current default time zone can be changed at any moment by any code in any thread of any app executing within that JVM. Since this can change at any moment you cannot rely on a certain value.
When you omit the optional argument for time zone, the date-time classes implicitly silently apply the current default time zone. So the solution is simple: Always specify the expected/desired time zone.
Notice how the code example above specifies the zone at every opportunity.
(Ditto for Locale, by the way. Specify explicitly rather than rely on current default.)
Your problem is the ambiguity of CST. In most cases SimpleDateFormat understands CST as Central Standard Time as you had expected. But when your computer is running Shanghai Time, this becomes the time zone of the formatter’s calendar, and then it suddenly understands CST as China Standard Time, the same as Shanghai time.
Therefore Darshan Mehta’s solution, setting inputFormatter’s time zone to something other than China Standard Time, works on my computer. I don’t know why it didn’t work on yours (I set it to TimeZone.getTimeZone("America/Chicago") to match the intended interpretation of CST).
The correct and good solution, though, is to avoid the three and four letter time zone abbreviations completely. Yours is just one example out of many where they cause trouble. If you can, give your input string with an explicit zone offset from UTC, such is never ambiguous:
String inputDate = "01-19-2017 06:01 -0600";
Now your code works as expected no matter your computer’s time zone setting. The Z in the pattern string you already have matches -0600 nicely.
All of this said, if you can use the Java 8 date and time classes, you can do yourself a favour of switching over to them. I hesitate to call them “Java 8 date and time classes” because they have also been backported to Java 6 and 7, so if you can live with a library dependency until you get to Java 8, you should have every chance. The newer classes are significantly more programmer-friendly and convenient to work with. A simple example to get you started:
String inputDate = "01-19-2017 06:01 -0600";
DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("MM-dd-yyyy HH:mm Z");
ZonedDateTime parsedInput = ZonedDateTime.parse(inputDate, inputFormatter);
OffsetDateTime gmtTime = parsedInput.toOffsetDateTime().withOffsetSameInstant(ZoneOffset.UTC);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM-dd-yyyy hh:mm a Z");
String formattedDate = gmtTime.format(formatter);
System.out.println(formattedDate); // prints 01-19-2017 12:01 PM +0000
Try setting the TimeZone to inputFormatter as well, e.g.:
SimpleDateFormat inputFormatter = new SimpleDateFormat("MM-dd-yyyy hh:mm Z");
inputFormatter.setTimeZone(TimeZone.getTimeZone("GMT"));
If you don't specify the time zone, it takes the default value into account which is Asia/Shanghai in your case and hence, incorrect result.
You can reproduce it in your current system by replacing GMT with Asia/Shanghai in the above snippett
I have a Calendar object [ localDate ] which is in EST : say Nov 6, 15:34... and i set the timeZone to GMT+5:30...
Now when i do the Calendar.HOUR_OF_DAY it returns me 2... which i know is perfect.. since its 15:34 + 5 hrs to GMT and then +5:30 to the timezone.. which just means.. 26:04 which is 2 of 7th .
However , the date still stays as Nov 6... and localDate.getTime() still returns Nov 6.. and even when i print the localDate.. it shows the timezone as +5:30 , but the day and everything else is still as the original local Time..[ i.e Nov 6 ]
I am simply unable to understand why so...
Edit ::
So i understand that i do not need to change the date along with the timezone.. Only change the way date is displayed suited to that location and that can be done using the timezone that has been set.
localDate.getTime() returns a java.util.Date which is a quantity of raw time since a fixed point. Timezones only affect the human readable representation of the point in time.
15:34 Nov 6th UTC - 5
and
02:04 Nov 7th UTC + 5:30
are both the exact same point in absolute time. It's just two different human ways of describing the same instant.
So changing the timezone on the calendar has no effect on the value returned by getTime()
Date objects do not have a timezone - a Date object represents an "absolute" moment in time. When you print a Date object (by implicitly or explicitly calling toString() on it):
Date date = ...;
System.out.println(date);
then it will be formatted using some default format that will show the date in your local timezone - no matter if you got the Date object from a Calendar which was set to a different timezone.
If you want to display the Date in a different timezone, use a DateFormat object and set the timezone that you want to display your date in on that object:
Date date = ...;
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
df.setTimeZone(TimeZone.getTimeZone("UTC")); // For example, UTC
// Prints 'date' in UTC
System.out.println(df.format(date));
Question Is Not Clear
Your question is confusing.
You may be confused about the meaning of time zones. As the correct answers by Jesper and Affe said, shifting time zones does not change the point on the time line of the Universe. Suppose Bob in New York US calls Susan in Reykjavík Iceland. Iceland uses UTC as their time zone all year round. Bob and Susan are speaking to each other at the same moment in time. But if Bob looks at the clock on his wall, he sees a time displayed that is 5 hours earlier than a clock on Susan’s wall. New York has a five hour offset behind UTC (-5:00).
Another problem with your question: You also talk about a 5:00 time zone offset as well as a 5:30 offset. Which is it? Or do you have two time zones in mind as well as GMT/UTC?
Joda-Time
I'll take a stab at giving you a bit of example source code.
The Joda-Time library makes date-time work easier.
// © 2013 Basil Bourque. This source code may be used freely forever by anyone taking full responsibility for doing so.
// Use time zone names rather than explicit number of hours offset is generally a good thing.
// Affords Joda-Time an opportunity to make adjustments such as Daylight Saving Time (DST).
// Question asked:
// (1) Start with a US east coast time (Standard offset of -5:00) of November 6, 2013 15:34.
// (2) Move that datetime to UTC (GMT) time zone (no offset).
// (3) Move that datetime to Kolkata (formerly known as Calcutta) India time zone (Standard offset of +05:30).
// Joda-Time has deprecated use of 3-letter time zone codes because of their inconsistency. Use other identifier for zone.
// Time Zone list: http://joda-time.sourceforge.net/timezones.html (Possibly out-dated, read note on that page)
org.joda.time.DateTimeZone newyorkTimeZone = org.joda.time.DateTimeZone.forID( "America/New_York" );
org.joda.time.DateTimeZone kolkataTimeZone = org.joda.time.DateTimeZone.forID( "Asia/Kolkata" );
// Question calls for: EST Nov 6, 15:34 (Standard offset of -5:00).
// This DateTime constructor calls for passing: year, month, day, time zone.
org.joda.time.DateTime dateTimeInNewYork = new org.joda.time.DateTime( 2013, org.joda.time.DateTimeConstants.NOVEMBER, 6, 15, 34, newyorkTimeZone );
// Move to UTC time zone (no offset).
org.joda.time.DateTime dateTimeUtc = dateTimeInNewYork.toDateTime( org.joda.time.DateTimeZone.UTC );
// Move to Kolkata IN time zone (Standard offlet of +05:30).
org.joda.time.DateTime dateTimeInKolkata = dateTimeUtc.toDateTime( kolkataTimeZone ); // Or invoke this method on dateTimeInNewYork, does not matter which.
// All three of these date-time objects represent the same moment in the time-line of the Universe,
// but present themselves with different time-zone offsets.
System.out.println( "dateTimeInNewYork: " + dateTimeInNewYork );
System.out.println( "dateTimeUtc: " + dateTimeUtc );
System.out.println( "dateTimeInKolkata: " + dateTimeInKolkata );
When run…
dateTimeInNewYork: 2013-11-06T15:34:00.000-05:00
dateTimeUtc: 2013-11-06T20:34:00.000Z
dateTimeInKolkata: 2013-11-07T02:04:00.000+05:30
About Joda-Time…
// Joda-Time - The popular alternative to Sun/Oracle's notoriously bad date, time, and calendar classes bundled with Java 7 and earlier.
// http://www.joda.org/joda-time/
// Joda-Time will become outmoded by the JSR 310 Date and Time API introduced in Java 8.
// JSR 310 was inspired by Joda-Time but is not directly based on it.
// http://jcp.org/en/jsr/detail?id=310
// By default, Joda-Time produces strings in the standard ISO 8601 format.
// https://en.wikipedia.org/wiki/ISO_8601
// About Daylight Saving Time (DST): https://en.wikipedia.org/wiki/Daylight_saving_time
// Time Zone list: http://joda-time.sourceforge.net/timezones.html