I am trying to convert a UTC date String of the format 2020-06-09T06:30:00Z to a Timezone like Asia/Calcutta.
The time offset between UTC and Asia/Calcutta is +5:30, so the expected result on converting 2020-06-09T06:30:00Z to Asia/Calcutta is 2020-06-09T12:00:00Z but I am getting 2020-06-09T01:00:00Z
String utcDate = "2020-06-09T06:30:00Z";
String timezone = "Asia/Calcutta";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
LocalDateTime date = LocalDateTime.parse(utcDate, formatter); //2020-06-09T06:30
ZonedDateTime zonedDate = ZonedDateTime.of(date, ZoneId.of(timezone)); //2020-06-09T06:30+05:30[Asia/Calcutta]
Date dateConverted = Date.from( zonedDate.toInstant()); //Tue Jun 09 01:00:00 UTC 2020
I think zonedDate.toInstant() is converting subtracting 5:30 instead of adding 5:30 to 2020-06-09T06:30. My machine is in UTC timezone. may be its converting to local time don't know what is the issue. Also tried below method, still same issue.
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
sdf.setTimeZone(TimeZone.getTimeZone(timezone));
sdf.parse(dateString);
java.time
I agree with you, you should strongly prefer to use java.time, the modern Java date and time API. It’s not complicated when you know how.
String utcDate = "2020-06-09T06:30:00Z";
String timezone = "Asia/Calcutta";
ZoneId zone = ZoneId.of(timezone);
ZonedDateTime zdt = Instant.parse(utcDate).atZone(zone);
System.out.println("zdt: " + zdt);
Output is:
zdt: 2020-06-09T12:00+05:30[Asia/Calcutta]
You notice that the time of day in the ZonedDateTime is 12:00 as you had expected. The trailing Z that you also said you expected is wrong, though. Z means an offset of 0 from UTC, so since the offset is +05:30, we are very happy not to have the Z there.
If what you needed was an old-fashioned Date object, presumably for a legacy API that you don’t want to upgrade just now, there’s no reason to convert to your time zone first. This works:
Date oldfashionedDate = Date.from(Instant.parse(utcDate));
System.out.println("oldfashionedDate: " + oldfashionedDate);
Output in my time zone is:
oldfashionedDate: Tue Jun 09 08:30:00 CEST 2020
You may be in doubt whether the Date is right. It is. Since I am in Europe/Copenhagen time zone, currently at offset +02:00, the Date prints in this time zone, so the time of day is 8:30 (the Date doesn’t hold a time zone or offset). If instead I run in Asia/Calcutta time zone:
oldfashionedDate: Tue Jun 09 12:00:00 IST 2020
You notice IST for India Standard Time instead of CEST for Central European Summer Time. And the time is 12:00 as expected.
Points for you to take with you
Never hardcode Z as a literal in a format pattern string. As I said, it’s an offset, so it needs to be parsed as an offset.
For the same reason don’t parse into a LocalDateTime. A LocalDateTime. hasn’t got any offset, so you are losing information.
In this case use no formatter at all. Your format is ISO 8601, and Instant parses it as its default, that is, without any explicit formatter (OffsetDateTime and ZonedDateTime do too).
Make the conversion from UTC to Asia/Calcutta. It seems to me that you were doing the opposite conversion.
Link
Wikipedia article: ISO 8601
Got it work using SimpleDateFormat as shown below. But anyone coming across this issue should probably use ZonedDateTime.
String utcDate = "2020-06-09T06:30:00Z";
String timezone = "Asia/Calcutta";
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
sdf1.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = sdf1.parse(utcDate); // Tue Jun 09 06:30:00 UTC 2020
SimpleDateFormat sdf2 = new SimpleDateFormat("dd-M-yyyy hh:mm:ss a");
sdf2.setTimeZone(TimeZone.getTimeZone(timezone));
String sDateInZone = sdf2.format(date); // 09-6-2020 12:00:00 PM
SimpleDateFormat formatter = new SimpleDateFormat("dd-M-yyyy hh:mm:ss a");
Date dateInZone = formatter.parse(sDateInZone); // Tue Jun 09 12:00:00 UTC 2020
EDIT: Got it to work using ZonedDateTime also as shown below:
String utcDate = "2020-06-09T06:30:00Z";
String timezone = "Asia/Calcutta";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
LocalDateTime dateUtc = LocalDateTime.parse(utcDate, formatter);
ZonedDateTime utcZonedDateTime = dateUtc.atZone(ZoneId.systemDefault());
ZoneId zoneId = ZoneId.of(timezone);
ZonedDateTime zonedDateTime = utcZonedDateTime.withZoneSameInstant(zoneId);
String zoneDateString = formatter.format(zonedDateTime);
Date date = Date.from(LocalDateTime.parse(zoneDateString, formatter).atZone(ZoneId.systemDefault()).toInstant());
Related
I am getting following time from service
'Nov 11 2019 7:30 PM'
I know this is US Central Time, I need to get hours between current time and this event time, but i am unable to understand how to convert this date to UTC.
I am using following approach, but this does not seem to be working fine.
public Date ticketJSONDateFormatter(String dateTime){
SimpleDateFormat simpleDateFormatter
= new SimpleDateFormat("MMM d yyyy HH:mm a");
Date parsedDate = null;
try {
simpleDateFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
parsedDate = simpleDateFormatter.parse(dateTime);
} catch (ParseException e) {
e.printStackTrace();
}
return parsedDate;
}
This method returns following date
Fri Oct 11 12:30:00 GMT+05:00 2019
Although the expected output may be something like this. My device is at (+5:00 UTC)
Fri Oct 12 12:30:00 GMT+05:00 2019
You can use `LocalDateTime' to parse the string to date,
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM dd yyyy hh:mm a");
String date = "Nov 11 2019 07:30 PM";
LocalDateTime ldt = LocalDateTime.parse(date, formatter);
Then convert it to your preferred zone,
Instant cdt = ldt.atZone(ZoneId.of("America/Chicago")).toInstant();
return cdt.atOffset(ZoneOffset.UTC)
This will return an Instant.
And as Ole V.V suggested in the comment, I wouldn't recommend using old Date and Calendar API. I would suggest reading this answer to understand the issues associated with the old Date API.
You can get this in following steps:
Use ZonedDateTime.parse to parse the time you are receiving.
Convert the America Central time to your local time.
Get your current time.
Find the difference between your current time and the event time converted to your local.
Example:
// Parsing the time you are receiving in Central Time Zone. Using Chicago as a representative Zone.
String dateWithZone = "Nov 11 2019 7:30 PM".concat("America/Chicago") ;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM dd uuuu h:m aVV");
ZonedDateTime zonedDateTime = ZonedDateTime.parse(dateWithZone, formatter);
System.out.println(zonedDateTime); // This is the time you received in Central time zone.
// Now convert the event time in your local time zone
ZonedDateTime eventTimeInLocal = zonedDateTime.withZoneSameInstant(ZoneId.systemDefault());
// Then find the duration between your current time and event time
System.out.println(Duration.between(ZonedDateTime.now(), eventTimeInLocal).toHours());
The duration class provides many other utilities methods to get more precise duration.
I have a MapperUtility class that needs to map a string from a web service that sends a string time "Fri Nov 22 2013 12:12:13 GMT+0000 (UTC)"
Now, I am converting it to LocalDateTime with this code:
String time = "Fri Nov 22 2013 12:12:13 GMT+0000 (UTC)";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("E MMM dd yyyy HH:mm:ssZ");
dtf.withZone(ZoneId.of("UTC"));
LocalDateTime convertedDate = LocalDateTime.parse(time, dtf);
But I am having an exception starting the GMT+0000 (UTC).
It works when I removed the characters beyond the GMT.
After converting them to Date Time, I need to convert them to long milliseconds.
Please advise. Thanks.
You may build such pattern using DateTimeFormatterBuilder:
static final DateTimeFormatter DF = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ofPattern("E MMM dd yyyy HH:mm:ss"))
.appendLiteral(" GMT")
.appendOffset("+HHmm", "+0000")
.optionalStart()
.appendLiteral(" (")
.appendZoneId()
.appendLiteral(')')
.optionalEnd()
.toFormatter()
.withLocale(Locale.US);
Then, just:
String date = "Fri Nov 22 2013 12:12:13 GMT+0000 (UTC)";
long ms = OffsetDateTime.parse(date, DF).toInstant().toEpochMilli(); // 1385122333000
An inefficient way to make the parser accept your string verbatim is:
String[] timezones = {"UTC", "BST", "CET", "PST", ...};
StringBuilder sb = new StringBuilder(timezones.length * 8 + 38);
sb.append("E MMM dd yyyy HH:mm:ss' GMT'Z' ('");
for(String timezone : timezones)
sb.append("['").append(timezone).append("']");
sb.append("')'");
DateTimeFormatter dtf = DateTimeFormatter.ofPattern(sb.toString());
String time = "Fri Nov 22 2013 12:12:13 GMT+0000 (UTC)";
ZonedDateTime convertedDate = ZonedDateTime.parse(time, dtf);
System.out.println(convertedDate);
I changed to ZonedDateTime too because otherwise it discards the timezone and always returns 12:12:13 regardless of what's after the GMT+.
But it gets unwieldy pretty quick because of the inexhaustible list of possible time zone abbreviations.
A better way is to preprocess the string:
String time = "Fri Nov 22 2013 12:12:13 GMT+0000 (UTC)";
String preprocessed = time.replaceAll("(.*) GMT([+-][0-9]{4}).*", "$1$2");
System.out.println(preprocessed);
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("E MMM dd yyyy HH:mm:ssZ");
ZonedDateTime convertedDate = ZonedDateTime.parse(preprocessed, dtf);
System.out.println(convertedDate);
Then the conversion to milliseconds is a bit tricky to find in the extensive java.time API but eventually it turns out to be as simple as:
convertedDate.toInstant().toEpochMilli()
String time = "Fri Nov 22 2013 12:12:13 GMT+0000 (UTC)";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("E MMM dd yyyy HH:mm:ss 'GMT'xx (zzz)", Locale.ENGLISH);
ZonedDateTime zdt = ZonedDateTime.parse(time, dtf);
OffsetDateTime odt = OffsetDateTime.parse(time, dtf);
boolean offsetAgrees = zdt.getOffset().equals(odt.getOffset());
if (offsetAgrees) {
long millisecondsSinceEpoch = odt.toInstant().toEpochMilli();
System.out.println("Milliseoncds: " + millisecondsSinceEpoch);
} else {
System.out.println("Offset " + odt.getOffset() + " does not agree with time zone " + zdt.getZone());
}
Output:
Milliseoncds: 1385122333000
I am parsing GMT as a literal, +0000 as an offset and UTC as a time zone abbreviation. For the offset we could have used xx or ZZZ. Since Fri and Nov are in English, we need to specify an English speaking locale.
I have added a little complication compared to your code because we want to validate that the offset and the time zone agree. OffsetDateTime.parse uses the offest (+0000) directly whereas ZonedDateTime.parse derives the offset from the time zone ( UTC). My check is very bare-bones and may be extended to accept two possible offsets in the transition from summer time (DST) and multiple time zones sharing the same abbreviation.
PS Don’t use LocalDateTime. This type can neither hold an offset nor time zone, so you can no longer attach your date and time to a specific point on the time line, which you need to do to obtain milliseconds since the epoch.
I want to convert a date to GMT.
I get a date in BST, I want to convert it to GMT without time zone conversion.
Example:
**If the BST date is: Wed June 26 13:30:13 BST 2019
I want to convert it to Wed 26 Jun 2019 13:30:13 GMT**
I want to ignore the timezone info and return the same date as GMT.
For this I am trying
private SimpleDateFormat dateFormatLocal = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
private SimpleDateFormat dateFormatGmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
private SimpleDateFormat dateFormatGmtText = new SimpleDateFormat("EEE dd MMM yyyy HH:mm:ss 'GMT'");
dateFormatGmt.setTimeZone(TimeZone.getTimeZone("GMT"));
String textDate = dateFormatLocal.format(date);
//Date is Wed June 26 13:30:13 BST 2019
private Date toGMTDate(final Date date) {
String textDate = dateFormatLocal.format(date);
try {
String[] dateParts = textDate.split("\\+");
textDate = dateParts[0] + "+0000";
return dateFormatGmt.parse(textDate);
} catch (ParseException e) {
return null;
}
}
private String toGMT(final Date date) {
return dateFormatGmtText.format(toGMTDate(date));
}
When I call toGMT it returns Wed 26 Jun 2019 14:30:13 GMT
I am not sure why it is so?
What is wrong here?
java.time
You said you can’t use the modern date and time API, but for other readers I should like to present that option first. SimpleDateFormat and Date are poorly designed and long outdated, the former in particular notoriously troublesome, so I recommend avoiding them.
I am assuming that BST is for British Summer Time (other interpretations exist). And I am assuming that you cannot avoid getting an old-fashioned Date object.
private static DateTimeFormatter formatter
= DateTimeFormatter.ofPattern("EEE dd MMM yyyy HH:mm:ss z", Locale.UK);
private static ZoneId britain = ZoneId.of("Europe/London");
private static ZoneId gmt = ZoneId.of("Etc/GMT");
private static String toGMT(final Date date) {
ZonedDateTime britishTime = date.toInstant().atZone(britain);
ZonedDateTime gmtTime = britishTime.withZoneSameLocal(gmt);
return gmtTime.format(formatter);
}
Try it out with your Date of Wed Jun 26 13:30:13 BST 2019:
String textDate = dateFormatLocal.format(date);
System.out.println(textDate);
System.out.println(toGMT(date));
Output is:
2019-06-26T13:30:13+0100
Wed 26 Jun 2019 13:30:13 GMT
Whenever you get an old-fashioned Date, the first thing to do is to convert it to Instant. Then do any further conversions from there. The key to changing time zone and keeping the date and time of day (hour-minute-second of day) is the withZoneSameLocal method of the ZonedDateTime class.
I recommend specifying locale for the formatter.
I am not sure why it is so? What is wrong here?
A Date hasn’t got, as in cannot have a time zone. It’s a point in time, nothing more. YourtoGMTDate method returns a point in time that is an hour later: The time you gave it was 13:30:13+0100, and it returned 13:30:13+0000, which is the same point in time as 14:30:13+0100. Next you formatted this point in time using a formatter that used your default time zone, Europe/London, and therefore produced 14:30:13, but at the same time printed GMT in the string — the result you reported.
…the new time library, but for some reasons I can't use them.
If you really have got an evil boss that either forces you to use Java 1.4 or 1.5 and/or forbids the use external dependencies, the pretty simple hack is:
private String toGMT(final Date date) {
return dateFormatGmtText.format(date);
}
The cheating is: Your dateFormatGmtText uses your default time zone, Europe/London, but lies and prints GMT in the formatted string. This gives the same output as above — the output you asked for. Compared to your code I am just leaving out the date conversion.
Link: Oracle tutorial: Date Time explaining how to use java.time.
I tried the below approach and searched in Web to find the solution for this but no luck : looking for the solution for converting a String in IST to PST:
String string = new Date().toString();
System.out.println(string);
SimpleDateFormat dt = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy");
dt.setTimeZone(TimeZone.getTimeZone("PST"));
Date D = dt.parse(string);
System.out.println(""+ D);
Even when I set time zone as PST, I am seeing out put in IST
here is the out put:
Tue Apr 18 18:58:09 IST 2017
Tue Apr 18 18:58:09 IST 2017
I tried another Option here I am seeing even it is showing the time in PST but I see below output it is a bit confusing:
public static Date convertFromOneTimeZoneToOhter(Date dt,String from,String to ) {
TimeZone fromTimezone =TimeZone.getTimeZone(from);//get Timezone object
TimeZone toTimezone=TimeZone.getTimeZone(to);
long fromOffset = fromTimezone.getOffset(dt.getTime());//get offset
long toOffset = toTimezone.getOffset(dt.getTime());
//calculate offset difference and calculate the actual time
long convertedTime = dt.getTime() - (fromOffset - toOffset);
Date d2 = new Date(convertedTime);
return d2;
}
OUT PUT:
Converted Date : Tue Apr 18 06:28:09 IST 2017
Can someone please help on this: I found lot of solutions on converting IST Date time to PST String but not IST/EST Date to PST Date.
As I mentioned above we can format to a String, but I am looking for an example of converting back to Date
You should look into Java 8's new Date API that handles timezones directly
// Get the current date and time
ZonedDateTime date1 = ZonedDateTime.parse("2007-12-03T10:15:30+05:30[Asia/Karachi]");
System.out.println("date1: " + date1);
ZonedDateTime zonedDateTime = ZonedDateTime.now();
System.out.println("Zoned Date Time: " + zonedDateTime);
ZoneId id = ZoneId.of("Europe/Paris");
System.out.println("ZoneId: " + id);
ZoneId currentZone = ZoneId.systemDefault();
System.out.println("CurrentZone: " + currentZone);
Prints :
date1: 2007-12-03T10:15:30+05:00[Asia/Karachi]
Zoned Date Time: 2017-04-18T09:52:09.045-04:00[America/New_York]
ZoneId: Europe/Paris
CurrentZone: America/New_York
Since some readers here will use Java 8 or later and some Java 7 or earlier, I will treat both.
I recommend you use the java.time classes introduced in Java 8 if you can:
ZoneId targetTz = ZoneId.of("America/Los_Angeles");
String string = "Tue Apr 18 18:58:09 +0300 2017";
DateTimeFormatter format = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss ZZZ uuuu",
Locale.ENGLISH);
ZonedDateTime sourceTime = ZonedDateTime.parse(string, format);
ZonedDateTime targetTime = sourceTime.withZoneSameInstant(targetTz);
String result = targetTime.format(format);
System.out.println(result);
This prints:
Tue Apr 18 08:58:09 -0700 2017
You said you wanted a PST date, and this is exactly what the ZonedDateTime gives you: date and time with time zone information.
In the example I am giving a zone offset, +0300 (corresponding to Israel Daylight Time) in the string. I understood that it wasn’t important to you how the time zone was given. I want to avoid the three and four letter time zone abbreviations like IST. Not only may IST mean either Irish Standard Time, Israel Standard Time or India Standard Time. I furthermore noticed that the java.time classes pick up 18:58:09 IST as 18:58:09 IDT (UTC+3) because it knows Israel is on DST on April 18; the SimpleDateFormat that I return to below takes IST more literally and interprets 18:58:09 IST as 18:58:09 +0200, which is 1 hour later in UTC.
You can use the java.time classes with Java 1.7 if you want. You can get them in the ThreeTen Backport.
If you don’t want to use java.time, the way to do it with the outdated classes from Java 1.0 and 1.1 is not that different in this case, only I cannot give you the PST date you asked for:
TimeZone targetTz = TimeZone.getTimeZone("America/Los_Angeles");
String string = "Tue Apr 18 18:58:09 +0300 2017";
SimpleDateFormat dt = new SimpleDateFormat("EEE MMM dd HH:mm:ss ZZZ yyyy", Locale.ENGLISH);
Date d = dt.parse(string);
dt.setTimeZone(targetTz);
String result = dt.format(d);
System.out.println(result);
It prints the same result as above. However, you notice there is only one Date object. A Date object cannot hold any time zone information, so if you need this, you will have to bring the targetTz object along with d. It’s a common misunderstanding that there’s supposed to be a time zone in the Date object, probably greatly helped by the fact that its toString() prints a time zone. This is always the JVM’s default time zone and doesn’t come from the Date object, though.
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();