Java: Convert MST to EST - java

I've been trying to convert the time since epoch until today and display it in Eastern Standard Time. Here is what outputs on the remote machine (it's remotely hosted):
Date now = new Date(System.currentTimeMillis());
System.out.println(now.toString());
// Thu Apr 24 14:36:11 MST 2014
No idea what MST is, but I want to get the current milliseconds since epoch in EST, and display the result in EST.
No matter what I do, I can't get daylights savings to work (it's currently Daylights Savings Time in the EST Time Zone); I either end up in PST, GMT or UTC, and when I do get "EST" it's either some random value or 1 hour behind or 3 hours behind.
I would like the output to be formatted using this DateFormat:
DateFormat EXPIRE_FORMAT = new SimpleDateFormat("MMM dd, yyyy h:mm a z");

Just set the time zone you want the time to be displayed in using DateFormat#setTimeZone(TimeZone)
Date now = new Date(System.currentTimeMillis());
DateFormat EXPIRE_FORMAT = new SimpleDateFormat("MMM dd, yyyy h:mm a z");
EXPIRE_FORMAT.setTimeZone(TimeZone.getTimeZone("America/Montreal")); // or whatever relevant TimeZone id
System.out.println(EXPIRE_FORMAT.format(now));
AFAIK, there is no EST currently. It's all EDT in Spring.
The above prints
Apr 24, 2014 5:53 PM EDT

The comments and the answer by Sotirios Delimanolis are correct.
Avoid 3 or 4 Letter Time Zone Codes
You should avoid the 3 or 4 letter codes for time zones as they are neither standardized nor unique. Instead use proper time zone names, usually a continent+city.
Avoid j.u.Date
The java.util.Date and .Calendar & SimpleDateFormat classes bundled with Java are notoriously troublesome. Use a decent date-time library with an updated time zone database. For Java, that means either Joda-Time or the new java.time package in Java 8 (inspired by Joda-Time).
Avoid Milliseconds-Since-Epoch
I suggest you avoid working with milliseconds since epoch. Gets confusing fast as the number is meaningless when read by a human. Let the date-time library manage the milliseconds for you.
Specify Time Zone
Generally best to specify the desired/intended time zone. If you omit the time zone, all the major date-time libraries (java.util.Date, Joda-Time, java.time) apply the JVM's default time zone.
Joda-Time Example
Example code in Joda-Time 2.3.
DateTimeZone timeZoneToronto = DateTimeZone.forID( "America/Toronto" );
DateTime dateTimeToronto = new DateTime( timeZoneToronto ); // Current moment.
DateTime dateTimeUTC = dateTimeToronto.withZone( DateTimeZone.UTC );
DateTime dateTimeParis = dateTimeToronto.withZone( DateTimeZone.forID( "Europe/Paris" ) );
If you really want the milliseconds since epoch, call the getMillis method. In example code above, all three DateTime objects have the same number of milliseconds-since-epoch.
long millis = dateTimeToronto.getMillis();
If you need a java.util.Date for use with other classes…
java.util.Date date = dateTimeToronto.toDate();
While Joda-Time uses the ISO 8601 standard formats as its defaults, you may specify other formats for generating strings.
DateTimeFormatter formatter = DateTimeFormat.forPattern( "MMM dd, yyyy h:mm a z" );
String output = formatter.print( dateTimeToronto );

Related

Different time conversion by using java.util.Date in Java

I am using the below code for epoch to time conversion by using java.util.Date class in Java.
Long scheduledTime = 1602258300000L;
Date date = new Date(scheduledTime);
System.out.println("Date obj :" + date);
Below are the outputs while running the same code on two different timezone server :
On EDT server-
Date obj :Fri Oct 09 11:45:00 EDT 2020
On IST server -
Date obj :Fri Oct 09 21:15:00 IST 2020
Why does this happen? I am only passing milliseconds. This data is supposed to be treated as 21:15 on all servers. Why does Date class change the data?
Please share a sample piece of code for getting the same time data regardless of the timezone of the server.
A Date object represents a specific instant in time, represented by a given number of milliseconds since the Unix epoch.
The toString() method converts that instant in time into a local time based on the default time zone. It's not that the Date value itself "has" a time zone - it's just toString() that uses the default one.
This data is supposed to be treated as 21:15 on all servers.
That suggests you want to use the Indian time zone in all servers, at least when converting the instant in time for display. Without knowing anything more about your application, that's all we can say... other than "don't use java.util.Date or java.util.Calendar; use the java.time classes instead". They're much better designed, and you're less likely to run into problems like this.
java.time
I recommend you use java.time, the modern Java date and time API, for your date and time work.
long scheduledTime = 1_602_258_300_000L;
Instant pointInTime = Instant.ofEpochMilli(scheduledTime);
System.out.println(pointInTime);
Output from this snippet will be the same on all servers in all time zones:
2020-10-09T15:45:00Z
Since you want 21:15, specify the time zone for India:
ZoneId serverTimeZone = ZoneId.of("Asia/Kolkata");
ZonedDateTime dateTime = pointInTime.atZone(serverTimeZone);
System.out.println(dateTime);
2020-10-09T21:15+05:30[Asia/Kolkata]
What went wrong?
The epoch is one point in time independent of time zone. so a count of milliseconds also denotes one point in time. In your case that point in time is Friday 9. October 2020 15:45:00 UTC. And at that point in time it was 21:15 in India and 11:45 on the East coast of North America. It’s a confusing trait of the outdated Date class that on one hand it represents just a point in time, on the other hand its toString method grabs the time zone setting of the JVM and uses it for rendering the string to be returned, thus giving you the false impression that you get different Date objects in different time zones when in fact they are equal.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Epoch & Unix Timestamp Conversion Tools where you can check what’s the equivalent of your milliseconds in UTC/GMT and in your own time zone.
As pointed by others you should now use the java.time package for working with time. If you look at the documentation of the toString() method of java.util.Date, it says that it coverts the Date object to a String of form:
EEE MMM d m dd hh:mm:ss zzz yyyy
It is like the following code is running in the background:
public String toString(){
Date date=this;
SimpleDateFormat simpleDateFormat=new SimpleDateFormat(
"EEE MMM d m dd hh:mm:ss zzz yyyy");
simpleDateFormat.setTimeZone(TimeZone.getDefault()); //This line is important.
return simpleDateFormat.format(date);
}
Now, if you wanna format your Date object for a certain timezone you can do the same including setting the timezone:
Long scheduledTime = 1602258300000L;
Date date = new Date(scheduledTime);
SimpleDateFormat simpleDateFormat=new SimpleDateFormat(
"EEE MMM d m dd hh:mm:ss zzz yyyy");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("IST"));
String dateStr = simpleDateFormat.format(date);
System.out.println("Date obj :" + dateStr);

How to change timezone without changing the time?

I have a timestamp 2018-01-01 18:20:23.11 which is in UTC. I need to print this in a different format but retain the UTC timezone. However if I use SimpleDateFormat ("dd MMM YYYY yyyy kk:mm z"), it takes my current timezone and gives me 01 Jan 2018 18:20 EST. I want this to print 01 Jan 2018 18:20 UTC. Doing a Timezone.getTimeZone("UTC") converts this time to UTC (does a +4 to hours)which is not the desired result.
DateTimeFormatter originalFormatter
= DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.SS");
DateTimeFormatter newFormatter
= DateTimeFormatter.ofPattern("dd MMM uuuu HH:mm z", Locale.ENGLISH);
String originalTimestamp = "2018-01-01 18:20:23.11";
String differentFormat = LocalDateTime.parse(originalTimestamp, originalFormatter)
.atZone(ZoneId.of("Etc/UTC"))
.format(newFormatter);
System.out.println(differentFormat);
This prints
01 Jan 2018 18:20 UTC
ZoneId.of("Etc/UTC") or ZoneOffset.UTC?
A possibly nerdy edit: I had first written .atZone(ZoneOffset.UTC) in the conversion. I usually use ZoneOffset.UTC to denote UTC and consider this the nice and idiomatic way of specifying it. However in the case of your code, this resulted in the zone being given as Z in the output where you had asked for UTC. Using ZoneId.of("Etc/UTC") instead gives you what you want. I can find no other way of making sure that the zone is formatted as UTC (save hardcoding UTC in the format pattern string, but that would be an ugly hack).
BTW ZoneId.of("Etc/UTC").normalized() returns ZoneOffset.UTC (at least on my Java 10, but I expect it to be the case always).
SimpleDateFormat vs. java.time
SimpleDateFormat is not only long outdated, it is also notoriously troublesome. I recommend you avoid it. It is correct, as you have observed, that it uses your JVM’s default time zone. There is a way to persuade it to do differently, but I would not bother.
java.time is the modern Java date and time API. It came out in 2014 as a replacement for the old and poorly designed date and time classes. IMHO it is so much nicer to work with.
Link: Oracle tutorial: Date Time explaining how to use java.time

Joda: convert the system date and time to date/time in another zone

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.

Date format conversion from CST to GMT is not working

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

Convert Epoch seconds to date and time format in Java

I have seconds since 1970 january 1 UTC (Epoch time).
1320105600
I need to convert that seconds into date and time in below format.
Friday,November 4,2011 5:00,AM
How can I achieve this?
In case you're restricted to legacy java.util.Date and java.util.Calendar APIs, you need to take into account that the timestamps are interpreted in milliseconds, not seconds. So you first need to multiply it by 1000 to get the timestamp in milliseconds.
long seconds = 1320105600;
long millis = seconds * 1000;
This way you can feed it to a.o. the constructor of java.util.Date and finally use SimpleDateFormat to convert a java.util.Date to java.lang.String in the desired date format pattern, if necessary with a predefined time zone (otherwise it would use the system default time zone, which is not GMT/UTC per se and thus the formatted time might be off).
Date date = new Date(millis);
SimpleDateFormat sdf = new SimpleDateFormat("EEEE,MMMM d,yyyy h:mm,a", Locale.ENGLISH);
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
String formattedDate = sdf.format(date);
System.out.println(formattedDate); // Tuesday,November 1,2011 12:00,AM
In case you're already on Java8, there's a LocalDateTime#ofEpochSecond() which allows you to feed epoch seconds directly without the need for multiplying into milliseconds flavor.
LocalDateTime dateTime = LocalDateTime.ofEpochSecond(seconds, 0, ZoneOffset.UTC);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEEE,MMMM d,yyyy h:mm,a", Locale.ENGLISH);
String formattedDate = dateTime.format(formatter);
System.out.println(formattedDate); // Tuesday,November 1,2011 12:00,AM
long yourSeconds = 1320105600L;
Date date = new Date(yourSeconds * 1000);
See this javadoc for more info. The constructor needs milliseconds.
To display this date in an appropriate format you should check DateFormat
Here is an example:
DateFormat df = new SimpleDateFormat("dd MMM yyyy hh:mm:ss zzz");
System.out.println(df.format(date));
java.time
The Answer by BalusC is good in that it points you to using java.time. But that Answer uses LocalDateTime where Instant is more appropriate. A LocalDateTime is not a moment on the timeline as it purposely has no concept of offset-from-UTC or time zone.
java.time
The java.time framework is built into Java 8 and later. These classes supplant the old troublesome date-time classes such as java.util.Date, .Calendar, & java.text.SimpleDateFormat. The Joda-Time team also advises migration to java.time.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations.
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport and further adapted to Android in ThreeTenABP.
Instant
A moment on the timeline in UTC with a resolution up to nanoseconds is represented by the Instant class.
Instant instant = Instant.ofEpochSecond ( 1_320_105_600L );
Dump to console. Your input value is the first moment of November 1, 2011 in UTC. The Z on the end, short for 'Zulu', means UTC.
System.out.println ( "instant: " + instant );
instant: 2011-11-01T00:00:00Z
ZonedDateTime
In your comments you mention wanting to see this date-time through the lens of the America/Chicago time zone. Use a proper time zone name. Apply a time zone, ZoneId, to get a ZonedDateTime object. We see that Chicago is five hours behind UTC on that date.
ZoneId zoneId = ZoneId.of ( "America/Chicago" );
ZonedDateTime zdt = instant.atZone ( zoneId );
zdt: 2011-10-31T19:00-05:00[America/Chicago]
Strings
The Strings seen above are in standard ISO 8601 format. To generate strings in other formats, use the DateTimeFormatter class. You can specify your own custom pattern. But generally best to let java.time automatically localize to the human language and cultural norms encoded in a Locale object.
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime ( FormatStyle.FULL );
formatter = formatter.withLocale ( Locale.US );
String output = zdt.format ( formatter );
Monday, October 31, 2011 7:00:00 PM CDT
To specify your own custom format, search Stack Overflow for many examples and more discussion.
int seconds = 1320105600;
Date date = new Date(seconds * 1000);
SimpleDateFormat sdf = new SimpleDateFormat("EEEE,MMMM d,yyyy h:mm,a");
System.out.println(sdf.format(date));
The trick is to use java.util.Date and java.text.DateFormat to get the format you want. You can look up how to do it in tutorials on the Web.

Categories