SimpleDateFormat is not adding IST timezone offset - java

I am trying to correct a date with some offset value according to the time zone. So, when I format a timestamp with a time zone offset, I expected that SimpleDateFormat will add the offset value to the time.
Here is what I tried:
package com.krishna.mytrials;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
public class DateExperiments {
public static void main(String[] args) throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
//Date we set in UI
Date today = new Date();
//The long value
String todayBrowserLocalTimeStamp = sdf.format(today);
System.out.println(todayBrowserLocalTimeStamp);
Date todayBrowserLocalTimeStampDate = sdf.parse(todayBrowserLocalTimeStamp);
System.out.println("Today's browser local time stamp: " + todayBrowserLocalTimeStampDate);
System.out.println("And its long value:" + todayBrowserLocalTimeStampDate.getTime());
System.out.println("Date generate from long:"+ new Date(todayBrowserLocalTimeStampDate.getTime()));
//What server does to the above mid night time stamp of browser-local time zone
sdf.setTimeZone(TimeZone.getTimeZone("GMT"));
//What we get after it applied the server time zone to browser-local date
//### This is the wrong date
SimpleDateFormat sdf2 = new SimpleDateFormat();
sdf2.setTimeZone(TimeZone.getTimeZone("GMT"));
sdf2.applyPattern("yyyy-MM-dd HH:mm");
System.out.println(sdf2.format(todayBrowserLocalTimeStampDate));
String utcDateString = sdf.format(todayBrowserLocalTimeStampDate);
System.out.println("The above mid night time stamp of browser-local time zone"
+ "is converted to GMT.### The wrong one:");
System.out.println(utcDateString);
//### The wrong date constructed
Date utcDate = sdf.parse(utcDateString);
System.out.println("###Wrong date:"+utcDate);
//### The wrong long
Long utcLong = utcDate.getTime();
System.out.println("###Wrong long:"+utcLong);
// What we will do with the GMT+05:30
sdf.setTimeZone(TimeZone.getTimeZone("GMT+05:30"));
String dateToBeCorrected = sdf2.format(todayBrowserLocalTimeStampDate);
System.out.println("Date to be corrected:"+ dateToBeCorrected);
SimpleDateFormat sdf3 = new SimpleDateFormat("yyyy-MM-dd HH:mm");
Date correctedDate = sdf3.parse(dateToBeCorrected);
System.out.println(correctedDate);
SimpleDateFormat sdf4 = new SimpleDateFormat();
sdf4.setTimeZone(TimeZone.getTimeZone("IST"));
String correctedString = sdf4.format(correctedDate);
System.out.println("Corrected date:" + formatDateToString(correctedDate,"dd MMM yyyy hh:mm:ss a", "IST"));
}
public static String formatDateToString(Date date, String format,
String timeZone) {
// null check
if (date == null) return null;
// create SimpleDateFormat object with input format
SimpleDateFormat sdf = new SimpleDateFormat(format);
// default system timezone if passed null or empty
if (timeZone == null || "".equalsIgnoreCase(timeZone.trim())) {
timeZone = Calendar.getInstance().getTimeZone().getID();
}
// set timezone to SimpleDateFormat
sdf.setTimeZone(TimeZone.getTimeZone(timeZone));
// return Date in required format with timezone as String
return sdf.format(date);
}
}
Here is the output:
2017-01-12
Today's browser local time stamp: Thu Jan 12 00:00:00 IST 2017
And its long value:1484159400000
Date generate from long:Thu Jan 12 00:00:00 IST 2017
2017-01-11 18:30
The above mid night time stamp of browser-local time zoneis converted to GMT.### The wrong one:
2017-01-11
###Wrong date:Wed Jan 11 05:30:00 IST 2017
###Wrong long:1484092800000
Date to be corrected:2017-01-11 18:30
Wed Jan 11 18:30:00 IST 2017
Corrected date:11 Jan 2017 06:30:00 PM
It is supposed add 05:30. to the date. What am I doing wrong?

You have to consider that roundtrips using formatting and parsing can loose informations. This is due to the fact that a formatted date might contain less informations than the original Date-instance had. Look at this data loss:
Original timestamp (variable todayBrowserLocalTimeStampDate) was: 2017-01-11 18:30 (in UTC) or as long: 1484175600000L
String utcDateString = sdf.format(new Date(1484175600000L));
// 2017-01-11
Date utcDate = sdf.parse(utcDateString);
Here you strip away the time part AND parse the stripped string again. Of course, the resulting new Date-instance must loose the corresponding time part, too, and cannot be the same as before.
2017-01-11 (with zero time) would be rendered in your IST-zone 5:30 hours later, that is: 2017-01-11T05:30+05:30 (remember: Date.toString() uses your IST-zone assuming that is your system zone). This is same instant as 2017-01-11T00:00Z. All is fine, only your expectation expressed in line indicated by prefix ###Wrong date is wrong.

Related

Convert current GMT time into CST timezone using java

I am trying to convert current GMT time to CST time using JDK8. But my code always returns the same time for both the timezones. I also verified getAvailableIDs() has "CST".
Calendar cal = Calendar.getInstance();
TimeZone timeZone = TimeZone.getTimeZone("GMT");
cal.setTimeZone(timeZone);
// System.out.println("GMT time = " + cal.getTime());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(timeZone);
String gmtDateStr = sdf.format(cal.getTime());
System.out.println("Formatted GMT time = " + gmtDateStr);
// To CST
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
TimeZone cst = TimeZone.getTimeZone("CST");
sdf2.setTimeZone(cst);
Date cstDate = sdf2.parse(gmtDateStr);
// System.out.println("PARSED CST DATE = " + cstDate);
Calendar cal2 = new GregorianCalendar();
cal2.setTime(cstDate);
cal2.setTimeZone(cst);
String cstDateStr = sdf2.format(cal2.getTime());
// System.out.println("cal2 time = " + cal2.getTime());
System.out.println("FORMATTED CST DATE = " + cstDateStr);
What is wrong here? Can any one provide me an answer?
You don't have to do all of this conversion to get time in your preferred timezone. You can simply do following..
Calendar cal = Calendar.getInstance();
TimeZone timeZone = TimeZone.getTimeZone("GMT");
cal.setTimeZone(timeZone);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(timeZone);
String gmtDateStr = sdf.format(cal.getTime());
System.out.println("Formatted GMT time = " + gmtDateStr);
// To CST
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
TimeZone cst = TimeZone.getTimeZone("CST");
sdf2.setTimeZone(cst);
System.out.println("FORMATTED CST DATE = " + sdf2.format(cal.getTime()));
Java 8 java.time doesn't find a zone identified by CST
ZoneId cst = ZoneId.of("CST");
results in java.time.zone.ZoneRulesException: Unknown time-zone ID: CST. This makes sense as there is Central Standard Time in North America, China Standard Time, and possibly other.
If you mean Central Standard Time in North America which is represented by America/Chicago:
ZoneId gmt = ZoneId.of("GMT");
ZoneId cst = ZoneId.of("America/Chicago");
ZonedDateTime gmtTime = ZonedDateTime.now(gmt);
ZonedDateTime cstTime = gmtTime.withZoneSameInstant(cst);
What is wrong with your code is all those calls to Calendar.getTime(). This returns a Date instance, and those objects do not have the concept of a time zone. So it is useless to set the time zone in the Calendar instances because you then discard that info when converting to Date.
Just specify the time zone in your formatter, as in Yogesh Badke's answer.

java.util.date get milliseconds for y-m-d only

Situation: There is an Object AuditLog, which contains the variable java.util.Date date. This Object is saved in a mySQL Database.
#Entity
public class AuditLog implements Persistable<Long> {
...
#Temporal(TemporalType.DATE)
private Date date;
...
}
I am writing some JUnit tests and need to verify that a saved Date equals the actual date. Where date is a local Copy of the value actually passed to the log Object before it got saved and then loaded again.
Assert.assertEquals(date, log.getDate());
Output:
expected:<Wed May 24 15:54:40 CEST 2017> but was:<2017-05-24>
So you can see that the date actually is the right one but only y-m-d
I then tried this (below) to check if the milliseconds get altered.
Assert.assertEquals(date.getTime(), log.getDate().getTime());
Output:
expected:<1495634973799> but was:<1495576800000>
Now i think the best way would be to get the Milliseconds for year month day only.
Question: Can this be achieved relatively simple and should i do this? I think the Date gets altered because of a Database operation of some kind, so adapting the Test is OK right?
There are two ways to do this:
Using local date : You can convert util Date to LocalDate and do assertEquals on both the objects. LocalDate won't have time, e.g.:
Date input = new Date();
LocalDate date = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
System.out.println(date);
Using Apache commons' DateUtils: You can use truncate method to set non date fields to zero, e.g.:
Date input = new Date();
Date truncated = DateUtils.truncate(input, Calendar.DATE);
System.out.println(truncated);
Here's the maven dependency for Apache commons library.
You can get the "just the day, month, year by using the following code:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
public class Answer {
public static void main(String args[]) throws ParseException {
// parse the date and time
String input = "Wed May 24 15:54:40 CEST 2017";
SimpleDateFormat parser = new SimpleDateFormat("EEE MMM d HH:mm:ss zzz yyyy");
Date date = parser.parse(input);
// parse just the date
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
formatter.setTimeZone(TimeZone.getTimeZone("CEST"));
String formattedDate = formatter.format(date);
Date parsedDate = formatter.parse(formattedDate);
System.out.println(parsedDate);
// use https://currentmillis.com/ to check the milliseconds figures
System.out.println("Wed May 24 15:54:40 CEST 2017 in milliseconds \t" + date.getTime());
System.out.println("Wed May 24 00:00:00 CEST 2017 in milliseconds \t" + parsedDate.getTime());
}
}
The second SimpleDateFormat("yyyy-MM-dd"); parses on the year-month-day.
Use Date.getTime()); to get the milliseconds.
The output is:
Wed May 24 15:54:40 CEST 2017 in milliseconds 1495634080000
Wed May 24 00:00:00 CEST 2017 in milliseconds 1495584000000
1495584000000 = Wed May 24 2017 00:00:00 (using https://currentmillis.com/)
Calendar cal = GregorianCalendar.getInstance();
cal.set(Calendar.YEAR, 2017);
cal.set(Calendar.MONTH, 5 - 1);
cal.set(Calendar.DAY_OF_MONTH, 24);
cal.set(Calendar.MILLISECOND, 0);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
Date d = cal.getTime();
System.out.println(d.getTime());
this code creates a new java.util.Date with only year, month and day set. result of this example is 1495576800000 which is what you want.
A shorter way would be this:
Date d = new Date(0l);
d.setYear(117);
d.setMonth(4);
d.setDate(24);
d.setHours(0);
You should format the two dates:
Date date = new Date();
SimpleDateFormat dt = new SimpleDateFormat("yyyy-MM-dd");
dt.format(date);
Then compare each other.

Why does SimpleDateFormat show unix date as December 31 1969?

I am working on a date picker where there is a range of dates. I noticed that all of my dates are a day behind and while investigating the issue I narrowed down the problem to the SimpleDateFormat object. No matter what Unix time I give it it sets the date to the previous day. An example of this behaviour is
String myFormat = "MM/dd/yyyy";
SimpleDateFormat sdf = new SimpleDateFormat(myFormat);
Log.d(TAG, "Time Zone: " + cal.getTimeZone().getDisplayName());
Log.d(TAG, "Printable: " + sdf.format(0));
The output that I see in the logger is
Time Zone: UTC
Printable: 12/31/1969
Why does the formatter use December 31 instead of January 1st 1970?
Because the DateFormat also has a TimeZone (and your system isn't set to UTC). You can change it with DateFormat.setTimeZone(TimeZone). Something like
String myFormat = "MM/dd/yyyy";
SimpleDateFormat sdf = new SimpleDateFormat(myFormat);
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println("Printable: " + sdf.format(0));
Output is (as you expected)
Printable: 01/01/1970

computing time based on TimeZones

my code computes the date and time correctly including the dayLightSaving time,when run on my local server from india. But when I run the same code from US server I am getting the time which is one hour ahead for the timeZoneId which is not abserving DST.
TimeZone tz = TimeZone.getTimeZone("America/Phoenix");
Date currTime = getDateByTZ(new Date(), tz);
System.out.println("currTime" + currTime);
public static Date getDateByTZ(Date d, TimeZone tz) throws Exception {
if (tz == null) {
tz = TimeZone.getDefault();
}
Integer tzOffSet = tz.getRawOffset();
Integer tzDST = tz.getDSTSavings();
Integer defOffSet = TimeZone.getDefault().getRawOffset();
Integer defDST = TimeZone.getDefault().getDSTSavings();
Calendar cal = Calendar.getInstance(tz);
cal.setTime(d);
if (tz.inDaylightTime(d)) {
cal.add(Calendar.MILLISECOND, -defOffSet);
cal.add(Calendar.MILLISECOND, -defDST);
cal.add(Calendar.MILLISECOND, +tzOffSet);
cal.add(Calendar.MILLISECOND, +tzDST);
} else {
cal.add(Calendar.MILLISECOND, -defOffSet);
cal.add(Calendar.MILLISECOND, tzOffSet);
}
return cal.getTime();
}
Results from Localserver:
currTime:Mon Oct 22 01:52:21 IST 2012
Results from USserver:
currTime:Mon Oct 22 02:52:21 IST 2012
This code doesn't make much sense. A Date object doesn't have to be transformed to be used in another time zone. It represents a universal instant.
What makes sense is to use the time zone when displaying (or formatting as a string) a Date object. In this case, you should simply set the time zone on the DateFormat instance, and the universal instant that constitutes a date will be formatted in order to make sense for the given time zone.
Date now = new Date(); // now, whatever the timezone is
DateFormat df = DateFormat.getDateTimeInstance();
df.setTimeZone(TimeZone.getDefault());
System.out.println("Now displayed in the default time zone : " + df.format(now));
df.setTimeZone(TimeZone.getTimeZone("America/New_York"));
System.out.println("Now displayed in the New York time zone : " + df.format(now));

Parsing formatted Date String in another TimeZone to date object not working

I am trying to convert a formatted date String to Date object. Date String is formatted to some other timezone.
When I do sdf.parse(String) it returns me my System date object.
Code is as below,
static Date convertGMTTime(String timeZone, long longDate){
Date convertedTime = null;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try{
Date date = new Date(longDate);
System.out.println("timezone: "+timeZone +", timestamp: "+date);
Locale locale = Locale.ENGLISH;
TimeZone destTimeZone = TimeZone.getTimeZone(timeZone);// TimeZone.getDefault();
System.out.println("Source timezone: "+destTimeZone);
/* DateFormat formatter = DateFormat.getDateTimeInstance(
DateFormat.DEFAULT,
DateFormat.DEFAULT,
locale);
formatter.setTimeZone(destTimeZone);*/
sdf.setTimeZone(destTimeZone);
String convertedDateStr = sdf.format(date);
System.out.println("convertedDateStr: "+convertedDateStr);
convertedTime = sdf.parse(convertedDateStr);
System.out.println("convertedTime: "+convertedTime + "sdf: "+sdf.getTimeZone());
}catch(Exception e){
e.printStackTrace();
}
return convertedTime;
}
I would appreciate if anyone could help and point out where I am going wrong.
Thanks in advance.
Output:
timezone: Atlantic/Cape_Verde, timestamp: Tue Jun 26 17:38:11 IST 2012
Source timezone: sun.util.calendar.ZoneInfo[id="Atlantic/Cape_Verde",offset=-3600000,dstSavings=0,useDaylight=false,transitions=6,lastRule=null]
convertedDateStr: 2012-06-26 11:08:11
convertedTime: Tue Jun 26 17:38:11 IST 2012
sdf:sun.util.calendar.ZoneInfo[id="Atlantic/Cape_Verde",offset=-3600000,dstSavings=0,useDaylight=false,transitions=6,lastRule=null]
Some more details to share, When I use another sdf object(without setting timezone for it), It do return me correct time and date but still timezone is picked from System clock
Code
static Date convertGMTTime(String timeZone, long longDate){
Date convertedTime = null;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat sdfParse = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try{
Date date = new Date(longDate);
TimeZone destTimeZone = TimeZone.getTimeZone(timeZone);// TimeZone.getDefault();
System.out.println("Source timezone: "+destTimeZone);
sdf.setTimeZone(destTimeZone);
String convertedDateStr = sdf.format(date);
System.out.println("convertedDateStr: "+convertedDateStr );
convertedTime = sdfParse.parse(convertedDateStr,new ParsePosition(0));
System.out.println("convertedTime: "+convertedTime + "sdf: "+sdf.getTimeZone());
}catch(Exception e){
e.printStackTrace();
}
return convertedTime;
}
Output
Source timezone: sun.util.calendar.ZoneInfo[id="Atlantic/Cape_Verde",offset=-3600000,dstSavings=0,useDaylight=false,transitions=6,lastRule=null]
convertedDateStr: 2012-06-26 12:24:56
convertedTime: Tue Jun 26 12:24:56 IST 2012
sdf: sun.util.calendar.ZoneInfo[id="Atlantic/Cape_Verde",offset=-3600000,dstSavings=0,useDaylight=false,transitions=6,lastRule=null]
I understand that when I do not assign timezone to sdf it takes System time zone, but why doesn't it show time in System time zone? I shows it in timezone as it was in String but Timezone is different.
Ans when I set timezone it returns date object as per my system time irrespective of the fact that sdf has some other time zone set.
Can anyone please explain the functional behavior for sdf.parse and sdf.format.
For me sdf.setTimeZone() does have its impact when we use format and it is nullified when we use sdf.parse(). I find it quite strange.
Appreciate help in this regard.
You already have a Date (or the number of milliseconds of the Date), so there is nothing to convert. A Date doesn't have any time zone. It's a universal instant in time. The time zone is relevant only when you display this date, because the date 65647678000 could be 12:38 in some time zone, but 10:38 in some other time zone. It's also relevant when you parse the String representation of a Date, because 10:38 is 65647678000 in some time zone, but is 65657678000 in some other.
While you don't display a Date object, or parse a String to a Date, you don't need to care about time zones. And to choose the time zone used when displaying/parsing it, set the time zone of the DateFormat, and then use DateFormat.format()/DateFormat.parse() to format/parse the date.
When you use Date.toString() to display a date, it will always use your current time zone.
I find it easier to understand what I mean by not thinking of a Date as a day, a month, a year, an hour, etc., but as a moment: "when Kennedy was shot". "When Kennedy was shot" is the same moment for everyone. But if you represent the moment "when Kennedy was shot" in Dallas time zone, it's not the same result as the result you get when you represent this same moment in Paris time zone.

Categories

Resources