Setting a timezone doesn't give what I'm expecting - java

I must say that working with timezones is my nemesis!
In my DB(PostGres) I have a field of type "timestamp without time" zone.
The value I save in it is in UTC time.
What I want to do is displaying the value according to the default timezone of my machine.
So, when I retrieve the value from the DB I first have to "say" that this is a UTC time and therefore I set its time zone to UTC:
private Date lastUpdateToUTC( Date myDate)
{
if ( myDate!= null )
{
SimpleDateFormat dateFormat = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" );
dateFormat.setTimeZone( TimeZone.getTimeZone( "UTC" ) );
System.out.println("orig: " + dateFormat.format(myDate));
try
{
Date convertedDate = dateFormat.parse( dateFormat.format( myDate) );
dateFormat.setTimeZone( TimeZone.getDefault( ) );
System.out.println("converted: " + dateFormat.format(convertedDate));
return dateFormat.parse( dateFormat.format( convertedDate ) );
....
Let say myDate is 2013-08-05 10:44:08.
What I'm expecting is that the first output is 2013-08-05 10:44:08 and the second 2013-08-05 12:44:08.
Instead I get 2013-08-05 08:44:08 and 2013-08-05 10:44:08...
Where is my error in reasoning?
What should I do to have what I'm expecting?

java.util.Date doesn't support timezone.
For the first output, you set dateFormat to UTC. When you provide the Date to dateFormat, it converts it to UTC. It has no way of knowing which timezone your Date is, it assumes it's local timezone. So looks like in your case it's GMT+2, dateFormat subtracts 2 to make it UTC.
Then you do format - parse, which should not have any affect, then you change timezone to your local timezone and print your original Date value. (This time w/o any conversion).

Your current local timezone is GMT+2, that's why the first output would be T+2 hours.
In order to print the date in UTC timezone, the date will minus 2 hours because of +2 hour offset.
intput: 2013-08-05 10:44:08 // GMT+2 timezone
orig: 2013-08-05 08:44:08 // UTC timezone
converted: 2013-08-05 10:44:08 // default timezone: GMT+2 timezone
You can print the current local timezone to know the offset using dateFormat.getTimeZone().
Take myself as an example, my local timezone is GMT+8, the offset would be 8 * 60 * 60 * 1000 = 28800000 milliseconds.
System.out.println("My Current Timezone: " + dateFormat.getTimeZone());
// My Current Timezone: sun.util.calendar.ZoneInfo[id="Asia/Taipei",offset=28800000,dstSavings=0,useDaylight=false,transitions=42,lastRule=null]

Related

How to convert joda DateTime with timezone to DateTime with same time in UTC?

I have such input date:
DateTime date = new DateTime("2022-10-30T00:00:00.000+11:00");
How can I convert it to UTC while keeping the same time:"2022-10-30 00:00:00.000"?
In other words, I want the date.getMillis(); method to return the midnight of "2022-10-30" in UTC.
For now, if I call date.getMillis();, I get "2020-10-29" in UTC.
I'm not sure you're doing what you think you're doing here.
The date you're creating doesn't have a UTC+11 offset, it's in your JVM's default time zone (demo):
System.out.println(DateTimeZone.getDefault());
DateTime date = new DateTime("2022-10-30T00:00:00.000+11:00");
System.out.println(date);
prints
Etc/UTC
2022-10-29T13:00:00.000Z
so the local date portion of this is 2022-10-29.
If you want to create it with the specified offset, you have to either use DateTime.parse:
DateTime date = DateTime.parse("2022-10-30T00:00:00.000+11:00");
or specify it:
DateTime date = new DateTime("2022-10-30T00:00:00.000+11:00", DateTimeZone.forOffsetHours(11));
Printing either of these results in:
2022-10-30T00:00:00.000+11:00
Now, date.withZoneRetainFields(DateTimeZone.UTC) is:
2022-10-30T00:00:00.000Z
as required.

My question is about converting epoch millisecond from date in groovy

I am generating epoch timestamp in milliseconds with the following code and it works (verified with https://www.epochconverter.com/). However, when we are setting timezone with JVM option -Duser.timezone=America/Toronto then for some historical dates time offset is differ by one hour. i.e Date=1950-11-19 (yyyy-MM-dd) correct epoch milliseconds -603313200000 (Sunday, November 19, 1950 12:00:00 AM GMT-05:00) but when timezone is set with JVM options value is -603316800000 and Epoch converted shows Saturday, November 18, 1950 11:00:00 PM GMT-05:00. I have used joda time lib with JDK 10
def static Long getEpochTimeStampInMilliSeconds(String simpleDate, String dateFormat) {
Long retVal = null
try {
org.joda.time.format.DateTimeFormatter fmt = DateTimeFormat.forPattern(dateFormat)
DateTimeZone dtz2 = DateTimeZone.forID("America/Toronto")
DateTime parsedDateTime = DateTime.parse(simpleDate, fmt).withZone(dtz2)
retVal = parsedDateTime.getMillis()
} catch (Exception e) {
retVal = null
}
return retVal
}
date format is : "yyyy-MM-dd"
You need to parse with the correct time zone, so instead of calling dateTime.withZone(...) after parsing is done, you need to call dateTimeFormatter.withZone(...) before parsing with the formatter.
If the default time zone, as set by the user.timezone system property is America/Toronto, then the parsed DateTime value is already in that time zone, and dateTime.withZone(...) will do nothing.
If the default time zone is something else, then the parsed DateTime value is in that time zone, which would be a different UTC epoch millisecond value. Calling dateTime.withZone(...) will change the time zone, and hence the time value, but will not change the UTC epoch millisecond value.
def dtz2 = org.joda.time.DateTimeZone.forID("America/Toronto")
def fmt = org.joda.time.format.DateTimeFormat.forPattern(dateFormat).withZone(dtz2)
retVal = org.joda.time.DateTime.parse(simpleDate, fmt).getMillis()
UPDATE
From comment:
I am receiving -603316800000 for 1950-11-19 for all scenario but correct value is -603313200000
Lets test which value is correct, using Java-Time API:
ZoneId zone = ZoneId.of("America/Toronto");
System.out.println(Instant.ofEpochMilli(-603316800000L));
System.out.println(Instant.ofEpochMilli(-603316800000L).atZone(zone));
System.out.println(Instant.ofEpochMilli(-603313200000L));
System.out.println(Instant.ofEpochMilli(-603313200000L).atZone(zone));
Output
1950-11-19T04:00:00Z
1950-11-19T00:00-04:00[America/Toronto] ⬅ Correct value
1950-11-19T05:00:00Z
1950-11-19T01:00-04:00[America/Toronto]
As you can see, the value you get (-603316800000) is the correct value for 1950-11-19 at midnight, Toronto time.
You get offset -04:00 for Toronto, because in 1950, DST lasted until Sun, Nov 26 at 2:00 am (see https://www.timeanddate.com/time/zone/canada/toronto), so the offset is correct for Eastern Daylight Time (EDT).
Don't know why you think -603313200000 is the correct value, but it is not.

Converting to UTC considering daylight saving

I have a date, for example Thu April 17 09:03:01 GMT 2014 in the timezone:
sun.util.calendar.ZoneInfo[id="Europe/London",offset=0,dstSavings=3600000,useDaylight=true,transitions=242,lastRule=java.util.SimpleTimeZone[id=Europe/London,offset=0,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]]
and everytime a try to convert to UTC it returns Thu April 17 10:03:01 GMT 2014
This does not make sense because the corresponding UTC time is actually Thu April 17 08:03:01 GMT 2014 since that the in my timezone time is added 1hour due to daylight savings.
The code I use to convert is this:
//timeZone - id="Europe/London"
public static Date timeZoneConvertDate(Date date, TimeZone timeZone) {
SimpleDateFormat sdf = new SimpleDateFormat();
sdf.setTimeZone(timeZone);
sdf.applyPattern("dd-MM-yyyy HH:mm:ss");
String newDate = sdf.format(date);
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
try {
Date nd = sdf.parse(newDate);
return nd;
} catch (ParseException e) {
return null;
}
}
Could someone explain what I'm doing wrong?
tl;dr
A Date has no timezone associated with it, so you cannot create a method that adjusts the timezone of a date object. You need to work with Calendar objects if you want to retain TZ information or, preferably, take a look at Joda-Time.
Explanation of Your Output
A Date value has no timezone information; it's merely the number of milliseconds since the epoch. With that in mind, let's see what you're doing:
SimpleDateFormat sdf = new SimpleDateFormat();
sdf.setTimeZone(timeZone);
sdf.applyPattern("dd-MM-yyyy HH:mm:ss");
String newDate = sdf.format(date);
This part of your code creates a formatter that will print the date in the London timezone. So the result you'll get at the time of writing is approximately: 17-04-2014 11:38:15 (assuming you just created your date object).
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
try {
Date nd = sdf.parse(newDate);
return nd;
} catch (ParseException e) {
return null;
}
Here you tell the date parser to read the date as though it were a UTC date. It uses that information to know how many milliseconds since the epoch have passed. The date object you get back still has no timezone associated with it.
UTC is an hour behind British Summer Time, so it will create a date object that appears an hour ahead when printed in the BST timezone. So when I print nd, I get: Thu Apr 17 12:38:15 BST 2014.
No Time Zone In java.util.Date
As the correct answer by Duncan said a java.util.Date has no time zone component. Confusingly its toString method applies the JVM's default time zone. To display in another time zone, use SimpleDateFormat to apply an adjustment.
Even better, avoid the notoriously troublesome java.util.Date, .Calendar, and SimpleDateFormat. Use either Joda-Time or the new java.time package in Java 8.
Joda-Time
In Joda-Time, a DateTime object truly does contain an assigned time zone. If you do not specify a time zone, the JVM's default time zone is assigned.
DateTimeZone timeZone = DateTimeZone.forID( "Europe/London" );
DateTime dateTime = new DateTime( 2014, 4, 17, 9, 3, 1, timeZone );
DateTime dateTimeUtc = dateTime.withZone( DateTimeZone.UTC );
DateTime dateTimeIndia = dateTime.withZone( DateTimeZone.forID( "Asia/Kolkata" ) );
When run…
dateTime: 2014-04-17T09:03:01.000+01:00
dateTimeUtc: 2014-04-17T08:03:01.000Z
dateTimeIndia: 2014-04-17T13:33:01.000+05:30 (note the half-hour difference, +05:30)
You can easily convert back and forth to java.util.Date.
DateTime dateTime = new DateTime( myJUDate, timeZone );
…and…
Java.util.Date date = dateTime.toDate();

Convert IST to US timezones considering the daylight saving time in java

I have a date-time in IST. I want to convert it to US timezones based on input considering the daylight saving time,
if there is daylight saving time for the given date-time in java.
This is what i tried
function convert(Date dt,int toTimeZoneId){
Calendar cal = Calendar.getInstance();
cal.setTime(dt); // Geting time in IST
//Converted to GMT and set in cal
switch(toTimeZoneId){
case 1: tzTarget = TimeZone.getTimeZone("America/Adak");
offset = -10;
break;
case 2: tzTarget = TimeZone.getTimeZone("America/Anchorage");
offset = -9;
break;
case 3: tzTarget = TimeZone.getTimeZone("America/Los_Angeles");
offset = -8;
break;
case 4: tzTarget = TimeZone.getTimeZone("America/Denver");
offset = -7;
break;
case 5: tzTarget = TimeZone.getTimeZone("America/Chicago");
offset = -6;
break;
case 6: tzTarget = TimeZone.getTimeZone("America/New_York");
offset = -5;
break;
}
//converting from GMT to US timezones based on offset and dst
cal.setTimeZone(tzTarget);
dst = tzTarget.getDSTSavings();
dst = dst/3600000;
offset = offset + dst;
cal.add(Calendar.HOUR, offset);
Date date = cal.getTime();
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date));
}
To just convert a given date to different time zones, you need to create the date formatter with appropriate time zone. A date instance is just a long value relative to epoch; it doesn't have time zone information. So we aren't converting it to a different time zone, we are just representing it in different time zones. That is why we need time zone information when we want to create a string representation of the date instance.
Here's some code to illustrate the above. I've just added the time zone to your date format string to make things clear.
/*
* Converts a specified time to different time zones
*/
public void convert(Date dt) {
// This prints: Date with default formatter: 2013-03-14 22:00:12 PDT
// As my machine is in PDT time zone
System.out.println("Date with default formatter: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z").format(dt));
// This prints: Date with IST time zone formatter: 2013-03-15 10:30:12 GMT+05:30
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
TimeZone tz = TimeZone.getTimeZone("GMT+0530");
sdf.setTimeZone(tz);
String dateIST = sdf.format(dt);
System.out.println("Date with IST time zone formatter: " + dateIST);
// This prints: Date CST time zone formatter: 2013-03-15 00:00:12 CDT
tz = TimeZone.getTimeZone("CST");
sdf.setTimeZone(tz);
System.out.println("Date CST time zone formatter: " + sdf.format(dt));
}
I think this is what you are trying to do - convert a given time to different time zones. To do that I don't think you need to add/subtract any offset, as you just want the same time represented in a different time zone and the TimeZone instance should be able to take care of that during formatting.
As for daylight saving, the TimeZone should be able to take care of that as well. If you notice in my example code, I've used CST to create TimeZone instance and CST is "GMT -06 hours". But the output it gives is in CDT, which is "GMT -05 hours", because this time zone instance uses daylight saving. So by using the appropriate time zone you should be able to handle daylight saving as well.
java.util.GregorianCalendar allows you create dates with timezones. Unfortunately, addition and subtraction suck from there. (How do you subtract Dates in Java?)
Since you're converting between two timezones, you can also make use of java.util.TimeZone and use the difference of tz1.getOffset(date) - tz2.getOffset(date). Mind the ordering when doing subtraction.
Joda-Time
Using Joda-Time makes this much easier. Or try the new java.time package in Java 8.
Here is some example code using Joda-Time 2.3. Search StackOverflow for many more examples.
India time…
DateTimeZone timeZone_India = DateTimeZone.forID( "Asia/Kolkata" );
DateTime dateTimeIndia = new DateTime( date, timeZone_India );
Adjusting the same moment for display as New York time…
DateTimeZone timeZone_NewYork = DateTimeZone.forID( "America/New_York" );
DateTime dateTimeNewYork = dateTimeIndia.withZone( timeZone_NewYork ); // Same moment, different wall-clock time.
Still the same moment, but in UTC (no time zone offset).
DateTime dateTimeUtc = dateTimeIndia.withZone( DateTimeZone.UTC );
Use Proper Time Zone Names
Avoid using 3-4 letter time zone codes. They are neither standardized nor unique. Your IST for example can mean either Irish Standard Time or India Standard Time. Use proper time zone names.

how to convert local time to UTC keeping in mind the DayLightSaving factor

In my web application, I am storing all end-user's date information as UTC format in database, and before showing it to them, just converting the UTC dates to timezones of their choice.
I am using this method to convert a localtime to UTC time (while storing):
public static Date getUTCDateFromStringAndTimezone(String inputDate, TimeZone timezone){
Date date
date = new Date(inputDate)
print("input local date ---> " + date);
//Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT
long msFromEpochGmt = date.getTime()
//gives you the current offset in ms from GMT at the current date
int offsetFromUTC = timezone.getOffset(msFromEpochGmt)*(-1) //this (-1) forces addition or subtraction whatever is reqd to make UTC
print("offsetFromUTC ---> " + offsetFromUTC)
//create a new calendar in GMT timezone, set to this date and add the offset
Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"))
gmtCal.setTime(date)
gmtCal.add(Calendar.MILLISECOND, offsetFromUTC)
return gmtCal.getTime()
}
And this method for converting UTC date to local (while showing):
public static String getLocalDateFromUTCDateAndTimezone(Date utcDate, TimeZone timezone, DateFormat formatter) {
printf ("input utc date ---> " + utcDate)
//Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT
long msFromEpochGmt = utcDate.getTime()
//gives you the current offset in ms from GMT at the current date
int offsetFromUTC = timezone.getOffset(msFromEpochGmt)
print("offsetFromUTC ---> " + offsetFromUTC)
//create a new calendar in GMT timezone, set to this date and add the offset
Calendar localCal = Calendar.getInstance(timezone)
localCal.setTime(utcDate)
localCal.add(Calendar.MILLISECOND, offsetFromUTC)
return formatter.format(localCal.getTime())
}
My question is, if the end-user is within a DST zone, then how do I improve the methods to accommodate their local clock times perfectly.
If you use a custom time zone ID, like GMT+10 you will get TimeZone that does not support DST, eg TimeZone.getTimeZone("GMT+10").useDaylightTime() returns false. But if you use a supported ID eg "America/Chicago" you will get a TimeZone that supports DST. The full list of supported IDs is returned by TimeZone.getAvailableIDs(). Internally Java stores time zone info in jre/lib/zi.

Categories

Resources