I have a String with local time:
"2012-12-12T08:26:51+000"
I now need to create a String with GMT time based on the old String. For example, assuming 2 hours difference between local and GTM:
"2012-12-12T10:26:51+000"
I have created a SimpleDateFormat:
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss+SSSS");
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
String time = dateFormat.parse(mCreatedTime).toString();
But the time String is now in different format:
Wed Dec 12 etc
How do I get the output to be in format yyy-MM-dd'T'HH:mm:ss+SSSS but GMT time?
The dateFormat.parse() method returns an instance of Date, and when you call toString() on it the date will be printed in the default locale.
Use dateFormat.format() to get your Date value back to your required format.
As my comments on this question suggest, I believe the original poster of this question is confused and miseducated about date-time work. Nevertheless, I wrote some example code delivering exactly what Pierre asked for, along with my caveat that he is following some very bad practices.
Using Joda-Time 2.3 library and Java 7.
// © 2013 Basil Bourque. This source code may be used freely forever by anyone taking full responsibility for doing so.
// import org.joda.time.*;
// import org.joda.time.format.*;
// CAUTION: The question asked specifically for the format used here.
// But this format incorrectly uses the PLUS SIGN to mean milliseconds rather than offset from UTC/GMT.
// Very bad thing to do. Will create no end of confusion.
// Another bad thing: This code creates strings representing date-times in different time zones without indicating they are in different time zones.
// Time Zone list: http://joda-time.sourceforge.net/timezones.html
// "Atlantic/South_Georgia" is a time zone two hours behind UTC.
DateTimeZone southGeorgiaZone = DateTimeZone.forID( "Atlantic/South_Georgia" );
DateTimeFormatter formatter = DateTimeFormat.forPattern( "yyyy-MM-dd'T'HH:mm:ss+SSS" );
DateTime dateTimeInSouthGeorgia = formatter.withZone( southGeorgiaZone ).parseDateTime( "2012-12-12T08:26:51+000" );
DateTime dateTimeInUtc = dateTimeInSouthGeorgia.toDateTime( DateTimeZone.UTC );
String screwyBadPracticeDateTimeString = formatter.print( dateTimeInUtc );
System.out.println( "2012-12-12T08:26:51+000 in southGeorgiaDateTime: " + dateTimeInSouthGeorgia );
System.out.println( "same, in UTC: " + dateTimeInUtc );
System.out.println( "screwyBadPracticeDateTimeString: " + screwyBadPracticeDateTimeString );
When run…
2012-12-12T08:26:51+000 in southGeorgiaDateTime: 2012-12-12T08:26:51.000-02:00
same, in UTC: 2012-12-12T10:26:51.000Z
screwyBadPracticeDateTimeString: 2012-12-12T10:26:51+000
Related
I am currently stuck with XMLGregorianCalendar formatting problem and would like to seek help from you java gurus. With a function call from other system, I got a data object displayed on web page with "SUBMIT_DATE":1516032000000 and "SUBMIT_TIME":36895000 (both with returned type XMLGregorianCalendar). How can I know the correct human readable date and time in this case?
Thank you for your time and help.
Update after clarification
// We first need to check that the fields we need are defined
if (submitDate.getTimezone() == DatatypeConstants.FIELD_UNDEFINED) {
throw new IllegalStateException("No time zone defined in submit date " + submitDate);
}
if (submitDate.getYear() == DatatypeConstants.FIELD_UNDEFINED
|| submitDate.getMonth() == DatatypeConstants.FIELD_UNDEFINED
|| submitDate.getDay() == DatatypeConstants.FIELD_UNDEFINED) {
throw new IllegalStateException("Date not defined in submit date " + submitDate);
}
if (submitTime.getHour() == DatatypeConstants.FIELD_UNDEFINED
|| submitTime.getMinute() == DatatypeConstants.FIELD_UNDEFINED
|| submitTime.getSecond() == DatatypeConstants.FIELD_UNDEFINED) {
throw new IllegalStateException("Time of day not defined in submit time " + submitTime);
}
if (submitTime.getTimezone() != DatatypeConstants.FIELD_UNDEFINED
&& submitTime.getTimezone() != submitDate.getTimezone()) {
throw new IllegalStateException("Conflicting offsets " + submitDate.getTimezone()
+ " and " + submitTime.getTimezone() + " minutes");
}
// then format into a human readable string
final ZoneId userZone = ZoneId.of("Asia/Taipei");
final Locale userLocale = Locale.forLanguageTag("zh-TW");
DateTimeFormatter localizedFormatter = DateTimeFormatter
.ofLocalizedDateTime(FormatStyle.LONG)
.withLocale(userLocale);
ZonedDateTime dateTime = submitDate.toGregorianCalendar()
.toZonedDateTime()
.with(LocalTime.of(submitTime.getHour(), submitTime.getMinute(), submitTime.getSecond()))
.withZoneSameInstant(userZone);
String humanReadableDateTime = dateTime.format(localizedFormatter);
System.out.println(humanReadableDateTime);
This prints:
2018年1月16日 上午10時14分55秒
I am assuming that submitDate and submitTime are XMLGregorianCalendar objects that you have got from the complex object that you have received from a remote system. I am further assuming that you can require the date to contain a UTC offset. Though the method is called getTimezone, what it really returns is not a time zone, but an offset in minutes from UTC (or GMT). The extensive checks in the four if statements are necessary because XMLGregorianCalendar is very flexible with which fields are defined and which not.
To display the date and time in a format suitable for a user audience, you need to know that audience’s time zone and locale. Once you know those, please fill them in in the above snippet. If you trust the JVM’s settings, you may use ZoneId.systemDefault() and/or Locale.getDefault(Locale.Category.FORMAT) You may also choose between format styles FULL, LONG, MEDIUM and SHORT.
If you don’t receive an offset, you will need to rely on the date and time already being at the user’s offset. On one hand it’s simpler, on the other hand it is more fragile since if the date and time are given at another offset than the user expects, s/he will receive incorrect information, which is worse than receiving no information at all. First check that there is indeed no offset:
if (submitDate.getTimezone() != DatatypeConstants.FIELD_UNDEFINED
|| submitTime.getTimezone() != DatatypeConstants.FIELD_UNDEFINED) {
throw new IllegalStateException("Unexpected offset");
}
Also check that required fields are defined, this is the same as before. Then create a LocalDateTime object and format it:
LocalDateTime dateTime = LocalDateTime.of(
submitDate.getYear(), submitDate.getMonth(), submitDate.getDay(),
submitTime.getHour(), submitTime.getMinute(), submitTime.getSecond());
String humanReadableDateTime = dateTime.format(localizedFormatter);
I got the same result as above.
Original answer
final ZoneId userZone = ZoneId.of("Asia/Taipei");
final Locale userLocale = Locale.forLanguageTag("zh-TW");
ZonedDateTime submitDateTime
= Instant.ofEpochMilli(submitDate + submitTime).atZone(userZone);
DateTimeFormatter localizedFormatter = DateTimeFormatter
.ofLocalizedDateTime(FormatStyle.LONG)
.withLocale(userLocale);
String humanReadableDateTime = submitDateTime.format(localizedFormatter);
System.out.println(humanReadableDateTime);
This prints
2018年1月16日 上午10時14分55秒
To display the date and time in a format suitable for a user audience, you need to know that audience’s time zone and locale. Once you know those, please fill them in in the first two lines of the above snippet. If you trust the computer’s settings, you may use ZoneId.systemDefault() and/or Locale.getDefault(Locale.Category.FORMAT) You may also choose between format styles FULL, LONG, MEDIUM and SHORT. For this purpose I think you can ignore the information that the returned type is XMLGregorianCalendar.
As #user unknown in another answer I am assuming that you can just add the two numeric values. The first almost certainly denotes milliseconds since the epoch, the sum probably too. So why were they passed as two values and not just one? My best guess is that they pass the date separately for any client that just needs the date and not the time of day. The date value falls at midnight in time zones at offset +08:00, this would agree with China, Philippines, Malaysia and a dozen other time zones.
If instead of the numbers you have got two XMLGregorianCalendar objects, getting the date and time is a different story, but you may still use the same way of formatting them.
final GregorianCalendar calendar = new GregorianCalendar();
calendar.setTimeInMillis(date);
return DatatypeFactory.newInstance().newXMLGregorianCalendar(
calendar);
This should work..
Pass your millisecs
Your inputs look like they're just the date without time in milliseconds and the time without date in milliseconds.
If you divide both values by 1000:
date -d #1516032000
Mo 15. Jan 17:00:00 CET 2018
date -d #36895
Do 1. Jan 11:14:55 CET 1970
Well - but why 17:00:00? Maybe a time zone issue.
Here is the aggregate:
date -d #$((1516032000+36895))
Di 16. Jan 03:14:55 CET 2018
The various date/time formats for Java have methods, which take a long parameter for seconds since epoch (1.1.1970) to set the time.
I have got a String of Date which has the nonstandard GMT format, like "2016-08-31T02:04:58.893GMT".
Now I need to transfer it to Local time format, like "2016-08-31 10:04:58". By the way, I am in China, there's 8 hours between the Local time and GMT time.
Oh, I use Java. Thank everyone.
You parse the string using DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSz").
Example code, showing intermediate results:
String input = "2016-08-31T02:04:58.893GMT";
DateTimeFormatter fmt1 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSz");
ZonedDateTime zdtGMT = ZonedDateTime.parse(input, fmt1);
System.out.println(zdtGMT); // prints 2016-08-31T02:04:58.893Z[GMT]
ZonedDateTime zdtChina = zdtGMT.withZoneSameInstant(ZoneId.of("Asia/Shanghai"));
System.out.println(zdtChina); // prints 2016-08-31T10:04:58.893+08:00[Asia/Shanghai]
LocalDateTime ldt = zdtChina.toLocalDateTime();
System.out.println(ldt); // prints 2016-08-31T10:04:58.893
DateTimeFormatter fmt2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println(ldt.format(fmt2)); // prints 2016-08-31 10:04:58
You'd better transform the non-standard GMT format to a standard time format and then use some proper built-in function to add or subtract a difference. You may use another function to transform the standard time format to your designated format.
This will be great because you don't have to consider the complicated cases, for example, "2016-12-31 23:00" + 8hrs.
I use SimpleDateFormat to parse strings to Date objects and I wonder why the results are not what I expect.
For example:
DateFormat yyyyMMdd = new SimpleDateFormat("yyyyMMdd");
Date date = yyyyMMdd.parse("20100725");
System.out.println(date);
works as expected and outputs
Sun Jul 25 00:00:00 CEST 2010
But
Date date = yyyyMMdd.parse("2010-07-25");
System.out.println(date);
also works and outputs
Mon Dec 07 00:00:00 CET 2009
I expected a ParseException, but it seems that SimpleDateFormat interpretes the month part -07 and the day part -25 as a negative number. First I couldn't figure out how it comes to 7th of december. So I tried another value:
Date date = yyyyMMdd.parse("2010-7-25");
System.out.println(date);
and it outpus
Sun Apr 05 00:00:00 CEST 2009
So it seems that it somehow subtracts 7 month from the year 2010 which whould be 1th of may, and 25 days so the result is 5th of april 2009.
Image that you use the pattern yyyyMMdd in an service implementation and some client accidentially sends the date as yyyy-MM-dd. You will not get an exception. Instead you will get totally different dates. I guess this is not what you expect.
E.g.
String clientData = "2010-05-23";
DateFormat yyyyMMdd = new SimpleDateFormat("yyyyMMdd");
Date parsedDate = yyyyMMdd.parse(clientData);
System.out.println("Client : " + clientData);
System.out.println("Service : " + yyyyMMdd.format(parsedDate));
Do I miss something?
How do I prevent SimpleDateFormat to parse 'wrong' dates?
Sure I can use a regular expression to check first, but is there a better way?
Use SimpleDateFormat.setLenient(false); to get an exception. Otherwise it will try to parse the input as best as it can, which is usually wrong.
For some reason they decided that leniency should be true by default, but that is hardly a surprise.
Specify whether or not date/time parsing is to be lenient. With
lenient parsing, the parser may use heuristics to interpret inputs
that do not precisely match this object's format. With strict
parsing, inputs must match this object's format.
The accepted Answer by Cayman is correct: leniency in parsing by default is the problem.
java.time
You are using troublesome old date-time classes now supplanted by the java.time classes.
No such leniency-by-default problem in java.time. If the input does not strictly match the formatting pattern, a DateTimeParseException is thrown.
The LocalDate class represents a date-only value without time-of-day and without time zone.
ISO 8601 format
For standard ISO 8601 formatted inputs of YYYY-MM-DD, simply call parse directly.
String input = "2010-05-23";
try {
LocalDate ld = LocalDate.parse( input ); // Expects standard ISO 8601 input format.
} catch ( DateTimeParseException e ) {
…
}
“Basic” ISO 8601 format
The ISO 8601 standard allows for “basic” formats that minimize the use of separators. Not that I recommend these variations, but they exist.
Currently java.time predefines only a single one of these “basic” variations, DateTimeFormatter.BASIC_ISO_DATE.
String input = "20100725";
try {
LocalDate ld = LocalDate.parse( input , DateTimeFormatter.BASIC_ISO_DATE );
} catch ( DateTimeParseException e ) {
…
}
Custom format
For other formats, specify a formatter.
String input = "2010/07/25";
try {
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu/MM/dd" );
LocalDate ld = LocalDate.parse( input , f ); // Custom format.
} catch ( DateTimeParseException e ) {
…
}
Localized format
Or let java.time determine the localized format.
String input = … ;
try {
Locale l = Locale.CANADA_FRENCH ;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDate( FormatStyle.MEDIUM ).withLocale( l );
LocalDate ld = LocalDate.parse( input , f ); // Localized format.
} catch ( DateTimeParseException e ) {
…
}
SimpleDateFormat.setLenient(false);
Is what needs to be done, or the input will be tried to be parsed well, and as you know, that doesn't always work. With the function above, the compiler will be strict about the format.
First, if you want to parse String "2010-05-23" your mask should be "yyyy-MM-dd" and not "yyyyMMdd". Second SimpleDateFormat has serious problems as it is not Thread safe. If you use java 8 then use learn and use new package "java.time". If you use any java earlier then version 8 then use some other frameworks for parsing date. One of the most popular is Joda time. Works much better.
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;
}
}
I ran into a strange issue. Here is a snippet of code that describes it:
DateTimeZone dtz = DateTimeZone.forOffsetHours(0);
DateTime dt = new DateTime(dtz);
System.out.println(dt);
System.out.println(dt.toDate());
the output is:
2012-02-29T17:24:39.055Z
Wed Feb 29 19:24:39 EET 2012
I'm located UTC+2, but this action is supposed to create a java.util.Date object which is initialized for UTC time. What am I missing?
Date doesn't know about a time zone at all - it only represents an instant in time (like Joda Time's Instant type). It's just a number of milliseconds since the Unix epoch. When you call Date.toString(), it always uses the system local time zone for converting that into a readable text form.
So there's nothing wrong here - just an expectations failure over either the meaning of java.util.Date or its toString() behaviour, or both.
(As an aside, prefer DateTimeZone.UTC over creating your own.)
To get a JDK Date that matches Joda's DateTimeconvert to LocalDateTimefirst.
As explained in the other answers, the time in milliseconds does not change depending on the timezone:
DateTime local = DateTime.now()
Date localJDK = local.toDate()
assert localJDK.getTime() == local.toInstant().getMillis()
DateTime differentTimeZone = DateTime.now(DateTimeZone.forID('America/Chicago'))
Date localJDK2 = differentTimeZone.toDate()
assert differentTimeZone.toInstant().getMillis() == localJDK2.getTime()
assert localJDK.getTime() == localJDK2.getTime()
Converting a LocalDateTime to Date will change that:
Date differentTimeZoneJDK = differentTimeZone.toLocalDateTime().toDate()
assert localJDK.getTime() != differentTimeZoneJDK.getTime()
The behaviour you want is this:
Date jdkDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dt.toString("yyyy-MM-dd HH:mm:ss"));
Like Jon noted, JDK date is time zone agnostic. Hope this helps someone.