I am getting a parse exception when trying to parse the time string 02:22 p.m.
.
I have the following conversion function:
public static long convertdatetotimestamp(String datestring, String newdateformat, String olddateformat){
SimpleDateFormat originalFormat = new SimpleDateFormat(olddateformat,Locale.ROOT);
SimpleDateFormat targetFormat = new SimpleDateFormat(newdateformat,Locale.ROOT);
Date date = null;
try {
date = originalFormat.parse(datestring);
String formattedDate = targetFormat.format(date);
Date parsedDate = targetFormat.parse(formattedDate);
long nowMilliseconds = parsedDate.getTime();
return nowMilliseconds;
} catch (ParseException e) {
e.printStackTrace();
return 0;
}
}
The method is called in another activity with a time format "02:22 p.m."
. olddateformat
and newdateformat
are the same: hh:mm a
.
It causes following error in log:
java.text.ParseException: Unparseable date: "02:22 p.m." (at offset 6)
How to resolve this issue? Time is in exactly above mentioned format.
I believe that
SimpleDateFormat
can't be customized to parse thep.m.
part (it only recognizesAM
orPM
).So one alternative is to remove the dots:
One detail: to get the
nowMilliseconds
value, you need all the date fields (day/month/year) and a timezone. As those fields are not in the inputString
,SimpleDateFormat
sets them to January 1st of 1970 (and also set the seconds and milliseconds to zero), and use the system's default timezone.I'm not sure if this behaviour of getting January 1970 is consistent among all Java versions, which is another problem because you can get different values depending on the environment/device the code is running. Actually, you might have a different result anyway because it uses the system's default timezone and this can vary among different environments.
If I run this code in my machine, it uses my system's default timezone (
America/Sao_Paulo
), and the result is62520000
. But if I change the timezone to another (let's say,Asia/Kolkata
), the result is31920000
. You must be aware of this variation and check if that's what you really need.Another detail is that, if
olddateformat
andnewdateformat
are the same, there's no need to create 2 different formatters.Java's new Date/Time API
The old classes (
Date
,Calendar
andSimpleDateFormat
) have lots of problems and design issues, and they're being replaced by the new APIs.In Android you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. You'll also need the ThreeTenABP (more on how to use it here).
All the relevant classes are in the
org.threeten.bp
package.With this new API, you can customize the text that corresponds to AM/PM using a
org.threeten.bp.format.DateTimeFormatterBuilder
(so no need to remove the dots manually). And there are specific classes to each case - in this case, the input has only the time fields (hour and minutes), so I'm going to use theorg.threeten.bp.LocalTime
class (which represents only a time - hour/minute/second/nanosecond - without a date):The
parsedTime
variable will contain the values corresponding to02:22 PM
(and only this value, it has no date fields (day/month/year) nor a timezone).To get the milliseconds value (number of milliseconds since
1970-01-01T00:00Z
), you also need a date (day/month/year) and a timezone. As I said previously, those fields can affect the final value.In the old API,
SimpleDateFormat
tries to be "smart" and sets default values for those fields (January 1st of 1970 in the system's default timezone), but the new API is more strict about that and you must tell explicity what date and timezone you want.In this example, I'm using the
Asia/Kolkata
timezone but you can change it according to your needs (more on that below):If you want a specific date instead of the current date, you can use
LocalDate.of(2017, 5, 20)
- this will get May 20th, 2017, for example. With this, you can set the code above to the date and timezone you need.Note that the API uses IANA timezones names (always in the format
Region/City
, likeAmerica/Sao_Paulo
orAsia/Kolkata
). Avoid using the 3-letter abbreviations (likeIST
orPST
) because they are ambiguous and not standard.You can get a list of available timezones (and choose the one that fits best your system) by calling
ZoneId.getAvailableZoneIds()
.If you want to emulate exactly what
SimpleDateFormat
does, you can useLocalDate.of(1970, 1, 1)
and use the default timezone withZoneId.systemDefault()
- but this is not recommended, because the system's default can be changed without notice, even at runtime. It's better to explicit what timezone you're using.Or you can create a formatter that always sets default values for the date (using the
org.threeten.bp.temporal.ChronoField
class) and always uses the same timezone. So you can parse it directly to aorg.threeten.bp.Instant
and get the millis value:Following changes that i've made works fine for me.
Locale.ENGLISH you can use your locale, english solved my issue. Reference.
Thanks for responses and references.
It so happens that a.m. and p.m. are called just this in Gaelic locale. At least on my Java 8. I am far from sure that it will be the case on (all) Android phones, but you may do some experiments with it.
This prints
As Hugo so warmly and rightly recommends is his answer, I am using the modern Java date and time API, so you will need ThreeTenABP for the above code. See How to use ThreeTenABP in Android Project. Alternatively you may want to try the same locale with your otherwise outdated
SimpleDateFormat
.US Spanish locale shows the same behaviour on my Java 8, so you may try that too:
Locale.forLanguageTag("es-US")
.