1 /*
   2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  *
  26  * @test
  27  * @bug 8176841
  28  * @summary Tests java.time classes deals with Unicode extensions
  29  *      correctly.
  30  * @modules jdk.localedata
  31  * @run testng/othervm -Djava.locale.providers=CLDR JavaTimeTests
  32  */
  33 
  34 import static org.testng.Assert.assertEquals;
  35 
  36 import java.time.DayOfWeek;
  37 import java.time.ZonedDateTime;
  38 import java.time.ZoneId;
  39 import java.time.chrono.Chronology;
  40 import java.time.chrono.HijrahChronology;
  41 import java.time.chrono.JapaneseChronology;
  42 import java.time.format.DateTimeFormatter;
  43 import java.time.format.DateTimeFormatterBuilder;
  44 import java.time.format.FormatStyle;
  45 import java.time.temporal.WeekFields;
  46 import java.util.Locale;
  47 import java.util.TimeZone;
  48 
  49 import org.testng.annotations.AfterTest;
  50 import org.testng.annotations.BeforeTest;
  51 import org.testng.annotations.DataProvider;
  52 import org.testng.annotations.Test;
  53 
  54 /**
  55  * Test JavaTime with BCP47 U extensions
  56  */
  57 @Test
  58 public class JavaTimeTests {
  59     private static TimeZone defaultTZ;
  60 
  61     private static final Chronology JAPANESE = JapaneseChronology.INSTANCE;
  62     private static final Chronology HIJRAH = HijrahChronology.INSTANCE;
  63 
  64     private static final ZoneId ASIATOKYO = ZoneId.of("Asia/Tokyo");
  65     private static final ZoneId AMLA = ZoneId.of("America/Los_Angeles");
  66 
  67     private static final Locale JPTYO = Locale.forLanguageTag("en-u-tz-jptyo");
  68     private static final Locale JCAL = Locale.forLanguageTag("en-u-ca-japanese");
  69     private static final Locale HCAL = Locale.forLanguageTag("en-u-ca-islamic-umalqura");
  70 
  71     private static final Locale FW_SUN = Locale.forLanguageTag("en-US-u-fw-sun");
  72     private static final Locale FW_MON = Locale.forLanguageTag("en-US-u-fw-mon");
  73     private static final Locale FW_TUE = Locale.forLanguageTag("en-US-u-fw-tue");
  74     private static final Locale FW_WED = Locale.forLanguageTag("en-US-u-fw-wed");
  75     private static final Locale FW_THU = Locale.forLanguageTag("en-US-u-fw-thu");
  76     private static final Locale FW_FRI = Locale.forLanguageTag("en-US-u-fw-fri");
  77     private static final Locale FW_SAT = Locale.forLanguageTag("en-US-u-fw-sat");
  78 
  79     private static final Locale RG_GB = Locale.forLanguageTag("en-US-u-rg-gbzzzz");
  80 
  81     private static final ZonedDateTime ZDT = ZonedDateTime.of(2017, 8, 10, 15, 15, 0, 0, AMLA);
  82 
  83     private static final String PATTERN = "GGGG MMMM-dd-uu HH:mm:ss zzzz";
  84 
  85     @BeforeTest
  86     public void beforeTest() {
  87         defaultTZ = TimeZone.getDefault();
  88         TimeZone.setDefault(TimeZone.getTimeZone(AMLA));
  89     }
  90 
  91     @AfterTest
  92     public void afterTest() {
  93         TimeZone.setDefault(defaultTZ);
  94     }
  95 
  96     @DataProvider(name="withLocale")
  97     Object[][] withLocale() {
  98         return new Object[][] {
  99             // Locale, Chrono override, Zone override, Expected Chrono, Expected Zone,
 100             // Expected formatted string
 101             {Locale.JAPAN, null, null, null, null,
 102             "2017\u5e748\u670810\u65e5\u6728\u66dc\u65e5 15\u664215\u520600\u79d2 \u30a2\u30e1\u30ea\u30ab\u592a\u5e73\u6d0b\u590f\u6642\u9593"
 103             },
 104             {Locale.JAPAN, JAPANESE, null, JAPANESE, null,
 105             "\u5e73\u621029\u5e748\u670810\u65e5\u6728\u66dc\u65e5 15\u664215\u520600\u79d2 \u30a2\u30e1\u30ea\u30ab\u592a\u5e73\u6d0b\u590f\u6642\u9593"
 106             },
 107             {Locale.JAPAN, JAPANESE, ASIATOKYO, JAPANESE, ASIATOKYO,
 108             "\u5e73\u621029\u5e748\u670811\u65e5\u91d1\u66dc\u65e5 7\u664215\u520600\u79d2 \u65e5\u672c\u6a19\u6e96\u6642"
 109             },
 110 
 111             {JCAL, null, null, JAPANESE, null,
 112             "Thursday, August 10, 29 Heisei at 3:15:00 PM Pacific Daylight Time"
 113             },
 114             {JCAL, HIJRAH, null, JAPANESE, null,
 115             "Thursday, August 10, 29 Heisei at 3:15:00 PM Pacific Daylight Time"
 116             },
 117             {HCAL, JAPANESE, null, HIJRAH, null,
 118             "Thursday, Dhu\u02bbl-Qi\u02bbdah 18, 1438 AH at 3:15:00 PM Pacific Daylight Time"
 119             },
 120 
 121 
 122             {JPTYO, null, null, null, ASIATOKYO,
 123             "Friday, August 11, 2017 at 7:15:00 AM Japan Standard Time"
 124             },
 125             {JPTYO, null, AMLA, null, ASIATOKYO,
 126             "Friday, August 11, 2017 at 7:15:00 AM Japan Standard Time"
 127             },
 128             // invalid tz
 129             {Locale.forLanguageTag("en-US-u-tz-jpzzz"), null, null, null, null,
 130             "Thursday, August 10, 2017 at 3:15:00 PM Pacific Daylight Time"
 131             },
 132             {Locale.forLanguageTag("en-US-u-tz-jpzzz"), null, AMLA, null, AMLA,
 133             "Thursday, August 10, 2017 at 3:15:00 PM Pacific Daylight Time"
 134             },
 135 
 136             {RG_GB, null, null, null, null,
 137             "Thursday, 10 August 2017 at 15:15:00 Pacific Daylight Time"
 138             },
 139         };
 140     }
 141 
 142     @DataProvider(name="firstDayOfWeek")
 143     Object[][] firstDayOfWeek () {
 144         return new Object[][] {
 145             // Locale, Expected DayOfWeek,
 146             {Locale.US, DayOfWeek.SUNDAY},
 147             {FW_SUN, DayOfWeek.SUNDAY},
 148             {FW_MON, DayOfWeek.MONDAY},
 149             {FW_TUE, DayOfWeek.TUESDAY},
 150             {FW_WED, DayOfWeek.WEDNESDAY},
 151             {FW_THU, DayOfWeek.THURSDAY},
 152             {FW_FRI, DayOfWeek.FRIDAY},
 153             {FW_SAT, DayOfWeek.SATURDAY},
 154 
 155             // invalid case
 156             {Locale.forLanguageTag("en-US-u-fw-xxx"), DayOfWeek.SUNDAY},
 157 
 158             // region override
 159             {RG_GB, DayOfWeek.MONDAY},
 160             {Locale.forLanguageTag("zh-CN-u-rg-eszzzz"), DayOfWeek.MONDAY},
 161 
 162             // "fw" and "rg".
 163             {Locale.forLanguageTag("en-US-u-fw-wed-rg-gbzzzz"), DayOfWeek.WEDNESDAY},
 164             {Locale.forLanguageTag("en-US-u-fw-xxx-rg-gbzzzz"), DayOfWeek.MONDAY},
 165             {Locale.forLanguageTag("en-US-u-fw-xxx-rg-zzzz"), DayOfWeek.SUNDAY},
 166         };
 167     }
 168 
 169     @DataProvider(name="minDaysInFirstWeek")
 170     Object[][] minDaysInFrstWeek () {
 171         return new Object[][] {
 172             // Locale, Expected minDay,
 173             {Locale.US, 1},
 174 
 175             // region override
 176             {RG_GB, 4},
 177             {Locale.forLanguageTag("zh-CN-u-rg-eszzzz"), 4},
 178         };
 179     }
 180 
 181     @DataProvider(name="ofPattern")
 182     Object[][] ofPattern() {
 183         return new Object[][] {
 184             // Locale, Expected Chrono, Expected Zone,
 185             // Expected formatted string
 186             {JCAL, JAPANESE, null,
 187             "Heisei August-10-17 15:15:00 Pacific Daylight Time"
 188             },
 189             {HCAL, HIJRAH, null,
 190             "AH Dhu\u02bbl-Qi\u02bbdah-18-38 15:15:00 Pacific Daylight Time"
 191             },
 192 
 193             {JPTYO, null, ASIATOKYO,
 194             "Anno Domini August-11-17 07:15:00 Japan Standard Time"
 195             },
 196             {Locale.forLanguageTag("en-US-u-tz-jpzzz"), null, null,
 197             "Anno Domini August-10-17 15:15:00 Pacific Daylight Time"
 198             },
 199 
 200             {RG_GB, null, null,
 201             "Anno Domini August-10-17 15:15:00 Pacific Daylight Time"
 202             },
 203 
 204         };
 205     }
 206 
 207     @Test(dataProvider="withLocale")
 208     public void test_withLocale(Locale locale, Chronology chrono, ZoneId zone,
 209                                 Chronology chronoExpected, ZoneId zoneExpected,
 210                                 String formatExpected) {
 211         DateTimeFormatter dtf =
 212             DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL, FormatStyle.FULL)
 213                 .withChronology(chrono).withZone(zone).withLocale(locale);
 214         assertEquals(dtf.getChronology(), chronoExpected);
 215         assertEquals(dtf.getZone(), zoneExpected);
 216         String formatted = dtf.format(ZDT);
 217         assertEquals(formatted, formatExpected);
 218         assertEquals(dtf.parse(formatted, ZonedDateTime::from),
 219             zoneExpected != null ? ZDT.withZoneSameInstant(zoneExpected) : ZDT);
 220     }
 221 
 222     @Test(dataProvider="firstDayOfWeek")
 223     public void test_firstDayOfWeek(Locale locale, DayOfWeek dowExpected) {
 224         DayOfWeek dow = WeekFields.of(locale).getFirstDayOfWeek();
 225         assertEquals(dow, dowExpected);
 226     }
 227 
 228     @Test(dataProvider="minDaysInFirstWeek")
 229     public void test_minDaysInFirstWeek(Locale locale, int minDaysExpected) {
 230         int minDays = WeekFields.of(locale).getMinimalDaysInFirstWeek();
 231         assertEquals(minDays, minDaysExpected);
 232     }
 233 
 234     @Test(dataProvider="ofPattern")
 235     public void test_ofPattern(Locale locale,
 236                                 Chronology chronoExpected, ZoneId zoneExpected,
 237                                 String formatExpected) {
 238         DateTimeFormatter dtf =
 239             DateTimeFormatter.ofPattern(PATTERN, locale);
 240         assertEquals(dtf.getChronology(), chronoExpected);
 241         assertEquals(dtf.getZone(), zoneExpected);
 242         String formatted = dtf.format(ZDT);
 243         assertEquals(formatted, formatExpected);
 244         assertEquals(dtf.parse(formatted, ZonedDateTime::from),
 245             zoneExpected != null ? ZDT.withZoneSameInstant(zoneExpected) : ZDT);
 246     }
 247 
 248     @Test(dataProvider="ofPattern")
 249     public void test_toFormatter(Locale locale,
 250                                 Chronology chronoExpected, ZoneId zoneExpected,
 251                                 String formatExpected) {
 252         DateTimeFormatter dtf =
 253             new DateTimeFormatterBuilder().appendPattern(PATTERN).toFormatter(locale);
 254         assertEquals(dtf.getChronology(), chronoExpected);
 255         assertEquals(dtf.getZone(), zoneExpected);
 256         String formatted = dtf.format(ZDT);
 257         assertEquals(formatted, formatExpected);
 258         assertEquals(dtf.parse(formatted, ZonedDateTime::from),
 259             zoneExpected != null ? ZDT.withZoneSameInstant(zoneExpected) : ZDT);
 260     }
 261 }