Time Zone off by 10 hours in Joda-Time - java

I need to parse a string into a Joda-Time DateTime (or java.util.Date.) This is an example of the string I'm getting:
eventDateStr = 2013-02-07T16:05:54-0800
The code I'm using:
DateTimeFormatter presentation = DateTimeFormat.forPattern("yyyy-MM-dd kk:mm:ssZ");
DateTime eveDate = presentation.parseDateTime(eventDateStr);
The above throws this exception:
Invalid format: "2013-02-07T16:05:54-0800" is malformed at "T04:03:20-0800"
So I'm parsing the 'T' out of there:
eventDateStr = eventDateStr.indexOf("T") > 0 ? eventDateStr.replace("T", " ") : eventDateStr;
and trying again. This time no exception but the time zone is off:
2013-02-08T02:05:54.000+02:00
Note the difference: in the original string the timezone is '-0800' and here it's '+02:00'. This in turn changes the entire date, which is now a day later.
What am I doing wrong?

Call the method withOffsetParsed on the DateTimeFormatter object to get a DateTimeFormatter that keeps the time zone parsed from the String, instead of offsetting it to the local time zone.
Regarding why T is shown when you print out the DateTime, Basil Bourque has a nice explanation in the comment below.
Regarding T, a DateTime is not a string nor does it contain a string. A DateTimeFormatter instance can generate a string representation of the date, time, and time zone information stored within a DateTime. When you invoke the toString method on a DateTime (either implicitly or explicitly), a built-in formatter based on ISO 8601 is used automatically. That formatter uses YYYY-MM-DDTHH:MM:SS.ssssss+00:00 format.

Related

I have to get OffsetDateTime from a string

String is in this format - "2021-07-13 05:22:18.712"
I tried using this code to parse it.
OffsetDateTime parsedDateTime = OffsetDateTime.parse("2021-07-13 05:22:18.712");
But i keep getting this error -
org.threeten.bp.format.DateTimeParseException: Text '2021-07-13 05:22:18.712' could not be parsed at index 10.
How do i make it work? Any suggestions will be helpful. Thanks
You need to decide on a time zone (or at least on an offset, but time zone is usually the correct means). And then you need to use a formatter that defines the format that you are trying to parse:
private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral(' ')
.append(DateTimeFormatter.ISO_LOCAL_TIME)
.toFormatter(Locale.ROOT);
With this formatter it’s a pretty simple operation:
ZoneId zone = ZoneId.of("Asia/Kolkata");
String input = "2021-07-13 05:22:18.712";
OffsetDateTime dateTime = LocalDateTime.parse(input, FORMATTER)
.atZone(zone)
.toOffsetDateTime();
System.out.println(dateTime);
Output from the example snippet is:
2021-07-13T05:22:18.712+05:30
Since as #Sweeper noted in a comment your string does not contain UTC offset nor time zone, parse it into a LocalDateTime first. The Local in some java.time class names means without time zone or UTC offset. Then convert into a ZonedDateTime in the intended time zone and further into the desired type, OffsetDateTime.
If you want to use the default time zone of the JVM, set zone to ZoneId.systemDefault(). Be aware that the default time zone may be changed at any time from another part of your program or another program running in the same JVM, so this is fragile.
Possible shortcuts
My formatter is wordy because I wanted to reuse as much as I could from built-in formatters. If you don’t mind building the formatter by hand from a pattern, you may use:
private static final DateTimeFormatter FORMATTER
= DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.SSS", Locale.ROOT);
Or even shorter, you may hand substitute the space in your string with a T to obtain ISO 8601 format and then parse into a LocalDateTime without specifying any formatter at all.
What went wrong in your code?
The exception message you got is trying to be helpful:
But i keep getting this error -
org.threeten.bp.format.DateTimeParseException: Text '2021-07-13
05:22:18.712' could not be parsed at index 10.
Index 10 in your string is where the space between date and time is. The one-arg OffsetDateTime.parse method expects ISO 8601 format, like 2021-07-13T05:22:18.712+05:30, so with a T to denote the start of the time part and with a UTC offset at the end. The absence of the T caused your exception. Had you fixed that, you would have got another exception because of the missing UTC offset.
Link
Wikipedia article: ISO 8601
You first need to check the document.
It indicates parse need to go with a date format such as 2007-12-03T10:15:30 +01:00.
You date is missing a "T", "2021-07-13 05:22:18.712". Hence it did not go well, counting it from index 0, it's character at 10.
If you need to parse 2021-07-13T05:22:18.712, you will still get error. An error Text '2021-07-13T05:22:18.712' could not be parsed at index 23. Which is the problem of miliseconds.
So a big round to go:
//Format to date
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
//Format to new string
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
java.util.Date date1=simpleDateFormat.parse("2021-07-13 05:22:18.712");
String newDate = formatter.format(date1);
//finally.
OffsetDateTime parsedDateTime = OffsetDateTime.parse(newDate);

DateTimeFormatter can format a date but can't read its own format

I'm trying to parse an ISO-8601 date with a literal 'Z' on the end.
This will format the date correctly:
String pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
String string = formatter.format(OffsetDateTime.now());
System.out.println(string);
Prints:
2020-01-19T03:06:58.090Z
But then, trying to immediately read it back in:
TemporalAccessor acc = formatter.parse(string);
OffsetDateTime time = OffsetDateTime.from(acc);
System.out.println(time);
Fails with:
Exception in thread "main" java.time.DateTimeException: Unable to obtain OffsetDateTime from TemporalAccessor: {MinuteOfHour=6, MilliOfSecond=90, MicroOfSecond=90000, HourOfAmPm=3, NanoOfSecond=90000000, SecondOfMinute=58},ISO resolved to 2020-01-19 of type java.time.format.Parsed
at java.time.OffsetDateTime.from(OffsetDateTime.java:370)
at HelloWorld.main(HelloWorld.java:27)
Caused by: java.time.DateTimeException: Unable to obtain ZoneOffset from TemporalAccessor: {MinuteOfHour=6, MilliOfSecond=90, MicroOfSecond=90000, HourOfAmPm=3, NanoOfSecond=90000000, SecondOfMinute=58},ISO resolved to 2020-01-19 of type java.time.format.Parsed
at java.time.ZoneOffset.from(ZoneOffset.java:348)
at java.time.OffsetDateTime.from(OffsetDateTime.java:359)
... 1 more
I cannot change the pattern to use the non-literal 'Z', but I noticed that changing it to Z so it formats dates with +0000 on the end can successfully be read in. I also can't change using TemporalAccessor to read the date since it's coming from a third party (Jackson). Any ideas?
Since you're using a literal 'Z', there's no time zone in the data, so you'll have to specify one manually:
OffsetDateTime time = LocalDateTime.from(acc).atOffset(ZoneOffset.UTC);
The Z is not a literal
The Z in the ISO 8601 format is an offset of zero from UTC (also known as Zulu time zone). You need to format and parse it as such, or you will get incorrect results. OffsetDateTime.now() may and usually does return a date-time with a non-zero offset. When you are printing it with a Z as offset, you are printing incorrect information, a different point in time.
I cannot change the pattern to use the non-literal 'Z',
I hope I misunderstood this part. It sounds to me like you have got an error that you cannot fix.
You need no formatter
OffsetDateTime and the other date and time classes from java.time print ISO 8601 format from their toString methods and parse the same format back. So you don’t need to specify any formatter explicitly.
Possibly the Instant class fits your requirements better than OffsetDateTime. An Instant is a point in time independent of time zone and offset. Its toString method generates an ISO 8601 string in UTC and therefore always with the Z in the end.
String string = Instant.now().toString();
System.out.println(string);
// Parse back
Instant time = Instant.parse(string);
System.out.println(time);
When I ran this just now, the output was:
2020-01-19T05:37:29.630135Z
2020-01-19T05:37:29.630135Z
If you can require your incoming date-time string to have a Z as offset always, I should say that this is the solution for you. You can always convert the Instant to a different type after parsing if you need.
If you prefer OffsetDateTime:
String string = OffsetDateTime.now().toString();
System.out.println(string);
// Parse back
OffsetDateTime time = OffsetDateTime.parse(string);
System.out.println(time);
Example output from my time zone:
2020-01-19T06:37:29.721666+01:00
2020-01-19T06:37:29.721666+01:00
You notice that the correct offset for my time zone, +01:00, is printed rather than Z, which goes nicely to illustrate that the Z would have been incorrect. If you want the UTC time, just tell OffsetDateTime so by passing ZoneOffseet.UTC to the now method:
String string = OffsetDateTime.now(ZoneOffset.UTC).toString();
System.out.println(string);
2020-01-19T05:43:06.700402Z
Parsing back works as before.

Extract time zone from jodatime DateTime

I receive a datetime string containing an ISO8601 datetime, like this "2001-07-04T12:08:56.235-07:00", then this string is parsed to a jodatime datetime object this way new DateTime("2001-07-04T12:08:56.235-07:00"), and after that it is converted to a string again using a variable formatter pattern passed by arguments, but when that happens, no timezone is used, so the system's default timezone is being used. What I want is to extract the timezone (or offset) from the first given date, and use it to print it accordingly. Is it possible?
Thanks beforehand!
Don't use new DateTime("..."). Use DateTime.parse("...").
See difference:
DateTime dateTime1 = new DateTime("2001-07-04T12:08:56.235-07:00");
System.out.println(dateTime1);
System.out.println(dateTime1.getZone());
DateTime dateTime2 = DateTime.parse("2001-07-04T12:08:56.235-07:00");
System.out.println(dateTime2);
System.out.println(dateTime2.getZone());
Output (I'm in eastern US)
2001-07-04T15:08:56.235-04:00
America/New_York
2001-07-04T12:08:56.235-07:00
-07:00
As you can see, using new converts to default time zone, while using parse retains the given time zone.

java.lang.IllegalArgumentException: Parse error: Unable to parse Date format

I need to format following date fomat
timeBooked: "2015-05-20T02:08:00.000Z",
ExpiryTime: "2015-05-20T04:08:00.000Z",
My code follows to format the date:
try {
Date currentDate = new Date(timeBooked);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.ZZ", Locale.ENGLISH);
Log.e(TAG, "formatted time string: " + sdf.format(currentDate.getTime()));
Log.e(TAG, "date string:=" + currentDate.getDate());
} catch (Exception e) {
Log.e(TAG, e.getMessage());
e.printStackTrace();
}
While running this code getting java.lang.IllegalArgumentException: Parse error:2015-05-20T02:08:00.000Z.
Your format String is incorrect. It should be "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'".
Then to get your date correctly you should use:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH);
// Set time zone to UTC since 'Z' at end of String specifies it.
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
// The parsed date will be offset from UTC in device's current TimeZone.
Date currentDate = sdf.parse(timeBooked);
The Date constructor taking a string is deprecated: see http://developer.android.com/reference/java/util/Date.html#Date(java.lang.String). It won't accept a custom date format; only a pre-defined set of formats are allowed.
Once you get your DateFormat string correct (I think "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" should work) you can call sdf.parse(timeBooked) to get a valid Date.
Z Has Meaning
Both of the previous Answers tell you to expect-and-ignore the Z character, by surrounding with single quotes in the coded parsing pattern. Bad advice. You would be ignoring valuable data, and would be rejecting valid alternative inputs such as 2015-05-20T02:08:00.000+05:30. The pattern code Z means "any valid offset-from-UTC". Adding the single quotes for 'Z' says "expect an uppercase Z to appear here, ignore any meaning it may have, and throw an exception if the Z is missing".
Joda-Time
You are using the old date-time classes bundled with early versions of Java. Those classes have proven to be troublesome, flawed in both design and implementation. Avoid them. In current Android, add the Joda-Time library to your project. In Java 8 and later, use the java.time framework that was inspired by Joda-Time.
Your string inputs are in ISO 8601 standard format. Both Joda-Time and java.time use ISO 8601 as their defaults when parsing/generating textual representations of date-time values. So you these classes can directly parse such strings without you needing to specify any coded parsing patterns.
The following code creates a date-time assigned an offset-from-UTC of 0, which is what the Z (for Zulu) means.
DateTime dateTime = DateTime.parse( "2015-05-20T02:08:00.000Z" );
Using a constructor has a different meaning. The following code parses the value with an offset of zero but then adjusts the results into your JVM’s current default time zone.
DateTime dateTime = new DateTime( "2015-05-20T02:08:00.000Z" ) );
I suggest you always explicitly assign your desired/expected time zone.
DateTimeZone zone = DateTimezone.forID( "Europe/Paris" );
DateTime dateTime_Europe_Paris = dateTime.withZone( zone );
If you really need a java.util.Date, convert after doing your parsing and business logic with Joda-Time.
java.util.Date date = dateTime.toDate();
Search StackOverflow for many more Questions and Answers with example code for Joda-Time.

Joda time getting right time from dateTime

I have this simple code:
DateTimeFormatter formatter = DateTimeFormat.forPattern("HH:mm yyyy-MM-dd");
DateTime dateTime = formatter.withZone(DateTimeZone.forID("America/New_York")).parseDateTime("08:30 2015-06-01");
DateTime dateTime2 = formatter.withZone(DateTimeZone.forID("America/New_York")).parseDateTime("08:30 2015-12-01");
these are leap times. when I hit toString method, I got something like this:
2015-06-01T08:30:00.000-04:00
2015-12-01T08:30:00.000-05:00
which is correct, we can see UTC time - offset. But when I call getHourOfDay, I got 8 and not 4/3 as expected. What am I doing wrong? Please, share some advices here.
Well, from the Javadoc for DateTimeFormatter#withZone():
Returns a new formatter that will use the specified zone in preference to the zone of the printed object, or default zone on a parse.
So, you told the formatter to use the specific timezone on parsing AND output, and the input you gave it did NOT contain a timezone, so this is the expected result. In essence you said:
Here's a date string without timezone, parse it assuming America/New_York
Convert the date back to String, in the timezone America/New_York
This is what it did.

Categories

Resources