Home Posts Books About Me RSS Date Management in Java Considering how crucial date representation and computation is to so many of the sorts of commercial transactions that computers manage, you'd expect computers to be better at handling them. Contrary to reasonable expectation though, date representation, parsing, and storage have been a thorn in the side of programmers since the dawn of computers. In the early days of computing, when space was a very scarce resource, programmers stored dates as six-digit numeric strings — two digits for the year, two for the month, and two for the day. The values themselves were stored as strings, so that string sorting and comparison routines would work correctly on these dates, although date arithmetic was difficult. Worse was the decision to store (on disk!) dates using two-digit years. Sorting and comparison worked just fine as long as the dates themselves occurred before 12/31/99; anything after would be interpreted as the year 1900. I worked as a developer at a life insurance company in the mid 90's; the Y2K bug was already causing problems there, since they carried life insurance policies — on people still living — that dated back to 1899. If you want to do any sort of arithmetic on dates — adding them or comparing them — you need a canonical form to represent them in. Americans and Europeans can't even agree on whether the month should come before they day, and most computer-friendly date formats put the year first so that pure-text sorting will work correctly. To avoid a repeat of the Y2K problem, modern computer systems get away from the concept (and the problems associated with the concept) of days, months and years and instead assign each date its own unique integer code. Such a code has three varying aspects - the granularity, the range, and the starting point. The granularity refers to which points in time get their own codes. Earlier coding-based systems had second-based granularity — every second in time that they were capable or representing was assigned its own unique code. The granulariy, of course, directly impacted the range. An n-bit processor can deal with 2n unique integer values without loss of precision, so the range that could be represented in this scheme is 2n/granularity. C programs used a 32-bit integer and assigned each value to a unique second (not millisecond). This meant that there were 232 unique seconds that could be represented. This works out to: 232 = 4,294,967,296 seconds 232 / 60 = 71,582,788 minutes 232 / 60 / 60 = 1,193,046 hours 232 / 60 / 60 / 24 = 49,710 days 232 / 60 / 60 / 24 / 365 = 136 years This is sort of a short range. To be usefully portable, there must be some agreed-upon "starting point" — in other words, what actual time does the value 0 represent? Obviously, the value 1 would represent the second after that, the value 2 the second after that, and so on, since time moves forward linearly (at least until December 21st of this year, and then all bets are off, at least according to the Mayans...) Arbitrarily, early C library implementers chose the date January 1, 1970. A back-of-the-envelope calculation would suggest that dates in the range from January 1, 1970 - March 11, 2106 (there are 70 days left over when the counter hits 136 years). However, this fails to take into account leap years. There are 34 leap years in the 136 year range that 232 can represent; this means that a 32-bit date can only climb up to Feb 6, 2106. This is sort of a short range. To be usefully portable, there must be some agreed-upon "starting point" — in other words, what actual time does the value 0 represent? Obviously, the value 1 would represent the second after that, the value 2 the second after that, and so on, since time moves forward linearly (at least until December 21st of this year, and then all bets are off, at least according to the Mayans...) Arbitrarily, early C library implementers chose the date January 1, 1970. A back-of-the-envelope calculation would suggest that dates in the range from January 1, 1970 - March 11, 2106 (there are 70 days left over when the counter hits 136 years). However, this fails to take into account leap years. There are 34 leap years in the 136 year range that 2can represent; this means that a 32-bit date can only climb up to Feb 6, 2106. There's a problem with this, though - what about all of the dates before Jan 1, 1970? There's quite a bit of historical information of interest that occurred before 1970. To make maximal use of their 136-year range, C programmers instead consider the 32-bit counter to be a signed integer; the value 0 is still Jan 1, 1970, but negative numbers represent dates prior to that. This means that, following the canonical standard, C dates can represent any date in the range Dec 13, 1901 - Jan 18, 2038. (Meaning that those life insurance policies from 1899 still caused problems). Incidentally, this is why I don't worry too much about retirement. The Y2038 problem that will occur when the old 32-bit counters reach their end of life will be even bigger than the Y2K problem, and nastier to fix. I'll turn 65 in the year 2039 — and since I know how to fix Y2038 problems, I plan on billing $300/hour and working 60-hours a week for the three or four years leading up to retirement. Assuming, that is, that the computer that keeps track of my bank account doesn't crash due to Y2038 problems... Java implementers foresaw the Y2038 problem and gave dates in java a range of 264, and a granularity of one millisecond (one one- thousandth of a second). That means that java can represent 264 unique milliseconds. That works out to an astronomical-seeming 18,446,744,073,709,551,616 values. This represents: 264 / 1000 = 18,446,744,073,709,551.616 seconds 264 / 1000 / 60 = 307,445,734,561,825 minutes 264 / 1000 / 60 / 60 = 5,124,095,576,030 hours 264 / 1000 / 60 / 60 / 24 = 213,503,982,334 days 264 / 1000 / 60 / 60 / 24 / 365 = 584,942,417 years This 584 million year range centers on Jan. 1, 1970, for optimal backwards compatibility. That means that, in java, dates ranging from Dec 2, 292,269,055 B.C. to Aug. 17 292,278,994 A.D. That starts at about 1/50th of the age of the universe and 1/15th of the age of the earth, so it's not perfect — but probably good enough for banking transactions. The Java calendar will roll over four-and-a-half billion years before the predicted end of time, so future programmers will have a Y292278994 problem to contend with, but I plan to be comfortably retired well before that time. This 584 million year range centers on Jan. 1, 1970, for optimal backwards compatibility. That means that, in java, dates ranging from Dec 2, 292,269,055 B.C. to Aug. 17 292,278,994 A.D. That starts at about 1/50th of the age of the universe and 1/15th of the age of the earth, so it's not perfect — but probably good enough for banking transactions. The Java calendar will roll over four-and-a-half billion years before the predicted end of time, so future programmers will have a Y292278994 problem to contend with, but I plan to be comfortably retired well before that time. So, if the code 0 represents January 1, 1970, you'd expect this snippet: Date d = new Date( 0L ); // initialize a date from a long integer code System.out.println( d ); To output "Thu Jan 1 1970", right? Instead, on my computer, it outputs: Wed Dec 31 18:00:00 CST 1969 Wait, what? Isn't 0 supposed to be January 1, 1970? Well, it is... but in which time zone? The JVM will go ahead and translate dates into your local time zone for you — since I'm 6 hours behind GMT, I see 6 PM on Dec. 31, 1969. This suggests that "zero time" in Java is midnight, January 1, 1970, in GMT (Greenwich Mean Time). GMT is the timezone that longitude 0 falls in. In the 1800's, naval pilots divided the Earth into 360 equal vertical slices called longitudes and 360 horizontal slices called latitudes so that points on the Earth's surface could be roughly described in a standard way. Since the earth has a stable axis, it made perfect sense to standardize the 0 latitude as the equator. However, there's no logical standard for the 0 longitude. For some time, the 0 longitude varied from one country to the next (usually being the longitude that the capital of the reporters home country fell in). In 1884, the International Meridian Conference established London (particularly, Greenwich village) as the location of the standard 0 longitude. So far, so good. Assigning each millisecond in a 584-million-year range makes date arithmetic and sorting simple — computers are pretty good at numeric operations. However, computers still need to interface with people, who don't typically work well with 64-bit integers. (Do you know your 64-bit integer birthdate? Mine is 171781200000.) So any date system has to be able to convert from the year-month-day, hour-month-second format that humans are used to into the canonical 64-bit long integer format. This turns out to be quite a bit more difficult than it sounds. It's easy enough to convert seconds to canonical time — just divide by 1000. Minutes? Divide by 60,000. Hours? Divide by 3,600,000. Days? Divide by 86,400,000. Months? Hmmm... well, that depends on which month. (I spent some time looking at this last January if you're curious). Since this conversion is complex, and must take into account a lot of little details such as leap years, Java includes a conversion from year/month/day to canonical format in the java.util.Date class. You can instantiate a Date with a three-integer constructor which will convert to the canonical 64-bit standard representation. It does so, that is, under the assumption that what you want is a Western-style date relative to the year 1900, in the timezone of the currently running system. To support proper internationalization, Sun/Oracle would prefer that you instead use the java.util.Calendar class to specify the timezone and locale, and create dates from that. So, instead of saying: Date d = new Date( 111, 3, 24 ); // 3 = April; 0-based months. For 4/24/2011 (the date of my first blog entry, if you're curious), you should instead say: Calendar c = Calendar.getInstance(); c.set( Calendar.YEAR, 2011 ); c.set( Calendar.MONTH, 3 ); c.set( Calendar.DAY_OF_MONTH, 24 ); Date d = c.getTime(); Although this is more i18n friendly (give getInstance a Locale and it will take into account locale-specific settings), this does involve the instantiation of two objects rather than just one. Each call to Calendar.getInstance returns a unique object; it's not a singleton class. A few quick experiments indicate that listing 2, while easier to internationalize, is about three times slower than listing 1. So, given the three principal components (year, month and day) of a "human- friendly" date, it's a simple matter to convert, although you see quite a few date input boxes in interactive applications that look like Figure 1: Date: / / most users would prefer to type a date in as an atomic unit — and you'll undoubtedly end up dealing with automated forms that consider dates to be one single element, with each subelement separated by a text separator such as a slash or a dash. This means that, to deal with input dates, you need some means of converting dates to and from strings. To make this even trickier, users in different locales prefer different date formats. Europeans are used to day-month-year ordering, but Americans expect month-day-year. Java provides a utility class to deal with exactly this in java.text.DateFormat . This class, however, is pretty picky. If you instantiate one via: DateFormat fmt = DateFormat.getInstance(); and try to parse a date with it, you'll probably end up with a runtime exception. All of these input dates fail: fmt.parse( "4/24/2011" ); fmt.parse( "4/24/11" ); fmt.parse( "2011-04-24" ); fmt.parse( "2011-4-24" ); fmt.parse( "Apr 24, 2011" ); fmt.parse( "April 24, 2011" ); As it turns out, the default instance expects (and requires) both a date and a time, in a very specific format. This parses correctly: fmt.parse( "4/24/11 12:00 AM" ); But these don't: fmt.parse( "4/24/11 12:00:00 AM" ); fmt.parse( "4/24/11 12:00AM" ); fmt.parse( "4/24/11 12:00" ); fmt.parse( "4-24-11 12:00 AM" ); fmt.parse( "12:00 4/24/11" ); In short — DateFormat.getInstance() returns a parser that's not particularly useful. ( DateFormat is really designed to be used the other way — given a date in canonical milliseconds-since-the-epoch format, present it in a human-readable way, according to the current user's locale). There are some other instantiator methods that do permit just dates to be supplied, but they're still picky — for example, there's no way to get DateFormat to parse (or display) a date in the format YYYY-MM-DD. If you're curious, here's a table showing the formats of the various locales that JDK 1.6 supports out-of-the-box: Locale Short Medium Long Full Japanese (Japan) 11/04/24 2011/04/24 2011/04/24 2011年4月24日 Spanish (Peru) 24/04/11 24/04/2011 24 de abril de 2011 domingo 24 de abril de 2011 English 4/24/11 Apr 24, 2011 April 24, 2011 Sunday, April 24, 2011 Japanese (Japan,JP) H23.04.24 H23.04.24 H23.04.24 平成23年4月24日 Spanish (Panama) 04/24/11 04/24/2011 24 de abril de 2011 domingo 24 de abril de 2011 Serbian (Bosnia and Herzegovina) 11-04-24 2011-04-24 24. април 2011. недеља, 24. април 2011. Macedonian 24.4.11 24.4.2011 24, април 2011 недела, 24, април 2011 Spanish (Guatemala) 24/04/11 24/04/2011 24 de abril de 2011 domingo 24 de abril de 2011 Arabic (United Arab Emirates) 24/04/11 24/04/2011 24 أبريل, 2011 24 أبريل, 2011 Norwegian (Norway) 24.04.11 24.apr.2011 24. april 2011 24. april 2011 Albanian (Albania) 11-04-24 2011-04-24 2011-04-24 2011-04-24 Bulgarian 11-4-24 2011-4-24 Неделя, 2011, Април 24 Неделя, 2011, Април 24 Arabic (Iraq) 24/04/11 24/04/2011 24 أبريل, 2011 24 أبريل, 2011 Arabic (Yemen) 24/04/11 24/04/2011 24 أبريل, 2011 24 أبريل, 2011 Hungarian 2011.04.24. 2011.04.24. 2011. április 24. 2011. április 24. Portuguese (Portugal) 24-04-2011 24/Abr/2011 24 de Abril de 2011 Domingo, 24 de Abril de 2011 Greek (Cyprus) 24/04/2011 24 Απρ 2011 24 Απρίλιος 2011 Κυριακή, 24 Απρίλιος 2011 Arabic (Qatar) 24/04/11 24/04/2011 24 أبريل, 2011 24 أبريل, 2011 Macedonian (Macedonia) 24.4.11 24.4.2011 24, април 2011 недела, 24, април 2011 Swedish 2011-04-24 2011-apr-24 den 24 april 2011 den 24 april 2011 German (Switzerland) 24.04.11 24.04.2011 24. April 2011 Sonntag, 24. April 2011 English (United States) 4/24/11 Apr 24, 2011 April 24, 2011 Sunday, April 24, 2011 Finnish (Finland) 24.4.2011 24.4.2011 24. huhtikuuta 2011 24. huhtikuuta 2011 Icelandic 24.4.2011 24.4.2011 24. apríl 2011 24. apríl 2011 Czech 24.4.11 24.4.2011 24. duben 2011 Neděle, 24. duben 2011 English (Malta) 24/04/2011 24 Apr 2011 24 April 2011 Sunday, 24 April 2011 Slovenian (Slovenia) 24.4.11 24.4.2011 Nedelja, 24 april 2011 Nedelja, 24 april 2011 Slovak (Slovakia) 24.4.2011 24.4.2011 Nedeľa, 2011, apríl 24 Nedeľa, 2011, apríl 24 Italian 24/04/11 24-apr-2011 24 aprile 2011 domenica 24 aprile 2011 Turkish (Turkey) 24.04.2011 24.Nis.2011 24 Nisan 2011 Pazar 24 Nisan 2011 Pazar Chinese 11-4-24 2011-4-24 2011年4月24日 2011年4月24日 星期日 Thai 24/4/2011 24 เม.ย. 2011 24 เมษายน 2011 วันอาทิตย์ที่ 24 เมษายน ค.ศ. 2011 Arabic (Saudi Arabia) 24/04/11 24/04/2011 24 أبريل, 2011 24 أبريل, 2011 Norwegian 24.04.11 24.apr.2011 24. april 2011 24. april 2011 English (United Kingdom) 24/04/11 24-Apr-2011 24 April 2011 Sunday, 24 April 2011 Serbian (Serbia and Montenegro) 24.4.11. 24.04.2011. 24.04.2011. недеља, 24.април.2011. Lithuanian 11.4.24 2011-04-24 Sekmadienis, 2011, Balandžio 24 Sekmadienis, 2011, Balandžio 24 Romanian 24.04.2011 24.04.2011 24 aprilie 2011 24 aprilie 2011 English (New Zealand) 24/04/11 24/04/2011 24 April 2011 Sunday, 24 April 2011 Norwegian (Norway,Nynorsk) 24.04.11 24.apr.2011 24. april 2011 24. april 2011 Lithuanian (Lithuania) 11.4.24 2011-04-24 Sekmadienis, 2011, Balandžio 24 Sekmadienis, 2011, Balandžio 24 Spanish (Nicaragua) 04-24-11 04-24-2011 24 de abril de 2011 domingo 24 de abril de 2011 Dutch 24-4-11 24-apr-2011 24 april 2011 zondag 24 april 2011 Irish (Ireland) 24/04/2011 24 Aib 2011 24 Aibreán 2011 Dé Domhnaigh 24 Aibreán 2011 French (Belgium) 24/04/11 24-avr.-2011 24 avril 2011 dimanche 24 avril 2011 Spanish (Spain) 24/04/11 24-abr-2011 24 de abril de 2011 domingo 24 de abril de 2011 Arabic (Lebanon) 24/04/11 24/04/2011 24 نيسان, 2011 24 نيسان, 2011 Korean 11. 4. 24 2011. 4. 24 2011년 4월 24일 (일) 2011년 4월 24일 일요일 French (Canada) 11-04-24 2011-04-24 24 avril 2011 dimanche 24 avril 2011 Estonian (Estonia) 24.04.11 24.04.2011 pühapäev, 24. Aprill 2011. a pühapäev, 24. Aprill 2011 Arabic (Kuwait) 24/04/11 24/04/2011 24 أبريل, 2011 24 أبريل, 2011 Serbian (Serbia) 24.4.11. 24.04.2011. 24.04.2011. недеља, 24.април.2011. Spanish (United States) 4/24/11 abr 24, 2011 24 de abril de 2011 domingo 24 de abril de 2011 Spanish (Mexico) 24/04/11 24/04/2011 24 de abril de 2011 domingo 24 de abril de 2011 Arabic (Sudan) 24/04/11 24/04/2011 24 أبريل, 2011 24 أبريل, 2011 Indonesian (Indonesia) 24/04/11 24 Apr 11 24 April 2011 Minggu 24 April 2011 Russian 24.04.11 24.04.2011 24 Апрель 2011 г. 24 Апрель 2011 г. Latvian 11.24.4 2011.24.4 svētdiena, 2011, 24 aprīlis svētdiena, 2011, 24 aprīlis Spanish (Uruguay) 24/04/11 24/04/2011 24 de abril de 2011 domingo 24 de abril de 2011 Latvian (Latvia) 11.24.4 2011.24.4 svētdiena, 2011, 24 aprīlis svētdiena, 2011, 24 aprīlis Hebrew 24/04/11 24/04/2011 24 אפריל 2011 יום ראשון 24 אפריל 2011 Portuguese (Brazil) 24/04/11 24/04/2011 24 de Abril de 2011 Domingo, 24 de Abril de 2011 Arabic (Syria) 24/04/11 24/04/2011 24 نيسان, 2011 24 نيسان, 2011 Croatian 2011.04.24 2011.04.24 2011. travanj 24 2011. travanj 24 Estonian 24.04.11 24.04.2011 pühapäev, 24. Aprill 2011. a pühapäev, 24. Aprill 2011 Spanish (Dominican Republic) 04/24/11 04/24/2011 24 de abril de 2011 domingo 24 de abril de 2011 French (Switzerland) 24.04.11 24 avr. 2011 24. avril 2011 dimanche, 24. avril 2011 Hindi (India) २४/४/११ २४ अप्रैल, २०११ २४ अप्रैल, २०११ रविवार, २४ अप्रैल, २०११ Spanish (Venezuela) 24/04/11 24/04/2011 24 de abril de 2011 domingo 24 de abril de 2011 Arabic (Bahrain) 24/04/11 24/04/2011 24 أبريل, 2011 24 أبريل, 2011 English (Philippines) 4/24/11 04 24, 11 April 24, 2011 Sunday, April 24, 2011 Arabic (Tunisia) 24/04/11 24/04/2011 24 أبريل, 2011 24 أبريل, 2011 Finnish 24.4.2011 24.4.2011 24. huhtikuuta 2011 24. huhtikuuta 2011 German (Austria) 24.04.11 24.04.2011 24. April 2011 Sonntag, 24. April 2011 Spanish 24/04/11 24-abr-2011 24 de abril de 2011 domingo 24 de abril de 2011 Dutch (Netherlands) 24-4-11 24-apr-2011 24 april 2011 zondag 24 april 2011 Spanish (Ecuador) 24/04/11 24/04/2011 24 de abril de 2011 domingo 24 de abril de 2011 Chinese (Taiwan) 2011/4/24 2011/4/24 2011年4月24日 2011年4月24日 星期日 Arabic (Jordan) 24/04/11 24/04/2011 24 نيسان, 2011 24 نيسان, 2011 Belarusian 24.4.11 24.4.2011 нядзеля, 24, красавіка 2011 нядзеля, 24, красавіка 2011 Icelandic (Iceland) 24.4.2011 24.4.2011 24. apríl 2011 24. apríl 2011 Spanish (Colombia) 24/04/11 24/04/2011 24 de abril de 2011 domingo 24 de abril de 2011 Spanish (Costa Rica) 24/04/11 24/04/2011 24 de abril de 2011 domingo 24 de abril de 2011 Spanish (Chile) 24-04-11 24-04-2011 24 de abril de 2011 domingo 24 de abril de 2011 Arabic (Egypt) 24/04/11 24/04/2011 24 أبريل, 2011 24 أبريل, 2011 English (South Africa) 2011/04/24 24 Apr 2011 24 April 2011 Sunday 24 April 2011 Thai (Thailand) 24/4/2554 24 เม.ย. 2554 24 เมษายน 2554 วันอาทิตย์ที่ 24 เมษายน พ.ศ. 2554 Greek (Greece) 24/4/2011 24 Απρ 2011 24 Απρίλιος 2011 Κυριακή, 24 Απρίλιος 2011 Italian (Italy) 24/04/11 24-apr-2011 24 aprile 2011 domenica 24 aprile 2011 Catalan 24/04/11 24/04/2011 24 / abril / 2011 diumenge, 24 / abril / 2011 Hungarian (Hungary) 2011.04.24. 2011.04.24. 2011. április 24. 2011. április 24. French 24/04/11 24 avr. 2011 24 avril 2011 dimanche 24 avril 2011 English (Ireland) 24/04/11 24-Apr-2011 24 April 2011 24 April 2011 Ukrainian (Ukraine) 24.04.11 24 квіт 2011 24 квітня 2011 неділя, 24 квітня 2011 р. Polish (Poland) 24.04.11 2011-04-24 24 kwiecień 2011 niedziela, 24 kwiecień 2011 French (Luxembourg) 24/04/11 24 avr. 2011 24 avril 2011 dimanche 24 avril 2011 Dutch (Belgium) 24/04/11 24-apr-2011 24 april 2011 zondag 24 april 2011 English (India) 24/4/11 24 Apr, 2011 24 April, 2011 Sunday, 24 April, 2011 Catalan (Spain) 24/04/11 24/04/2011 24 / abril / 2011 diumenge, 24 / abril / 2011 Arabic (Morocco) 24/04/11 24/04/2011 24 أبريل, 2011 24 أبريل, 2011 Spanish (Bolivia) 24-04-11 24-04-2011 24 de abril de 2011 domingo 24 de abril de 2011 English (Australia) 24/04/11 24/04/2011 24 April 2011 Sunday, 24 April 2011 Serbian 24.4.11. 24.04.2011. 24.04.2011. недеља, 24.април.2011. Chinese (Singapore) 24/04/11 24-四月-11 24 四月 2011 24 四月 2011 Portuguese 24-04-2011 24/Abr/2011 24 de Abril de 2011 Domingo, 24 de Abril de 2011 Ukrainian 24.04.11 24 квіт 2011 24 квітня 2011 неділя, 24 квітня 2011 р. Spanish (El Salvador) 04-24-11 04-24-2011 24 de abril de 2011 domingo 24 de abril de 2011 Russian (Russia) 24.04.11 24.04.2011 24 Апрель 2011 г. 24 Апрель 2011 г. Korean (South Korea) 11. 4. 24 2011. 4. 24 2011년 4월 24일 (일) 2011년 4월 24일 일요일 Vietnamese 24/04/2011 24-04-2011 Ngày 24 tháng 4 năm 2011 Chủ nhật, ngày 24 tháng tư năm 2011 Arabic (Algeria) 24/04/11 24/04/2011 24 أبريل, 2011 24 أبريل, 2011 Vietnamese (Vietnam) 24/04/2011 24-04-2011 Ngày 24 tháng 4 năm 2011 Chủ nhật, ngày 24 tháng tư năm 2011 Serbian (Montenegro) 24.4.11. 24.04.2011. 24.04.2011. недеља, 24.април.2011. Albanian 11-04-24 2011-04-24 2011-04-24 2011-04-24 Arabic (Libya) 24/04/11 24/04/2011 24 أبريل, 2011 24 أبريل, 2011 Arabic 24/04/11 24/04/2011 24 أبريل, 2011 24 أبريل, 2011 Chinese (China) 11-4-24 2011-4-24 2011年4月24日 2011年4月24日 星期日 Belarusian (Belarus) 24.4.11 24.4.2011 нядзеля, 24, красавіка 2011 нядзеля, 24, красавіка 2011 Chinese (Hong Kong) 11年4月24日 2011年4月24日 2011年04月24日 星期日 2011年04月24日 星期日 Japanese 11/04/24 2011/04/24 2011/04/24 2011年4月24日 Hebrew (Israel) 24/04/11 24/04/2011 24 אפריל 2011 יום ראשון 24 אפריל 2011 Bulgarian (Bulgaria) 11-4-24 2011-4-24 Неделя, 2011, Април 24 Неделя, 2011, Април 24 Indonesian 11/04/24 2011 Apr 24 2011 April 24 Minggu, 2011 April 24 Maltese (Malta) 24/04/2011 24 Apr 2011 24 ta’ April 2011 Il-Ħadd, 24 ta’ April 2011 Spanish (Paraguay) 24/04/11 24/04/2011 24 de abril de 2011 domingo 24 de abril de 2011 Slovenian 24.4.11 24.4.2011 Nedelja, 24 april 2011 Nedelja, 24 april 2011 French (France) 24/04/11 24 avr. 2011 24 avril 2011 dimanche 24 avril 2011 Czech (Czech Republic) 24.4.11 24.4.2011 24. duben 2011 Neděle, 24. duben 2011 Italian (Switzerland) 24.04.11 24-apr-2011 24. aprile 2011 domenica, 24. aprile 2011 Romanian (Romania) 24.04.2011 24.04.2011 24 aprilie 2011 24 aprilie 2011 Spanish (Puerto Rico) 04-24-11 04-24-2011 24 de abril de 2011 domingo 24 de abril de 2011 English (Canada) 24/04/11 24-Apr-2011 April 24, 2011 Sunday, April 24, 2011 German (Germany) 24.04.11 24.04.2011 24. April 2011 Sonntag, 24. April 2011 Irish 11/04/24 2011 Aib 24 2011 Aibreán 24 Dé Domhnaigh, 2011 Aibreán 24 German (Luxembourg) 24.04.11 24.04.2011 24. April 2011 Sonntag, 24. April 2011 German 24.04.11 24.04.2011 24. April 2011 Sonntag, 24. April 2011 Spanish (Argentina) 24/04/11 24/04/2011 24 de abril de 2011 domingo 24 de abril de 2011 Slovak 24.4.2011 24.4.2011 Nedeľa, 2011, apríl 24 Nedeľa, 2011, apríl 24 Malay (Malaysia) 24/04/2011 24 April 2011 24 April 2011 Ahad 24 Apr 2011 Croatian (Croatia) 24.04.11. 24.04.2011. 2011. travanj 24 2011. travanj 24 English (Singapore) 4/24/11 Apr 24, 2011 April 24, 2011 Sunday, April 24, 2011 Danish 24-04-11 24-04-2011 24. april 2011 24. april 2011 Maltese 24/04/2011 24 Apr 2011 24 ta’ April 2011 Il-Ħadd, 24 ta’ April 2011 Polish 11-04-24 2011-04-24 24 kwiecień 2011 niedziela, 24 kwiecień 2011 Arabic (Oman) 24/04/11 24/04/2011 24 أبريل, 2011 24 أبريل, 2011 Turkish 24.04.2011 24.Nis.2011 24 Nisan 2011 Pazar 24 Nisan 2011 Pazar Thai (Thailand,TH) ๒๔/๔/๒๕๕๔ ๒๔ เม.ย. ๒๕๕๔ ๒๔ เมษายน ๒๕๕๔ วันอาทิตย์ที่ ๒๔ เมษายน พ.ศ. ๒๕๕๔ Greek 24/4/2011 24 Απρ 2011 24 Απρίλιος 2011 Κυριακή, 24 Απρίλιος 2011 Malay 11/04/24 2011 Apr 24 2011 April 24 Ahad, 2011 April 24 Swedish (Sweden) 2011-04-24 2011-apr-24 den 24 april 2011 den 24 april 2011 Danish (Denmark) 24-04-11 24-04-2011 24. april 2011 24. april 2011 Spanish (Honduras) 04-24-11 04-24-2011 24 de abril de 2011 domingo 24 de abril de 2011 There are some pretty sophisticated conversions going on in there — Japan,JP actually converts the year to the DateFormat.parse wants pretty much the exact input format of the current locale. There's a "lenient" setting whose behavior varies from one locale to the next; it's not documented exactly how lenient this behavior is or how it works, so the only way to determine what may be accepted is trial and error. Instead, it's usually easier to use java.text.SimpleDateFormat . SimpleDateFormat allows you to request a specific input format; although it doesn't allow "leniency", you (the developer) have a wide latitude on what sort of date formats you can accept. SimpleDateFormat is a concrete class (it has public constructors, unlike DateFormat ) and when you instantiate one, you supply a date/time format template. The most useful such template (IMHO) is yyyy-MM-dd which will allow you to parse four digit years, followed by a dash, a month (one-based, as a user would expect, not zero-based) another dash, and a day of the month. There are options to allow the user to input an era (such as "AD" or "BC"), a time zone, milliseconds, days of the week, and so on, but I've never had occasion to use any of these. most users would prefer to type a date in as an atomic unit — and you'll undoubtedly end up dealing with automated forms that consider dates to be one single element, with each subelement separated by a text separator such as a slash or a dash. This means that, to deal with input dates, you need some means of converting dates to and from strings. To make this even trickier, users in different locales prefer different date formats. Europeans are used to day-month-year ordering, but Americans expect month-day-year. Java provides a utility class to deal with exactly this in. This class, however, is pretty picky. If you instantiate one via:and try to parse a date with it, you'll probably end up with a runtime exception. All of these input dates fail:As it turns out, the default instance expects (and requires) both a date and a time, in a very specific format. This parses correctly:But these don't:In short —returns a parser that's not particularly useful. (is really designed to be used the other way — given a date in canonical milliseconds-since-the-epoch format, present it in a human-readable way, according to the current user's locale). There are some other instantiator methods that do permit just dates to be supplied, but they're still picky — for example, there's no way to getto parse (or display) a date in the format YYYY-MM-DD. If you're curious, here's a table showing the formats of the various locales that JDK 1.6 supports out-of-the-box:There are some pretty sophisticated conversions going on in there — Japan,JP actually converts the year to the era (年号) which is an older style of tracking years based on the ascendancy of the emporor.wants pretty much the exact input format of the current locale. There's a "lenient" setting whose behavior varies from one locale to the next; it's not documented exactly how lenient this behavior is or how it works, so the only way to determine what may be accepted is trial and error. Instead, it's usually easier to useallows you to request a specific input format; although it doesn't allow "leniency", you (the developer) have a wide latitude on what sort of date formats you can accept.is a concrete class (it has public constructors, unlike) and when you instantiate one, you supply a date/time format template. The most useful such template (IMHO) iswhich will allow you to parse four digit years, followed by a dash, a month (one-based, as a user would expect, not zero-based) another dash, and a day of the month. There are options to allow the user to input an era (such as "AD" or "BC"), a time zone, milliseconds, days of the week, and so on, but I've never had occasion to use any of these. One thing to be careful about with DateFormat and its subclasses such as SimpleDateFormat is that, for some odd reason, they're not thread-safe. If two threads try to to use the same instance to parse (or even format) dates concurrently, behavior is undefined. This is not an idle warning: I ran some tests and found that about 75% of the time, two threads invoking a shared SimpleDateFormat threw nonsensical exceptions such as: Exception in thread "Thread-2" java.lang.NumberFormatException: multiple points at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1082) at java.lang.Double.parseDouble(Double.java:510) at java.text.DigitList.getDouble(DigitList.java:151) at java.text.DecimalFormat.parse(DecimalFormat.java:1302) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1589) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1311) at java.text.DateFormat.parse(DateFormat.java:335) at DateTest$1.run(DateTest.java:48) at java.lang.Thread.run(Thread.java:680) Exception in thread "Thread-2" java.lang.NumberFormatException: For input string: "" at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48) at java.lang.Long.parseLong(Long.java:431) at java.lang.Long.parseLong(Long.java:468) at java.text.DigitList.getLong(DigitList.java:177) at java.text.DecimalFormat.parse(DecimalFormat.java:1297) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1934) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1311) at java.text.DateFormat.parse(DateFormat.java:335) at DateTest$1.run(DateTest.java:48) at java.lang.Thread.run(Thread.java:680) Exception in thread "Thread-1" java.lang.NumberFormatException: For input string: ".2233E2.2233E2" at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1222) at java.lang.Double.parseDouble(Double.java:510) at java.text.DigitList.getDouble(DigitList.java:151) at java.text.DecimalFormat.parse(DecimalFormat.java:1302) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1934) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1311) at java.text.DateFormat.parse(DateFormat.java:335) at DateTest$1.run(DateTest.java:48) at java.lang.Thread.run(Thread.java:680) Good luck trying to figure out what went wrong from a log file entry there. Worse, some times both threads would fail with errors, but other times one would throw an exception and the other would succeed. "Worse", you say? Well, in those cases, the parsed date for "2011-04-24" was, at various times: Fri Apr 23 00:00:00 CDT 2011

Mon Oct 24 00:00:00 CST 1200

Fri Apr 24 00:00:00 CST 1917

Sun Apr 24 00:00:00 CST 1197 In other words, don't ever share a SimpleDateFormat between multiple threads. In other words,share abetween multiple threads. The easiest solution is to just create a new SimpleDateFormat for each thread; if you're feeling particularly lazy, instantiate a new one just before you need to parse a date, and let the garbage collector pick it up after the parsing function returns. Conventional wisdom suggests that this is a major performance drain — however, in benchmarking tests I've run on several systems suggests that instantiating a SimpleDateFormat every time you need one is only about 2-3 times slower than trying to share one. Depending on the platform, this is probably in the 1/1,000,000 of a second (microsecond) range, so I don't worry too much about it. I've seen developers do some pretty funky things with ThreadLocal storage to try to beat this and share a single instance across multiple threads, but the application requirements rarely warrant this. (Remember — good enough is good engineering). In reality, if you're really pinched for performance, though, you should completely forego DateFormat and its subclasses and just do some simple date parsing yourself. The code in listing 3 is about twice as fast as SimpleDateFormat.parse , even when shared. StringTokenizer tok = new StringTokenizer( in, "-" ); Date d = new Date( Integer.parseInt( tok.nextToken() ) - 1900, Integer.parseInt( tok.nextToken() ) - 1, Integer.parseInt( tok.nextToken() ) ); The drawback to this approach is, of course, that it's not at all i18n compliant. However, if you're trying to squeeze every last drop of performance out of your application, you're going to have to bend somewhere. Add a comment: Name: Name is required

Email (will not be displayed publicly):

Comment:

Comment is required

The drawback to this approach is, of course, that it's not at all i18n compliant. However, if you're trying to squeeze every last drop of performance out of your application, you're going to have to bend somewhere.