I'm learning Java and come across this issue. I have a date string with the given format.
String dbTime = "01/01/1998 12:30:00";
final String DATE_FORMAT = "MM/dd/yyyy HH:mm:ss";
Now I wanted to initialize/create a Date object of UTC timezone.
For this, I have tried below code
SimpleDateFormat sdfAmerica = new SimpleDateFormat(DATE_FORMAT);
TimeZone utcTimeZone = TimeZone.getTimeZone("UTC");
sdfAmerica.setTimeZone(utcTimeZone);
String sDateInAmerica = sdfAmerica.format(date); // Convert to String first
Date dateInAmerica = new Date();
try {
dateInAmerica = formatter.parse(sDateInAmerica); // Create a new Date object
} catch (ParseException e) {
e.printStackTrace();
}
This will convert the time into UTC instead of just creating a date object.
01/02/1998 23:00:00
Now I'm confused as to which is the correct approach to convert the time.
I have time in string format and I have to convert it into different formats mainly UTC to PST or PST to UTC.
After some research, I found this tutorial but was unable to get the expected output.
The java.util.Date class is not optimal to start with. While it looks like a full date from the outside, it actually only represents a timestamp without storing actual timezone information.
On Java 8 and later I'd suggest to stick with the better designed java.time.* classes.
String dbTime = "01/01/1998 12:30:00";
String DATE_FORMAT = "MM/dd/yyyy HH:mm:ss";
// parsed date time without timezone information
LocalDateTime localDateTime = LocalDateTime.parse(dbTime, DateTimeFormatter.ofPattern(DATE_FORMAT));
// local date time at your system's default time zone
ZonedDateTime systemZoneDateTime = localDateTime.atZone(ZoneId.systemDefault());
// value converted to other timezone while keeping the point in time
ZonedDateTime utcDateTime = systemZoneDateTime.withZoneSameInstant(ZoneId.of("UTC"));
// timestamp of the original value represented in UTC
Instant utcTimestamp = systemZoneDateTime.toInstant();
System.out.println(utcDateTime);
System.out.println(utcTimestamp);
As you can see from the names alone there are different classes for different use-cases of dates.
java.time.LocalDateTime for example only represents a date and time without a specific timezone context and therefore can be used to parse your string value directly.
To convert timezones, you first have to convert into the a ZonedDateTime, which accepts date, time and timezone. I've intialized the sample on "systemDefault", as on most smaller apps you can use the JVM and OS'es default value to assume the current timezone.
You could also use ZoneId.of("America/Los_Angeles") directly if you want to make sure the value is interpreted as pacific time.
This value can be converted into another ZonedDateTime in another timezone, e.g. UTC.
For UTC especially you could also use the Instant class, which represents only a UTC timestamp and can also be used as a basis for most other types
I want to convert date and time to user requested timezone. date and time is in GMT format. i tried got the solution but the final string contains GMT String in resultant date like (2019-09-18T01:44:35GMT-04:00). i don't want GMT String in the resultant output.
public static String cnvtGMTtoUserReqTZ(String date, String format, String timeZone) {
// null check
if (date == null)
return null;
// create SimpleDateFormat object with input format
SimpleDateFormat sdf = new SimpleDateFormat(format);
// set timezone to SimpleDateFormat
sdf.setTimeZone(TimeZone.getTimeZone(timeZone));
try {
// converting date from String type to Date type
Date _date = sdf.parse(date);
// return Date in required format with timezone as String
return sdf.format(_date);
} catch (ParseException e) {
//log.info("Exception in cnvtGMTtoUserReqTime ::: " + e);
}
return null;
}
Actual Output : 2019-09-18T01:44:35GMT-04:00
Expected Output: 2019-09-18T01:44:35-04:00
Proces datetime objects, not strings
Your question is put in the wrong way, which is most likely due to a design flaw in your program. You should not handle date and time as strings in your program. Always keep date and time in proper datetime objects such as Instant, OffsetDateTime and ZonedDateTime. The mentioned classes are from java.time, the modern Java date and time API, which is the best we have for keeping and processing datetime data.
So your question may for example become: How to convert a moment in time to user requested timezone? A moment in time is represented by an Instant object. And the answer to the question is:
ZoneId userRequestedTimeZone = ZoneId.of("America/New_York");
Instant moment = Instant.parse("2019-09-18T05:44:35Z");
ZonedDateTime userDateTime = moment.atZone(userRequestedTimeZone);
System.out.println(userDateTime);
Please substitute your user’s desired time zone where I put America/New_York. Always give time zone in this format (region/city). Output from the snippet as it stands is:
2019-09-18T01:44:35-04:00[America/New_York]
Assuming that you don’t want the [America/New_York] part of the output, format the datetime to the string that you want:
String dateTimeWithNoZoneId = userDateTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
System.out.println(dateTimeWithNoZoneId);
2019-09-18T01:44:35-04:00
The latter output is in ISO 8601 format. This format is good for serialization, that is, if you need to convert the datetime to a machine readable textual format, for example for persistence or exchange with other systems. While also human readable, it’s not what your user prefers to see. And as I said, it’s certainly not what you should be handling and processing inside your program.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Wikipedia article: ISO 8601
Use these formats:
fromFormat = "yyyy-mm-dd'T'HH:mm:sszXXX"
toFormat = "yyyy-mm-dd'T'HH:mm:ssXXX"
For more details see examples listed here
I'm a bit puzzled regarding the Java timestamp formatting functions as they seem to change the timestamp by a few minutes.
My Pivotal CloudFoundry app writes a new entry into a PostgreSQL 9.4.5 database. Doing so, I let PostgreSQL generate and store a timestamp for that entry (within the SQL statement I use 'now()' for that particular column).
All that works fine so far. Problems arise when I read the entry with my app because I have to format the timestamp to ISO 8601 (company policy) and this seems to change the timestamp. I've used this code:
public String parseTimestamp(String oldDate) {
System.out.println("String oldDate: " + oldDate);
TimeZone timeZone = TimeZone.getTimeZone("Europe/Berlin");
SimpleDateFormat oldDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSS");
SimpleDateFormat newDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
newDateFormat.setTimeZone(timeZone);
Date tempDate = null;
try {
tempDate = oldDateFormat.parse(oldDate);
} catch (ParseException e) {
//TODO
}
System.out.println("tempDate before format(): " + tempDate);
String newDate = newDateFormat.format(tempDate);
System.out.println("tempDate after format(): " + tempDate);
System.out.println("String newDate: " + newDate);
return newDate;
}
Output:
String oldDate: 2018-06-18 13:07:27.624828+02
tempDate before format(): Mon Jun 18 13:17:51 CEST 2018
tempDate after format(): Mon Jun 18 13:17:51 CEST 2018
String newDate: 2018-06-18T13:17:51Z
As you can see, the timestamp changed from 13:07 to 13:17. All the other queried entries also show a difference, varying between ~2 to 10 minutes. However, when I re-run the query, each difference stays the same. OPs told me that all involved systems have synchronised time and I'm not sure if system time should play a role here at all.
Does someone know what is going on?
First, I was really puzzled and thought, you might have found some kind of bug in SimpleDateFormat. Your code can be reduced to the following lines to show the same behaviour:
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSS");
String str = "2018-06-18 13:07:27.624828+02";
System.out.println(str);
System.out.println(format.parse(str));
Result:
2018-06-18 13:07:27.624828+02
Mon Jun 18 13:17:51 CEST 2018
The parsed Date object got ten additional minutes (and some seconds).
Taking a closer look reveals the problem - it is NOT a bug in JDK:
Your millisecond-part is 624828 - these are six(!) digits (not three). 624828 milliseconds are about 624 seconds, which are 10 minutes and 24 seconds.
The SimpleDateFormat correctly summed up this additional seconds and minutes.
What's wrong too: Your date format contains five digits for the milliseconds part.
Solution:
The (new) Java Time API can handle larger fractions of a second - and also convert easily between different time zones:
String str = "2018-06-18 13:07:27.624828+02";
DateTimeFormatter pattern = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSx");
OffsetDateTime date = OffsetDateTime.parse(str, pattern);
System.out.println(date);
System.out.println(date.withOffsetSameInstant(ZoneOffset.UTC));
Result:
2018-06-18T13:07:27.624828+02:00
2018-06-18T11:07:27.624828Z
Your input date contains micro seconds but SimpleDateFormat supports only milli seconds (up to 3 digits). When parsing the value with the flag lenient enabled the complete 6 digits are evaluated as micro seconds. So at the end you have 624 seconds that are added to the date additionally.
You can try to use the SSSSSS pattern with DateTimeFormatter that has been introduced in Java 8.
But you may have further issues. Your code parses the input date without the time zone information +02 that is included at the end. You should add one letter X for the ISO8601 time zone format. When formatting the date for outputting you use letter 'Z' that indicates time zone UTC. But you set time zone CET (Berlin). So the output date is not in UTC. The correct ouput in UTC for the specified input date is:
2018-06-18 11:07:27Z
I bumped into this issue today. I have set my clock to UTC-6.00 (Central America) time zone. I am converting the Date "06/01/2015::12:00:00 AM" ("MM/dd/yyyy::hh:mm:ss a" format) to a java Date object. And then I am reconverting the date object to String. There is a slight twist in how I am doing this though. I am listing the re conversion steps below -
Calculate UTC offset from current time zone. (-21600000)
Get all available timezone ids for this offset. (All have same offset)
Select the first time zone id. (Will have same offset)
Set this as the timezone.
Convert the date to string format using Java's Simple Date Format.
I see that the time now rendered is "06/01/2015::01:00:00 AM"
My questions :
Since the timezone offset is same during the creation and during conversion I expect the same time to be shown. But what I see is different. Why is it so?
Imagine the re conversion to be happening in the server and the creation to be happening in the client. I need to render back the same date and time to the client. How do I do this?
Please help! Any help is much appreciated.
EDIT : Following is the code. Note that I have set my current timezone to Central America.
public class TimeTest {
public static void main (String args[]) {
SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy::hh:mm:ss a");
String dateInString = "01/06/2015::12:00:00 AM";
try {
Date date = formatter.parse(dateInString);
System.out.println("Before conversion --> " + formatter.format(date));
System.out.println("After conversion --> " + convertDateValueIntoString(date));
} catch (ParseException e) {
e.printStackTrace();
}
}
private static String convertDateValueIntoString(Date dateValue){
SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy::hh:mm:ss a");
String date;
int offset = TimeZone.getDefault().getRawOffset();
if (offset == 0) {
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
date = dateFormat.format(dateValue);
} else {
String TZ[] = TimeZone.getAvailableIDs(offset);
String timeZone = TZ[0];
if (timeZone == null) {
date = dateFormat.format(dateValue);
} else {
TimeZone tz = TimeZone.getTimeZone(timeZone);
dateFormat.setTimeZone(tz);
date = dateFormat.format(dateValue);
}
}
return date;
}
}
Why are the times different:
The difference appears to be in the handling of daylight savings time. Playing around with setting my machine to different time zones and printing the TimeZone toString() I ended up with:
Initial: sun.util.calendar.ZoneInfo[id="America/Tegucigalpa",offset=-21600000,dstSavings=0,useDaylight=false,transitions=9,lastRule=null]
Result: sun.util.calendar.ZoneInfo[id="America/Bahia_Banderas",offset=-21600000,dstSavings=3600000,useDaylight=true,...
Note that these two TimeZones have the same offset, but one uses daylight savings time and the other does not. The offset is all your code is looking at to find an appropriate TimeZone but the date formatting also uses the daylight savings offset.
How do I handle this:
The way every project I've been on that used times did it was to have all internal representation of time be in UTC (or a similar concept). I would have your client convert the time to UTC on input (before sending it to the server), have all server storage use UTC, then when times go back to the client have the client format to the default TimeZone only for output to the user.
That way all your internal times are consistent and all your displayed times are localized for the individual instance of the client, so a user in America/Tegucigalpa may get the time as 12:00 but the user in America/Bahia_Banderas would see 1:00. Both are correct for the users those times would be displayed to.
The Answer by 1337joe is correct. I'll add a few thoughts.
This Question has much confusion floating around.
Time Zone = Offset + Rules/Anomalies/Adjustments
First, a time zone is more than an offset from UTC. A time zone is an offset plus a set of past, present, and future rules about Daylight Saving Time and other anomalies & adjustments.
So whenever possible, use a named time zone rather than a mere offset. And certainly do not mix usage of offset-only with usage of time zones and expect sensible results. That seems to be the core problem in this Question.
So, dig deeper to discover the original intent of the programmers who devised your existing stored data. I suspect they did indeed have a particular time zone in mind rather than a mere offset.
Use Proper Time Zone Names
There is no such time zone as "Central America".
As 1337Joe points out, offsets and time zones vary around Central America. For example, America/Managua is six hours behind UTC while America/Panama is five.
By the way, avoid the 3-4 letter codes for time zones such as "EST" as they are neither standardized nor unique. The one exception is UTC of course.
Specify Your Expected/Desired Time Zone
When [a] you know your incoming data represents a particular time zone or offset, albeit implicitly, and [b] you desire a certain time zone to be applied, do not call on the default time zone. That is asking for trouble. The default time zone can vary by host OS setting on machine by machine. And both the host OS settings can be changed at any time by an admin person. Thirdly, the JVM’s current default time zone can be changed at any moment during runtime by a call to TimeZone.setDefault() by any code in any thread in any app in that same JVM.
So, instead of relying on the default time zone, specify the desired time zone.
Use UTC For Logic & Storage
As 1337joe said, your business logic, data storage, data communication, and database should all be in UTC (almost always). Only apply adjustments to local time zones when expected by the user/consumer.
In comments, the author said their project is already saddled with existing stored data implicitly representing a certain time zone or offset.
java.util.Date toString
The toString method on java.util.Date automatically applies the JVM’s current default time zone. This makes working with time zone adjustments tricky. One of many reasons to avoid using the java.util.Date/.Calendar & java.text.SimpleDateFormat classes.
Use Better Date-Time Library
Use either the new java.time package in Java 8 and later (Tutorial), or the Joda-Time library (which inspired java.time).
Joda-Time
Here is some example code in Joda-Time.
According to the author’s comments, the incoming string implicitly represents a date-time value for a certain known time zone. That time zone is not stated, so I'll arbitrarily use Panama time zone. In this first part, we parse a string while specifying the time zone to be used during parsing and assigned to the resulting object.
DateTimeZone zonePanama = DateTimeZone.forID( "America/Panama" );
DateTimeFormatter formatter = DateTimeFormat.forPattern( "dd/MM/yyyy::hh:mm:ss a" );
String input = "06/01/2015::12:00:00 AM";
DateTime dateTimePanama = formatter.withZone( zonePanama ).parseDateTime( input );
System.out.println( "Input as string: " + input + " becomes object: " + dateTimePanama + " with time zone: " + dateTimePanama.getZone() );
Now let's adjust to UTC. Here this is for demonstration. In real code you would generally do any further work using this UTC value.
DateTime dateTimeUtc = dateTimePanama.withZone( DateTimeZone.UTC );
System.out.println( "dateTimeUtc: " + dateTimeUtc );
For output, our user/consumer expects a String representation in the same Panama time zone and in the same format as our input.
String output = formatter.print( dateTimeUtc.withZone( zonePanama ) );
System.out.println( "Output in special format: " + output );
When run.
Input as string: 06/01/2015::12:00:00 AM becomes object: 2015-01-06T00:00:00.000-05:00 with time zone: America/Panama
dateTimeUtc: 2015-01-06T05:00:00.000Z
Output in special format: 06/01/2015::12:00:00 AM
For question #1: The timezone offset may be the same for different timezones, but the DST may be used or not and this results in a difference.
For question #2:
For the future, you can only be safe about the time when you use UTC. (you can work around, if your time data is "recent" - see below)
For the past, you cannot reliably extract the correct time.
General conversion advice:
I worked on a project dealing with timezones and DST in a JDBC driver. There were problems storing time values and reading them back correctly. I worked /real hard/ trying to get a conversion right, so we could spare the larger works of switching to UTC. There is no correct conversion without UTC. ( /real hard/ : Think of Pulp Fiction where Jules says "I'm trying real hard to be the shepherd." :-) )
Question #2 / Future:
If your client cannot send UTC times (maybe because it is a third party system):
When your server receives time data (non UTC) from the client, which you know to be current within a few minutes (maybe somewhat longer), you could try to use your UTC time and match that to the client's time. Imagine your client sends "2015-06-01 15:45" and you know, it is now "2015-06-01 18:51 UTC", then you may interpret the client's time as "2015-06-01 18:45 UTC". If the time data sent by the client may be older than about an hour, this will fail in some cases.
Or in other words: Say your client records temperature values. If the data sent by the client is not older than a few minutes, you can match that to the UTC time. If your client records temperature of one day and sends you that at the end of the day, you cannot correctly match the time.
Why will you not be able to make a fully(!) correct conversion?
Assume the night when DST changes, so that the clock is changed from 03:00 back to 02:00. You have once 02:30 before the switch and another 02:30 after the switch. The first 02:30 has another UTC time than the second 02:30. So with UTC you are fine. But only with the "client local" 02:30, you will never be sure.
Back to the client data age: If your client sends data not older than a few minutes for 02:30 and then later another for the second 02:30, you can distinguish this on the server. If at 04:00 you get two records for 02:30, you cannot restore UTC any more.
Question #2 / Past:
Can you add a flag in the database so that new times which are transferred as UTC are marked "reliable" and the old values are not?
The output and the source:
The output from running the modified source on my system which has a TZ of "Europe/Berlin". Note that this has DST in use, but the first fetched TZ ("Algiers") has DST not in use.
formatter's TZ is sun.util.calendar.ZoneInfo[id="Europe/Berlin",offset=3600000,dstSavings=3600000,useDaylight=true,transitions=143,lastRule=java.util.SimpleTimeZone[id=Europe/Berlin,offset=3600000,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]]
internal date value = 1433109600000 as UTC = 31/05/2015::10:00:00 PM
Before conversion --> 01/06/2015::12:00:00 AM
Conversion: offset != 0, using TZ sun.util.calendar.ZoneInfo[id="Africa/Algiers",offset=3600000,dstSavings=0,useDaylight=false,transitions=35,lastRule=null]
After conversion --> 31/05/2015::11:00:00 PM
Setting UTC...
formatter's TZ is sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
internal date value = 1433116800000 as UTC = 01/06/2015::12:00:00 AM
Before conversion --> 01/06/2015::12:00:00 AM
Conversion: offset != 0, using TZ sun.util.calendar.ZoneInfo[id="Africa/Algiers",offset=3600000,dstSavings=0,useDaylight=false,transitions=35,lastRule=null]
After conversion --> 01/06/2015::01:00:00 AM
The source code:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
public class TimeTest {
static TimeZone utc = TimeZone.getTimeZone("UTC");
public static void main (String args[]) {
SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy::hh:mm:ss a");
String dateInString = "01/06/2015::12:00:00 AM";
SimpleDateFormat utcformatter = new SimpleDateFormat("dd/MM/yyyy::hh:mm:ss a");
utcformatter.setTimeZone(utc);
try {
Date date = formatter.parse(dateInString);
System.out.println("formatter's TZ is " + formatter.getTimeZone());
System.out.println("internal date value = " + date.getTime() + " as UTC = " + utcformatter.format(date));
System.out.println("Before conversion --> " + formatter.format(date));
System.out.println("After conversion --> " + convertDateValueIntoString(date));
System.out.println("\nSetting UTC...\n");
formatter.setTimeZone(utc);
date = formatter.parse(dateInString);
System.out.println("formatter's TZ is " + formatter.getTimeZone());
System.out.println("internal date value = " + date.getTime() + " as UTC = " + utcformatter.format(date));
System.out.println("Before conversion --> " + formatter.format(date));
System.out.println("After conversion --> " + convertDateValueIntoString(date));
} catch (ParseException e) {
e.printStackTrace();
}
}
private static String convertDateValueIntoString(Date dateValue){
SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy::hh:mm:ss a");
String date;
int offset = TimeZone.getDefault().getRawOffset();
if (offset == 0) {
System.out.println("Conversion: offset == 0 -- setting UTC");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
date = dateFormat.format(dateValue);
} else {
String TZ[] = TimeZone.getAvailableIDs(offset);
String timeZone = TZ[0];
if (timeZone == null) {
System.out.println("Conversion: offset != 0, did not find TZ, tz of dateFormat is " + dateFormat.getTimeZone());
date = dateFormat.format(dateValue);
} else {
TimeZone tz = TimeZone.getTimeZone(timeZone);
System.out.println("Conversion: offset != 0, using TZ " + tz);
dateFormat.setTimeZone(tz);
date = dateFormat.format(dateValue);
}
}
return date;
}
}
In my Android application server will return some UTC date in following format(yyyy-MM-dd HH:mm:ss) 24hours and I need to convert those time into user's TimeZone for example CST, IST.
I did the following code but I do know is it correct or not, please assist me to do the time zone conversion in right way.
I get UTC date as json string and converting into user's time zone format and showing Android side
private static Date utcDate;
private static DateFormat expireFormat = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
try {
expireFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
utcDate = expireFormat.parse("2014-04-01 10:32:00");
System.out.println(TimeZone.getDefault().getID());
expireFormat.setTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getID()));
System.out.println(expireFormat.format(utcDate));
} catch (ParseException e) {
e.printStackTrace();
}
output of the code:
Asia/Calcutta
2014-04-01 16:02:00
The overall approach is OK if a re-formatted String is really what you're trying to get.
There are some issues though
SimpleDateFormat is not a threadsafe class. Setting it to a static field inside a server is a problem!
Same as #1 regarding using a static field to hold the intermediate Date object.
Is "CST" China Standard Time? Central Standard Time (US or Australia)? Cuba Standard Time? Three letter abbreviations TimeZone are bad news in general. Try to use an Olson Name or Alias if at all possible.
Is this the server side or the android? If it's a server, you could probably benefit from the new Java 8 API for DateTime handling.