1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package libcore.java.text; 18 19 import java.text.DateFormat; 20 import java.text.ParsePosition; 21 import java.text.SimpleDateFormat; 22 import java.util.Calendar; 23 import java.util.Date; 24 import java.util.GregorianCalendar; 25 import java.util.Locale; 26 import java.util.TimeZone; 27 28 public class SimpleDateFormatTest extends junit.framework.TestCase { 29 30 private static final TimeZone AMERICA_LOS_ANGELES = TimeZone.getTimeZone("America/Los_Angeles"); 31 private static final TimeZone AUSTRALIA_LORD_HOWE = TimeZone.getTimeZone("Australia/Lord_Howe"); 32 33 // The RI fails this test. 34 public void test2DigitYearStartIsCloned() throws Exception { 35 // Test that get2DigitYearStart returns a clone. 36 SimpleDateFormat sdf = new SimpleDateFormat(); 37 Date originalDate = sdf.get2DigitYearStart(); 38 assertNotSame(sdf.get2DigitYearStart(), originalDate); 39 assertEquals(sdf.get2DigitYearStart(), originalDate); 40 originalDate.setTime(0); 41 assertFalse(sdf.get2DigitYearStart().equals(originalDate)); 42 // Test that set2DigitYearStart takes a clone. 43 Date newDate = new Date(); 44 sdf.set2DigitYearStart(newDate); 45 assertNotSame(sdf.get2DigitYearStart(), newDate); 46 assertEquals(sdf.get2DigitYearStart(), newDate); 47 newDate.setTime(0); 48 assertFalse(sdf.get2DigitYearStart().equals(newDate)); 49 } 50 51 // The RI fails this test because this is an ICU-compatible Android extension. 52 // Necessary for correct localization in various languages (http://b/2633414). 53 public void testStandAloneNames() throws Exception { 54 TimeZone.setDefault(TimeZone.getTimeZone("GMT")); 55 Locale en = Locale.ENGLISH; 56 Locale pl = new Locale("pl"); 57 Locale ru = new Locale("ru"); 58 59 assertEquals("January", formatDate(en, "MMMM")); 60 assertEquals("January", formatDate(en, "LLLL")); 61 assertEquals("stycznia", formatDate(pl, "MMMM")); 62 assertEquals("stycze\u0144", formatDate(pl, "LLLL")); 63 64 assertEquals("Thursday", formatDate(en, "EEEE")); 65 assertEquals("Thursday", formatDate(en, "cccc")); 66 assertEquals("\u0447\u0435\u0442\u0432\u0435\u0440\u0433", formatDate(ru, "EEEE")); 67 assertEquals("\u0427\u0435\u0442\u0432\u0435\u0440\u0433", formatDate(ru, "cccc")); 68 69 assertEquals(Calendar.JUNE, parseDate(en, "yyyy-MMMM-dd", "1980-June-12").get(Calendar.MONTH)); 70 assertEquals(Calendar.JUNE, parseDate(en, "yyyy-LLLL-dd", "1980-June-12").get(Calendar.MONTH)); 71 assertEquals(Calendar.JUNE, parseDate(pl, "yyyy-MMMM-dd", "1980-czerwca-12").get(Calendar.MONTH)); 72 assertEquals(Calendar.JUNE, parseDate(pl, "yyyy-LLLL-dd", "1980-czerwiec-12").get(Calendar.MONTH)); 73 74 assertEquals(Calendar.TUESDAY, parseDate(en, "EEEE", "Tuesday").get(Calendar.DAY_OF_WEEK)); 75 assertEquals(Calendar.TUESDAY, parseDate(en, "cccc", "Tuesday").get(Calendar.DAY_OF_WEEK)); 76 assertEquals(Calendar.TUESDAY, parseDate(ru, "EEEE", "\u0432\u0442\u043e\u0440\u043d\u0438\u043a").get(Calendar.DAY_OF_WEEK)); 77 assertEquals(Calendar.TUESDAY, parseDate(ru, "cccc", "\u0412\u0442\u043e\u0440\u043d\u0438\u043a").get(Calendar.DAY_OF_WEEK)); 78 } 79 80 // The RI fails this test because it doesn't fully support UTS #35. 81 // https://code.google.com/p/android/issues/detail?id=39616 82 public void testFiveCount_parsing() throws Exception { 83 // It's pretty silly to try to parse the shortest names, because they're almost always ambiguous. 84 try { 85 parseDate(Locale.ENGLISH, "MMMMM", "J"); 86 fail(); 87 } catch (junit.framework.AssertionFailedError expected) { 88 } 89 try { 90 parseDate(Locale.ENGLISH, "LLLLL", "J"); 91 fail(); 92 } catch (junit.framework.AssertionFailedError expected) { 93 } 94 try { 95 parseDate(Locale.ENGLISH, "EEEEE", "T"); 96 fail(); 97 } catch (junit.framework.AssertionFailedError expected) { 98 } 99 try { 100 parseDate(Locale.ENGLISH, "ccccc", "T"); 101 fail(); 102 } catch (junit.framework.AssertionFailedError expected) { 103 } 104 } 105 106 // The RI fails this test because it doesn't fully support UTS #35. 107 // https://code.google.com/p/android/issues/detail?id=39616 108 public void testFiveCount_M() throws Exception { 109 TimeZone.setDefault(TimeZone.getTimeZone("GMT")); 110 assertEquals("1", formatDate(Locale.ENGLISH, "M")); 111 assertEquals("01", formatDate(Locale.ENGLISH, "MM")); 112 assertEquals("Jan", formatDate(Locale.ENGLISH, "MMM")); 113 assertEquals("January", formatDate(Locale.ENGLISH, "MMMM")); 114 assertEquals("J", formatDate(Locale.ENGLISH, "MMMMM")); 115 } 116 117 // The RI fails this test because it doesn't fully support UTS #35. 118 // https://code.google.com/p/android/issues/detail?id=39616 119 public void testFiveCount_L() throws Exception { 120 TimeZone.setDefault(TimeZone.getTimeZone("GMT")); 121 assertEquals("1", formatDate(Locale.ENGLISH, "L")); 122 assertEquals("01", formatDate(Locale.ENGLISH, "LL")); 123 assertEquals("Jan", formatDate(Locale.ENGLISH, "LLL")); 124 assertEquals("January", formatDate(Locale.ENGLISH, "LLLL")); 125 assertEquals("J", formatDate(Locale.ENGLISH, "LLLLL")); 126 } 127 128 // The RI fails this test because it doesn't fully support UTS #35. 129 // https://code.google.com/p/android/issues/detail?id=39616 130 public void testFiveCount_E() throws Exception { 131 TimeZone.setDefault(TimeZone.getTimeZone("GMT")); 132 assertEquals("Thu", formatDate(Locale.ENGLISH, "E")); 133 assertEquals("Thu", formatDate(Locale.ENGLISH, "EE")); 134 assertEquals("Thu", formatDate(Locale.ENGLISH, "EEE")); 135 assertEquals("Thursday", formatDate(Locale.ENGLISH, "EEEE")); 136 assertEquals("T", formatDate(Locale.ENGLISH, "EEEEE")); 137 // assertEquals("Th", formatDate(Locale.ENGLISH, "EEEEEE")); // icu4c doesn't support 6. 138 } 139 140 // The RI fails this test because it doesn't fully support UTS #35. 141 // https://code.google.com/p/android/issues/detail?id=39616 142 public void testFiveCount_c() throws Exception { 143 TimeZone.setDefault(TimeZone.getTimeZone("GMT")); 144 assertEquals("Thu", formatDate(Locale.ENGLISH, "c")); 145 assertEquals("Thu", formatDate(Locale.ENGLISH, "cc")); 146 assertEquals("Thu", formatDate(Locale.ENGLISH, "ccc")); 147 assertEquals("Thursday", formatDate(Locale.ENGLISH, "cccc")); 148 assertEquals("T", formatDate(Locale.ENGLISH, "ccccc")); 149 // assertEquals("Th", formatDate(Locale.ENGLISH, "cccccc")); // icu4c doesn't support 6. 150 } 151 152 // The RI fails this test because it doesn't fully support UTS #35. 153 // https://code.google.com/p/android/issues/detail?id=39616 154 public void testFiveCount_Z() throws Exception { 155 TimeZone.setDefault(TimeZone.getTimeZone("GMT")); 156 assertEquals("+0000", formatDate(Locale.ENGLISH, "Z")); 157 assertEquals("+0000", formatDate(Locale.ENGLISH, "ZZ")); 158 assertEquals("+0000", formatDate(Locale.ENGLISH, "ZZZ")); 159 assertEquals("GMT+00:00", formatDate(Locale.ENGLISH, "ZZZZ")); 160 assertEquals("+00:00", formatDate(Locale.ENGLISH, "ZZZZZ")); 161 } 162 163 // The RI fails this test because it doesn't fully support UTS #35. 164 // https://code.google.com/p/android/issues/detail?id=39616 165 public void test_parsing_Z() throws Exception { 166 TimeZone.setDefault(TimeZone.getTimeZone("GMT")); 167 assertEquals(1325421240000L, parseTime("yyyy-MM-dd' 'Z", "2012-01-01 -1234")); 168 assertEquals(1325421240000L, parseTime("yyyy-MM-dd' 'ZZ", "2012-01-01 -1234")); 169 assertEquals(1325421240000L, parseTime("yyyy-MM-dd' 'ZZZ", "2012-01-01 -1234")); 170 assertEquals(1325421240000L, parseTime("yyyy-MM-dd' 'ZZZZ", "2012-01-01 GMT-12:34")); 171 assertEquals(1325421240000L, parseTime("yyyy-MM-dd' 'ZZZZZ", "2012-01-01 -12:34")); 172 } 173 174 private static long parseTime(String fmt, String value) { 175 return parseDate(Locale.ENGLISH, fmt, value).getTime().getTime(); 176 } 177 178 public void test2038() { 179 SimpleDateFormat format = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy", Locale.US); 180 format.setTimeZone(TimeZone.getTimeZone("UTC")); 181 182 assertEquals("Sun Nov 24 17:31:44 1833", 183 format.format(new Date(((long) Integer.MIN_VALUE + Integer.MIN_VALUE) * 1000L))); 184 assertEquals("Fri Dec 13 20:45:52 1901", 185 format.format(new Date(Integer.MIN_VALUE * 1000L))); 186 assertEquals("Thu Jan 01 00:00:00 1970", 187 format.format(new Date(0L))); 188 assertEquals("Tue Jan 19 03:14:07 2038", 189 format.format(new Date(Integer.MAX_VALUE * 1000L))); 190 assertEquals("Sun Feb 07 06:28:16 2106", 191 format.format(new Date((2L + Integer.MAX_VALUE + Integer.MAX_VALUE) * 1000L))); 192 } 193 194 private String formatDate(Locale l, String fmt) { 195 DateFormat dateFormat = new SimpleDateFormat(fmt, l); 196 dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); 197 return dateFormat.format(new Date(0)); 198 } 199 200 private static Calendar parseDate(Locale l, String fmt, String value) { 201 SimpleDateFormat sdf = new SimpleDateFormat(fmt, l); 202 ParsePosition pp = new ParsePosition(0); 203 Date d = sdf.parse(value, pp); 204 if (d == null) { 205 fail(pp.toString()); 206 } 207 Calendar c = Calendar.getInstance(TimeZone.getTimeZone("UTC")); 208 c.setTime(d); 209 return c; 210 } 211 212 // http://code.google.com/p/android/issues/detail?id=13420 213 public void testParsingUncommonTimeZoneAbbreviations() { 214 String fmt = "yyyy-MM-dd HH:mm:ss.SSS z"; 215 String date = "2010-12-23 12:44:57.0 CET"; 216 // ICU considers "CET" (Central European Time) to be common in Britain... 217 assertEquals(1293104697000L, parseDate(Locale.UK, fmt, date).getTimeInMillis()); 218 // ...but not in the US. Check we can parse such a date anyway. 219 assertEquals(1293104697000L, parseDate(Locale.US, fmt, date).getTimeInMillis()); 220 } 221 222 public void testFormattingUncommonTimeZoneAbbreviations() { 223 // In Honeycomb, only one Olson id was associated with CET (or any 224 // other "uncommon" abbreviation). 225 String fmt = "yyyy-MM-dd HH:mm:ss.SSS z"; 226 String date = "1970-01-01 01:00:00.000 CET"; 227 SimpleDateFormat sdf = new SimpleDateFormat(fmt, Locale.US); 228 sdf.setTimeZone(TimeZone.getTimeZone("Europe/Berlin")); 229 assertEquals(date, sdf.format(new Date(0))); 230 sdf = new SimpleDateFormat(fmt, Locale.US); 231 sdf.setTimeZone(TimeZone.getTimeZone("Europe/Zurich")); 232 assertEquals(date, sdf.format(new Date(0))); 233 } 234 235 // http://code.google.com/p/android/issues/detail?id=8258 236 public void testTimeZoneFormatting() throws Exception { 237 Date epoch = new Date(0); 238 239 // Create a SimpleDateFormat that defaults to America/Chicago... 240 TimeZone.setDefault(TimeZone.getTimeZone("America/Chicago")); 241 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); 242 // We should see something appropriate to America/Chicago... 243 assertEquals("1969-12-31 18:00:00 -0600", sdf.format(epoch)); 244 // We can set any TimeZone we want: 245 sdf.setTimeZone(AMERICA_LOS_ANGELES); 246 assertEquals("1969-12-31 16:00:00 -0800", sdf.format(epoch)); 247 sdf.setTimeZone(TimeZone.getTimeZone("UTC")); 248 assertEquals("1970-01-01 00:00:00 +0000", sdf.format(epoch)); 249 250 // A new SimpleDateFormat will default to America/Chicago... 251 sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z"); 252 // ...and parsing an America/Los_Angeles time will *not* change that... 253 sdf.parse("2010-12-03 00:00:00 -0800"); 254 // ...so our time zone here is "America/Chicago": 255 assertEquals("1969-12-31 18:00:00 -0600", sdf.format(epoch)); 256 // We can set any TimeZone we want: 257 sdf.setTimeZone(AMERICA_LOS_ANGELES); 258 assertEquals("1969-12-31 16:00:00 -0800", sdf.format(epoch)); 259 sdf.setTimeZone(TimeZone.getTimeZone("UTC")); 260 assertEquals("1970-01-01 00:00:00 +0000", sdf.format(epoch)); 261 262 sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 263 sdf.setTimeZone(TimeZone.getTimeZone("UTC")); 264 Date date = sdf.parse("2010-07-08 02:44:48"); 265 assertEquals(1278557088000L, date.getTime()); 266 sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); 267 sdf.setTimeZone(AMERICA_LOS_ANGELES); 268 assertEquals("2010-07-07T19:44:48-0700", sdf.format(date)); 269 sdf.setTimeZone(TimeZone.getTimeZone("UTC")); 270 assertEquals("2010-07-08T02:44:48+0000", sdf.format(date)); 271 } 272 273 /** 274 * Africa/Cairo standard time is EET and daylight time is EEST. They no 275 * longer use their DST zone but we should continue to parse it properly. 276 */ 277 public void testObsoleteDstZoneName() throws Exception { 278 SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm zzzz", Locale.US); 279 Date normal = format.parse("1970-01-01T00:00 EET"); 280 Date dst = format.parse("1970-01-01T00:00 EEST"); 281 assertEquals(60 * 60 * 1000, normal.getTime() - dst.getTime()); 282 } 283 284 public void testDstZoneNameWithNonDstTimestamp() throws Exception { 285 SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm zzzz", Locale.US); 286 Calendar calendar = new GregorianCalendar(AMERICA_LOS_ANGELES); 287 calendar.setTime(format.parse("2011-06-21T10:00 Pacific Standard Time")); // 18:00 GMT-8 288 assertEquals(11, calendar.get(Calendar.HOUR_OF_DAY)); // 18:00 GMT-7 289 assertEquals(0, calendar.get(Calendar.MINUTE)); 290 } 291 292 public void testNonDstZoneNameWithDstTimestamp() throws Exception { 293 SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm zzzz", Locale.US); 294 Calendar calendar = new GregorianCalendar(AMERICA_LOS_ANGELES); 295 calendar.setTime(format.parse("2010-12-21T10:00 Pacific Daylight Time")); // 17:00 GMT-7 296 assertEquals(9, calendar.get(Calendar.HOUR_OF_DAY)); // 17:00 GMT-8 297 assertEquals(0, calendar.get(Calendar.MINUTE)); 298 } 299 300 // http://b/4723412 301 public void testDstZoneWithNonDstTimestampForNonHourDstZone() throws Exception { 302 SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm zzzz", Locale.US); 303 Calendar calendar = new GregorianCalendar(AUSTRALIA_LORD_HOWE); 304 calendar.setTime(format.parse("2011-06-21T20:00 Lord Howe Daylight Time")); // 9:00 GMT+11 305 assertEquals(19, calendar.get(Calendar.HOUR_OF_DAY)); // 9:00 GMT+10:30 306 assertEquals(30, calendar.get(Calendar.MINUTE)); 307 } 308 309 public void testNonDstZoneWithDstTimestampForNonHourDstZone() throws Exception { 310 SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm zzzz", Locale.US); 311 Calendar calendar = new GregorianCalendar(AUSTRALIA_LORD_HOWE); 312 calendar.setTime(format.parse("2010-12-21T19:30 Lord Howe Standard Time")); //9:00 GMT+10:30 313 assertEquals(20, calendar.get(Calendar.HOUR_OF_DAY)); // 9:00 GMT+11:00 314 assertEquals(0, calendar.get(Calendar.MINUTE)); 315 } 316 317 public void testLocales() throws Exception { 318 // Just run through them all. Handy as a poor man's benchmark, and a sanity check. 319 for (Locale l : Locale.getAvailableLocales()) { 320 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss zzzz", l); 321 sdf.format(new Date(0)); 322 } 323 } 324 325 // http://code.google.com/p/android/issues/detail?id=14963 326 public void testParseTimezoneOnly() throws Exception { 327 new SimpleDateFormat("z", Locale.FRANCE).parse("UTC"); 328 new SimpleDateFormat("z", Locale.US).parse("UTC"); 329 } 330 331 // http://code.google.com/p/android/issues/detail?id=36689 332 public void testParseArabic() throws Exception { 333 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", new Locale("ar", "EG")); 334 sdf.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles")); 335 336 // Can we parse an ASCII-formatted date in an Arabic locale? 337 Date d = sdf.parse("2012-08-29 10:02:45"); 338 assertEquals(1346259765000L, d.getTime()); 339 340 // Can we format a date correctly in an Arabic locale? 341 String formatted = sdf.format(d); 342 assertEquals("-- ::", formatted); 343 344 // Can we parse the Arabic-formatted date in an Arabic locale, and get the same date 345 // we started with? 346 Date d2 = sdf.parse(formatted); 347 assertEquals(d, d2); 348 } 349 350 public void test_59383() throws Exception { 351 SimpleDateFormat sdf = new SimpleDateFormat("d. MMM yyyy H:mm", Locale.GERMAN); 352 sdf.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles")); 353 assertEquals(1376927400000L, sdf.parse("19. Aug 2013 8:50").getTime()); 354 assertEquals(1376927400000L, sdf.parse("19. Aug. 2013 8:50").getTime()); 355 } 356 } 357