Home | History | Annotate | Download | only in text
      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