I recently moved to Java 8 to, hopefully, deal with local and zoned times more easily.
However, I'm facing an, in my opinion, simple problem when parsing a simple date.
public static ZonedDateTime convertirAFecha(String fecha) throws Exception {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(
ConstantesFechas.FORMATO_DIA).withZone(
obtenerZonaHorariaServidor());
ZonedDateTime resultado = ZonedDateTime.parse(fecha, formatter);
return resultado;
}
In my case:
fecha is '15/06/2014'
ConstantesFechas.FORMATO_DIA is 'dd/MM/yyyy'
obtenerZonaHorariaServidor returns ZoneId.systemDefault()
So, this is a simple example. However, the parse throws this exception:
java.time.format.DateTimeParseException: Text '15/06/2014' could not
be parsed: Unable to obtain ZonedDateTime from TemporalAccessor:
{},ISO resolved to 2014-06-15 of type java.time.format.Parsed
Any tips? I've been trying different combinations of parsing and using TemporalAccesor, but without any luck so far.
This does not work because your input (and your Formatter) do not have time zone information. A simple way is to parse your date as a LocalDate first (without time or time zone information) then create a ZonedDateTime:
public static ZonedDateTime convertirAFecha(String fecha) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
LocalDate date = LocalDate.parse(fecha, formatter);
ZonedDateTime resultado = date.atStartOfDay(ZoneId.systemDefault());
return resultado;
}
This is a bug, see JDK-bug-log. According to that information the problem was solved for Java 9 and Java 8u20. Try to download the latest Java 8 - version. Today on 2014-05-12: There is an early access release 8u20 available.
UPDATE:
Personally I think, since you only have and expect "dd/MM/yyyy" as pattern you should use LocalDate as your primary type as #assylias has already proposed. Regarding your context, it is almost sure a design failure to use ZonedDateTime. What do you want to do with objects of this type? I can only think of specialized timezone calculations as use-case. And you cannot even directly store these ZonedDateTime-objects in a database, so this type is far less useful than many people believe.
What I described as your use-case problem is indeed a new aspect introduced with Java-8 compared with the old GregorianCalendar-class (which is an all-in-one-type). Users have to start thinking about choosing the proper temporal type for their problems and use-cases.
In simple words, the line
ZonedDateTime.parse('2014-04-23', DateTimeFormatter.ISO_OFFSET_DATE_TIME)
throws an exception:
Text '2014-04-23' could not be parsed at index 10
java.time.format.DateTimeParseException: Text '2014-04-23' could not be parsed at index 10
It looks like a bug for me.
I used this workaround:
String dateAsStr = '2014-04-23';
if (dateAsStr.length() == 10) {
dateAsStr += 'T00:00:00';
}
ZonedDateTime.parse(dateAsStr, DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(ZoneId.systemDefault()));
If coming from Google:
Instead of doing:
ZonedDateTime.from(new Date().toInstant());
Try this:
ZonedDateTime.ofInstant(new Date(), ZoneId.of("UTC"));
Just an example conversions, I believe some folks will get the exception below
(java.time.DateTimeException: Unable to obtain LocalDateTime from TemporalAccessor: 2014-10-24T18:22:09.800Z of type java.time.Instant)
if they try
LocalDateTime localDateTime = LocalDateTime.from(new Date().toInstant());
to resolve the issue, please pass in Zone -
LocalDateTime localDateTime = LocalDateTime.from(new Date()
.toInstant().atZone(ZoneId.of("UTC")));
Related
From Server I get following values:
epochMillis=1556532279322
iso8601=2019-04-29T10:04:39.322Z
When I do serverTimeDateFormat.parse(iso8601), I get as a result Mon Apr 29 10:04:39 GMT+02:00 2019
and for serverTimeDateFormat.parse(iso8601).time, the result is 1556525079322, which is different from what I get from the server (2 hours behind from UNIX time), while I am in timeZone + 2 hours.
When I format it back with serverTimeDatFormat.format(1556525079322), the result is 2019-04-29T10:04:39.322Z
I understand that SimpleDateFormat is using local timezone, but why is the result 2 hours behind and how can I parse the Date without taking into account timezone? I don't understand the logic of all this.
My code:
private val serverTimeDateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",Locale.ENGLISH)
val iso8601 = "2019-04-29T10:04:39.322Z"
val epochMillis = 1556532279322
serverTimeDateFormat.parse(iso8601).time
The problem lies with the pattern for your SimpleDateFormat. At the end, you have 'Z', which indicates there should be a literal "Z" in the date string to be parsed. However, the "Z" at the end of the date has a special meaning, namely it signifies the UTC timezone. Hence, you should parse it as a timezone designator so that the correct date value will be obtained. You can do this with the pattern XXX (See JavaDocs).
private val serverTimeDateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX",Locale.ENGLISH)
val iso8601 = "2019-04-29T10:04:39.322Z"
print( serverTimeDateFormat.parse(iso8601).time ) // 1556532279322
Runnable example on pl.kotl.in
Addendum: While the above code should work for you, if at all possible, you should consider adding ThreeTen Android Backport to your project. This will give you access to the newer time classes added by JSR310 to Java/Kotlin (Also available by default in Android API >=26). The classes have generally easier API, and use ISO8601 by default, so you wouldn't need any formatter at all:
print( ZonedDateTime.parse(iso8601).toInstant().toEpochMilli() )
Avoid legacy date-time classes
You are using terrible date-time classes that were supplanted years ago by the modern java.time classes defined in JSR 310.
Date::toString lies
why is the result 2 hours behind
It is not actually two hours behind.
The problem is that while a java.util.Date represents a moment in UTC, its toString method dynamically applies the JVM’s current default time zone while generating the text representing the value of the date-time object. While well-intentioned, this anti-feature confusingly creates the illusion of the Date object having that time zone.
In other words, Date::toString lies. One of many poor design decisions found in these legacy classes. And one of many reasons to never use these legacy classes.
java.time
Instant
Parse your count of milliseconds since the epoch reference of first moment of 1970 in UTC as a Instant.
Instant instant = Instant.ofEpochMilli( 1556532279322 );
Your other input, a standard ISO 8601 string, can also be parsed as an instant.
Instant instant = Instant.parse( "2019-04-29T10:04:39.322Z" ) ;
ZonedDateTime
To see the same moment through the wall-clock time used by the people of a particular region (a time zone), apply a ZoneId to get a ZonedDateTime object.
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = instant.atZone( z ) ) ;
Both the instant and zdt objects represent the same simultaneous moment. Two ways of reading the same moment, as two people conversing on the phone in Iceland and Québec would each see a different time on the clock on the wall while glancing simultaneously.
So time formatting and adjusting has always been my biggest nemesis in programing and I'm having some issues in Android/Java that I can't figure out. I get a timestamp from a server that is formatted in UTC (here's an example 2016-06-17T18:30:00-07:00. Now this time needs to get formatted to the users local time (so for a user in PST it should show as 11:30AM) but so far whatever I try I either get 1AM or 6:30PM (so I know I'm doing something wrong I just don't know what). Here's what I've been trying to do
public static DateTime convertISOStringToDate(String inputString) {
//setup the ISO Date Formatter with GMT/UTC format
DateTimeFormatter formatter = ISODateTimeFormat.dateTimeParser()
.withLocale(Locale.US)
.withZone(DateTimeZone.forOffsetHours(0));
DateTime dateTime = formatter.parseDateTime(inputString);
//now convert the datetime object to a local date time object
DateTimeFormatter localFormatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")
.withZone(DateTimeZone.getDefault());
String localString = localFormatter.print(dateTime);
DateTime localDateTime = localFormatter.parseDateTime(localString);
return localDateTime;
So at this point I'm getting 1:30AM, so I know I'm messing it up somewhere in the conversion process but I can't figure it out. I've been trying to google around but so far haven't found much that use the ISODateTimeFormat parser so they don't work either when I try them.
You seem to have a basic mis-understanding of how dates are represented.
A date (in almost every known programming language / library) is represented internally as an offset from a specific 'origin time', known as the 'Epoch'.
In java.util.Date as well as joda dates, the internal representation is the number of milliseconds since midnight, Jan 1, 1980, UTC.
As such, a date does not have a timezone. You only introduce a timezone when you format a date (turn it into a String representation of the date).
You have made the common mistake of parsing a String into a date object, serializing (printing) it back out with a different timezone than the the original string indicated, and then parsing back into a date again, expecting something to have changed. If you do that correctly, you will get back exactly the same date that you started with.
In your case, the "localString" that you get shows the correct time in the local timezone. I'm in EDT, which is UTC-4:00, and I correctly get 2016-06-17 21:30:00 as the result.
As I said, parsing that back into a DateTime, and then looking at it is useless, because:
You'll get the same DateTime back that you started with
Your IDE (or whatever you're using to inspect the DateTime) probably isn't showing what you expect.
You should re-evaluate what you're doing here, and whether you really need to "convert" the DateTime, or to just parse it, and really understand how date formatting works with respect to timezones.
I'm writing unit tests for Android code using the Robolectric gradle test runner. The code I'm testing happens to be hitting date formatting methods that use the following format:
yyyy-MM-dd'T'HH:mm:ss.SSSZ
We store Unix Time milliseconds as strings in that format, and just before sending it through the formatter to convert it back into a millisecond offset, we replace any instances of "Z" in the string with "+00:00". the call ends up looking like this:
DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
format.setTimeZone(TimeZone.getTimeZone("UTC"));
format.parse(validDateString.replace("Z", "+00:00));
This operation works fine in production code, but attempting to write unit tests has revealed previously unseen ParseExceptions. I first assumed it was because of a formatting issue with the date string I was injecting, but ParseExceptions are being thrown on strings saved from a date successfully parsed in prod code.
What could possibly be causing this radical difference in behavior?
Things I've tried already:
-Checked date formatting
-This DateFormat is actually a global static variable. I'm aware that they're not thread-safe, but inlining all static references with new instances yields the same results.
UPDATE:
Partial stack trace
java.text.ParseException: Unparseable date: "2016-02-20T19:47:33.262+00:00"
at java.text.DateFormat.parse(DateFormat.java:357)
...nothing else useful
Additionally, I should mention that we use a complementary method that stores milliseconds as a String:
DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
Date date = new Date(unixTime);
String validDateString = format.format(date);
return validDateString.replace("+0000", "Z");
Note that we're replacing 4 0's without a colon, whereas the method that fails appends 00:00. That said, the not-quite-complementary operations work fine in production, but fail in unit tests.
Try setting your string for unit test directly:
String validDateString = "2016-02-20T19:47:33.262+0000"; // remove the colon
I had the same issue when running Robolectric. As per this issue on GitHub, replacing +00:00 with +0000 fixed the issue for me.
I encounter strange behaviour with Jodatime and Android. I want to parse string:
2014-05-19T18:13:00.000+02:00
to DateTime, and get year, month, hours to int. I started with some test on IntelliJ Studio, and I done something like that:
String date = "2014-05-19T18:13:00.000+02:00";
DateTime dateTime = new DateTime(date);
System.out.println(dateTime.toString());
System.out.println(dateTime.getYear());
System.out.println(dateTime.getMonthOfYear());
System.out.println(dateTime.getDayOfMonth());
System.out.println(dateTime.getHourOfDay());
System.out.println(dateTime.getMinuteOfHour());
System.out.println(dateTime.getMillis());
Which gave me correct answers:
2014-05-19T18:13:00.000+02:00
2014
5
19
18
13
1400515980000
Now, when I changed IDE to Android Studio, and do the same:
String dateT = "2014-05-19T18:13:00.000+02:00";
DateTime dateTime = new DateTime(dateT);
Lo.g(dateTime.getHourOfDay() + "");
Lo.g(dateTime.toString());
my results are:
16
2014-05-19T16:13:00.000Z
For some reason DateTime on Android Studio / Android not take into account the timezone which is +2:00.
I can not find solution for this. Also there is no simple method "addTimeZone" in Joda.
How to display correct time with DataTime? I tried LocalDateTime, construct DateTime with DateTimeZone.getDefault() (which gaves me UTF...)
Since you said that you use the same Joda-Time version on both platforms and regarding the fact that Joda-Time has its own timezone repository independent from system timezone data, there is probably only one explanation left why you observe different behaviour: Different input either explicit or implicit. Let's go into details:
Well, you say, obviously there is the same input given the same input string:
"2014-05-19T18:13:00.000+02:00"
So we have the same (explicit) input. But wait, there is another thing: implicit default settings which can also be considered as kind of input in an abstract way. You use the constructor DateTime(Object). This constructor first delegates to super constructor of class BaseDateTime as you can see in the source code.
public DateTime(Object instant) {
super(instant, (Chronology) null);
}
The javadoc of this super-constructor says:
"Constructs an instance from an Object that represents a datetime,
using the specified chronology. If the chronology is null, ISO in the
default time zone is used.
The recognised object types are defined in ConverterManager and
include ReadableInstant, String, Calendar and Date."
So finally we see that Joda-Time uses the default timezone. This is really the only possibility for different behaviour I can see by studying the source code and the documentation. All other things are equal: Same library version and same explicit string input and same test scenario.
Conclusion: You have different default timezones on your platforms. Note that you get the same instant on both platforms however, just represented with different local timestamps and offsets due to different internal timezone/offset setting inside the DateTime-object.
Update: I have tested the zone overriding behaviour with this code:
String date = "2014-05-19T19:13:00.000+03:00"; // same instant as yours (just with offset +03)
DateTime dateTime = new DateTime(date);
System.out.println(dateTime.toString());
// output: 2014-05-19T18:13:00.000+02:00 (in my default timezone Europe/Berlin)
So the default timezone has precedence over any offset in string input. If you instead want to use the parsed offset then look at using the DateTimeFormatter and its method withOffsetParsed().
I m developing android application. I need to convert datetime into date. I want to convert '25-07-2013 11:44AM' (datetime) into '25-07-2013' (date).
I am trying this function to convert SELECT date('25-07-2013 11:44AM'), but it was not working.Please suggest some solution for this problem.
According to this page, it does not seem that am/pm times are supported in date SQLite function (this should be noted as h or K according to the date format specification of Java at least; also it is explicitly mentioned %H hour: 00-24). Maybe experiment if using 24 hour clock will not trigger the issue.
I am not sure if you are search for Java code or SQL code , but if it is Java code then solution could be this :
String date = "25-07-2014 11:44AM";
SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy");
String newDate = dateFormat.format(dateFormat.parse(date));
System.out.println(newDate);
If in case your are looking for SQL code let me know I will share that as well.