TimeZone broken in SimpleDateFormat in android 2.3 - java

Android 2.3 was recently released last night. So naturally I tried my app on it and found there was date formatting issue. I have noticed the DateFormatter produces different formats. So do this in a simple Java program:
((SimpleDateFormat)DateFormat.getDateTimeInstance(DateFormat.LONG,
DateFormat.LONG)).format(new Date());
Output is
December 7, 2010 11:49:40 AM EST
Do the same thing in an android emulator and you get
December 7, 2010 11:42:50 AM GMT-05:00
Notice the different time zone. Has anybody ran in to this issue? Is there another formatter I can use that doesn't depend on Java's implementation?
EDIT:
Ok so here is more detail to why I think this is broken:
Using this code:
private final DateFormat format =
new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z");
I tried to parse a date but the following error is thrown:
12-07 12:55:49.556: ERROR/DateDeserializer(847): Error when parsing date
java.text.ParseException: Unparseable date: "Mon, 06 Dec 2010 17:13:35 EST"
at java.text.DateFormat.parse(DateFormat.java:626)
at com.currency.mobile.client.DateDeserializer
.deserialize(DateDeserializer.java:31)
at com.currency.mobile.client.DateDeserializer
.deserialize(DateDeserializer.java:19)
at org.codehaus.jackson.map.deser.SettableBeanProperty
.deserialize(SettableBeanProperty.java:149)

There is nothing wrong with the output.
You create a DateFormat-Instance which depends on the default Locale. It is not unusual that on different machines, different java-installations the default Locale vary and so the output of locale-dependent operations. In this case the default TimeZone is different, but the two outputs in your question represent the same Date, printed with the same format String MMMMM d, yyyy hh:mm:ss a z.
UPDATE:
parse() in Android 2.3 will work with TimeZones like GMT+xxxx etc, but it doesn't recognize EST for example as a valid TimeZone for parsing. Android knows about EST if you use TimeZone.getTimeZone("EST").
UPDATE2:
Three-letter timezone IDs "EST", "HST", and "MST" are deprecated. Do not use them.

Both are same TimeZone just different representation

Looks like support for pattern "EEE MMM dd HH:mm:ss zzz yyyy" is also broken.
Evgueni

Related

SimpleDateFormat parse

I have an issue with SimpleDateFormat:
Error:
Unparseable date: "Thu, 09 Nov 2017 16:17:42 GMT"
Code:
DF_SERVER_FORMAT="EEE, dd MMM yyyy HH:mm:ss'Z'"
....
var formater=SimpleDateFormat(DF_SERVER_FORMAT)
formater.parse(source)
as per SimpleDateFormat documentation, Z (capitalized) is for an RFC 822 time zone, e.g. -0800
for a General time zone use z.
this should work:
DF_SERVER_FORMAT="EEE, dd MMM yyyy HH:mm:ss z"
Try "EEE, d MMM yyyy HH:mm:ss z" this pattern works for me.
You can try to format some date using your pattern, to see the difference and then fix your pattern accordingly. Here is what I did in J2SE:
SimpleDateFormat df = new SimpleDateFormat("EEE dd MMM yyyy HH:mm:ss'Z'");
System.out.println(df.format(new Date()));
This is producing:
Thu 09 Nov 2017 17:49:07Z
But, when I used the pattern "EEE, dd MMM yyyy HH:mm:ss z", it produced the expected result:
Thu, 09 Nov 2017 17:51:09 CET
For anyone who either is fine with an external dependency (temporarily) or is using Java 8 or later I wanted to contribute the modern answer. Because I consider SimpleDateFormat long outdated.
The modern Java date and time API is generally much nicer to work with. In addition, your string is in RFC 1123 format, and the modern API comes with a formatter for this format. So no need to build your format pattern string yourself (my code is pure Java, I trust you to adopt to Kotlin):
String dateString = "Thu, 09 Nov 2017 16:17:42 GMT";
OffsetDateTime dateTime = OffsetDateTime.parse(dateString,
DateTimeFormatter.RFC_1123_DATE_TIME);
This produces an OffsetDateTime of 2017-11-09T16:17:42Z as expected.
To use this on Android, get ThreeTenABP, see this question: How to use ThreeTenABP in Android Project. Java 8 and later come with the modern API built-in. If using Java 6 or 7 on non-Android, you need the ThreeTen Backport.
What went wrong in your code? With your format pattern string you were asking for a literal Z right after the seconds, with no space in between. Since your input string didn’t have a Z there, parsing failed (instead it had a space and the offset ID GMT). In addition, your code seems to be sensitive to locale: if your default locale is one where the abbreviation for Thursday is not Thu or for November not Nov, parsing will fail (in contrast, RFC_1123_DATE_TIME expects (and requires) day and month abbreviations in English independently of locale).

Unparseable date exception in Android 2.3.3 not present in newer versions

I am parsing a date string using the following SimpleDateFormat:
Code Snippet:
SimpleDateFormat format = new SimpleDateFormat("'cappicor' yyyy-MM-dd HH:mm zz", Locale.US);
dateTime = format.parse(title)
Here is an example "title" date String I receive:
"cappicor 2013-12-07 11:54 UTC"
This works fine in many newer devices and versions of Android, but fails in android 2.3.3 with:
java.text.ParseException: Unparseable date: "cappicor 2013-12-07 11:54 UTC"
I have browsed many SO answers, related to the Locale used, and even parsing out possible control characters from the String - with no luck.
Any ideas?
None of the Java versions from 1.4 to 1.7 gave me that error, though in fact Android 2.3.3 did.
From the ORACLE documentation:
Text can be quoted using single quotes (') to avoid interpretation. "''" represents a single quote
From the Android documentation:
'' single quote (Literal) 'o''clock':o'clock
So there is no difference here. However, I noticed that the time zone UTC caused an issue. After changing the code to ...
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US);
format.setTimeZone(TimeZone.getTimeZone("UTC"));
Date dateTime = format.parse("2013-12-07 11:54");
... it works. Slightly strange because ...
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm zz", Locale.US);
Date dateTime = format.parse("2013-12-07 11:54 GMT");
... works pretty fine as well (same for EST, PST) and TimeZone.getAvailableIDs() returns UTC, CET and MET which all do not work.
Conclusion: use ...
SimpleDateFormat format = new SimpleDateFormat("'cappicor' yyyy-MM-dd HH:mm", Locale.US);
format.setTimeZone(TimeZone.getTimeZone("UTC"));
Date dateTime = format.parse("cappicor 2013-12-07 11:54");
... as a workaround.

Java - SimpleDateFormat wrong format

In my Postgresql database, I have a timestamp without time zone with this value : "2012-06-15 17:49:46.423" (default datatype used by Hibernate).
If I format with SimpleDateFormat and this pattern "EEEE dd MMMM yyyy", SimpleDateFormat#format returns "jeudi 14 juin 2012".
Why the result is not "vendredi 15 juin 2012" ?
French to English translation for non-french spoken :
"jeudi 14 juin 2012" => "Thursday 14th of June 2012"
"vendredi 15 juin 2012" => "Friday 15th of June 2012"
Hibernate 3.6.8.Final
JSF 2.0
JBoss 5.1.0.GA
Java 6
Thanks.
It's showing you just one day ahead of what you expect. Reason might be server time which is on another time zone I guess. Anyways the below code is working fine:
SimpleDateFormat sdf = new SimpleDateFormat("EEEE dd MMMM yyyy", Locale.FRENCH);
System.out.println(sdf.format(new Date()));
After searching, it turns out that I had two main options. Either I set the time zone through Hibernate or JSF.
Option 1 : Hibernate
I could change access type to Property. So in the setter, I would adjust the timestamp value to the right time zone. Another solution would be to create a custom Hibernate User Type.
Full details with code here : Mapping Dates and Time Zones with Hibernate, Part 2: Few Solutions
Option 2 : JSF
Last option, I either create a custom converter in JSF or more simply, I use the timeZone attribute of f:convertDateTime.
This how I did it :
<h:outputText value="#{row.date}">
<f:convertDateTime pattern="EEEE dd MMMM yyyy" locale="fr_FR" type="date" timeZone="Europe/Paris" />
</h:outputText>
All my dates are correct now !
Source (in french) : Comment utiliser le timezone du compensant JSF convertDateTime
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd MMMM yyyy zzzz G", Locale.FRENCH);
try this
also printing out a date will automatically print formatted date text in machine time zone.

Getting Date in HTTP format in Java

I'm trying to get a String of a date in Java in the format specified in HTTP 1.1. Which, as far as I can tell, is:
Fri, 31 Dec 1999 23:59:59 GMT
With the time always being GMT.
What would be the easiest way to get this from Date/Calendar/?
java.time
EDIT:
DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH).withZone(ZoneId.of("GMT"))
is the way to do it with pure java.time. HTTP 1.1 is to not a 100% match with RFC 1123, so using the java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME formatter will fail for day-of-month less than 10. (thanks to #PavanKamar and #ankon for pointing that out)
Note: to be backwards compliant, you would need to also support the other two formats specified by RFC 2616
In case someone else will try to find the answer here (like I did) here's what will do the trick:
String getServerTime() {
Calendar calendar = Calendar.getInstance();
SimpleDateFormat dateFormat = new SimpleDateFormat(
"EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
return dateFormat.format(calendar.getTime());
}
in order to set the server to speak English and give time in GMT timezone.
If you're using Joda-Time (which I would highly recommend for any handling of dates and times in Java), you can do:
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
...
private static final DateTimeFormatter RFC1123_DATE_TIME_FORMATTER =
DateTimeFormat.forPattern("EEE, dd MMM yyyy HH:mm:ss 'GMT'")
.withZoneUTC().withLocale(Locale.US);
...
RFC1123_DATE_TIME_FORMATTER.print(new DateTime())
Two-digit day-of-month
Some applications require the format to include a two digit day-of-month as per RFC7231. The Java 8 DateTimeFormatter.RFC_1123_DATE_TIME uses a single digit:
System.out.println(DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now(ZoneOffset.UTC)));
Output: Wed, 1 Aug 2018 14:56:46 GMT
Some applications don't like that. Before you use the old answers that use Joda-time or a pre-java8 SimpleDateFormat, here's a working Java-8 DateTimeFormatter:
DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss O")
Now, when you do this:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss O");
System.out.println(formatter.format(ZonedDateTime.now(ZoneOffset.UTC)));
You get Wed, 01 Aug 2018 14:56:46 GMT - note the leading zero in the day-of-month field.
If you, like me, are trying to format a Java 8 java.time.Instant you need to explicitly add the time zone to the formatter. Like this:
Instant instant = Instant.now();
String formatted = DateTimeFormatter.RFC_1123_DATE_TIME
.withZone(ZoneOffset.UTC)
.format(instant);
System.out.println(formatted);
Which prints:
Tue, 15 Mar 2016 14:45:34 GMT
If you are not afraid of additional dependencies, you can use apache DateUtils:
import org.apache.http.impl.cookie.DateUtils;
DateUtils.formatDate(new Date(System.currentTimeMillis()));
// Tue, 17 Apr 2012 18:59:02 GMT
This will format your date with respect to RFC 822 RFC1123.
I know this is a late response but just wanted to add it for completeness.
You did not mention what you need the string for. But if you need it for an HTTP response header, you can use HttpServletResponse.setDateHeader(). It does all the formatting for you.
Calendar calendar = Calendar.getInstance();
SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z");
System.out.println("Date: " + dateFormat.format(calendar.getTime()));
You can play with it. The documentation is here:
http://download.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html

Unable to parse DateTime-string with AM/PM marker

The string I want to format looks like this:
String datetime = "9/1/10 11:34:35 AM"
Following pattern for SimpleDateFormat works:
SimpleDateFormat sdf = SimpleDateFormat("M/d/yy h:mm:ss");
Date d = sdf.parse(datetime);
System.out.println(d);
Output> [Wed Sep 01 11:34:35 CEST 2010]
However I need to parse the AM/PM marker as well, and when I add that to the pattern I receive an exception.
Pattern that doesn't work:
SimpleDateFormat sdf = SimpleDateFormat("M/d/yy h:mm:ss a");
I have tried with this also with same exception:
SimpleDateFormat sdf = SimpleDateFormat("M/d/yy h:mm:ss aa");
Exception:
java.text.ParseException: Unparseable date: "9/1/10 11:34:35 AM"
I have looked through the API at http://download.oracle.com/javase/1.4.2/docs/api/java/text/SimpleDateFormat.html#text but canät seem to find where I do wrong.
Any suggestions?
One possibility is that your default Locale has different symbols for AM/PM. When constructing a date format you should always supply a Locale unless you really want to use the system's default Locale, e.g.:
SimpleDateFormat sdf = new SimpleDateFormat("M/d/yy h:mm:ss a", Locale.US)
Modern answer:
String datetime = "9/1/10 11:34:35 AM";
LocalDateTime dt = LocalDateTime.parse(datetime,
DateTimeFormatter.ofPattern("M/d/yy h:mm:ss a", Locale.ENGLISH));
This produces a LocalDateTime of 2010-09-01T11:34:35. Beware of two digit years, though; DateTimeFormatter will assume 2000 through 2099. For my birthday this would have been incorrect.
We still need to provide the locale. Since AM/PM markers are hardly used in practice in other locales than English, I considered Locale.ENGLISH a fairly safe bet. Please substitute your own.
The other answers were fine answers in 2010 and 2011. Already in 2014 the above was valid and I would have preferred it.
I am taking an example of date given below and print the formatted date into 24-hour format if suits your requirement.
String inputdate="9/1/10 11:34:35 AM";
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("dd/MM/yy hh:mm:ss aa",Locale.getDefault());
try {
System.out.println(""+new SimpleDateFormat("dd/MM/yy HH:mm:ss",Locale.getDefault()).format(simpleDateFormat.parse(inputdate)));
} catch (ParseException e) {
e.printStackTrace();
}
If you still have any query, Please respond. Thanks.
java.time
You can build a case-insensitive parser using DateTimeFormatterBuilder. Since a date-time parsing/formatting type (e.g. DateTimeFormatter, SimpleDateFormat etc.) is Locale-sensitive, you should always use a Locale with such a type. I've used Locale.ENGLISH because your date-time string has AM/PM marker in English.
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.util.Locale;
import java.util.stream.Stream;
public class Main {
public static void main(String args[]) {
DateTimeFormatter dtf = new DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendPattern("M/d/uu H:m:s a")
.toFormatter(Locale.ENGLISH);
//Test
Stream.of(
"9/1/10 11:34:35 AM",
"9/1/10 11:34:35 am",
"09/1/10 11:34:35 AM",
"9/01/10 11:34:35 Am"
).forEach(s -> System.out.println(LocalDateTime.parse(s, dtf)));;
}
}
Output:
2010-09-01T11:34:35
2010-09-01T11:34:35
2010-09-01T11:34:35
2010-09-01T11:34:35
Learn more about the the modern date-time API* from Trail: Date Time.
* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.
If you are working with FreeMarker for Java and pop on this issue use below code. I had this problem, my locale set AM/PM as DE. Not sure why...
<#setting locale="en_US">
Just a note about Locale:
the symbols used for AM/PM depend on Locale!
This affects parsing strings, eventually causing errors if the used AM/PM field does not match the predefined symbols. (obviously also affects formatting)
java.time.format.DateTimeFormatter and java.text.SimpleDateFormat accept an optional Locale when being created. If none is given. the systems default one is used.
Warning: using DateTimeFormatter the case of the AM/PM flag is also relevant when parsing, at least for some locales.
As an example, the Indian Locale requires the AM/PM flag being lowercase (am, pm), while some other locales (ROOT, ITALY, US, GERMANY) only accept uppercase AM/PM.
This throws DateTimeParseException: Text '2021-03-31 10:15:30 AM +05:30' could not be parsed at index 20
ZonedDateTime.parse("2021-03-31 10:15:30 AM +05:30",
DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss a Z",
new Locale("en", "IN")))
This results in 2021-03-31T10:15:30+05:30
ZonedDateTime.parse("2021-03-31 10:15:30 am +05:30",
DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss a Z",
new Locale("en", "IN")))
Using Locale.US, Locale.GERMANY or Locale.ROOT, the results are inverted.
Note: case of AM/PM does not matter when parsing with SimpleDateFormat (I am not recommending its use, I prefer DateTimeFormatter)

Categories