SimpleDateFormat timezone bug on Android - java

I've been trying to isolate a bug in my application. I succeeded in producing the following "riddle":
SimpleDateFormat f1 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
SimpleDateFormat f2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
Date d = f1.parse("2012-01-01T00:00:00+0700");
String s1 = f1.format(d); // 2011-12-31T18:00:00+0700
String s2 = f2.format(d); // 2011-12-31T18:00:00+0100
I get the values in comments when I run this code on Android API 7 (yes, really). This behavior depends on particular Java implementation.
My questions are:
Why s1 does not equal s2?
And more importantly, why s1 is incorrect? While s2 points to a proper point in time, s1 does not. There seems to be a bug in Android's SimpleDateFormat implementation.
ANSWER TO QUESTION 1: See the answer by BalusC:
[After using SimpleDateFormat#parse] any TimeZone value that has previously been set by a call to setTimeZone may need to be restored for further operations.
ANSWER TO QUESTION 2: See the answer by wrygiel (myself).
This is due to a bug in Android 2.1 (API 7).

This is mentioned in javadoc of DateFormat#parse():
Parse a date/time string according to the given parse position. For example, a time text "07/10/96 4:5 PM, PDT" will be parsed into a Date that is equivalent to Date(837039900000L).
By default, parsing is lenient: If the input is not in the form used by this object's format method but can still be parsed as a date, then the parse succeeds. Clients may insist on strict adherence to the format by calling setLenient(false).
This parsing operation uses the calendar to produce a Date. As a result, the calendar's date-time fields and the TimeZone value may have been overwritten, depending on subclass implementations. Any TimeZone value that has previously been set by a call to setTimeZone may need to be restored for further operations.
Note the last paragraph. It unfortunately doesn't explain when exactly this will occur. To fix your particular problem you need to explicitly set the desired timezone before the formatting operation.
As to the mutability of SimpleDateFormat itself, this is known for years. You should never create and assign an instance of it as a static or class variable, but always as a method (threadlocal) variable.

This is due to a bug in Android 2.1 (API 7). It seems that Android programmers missed some undocumented Java behavior (which is classified as an unfixable bug itself!) in their implementation of Android 2.1.

Your question intrigued me so I went ahead and compiled your code. The result? As expected...
2011-12-31T18:00:00+0100
2011-12-31T18:00:00+0100
The two values are the same, are you using some concurrency? Maybe the variable gets changed on another thread right before the f2.format(d).

I tried to compare s1 and s2 by running the same program. they come equal to me.

Related

Convert country-state code to time zone in Java

Having country-state code as input, is it possible to get the timezone using Java?
For example us-tx -> (GMT-6)
Tried using this answer, however this method uses only the country code, without the state and the output is :
US/Alaska_US/Aleutian_US/Arizona_US/Central_US/East-Indiana_US/Eastern_US/Hawaii_US/Indiana-Starke_US/Michigan_US/Mountain_US/Pacific_US/Pacific-New_US/Samoa
This isn't possible - in any language. Many US states have more than one time zone, depending on which part of the state you are referring to:
From Wikipedia:
You can clearly see on this map, many states where the time zone boundary does not follow the state boundary.

Converting ZonedDateTime to string

I need to store the current time as a String in a database. The time can be in different timezones, so I'm looking at using Java SE 8's new ZonedDateTime class.
I notice that the toString() method automatically outputs:
2016-04-15T17:40:49.305-05:00[America/Chicago]
This also seems to be readable by ZonedDateTime.parse() and convert to the right values.
If all I am doing is storing these values and I don't need to ever convert the value to a user-readable format, is this all I need to do to accurately store data with proper timezones? For example, if I insert two ZonedDateTimes into an SQL database by storing their toString() representations, and I later read in these times by using ZonedDateTime.parse(), can I expect things like isAfter() and isBefore() to work just fine?
Or am I missing a step in between? After trying to figure out timezones in Java 7 this feels almost too easy.
Generally speaking I would avoid relying on parsing the toString() representation of any class in my application.
toString() is meant to provide a human readable version of a class so it's subject to change from relase to release.
I would suggest you to force the format to be the one that you expect, e.g applying:
String toStoreInDb = DateTimeFormatter.ISO_ZONED_DATE_TIME.format(zonedDateTime);
...
ZonedDateTime fromDb =
ZonedDateTime.parse(stringFromDb, DateTimeFormatter.ISO_ZONED_DATE_TIME);
This way your application will resist to any toString() change.
Moreover take a look at this bug:
Bug in ZonedDateTime.parse()
Yes, that will accurately store the date, and using the .parse() method will allow you to use the other methods of ZoneDateTime. Though if you want to be able to use sorting functions with your db then you will need to either manually convert the ZonedDateTime into a timestamp or use your ORM's features to do it for you.

How can I assign an int to a String variable?

I am currently making an assignment for Java but I am stuck. I have to make a birthdate from the three parameters: day, month and year, which are numbers = int. With this I have to put in some checks for valid dates. That part I think is done, but I get stuck at the following:
I want an if statement to check the day, and if the day is correct, this block of code should be run trough
if (dag >=1 && dag <=31)
{
datum = dag;
}
datum Is a String, because I want to get the date like this: DD-MM-YYY
And dag is an Int. So whenever I try to compile this, BlueJ gives an error at this part saying "incompatible types". I assume this is because I try to place a Int in a String. Is this possible in any way, because I can't find out how.
Use String.valueOf method to convert int to string: -
int i = 32;
String str = String.valueOf(i);
And of course follow the advice in #Brian's answer as to what you should rather do in your case.
Don't make it a string. it's not. I think you should
create a Date object to represent your date (day/month/year combined)
use SimpleDateFormat to print that date out in the appropriate format
That's the proper OO way to do it. Otherwise you end up with a bunch of disparate disconnected variables representing in their combination some object type, but you can't manipulate them atomically, invoke methods on them etc. Holding everything as strings is known as stringly-typing (as opposed to strongly-typing) and is a particularly bad code smell!
At some stage check out Joda-Time for a better date/time API than those suggested above. However for the moment I suspect you've got enough on your plate without downloading extra jars.

What is the Java timezone name for Atlanta Georgia? or any location?

Our database keeps track of a number of "Production Facilities". Each facility has an address, name, and most importantly timezone. How do I figure out the name of a timezone?
It was obvious for our initial facilities because the code is named after our city (America/Edmonton). How can I more generally determine the correct timezone code to use in Java via the call DateTimeZone.forID("timeZone") ex DateTimeZone.forID("America/Edmonton")?
Specifically we are opening a facility in Atlanta Georgia and I am unsure which code to use? Is it one of these?
"Atlantic/South_Georgia"
US/Eastern"
"EST5EDT"
"EST"
This kind of information can be really tricky (not to mention tricky to keep current). It can actually vary county by county (there is an excellent West Wing scene about this :). Google Maps API recently included support to go from lat+lon to TimeZone
The IANA standard timezone identifier for Eastern Time is America/New_York. Presumably this is the standard Joda time is using, since (1) everyone uses it and (2) the city-name-as-identifier gives it away.
You can get a list with:
for (String string : TimeZone.getAvailableIDs(TimeZone.getTimeZone(
"GMT-05:00").getRawOffset())) {
System.out.println(string);
}
And it will include (among many others) "US/Eastern".
You can list the available timezone id's using this method:
http://docs.oracle.com/javase/7/docs/api/java/util/TimeZone.html#getAvailableIDs()
The time zone data seems to come from this database:
http://en.wikipedia.org/wiki/Tz_database
You can do lookups in it here:
http://twiki.org/cgi-bin/xtra/tzdatepick.html

SimpleDateFormat localized month names

I have searched throughout the site but I think I have a slightly different issue and could really do with some help before I either have heart failure or burn the computer.
I dynamically generate a list of month names (in the form June 2011, July 2011) and obviously I want this to be locale sensitive: hence I use the simple date format object as follows:
//the actual locale name is dependent on UI selection
Locale localeObject=new Locale("pl");
// intended to return full month name - in local language.
DateFormat dtFormat = new SimpleDateFormat("MMMM yyyy",localeObject);
//this bit just sets up a calendar (used for other bits but here to illustrate the issue
String systemTimeZoneName = "GMT";
TimeZone systemTimeZone=TimeZone.getTimeZone(systemTimeZoneName);
Calendar mCal = new GregorianCalendar(systemTimeZone); //"gmt" time
mCal.getTime(); //current date and time
but if I do this:
String value=dtFormat.format(mCal.getTime());
this "should" return the localized version of the month name. In polish the word "September" is "Wrzesień" -- note the accent on the n. However all I get back is "Wrzesie?"
What am I doing wrong?
Thanks to all - I accept now that it's a presentation issue - but how can I "read" the result from dtFormat safely - I added some comments below ref using getBytes etc. - this worked in other situations, I just can't seem to get access to the string result without messing it up
-- FINAL Edit; for anyone that comes accross this issue
The answer was on BalusC's blog : http://balusc.blogspot.com/2009/05/unicode-how-to-get-characters-right.html#DevelopmentEnvironment
Basically the DTformat object was returning UTF-8 and was being automatically transformed back to the system default character set when I read it into a string
so this code worked for me
new String(dtFormat.format(mCal.getTime()).getBytes("UTF-8"),"ISO-8859-1");
thank you very much for the assistance
Your problem has nothing to do with SimpleDateFormat - you're just doing the wrong thing with the result.
You haven't told us what you're doing with the string afterwards - how you're displaying it in the UI - but that's the problem. You can see that it's fetching a localized string; it's only the display of the accented character which is causing a problem. You would see exactly the same thing if you had a string constant in there containing the same accented character.
I suggest you check all the encodings used throughout your app if it's a web app, or check the font you're displaying the string in if it's a console or Swing app.
If you examine the string in the debugger I'm sure you'll see it's got exactly the right characters - it's just how they're getting to the user which is the problem.
In my tests, dtFormat.format(mCal.getTime()) returns
październik 2011
new SimpleDateFormat(0,0,localeObject).format(mCal.getTime()) returns:
poniedziałek, 3 październik 2011 14:26:53 EDT

Categories