Text could not be parsed at index 33 - android

I am working on ThreeTenABP library to parse date and time. However, it is crashing. API I consume sends DateTime like;
2018-10-20T14:27:47.3949709+03:00
This is the way I try to parse;
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ISO_OFFSET_DATE_TIME)
.append(DateTimeFormatter.ofPattern("yyyy-MM-dd"))
.toFormatter();
Timber.d(LocalDateTime.parse("2018-10-20T14:27:47.3949709+03:00", formatter).toString());
I am getting below error:
Text '2018-10-20T14:27:47.3949709+03:00' could not be parsed at index
33
Thanks in advance.

Explanation of the error message:
2018-10-20T14:27:47.3949709+03:00 is 33 chars long, therefore
Text '2018-10-20T14:27:47.3949709+03:00' could not be parsed at index 33
means it expected a 34th char which is not there (it's 0 indexed).
Problem
The way you defined the Formatter it would accept 2018-10-20T14:27:47.3949709+03:002018-10-20`
Solution:
To overcome this you can either drop the .append(DateTimeFormatter.ofPattern("yyyy-MM-dd")
Or define a Formatter that accepts both formats with startOptional and endOptional
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.optionalStart()
.append(DateTimeFormatter.ISO_OFFSET_DATE_TIME)
.optionalEnd()
.optionalStart()
.append(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
.optionalEnd().toFormatter();
You can see the example working at https://ideone.com/RDVHYG
Side note: "yyyy-MM-dd" does not yield enough information for a LocalDateTime therefore I added "HH:mm"

Related

How do I format this date [duplicate]

This question already has answers here:
Why can't OffsetDateTime parse '2016-08-24T18:38:05.507+0000' in Java 8
(5 answers)
Closed 8 months ago.
I am trying to parse the date which is in the format 2021-01-15T19:00:00+0000. I am not sure which format it is but based on my research I tried with methods below
1. val odt = OffsetDateTime.parse("2021-01-15T19:00:00+0000")
2. val zdt = ZonedDateTime.parse("2021-01-15T19:00:00+0000")
It is logging the exception:
java.time.format.DateTimeParseException: Text '2021-01-15T19:00:00+0000' could not be parsed at index 19
at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
at java.time.ZonedDateTime.parse(ZonedDateTime.java:591)
at java.time.ZonedDateTime.parse(ZonedDateTime.java:576)
tl;dr
OffsetDateTime
.parse(
"2021-01-15T19:00:00+0000" ,
DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ssxxxx" )
)
.toString()
2021-01-15T19:00Z
OffsetDateTime
Your input has an offset of zero hours-minutes-seconds from UTC, the +0000 portion.
So you should be parsing with OffsetDateTime rather than ZonedDateTime.
ZonedDateTime
The ZonedDateTime class is for a time zone rather than an offset. A time zone is the named history of the past, present, and future changes to the offset used by the people of a particular region, as decided by their politicians. Time zones have a name in format of Continent/Region such as Europe/Paris or America/Edmonton.
Optional COLON in offset
Unfortunately, your input omitted the COLON character from between the hours and minutes of the offset. While optional in the ISO 8601 standard, the parse method expects to find that character.
If you know all the inputs have the same +0000 I would simply perform a string manipulation.
OffsetDateTime odt = OffsetDateTime.parse( "2021-01-15T19:00:00+0000".replace( "+0000" , "+00:00" ) ) ;
If some other offsets might appear, you must specify a formatting pattern.
DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu-MM-dd'T'HH:mm:ssxxxx" ) ;
OffsetDateTime odt = OffsetDateTime.parse( input , f ) ;
See this code run live at Ideone.com.
2021-01-15T19:00Z
I would recommend to use the OffsetDateTime with the right offset - that should do the trick!

Which type of date format it is?? "2018-09-06T10:12:21-0300"

What type of date format is it?
"2018-09-06T10:12:21-0300"
And how can I format it to something like that "06 Sep" ???
What type of date format is it?
This format is one of the ISO 8601 standard, but obviously not for the java.time.format.DateTimeFormatter which considers it a custom format consisting of ISO-standard date and time of day plus an offset from UTC without a separator (colon) between hours and minutes.
And how can I format it to something like that "06 Sep" ???
You need to define two DateTimeFormatters, one for parsing the non-standard input and the other one for outputting day of month and abbreviated month name only. Here's an example:
fun main(args: Array<String>) {
// some non-ISO formatted String
val inputDateTime = "2018-09-06T10:12:21-0300"
// build up a DateTimeFormatter that can parse such a String
val inputParser = DateTimeFormatterBuilder()
// date part uuuu-MM-dd
.append(DateTimeFormatter.ISO_LOCAL_DATE)
.appendLiteral('T') // the T separating date from time
// the time of day part
.append(DateTimeFormatter.ISO_LOCAL_TIME)
// the offset part without a separator between hours and minutes
.appendPattern("X")
// (just for completeness) a locale
.toFormatter(Locale.ENGLISH)
// parse the String to an OffsetDateTime
val offsetDateTime = OffsetDateTime.parse(inputDateTime, inputParser)
// define another formatter for output, make it only use day of month and abbreviated month in English
val outputFormatter = DateTimeFormatter.ofPattern("dd MMM", Locale.ENGLISH)
// print the results
println("$offsetDateTime ---> ${offsetDateTime.format(outputFormatter)}")
}
Example output:
2018-09-06T10:12:21-03:00 ---> 06 Sep
This format respects the ISO 8601 standard for representing a date/time with timezone information.
There is enough information to parse this string into an OffsetDateTime, but apparently Java's formatter is a bit strict with respect to the missing separator between hours and minutes in the offset representation (which is technically allowed by the standard). This means a plain OffsetDateTime.parse(text) will throw an exception.
Therefore, you'll to define a custom DateTimeFormatter as explained by #deHaar.

ThreeTenABP DateTime parser giving exception for yyyy-MM-ddTHH:mm:ss formate

I need to convert a dateTime String to millis and I am using ThreeTenABP for this, but the OffSetDateTime.parse is unable to parse the dateTime String which is for ex. "2020-08-14T20:05:00" and giving the following exception.
Caused by: org.threeten.bp.format.DateTimeParseException:
Text '2020-09-22T20:35:00' could not be parsed:
Unable to obtain OffsetDateTime from TemporalAccessor:
DateTimeBuilder[, ISO, null, 2020-09-22, 20:35], type org.threeten.bp.format.DateTimeBuilder
I have already searched through similar questions but could not find the exact solution.
Below is the code that I am using in Kotlin.
val formatter: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss",
Locale.ROOT)
val givenDateString = event?.eventDateTime
val timeInMillis = OffsetDateTime.parse(givenDateString, formatter)
.toInstant()
.toEpochMilli()
The problem is the missing offset in the String that you are trying to parse to an OffsetDateTime. An OffsetDateTime cannot be created without a ZoneOffset but no ZoneOffset can be derived from this String (one could just guess it's UTC, but guessing is not suitable in such a situation).
You can parse the String to a LocalDateTime (a representation of a date and a time of day without a zone or an offset) and then add / attach the desired offset. You don't even need a custom DateTimeFormatter because your String is of ISO format and can be parsed using the default built-in formatter:
fun main() {
// example String
val givenDateString = "2020-09-22T20:35:00"
// determine the zone id of the device (you can alternatively set a fix one here)
val localZoneId: ZoneId = ZoneId.systemDefault()
// parse the String to a LocalDateTime
val localDateTime = LocalDateTime.parse(givenDateString)
// then create a ZonedDateTime by adding the zone id and convert it to an OffsetDateTime
val odt: OffsetDateTime = localDateTime.atZone(zoneId).toOffsetDateTime()
// get the time in epoch milliseconds
val timeInMillis = odt.toInstant().toEpochMilli()
// and print it
println("$odt ==> $timeInMillis")
}
this example code produces the following output (pay attention to the trailing Z in the datetime representation, that's an offset of +00:00 hours, the UTC time zone, I wrote this code in the Kotlin Playground and it seems to have UTC time zone ;-) ):
2020-09-22T20:35Z ==> 1600806900000
Please note that I tried this with java.time and not with the ThreeTen ABP, which is obsolete to use for many (lower) Android versions now, since there's Android API Desugaring. However, this shouldn't make a difference because your example code threw exactly the same exception when I tried it first, which means ThreeTen is not to blame for this.

Create Joda datetime UTC from String

I have the following String that I would like to change to UTC:
Thu Aug 24 07:38:32 GMT+01:00 2017
I'm using Joda-Time library.
I know how to create a new Datetime eg new dateTime(DateTimeZone.UTC) but how can I create a DateTime object from the above String?
I have tried the following but get an exception. Surely there must be another way to create a DT obect without chopping the original String up? What if the external API changes how it sends my app the orignal String, my String manipulation code would fail.
DateTimeFormatter df = DateTimeFormat.forPattern("dd-MMM-YYYY HH:mm");
String strOrigTime = "Thu Aug 24 07:38:32 GMT+01:00 2017";
DateTime dt = DateTime.parse(strOrigTime, df);
Log.e(TAG, "dt after parse = " + dt.toString());
Error:
Caused by: java.lang.IllegalArgumentException: Invalid format: "Thu Aug 24 07:38:32 GMT+01:00 2017"
at org.joda.time.format.DateTimeFormatter.parseDateTime(DateTimeFormatter.java:866)
at org.joda.time.DateTime.parse(DateTime.java:144)
The format used (dd-MMM-YYYY HH:mm) means: day (dd) followed by -, followed by month (MMM), followed by -, followed by year (YYYY) and so on (check the javadoc for more details).
This format doesn't match the input string (which has day-of-week followed by month, followed by day, then hour/minute/second, etc). So the first thing is to use a format that matches the input, otherwise you'll always get "Invalid format" errors.
Another detail is that day of week and month names are in English, so you must also use a java.util.Locale to specify the language you're using to parse the input. If you don´t use a locale, the system default will be used, and it's not guaranteed to always be English (and it can also be changed, even at runtime, so it's always better to specify one).
I also had to add "GMT" as a literal and call withOffsetParsed() to make it include the offset (+01:00) in the parsed object:
DateTimeFormatter df = DateTimeFormat
// use a pattern that matches input
.forPattern("EEE MMM dd HH:mm:ss 'GMT'Z yyyy")
// use English locale for day of week and month
.withLocale(Locale.ENGLISH)
// include the offset (+01:00) in the parsed object
.withOffsetParsed();
String strOrigTime = "Thu Aug 24 07:38:32 GMT+01:00 2017";
DateTime dt = DateTime.parse(strOrigTime, df);
System.out.println(dt.toString());
The output is:
2017-08-24T07:38:32.000+01:00
Then, you can set the UTC timezone to this object:
dt = dt.withZone(DateTimeZone.UTC);
System.out.println(dt.toString());
The output will be:
2017-08-24T06:38:32.000Z
Note that withZone method preserves the same instant (both dates represent the same point in time), just the timezone used in the output is changed. But both dates are equivalent (they represent the same instant, as 07:38 in offset +01:00 is the same as 06:38 in UTC).
If you want all dates to be converted to UTC, you can also set this in the formatter:
// set UTC to the formatter
df = df.withZone(DateTimeZone.UTC);
Then you don't need to call withZone in the DateTime objects: all parsed dates will be converted to UTC.
You also told that "if the external API changes how it sends my app the orignal String, my String manipulation code would fail".
Well, if the input String changes, you'll have to change your format as well - there's no other way, Joda-Time can't just guess what's the format, you have to tell it.
If you want to parse more than one format, there's a way to create a formatter that uses lots of different patterns and try to parse each one, until one of them works (or throw exception if none works). You could do something like that:
// format 1
DateTimeFormatter f1 = DateTimeFormat
// use a pattern that matches input
.forPattern("EEE MMM dd HH:mm:ss 'GMT'Z yyyy")
// use English locale for day of week and month
.withLocale(Locale.ENGLISH)
// include the offset (+01:00) in the parsed object
.withOffsetParsed();
// format 2
DateTimeFormatter f2 = DateTimeFormat.forPattern("dd/MM/yyyy HH:mm:ss Z");
// array of all possible formats
DateTimeParser[] parsers = { f1.getParser(), f2.getParser() };
// formatter that uses all the possible formats
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
// append array of possible formats
.append(null, parsers)
// create formatter
.toFormatter().withLocale(Locale.ENGLISH).withOffsetParsed()
// set all parsed objects to UTC
.withZone(DateTimeZone.UTC);
// parse first format
System.out.println(DateTime.parse("Thu Aug 24 07:38:32 GMT+01:00 2017", formatter));
// parse second format
System.out.println(DateTime.parse("24/08/2017 07:38:32 +01:00", formatter));
Both dates will be parsed to:
2017-08-24T06:38:32.000Z
Then you can add new formats to the array, as needed.
Java new Date/Time API
Joda-Time is in maintainance mode and is being replaced by the new APIs, so I don't recommend start a new project with it. Even in joda's website it says: "Note that Joda-Time is considered to be a largely “finished” project. No major enhancements are planned. If using Java SE 8, please migrate to java.time (JSR-310).".
If you can't (or don't want to) migrate from Joda-Time to the new API, you can ignore this section.
If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.
If you're using Java <= 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, there's the ThreeTenABP (more on how to use it here).
The code below works for both.
The only difference is the package names (in Java 8 is java.time and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp), but the classes and methods names are the same.
The code to parse the inputs is very similar, with minor changes in the format.
And I'm using the Instant class, because you want the output in UTC, and Instant represents a UTC instant:
// format 1
DateTimeFormatter f1 = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss O yyyy", Locale.ENGLISH);
// format 2
DateTimeFormatter f2 = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss XXX");
// formatter with both formats
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
// add format 1
.appendOptional(f1)
// add format 2
.appendOptional(f2)
// create formatter
.toFormatter(Locale.ENGLISH);
// parse first format
System.out.println(Instant.from(formatter.parse("Thu Aug 24 07:38:32 GMT+01:00 2017")));
// parse second format
System.out.println(Instant.from(formatter.parse("24/08/2017 07:38:32 +01:00")));
This will output:
2017-08-24T06:38:32Z
2017-08-24T06:38:32Z

joda-time can't read Parse dateTime string

I'm having an issue with joda-time formatting a date time string that Parse (parse.com) has stored in an sqlite table.
Sqlite creation string: "createdDate date time"
Storing parse date into table: "insert... parseObject.getCreatedAt()"
If i then use a SQLite browser to inspect the table, I see the date stored like this:
Sat Jun 15 15:44:52 PDT 2013
So going along with that, I wrote the following to convert it back into a DateTime object to give to parse as part of a query to get items that are newer than the last inserted in my table:
DateTimeFormatter format = DatetimeFormat.forPattern("yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSS'Z'");
DateTime dt = formatter.parseDateTime(datahelper.getLastInsertDate(..));
The formatter is this way, because in Parse's databrowser, I can see dateTimes being stored like this:
2013-06-24T08:11:45.280Z
I get an ANR though, so I tried using the following formatter:
DateTimeFormatter format = DatetimeFormat.forPattern("EEE' 'MMM' 'dd' 'HH':'mm':'ss 'z'' 'YYYY");
DateTime dt = formatter.parseDateTime(datahelper.getLastInsertDate(..));
and I still get an ANR. The trace in eclipse shows the following:
Caused by: java.lang.IllegalArgumentException: invalid format: "Tue Jun 25 00:13:29 PDT 2013"
at org.joda.time.format.DateTimeFormatter.parseDateTime"
The second ANR trace shows:
Invalid format: "Tue Jun 25 00:13:29 PDT 2013" is malformed at "PDT
2013"
I've tried getting around that, as joda time does not parse "z" to PDT/PST, so I've put 'PDT' in my formatter to hopefully get it to work, but nothing seems to work.
Any ideas?
Edit 1: Using the accepted answer, I have a timezone formatting issue)
DateFormat originalFormat = new SimpleDateFormat("EEE MMM DDD HH:mm:ss z yyyy");
Date originaldate = originalFormat.parse(datahelper.getLastInsertdate);
Log.i("converted date: ", String.valueOf(originalDate);
Log.i("a real date: ", "String.valueOf(new Date(new Date().getTime)));
I get two outputs:
Fri Jan 25 15:14:11 PST 2013
Tue Jun 25 17:11:44 PDT 2013
why does the converted date show PST, and a standard Date shows PDT?
It seems to be a known problem Joda cannot parse Timezone names sadly. In the documentation before all the pattern syntaxes you will see this line:
The pattern syntax is mostly compatible with java.text.SimpleDateFormat - time zone names cannot be parsed and a few more symbols are supported. All ASCII letters are reserved as pattern letters, which are defined as follows:
You can see that in the documentation link here
Now the solution to your answer can be found in this answer by #BalusC located here
Hope this helps.
I think that with SQLite, because the date type is somewhat broken, the best thing to do is to store the long that you get from Date.getTime() or related Joda methods.
When you get the long from the database, re-construct your date object (e.g. new Date(long)), and then format that.
Above all, remember that (IMHO) the only sensible way to store a date is in reference to UTC, which is what you get with Date.getTime() and new Date(long) : milliseconds since Jan 1, 1970 UTC.
Once you retrieve your date, format it with whatever timezone is appropriate.

Categories

Resources