1 /* 2 * Copyright (c) 2012, 2018, 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 * This file is available under and governed by the GNU General Public 26 * License version 2 only, as published by the Free Software Foundation. 27 * However, the following notice accompanied the original version of this 28 * file: 29 * 30 * Copyright (c) 2009-2012, Stephen Colebourne & Michael Nascimento Santos 31 * 32 * All rights reserved. 33 * 34 * Redistribution and use in source and binary forms, with or without 35 * modification, are permitted provided that the following conditions are met: 36 * 37 * * Redistributions of source code must retain the above copyright notice, 38 * this list of conditions and the following disclaimer. 39 * 40 * * Redistributions in binary form must reproduce the above copyright notice, 41 * this list of conditions and the following disclaimer in the documentation 42 * and/or other materials provided with the distribution. 43 * 44 * * Neither the name of JSR-310 nor the names of its contributors 45 * may be used to endorse or promote products derived from this software 46 * without specific prior written permission. 47 * 48 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 49 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 50 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 51 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 52 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 53 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 54 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 55 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 56 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 57 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 58 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 59 */ 60 61 /* 62 * @test 63 * @modules jdk.localedata 64 */ 65 package test.java.time.format; 66 67 import android.icu.util.VersionInfo; 68 import java.time.chrono.ChronoLocalDate; 69 import java.time.chrono.Chronology; 70 import java.time.chrono.IsoChronology; 71 import java.time.chrono.JapaneseChronology; 72 import java.time.chrono.JapaneseEra; 73 import java.time.chrono.MinguoChronology; 74 import java.time.chrono.ThaiBuddhistChronology; 75 import java.time.format.DateTimeFormatter; 76 import java.time.format.DateTimeFormatterBuilder; 77 import java.time.format.FormatStyle; 78 import java.time.format.ResolverStyle; 79 import java.time.LocalDate; 80 import java.time.temporal.ChronoField; 81 import java.time.temporal.Temporal; 82 import java.time.temporal.TemporalAccessor; 83 84 import java.util.HashMap; 85 import java.util.Locale; 86 import java.util.Map; 87 88 import static org.testng.Assert.assertEquals; 89 90 import org.testng.annotations.BeforeMethod; 91 import org.testng.annotations.DataProvider; 92 import org.testng.annotations.Test; 93 94 /** 95 * Test DateTimeFormatterBuilder. 96 */ 97 @Test 98 public class TestDateTimeFormatterBuilderWithLocale { 99 100 private DateTimeFormatterBuilder builder; 101 102 @BeforeMethod setUp()103 public void setUp() { 104 builder = new DateTimeFormatterBuilder(); 105 } 106 107 //----------------------------------------------------------------------- 108 @DataProvider(name="patternPrint") data_patternPrint()109 Object[][] data_patternPrint() { 110 return new Object[][] { 111 {"Q", date(2012, 2, 10), "1"}, 112 {"QQ", date(2012, 2, 10), "01"}, 113 {"QQQ", date(2012, 2, 10), "Q1"}, 114 {"QQQQ", date(2012, 2, 10), "1st quarter"}, 115 {"QQQQQ", date(2012, 2, 10), "1"}, 116 }; 117 } 118 119 @Test(dataProvider="patternPrint") test_appendPattern_patternPrint(String input, Temporal temporal, String expected)120 public void test_appendPattern_patternPrint(String input, Temporal temporal, String expected) throws Exception { 121 DateTimeFormatter f = builder.appendPattern(input).toFormatter(Locale.UK); 122 String test = f.format(temporal); 123 assertEquals(test, expected); 124 } 125 126 //----------------------------------------------------------------------- 127 @DataProvider(name="mapTextLookup") data_mapTextLookup()128 Object[][] data_mapTextLookup() { 129 return new Object[][] { 130 {IsoChronology.INSTANCE.date(1, 1, 1), Locale.ENGLISH}, 131 {JapaneseChronology.INSTANCE.date(JapaneseEra.HEISEI, 1, 1, 8), Locale.ENGLISH}, 132 {MinguoChronology.INSTANCE.date(1, 1, 1), Locale.ENGLISH}, 133 {ThaiBuddhistChronology.INSTANCE.date(1, 1, 1), Locale.ENGLISH}, 134 }; 135 } 136 137 @Test(dataProvider="mapTextLookup") test_appendText_mapTextLookup(ChronoLocalDate date, Locale locale)138 public void test_appendText_mapTextLookup(ChronoLocalDate date, Locale locale) { 139 final String firstYear = "firstYear"; 140 final String firstMonth = "firstMonth"; 141 final String firstYearMonth = firstYear + firstMonth; 142 final long first = 1L; 143 144 DateTimeFormatter formatter = builder 145 .appendText(ChronoField.YEAR_OF_ERA, Map.of(first, firstYear)) 146 .appendText(ChronoField.MONTH_OF_YEAR, Map.of(first, firstMonth)) 147 .toFormatter(locale) 148 .withResolverStyle(ResolverStyle.STRICT); 149 150 assertEquals(date.format(formatter), firstYearMonth); 151 152 TemporalAccessor ta = formatter.parse(firstYearMonth); 153 assertEquals(ta.getLong(ChronoField.YEAR_OF_ERA), first); 154 assertEquals(ta.getLong(ChronoField.MONTH_OF_YEAR), first); 155 } 156 157 158 //----------------------------------------------------------------------- 159 @DataProvider(name="localePatterns") localizedDateTimePatterns()160 Object[][] localizedDateTimePatterns() { 161 // Android-changed: Adapt for changes since old CLDR version this tests were written for. 162 return new Object[][] { 163 // French Locale and ISO Chronology 164 {FormatStyle.FULL, FormatStyle.FULL, IsoChronology.INSTANCE, Locale.FRENCH, "EEEE d MMMM y '\u00e0' HH:mm:ss zzzz"}, 165 {FormatStyle.LONG, FormatStyle.LONG, IsoChronology.INSTANCE, Locale.FRENCH, "d MMMM y '\u00e0' HH:mm:ss z"}, 166 // Android-changed: Since ICU 68, medium format uses ',' instead of '\u00e0', to separate the date and time in French. 167 // {FormatStyle.MEDIUM, FormatStyle.MEDIUM, IsoChronology.INSTANCE, Locale.FRENCH, "d MMM y '\u00e0' HH:mm:ss"}, 168 {FormatStyle.MEDIUM, FormatStyle.MEDIUM, IsoChronology.INSTANCE, Locale.FRENCH, "d MMM y, HH:mm:ss"}, 169 {FormatStyle.SHORT, FormatStyle.SHORT, IsoChronology.INSTANCE, Locale.FRENCH, "dd/MM/y HH:mm"}, 170 {FormatStyle.FULL, null, IsoChronology.INSTANCE, Locale.FRENCH, "EEEE d MMMM y"}, 171 {FormatStyle.LONG, null, IsoChronology.INSTANCE, Locale.FRENCH, "d MMMM y"}, 172 {FormatStyle.MEDIUM, null, IsoChronology.INSTANCE, Locale.FRENCH, "d MMM y"}, 173 {FormatStyle.SHORT, null, IsoChronology.INSTANCE, Locale.FRENCH, "dd/MM/y"}, 174 {null, FormatStyle.FULL, IsoChronology.INSTANCE, Locale.FRENCH, "HH:mm:ss zzzz"}, 175 {null, FormatStyle.LONG, IsoChronology.INSTANCE, Locale.FRENCH, "HH:mm:ss z"}, 176 {null, FormatStyle.MEDIUM, IsoChronology.INSTANCE, Locale.FRENCH, "HH:mm:ss"}, 177 {null, FormatStyle.SHORT, IsoChronology.INSTANCE, Locale.FRENCH, "HH:mm"}, 178 179 // Japanese Locale and JapaneseChronology 180 {FormatStyle.FULL, FormatStyle.FULL, JapaneseChronology.INSTANCE, Locale.JAPANESE, "Gy\u5e74M\u6708d\u65e5EEEE H\u6642mm\u5206ss\u79d2 zzzz"}, 181 {FormatStyle.LONG, FormatStyle.LONG, JapaneseChronology.INSTANCE, Locale.JAPANESE, "Gy\u5e74M\u6708d\u65e5 H:mm:ss z"}, 182 {FormatStyle.MEDIUM, FormatStyle.MEDIUM, JapaneseChronology.INSTANCE, Locale.JAPANESE, "Gy\u5e74M\u6708d\u65e5 H:mm:ss"}, 183 {FormatStyle.SHORT, FormatStyle.SHORT, JapaneseChronology.INSTANCE, Locale.JAPANESE, "GGGGGy/M/d H:mm"}, 184 {FormatStyle.FULL, null, JapaneseChronology.INSTANCE, Locale.JAPANESE, "Gy\u5e74M\u6708d\u65e5EEEE"}, 185 {FormatStyle.LONG, null, JapaneseChronology.INSTANCE, Locale.JAPANESE, "Gy\u5e74M\u6708d\u65e5"}, 186 {FormatStyle.MEDIUM, null, JapaneseChronology.INSTANCE, Locale.JAPANESE, "Gy\u5e74M\u6708d\u65e5"}, 187 {FormatStyle.SHORT, null, JapaneseChronology.INSTANCE, Locale.JAPANESE, "GGGGGy/M/d"}, 188 {null, FormatStyle.FULL, JapaneseChronology.INSTANCE, Locale.JAPANESE, "H\u6642mm\u5206ss\u79d2 zzzz"}, 189 {null, FormatStyle.LONG, JapaneseChronology.INSTANCE, Locale.JAPANESE, "H:mm:ss z"}, 190 {null, FormatStyle.MEDIUM, JapaneseChronology.INSTANCE, Locale.JAPANESE, "H:mm:ss"}, 191 {null, FormatStyle.SHORT, JapaneseChronology.INSTANCE, Locale.JAPANESE, "H:mm"}, 192 193 // Chinese Local and Chronology 194 // Android-changed: Since ICU 70, use 24-hour time format 195 // {FormatStyle.FULL, FormatStyle.FULL, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5EEEE zzzz ah:mm:ss"}, 196 // {FormatStyle.LONG, FormatStyle.LONG, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5 z ah:mm:ss"}, 197 // {FormatStyle.MEDIUM, FormatStyle.MEDIUM, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5 ah:mm:ss"}, 198 {FormatStyle.FULL, FormatStyle.FULL, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5EEEE zzzz HH:mm:ss"}, 199 {FormatStyle.LONG, FormatStyle.LONG, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5 z HH:mm:ss"}, 200 {FormatStyle.MEDIUM, FormatStyle.MEDIUM, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5 HH:mm:ss"}, 201 // Android-changed: Since ICU 68, use single 'y' to represent year in short form like other format styles 202 // {FormatStyle.SHORT, FormatStyle.SHORT, MinguoChronology.INSTANCE, Locale.CHINESE, "Gyy/M/d ah:mm"}, 203 {FormatStyle.SHORT, FormatStyle.SHORT, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy/M/d HH:mm"}, 204 {FormatStyle.FULL, null, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5EEEE"}, 205 {FormatStyle.LONG, null, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5"}, 206 {FormatStyle.MEDIUM, null, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5"}, 207 // Android-changed: Since ICU 68, use single 'y' to represent year in short form like other format styles 208 // {FormatStyle.SHORT, null, MinguoChronology.INSTANCE, Locale.CHINESE, "Gyy/M/d"}, 209 // {null, FormatStyle.FULL, MinguoChronology.INSTANCE, Locale.CHINESE, "zzzz ah:mm:ss"}, 210 // {null, FormatStyle.LONG, MinguoChronology.INSTANCE, Locale.CHINESE, "z ah:mm:ss"}, 211 // {null, FormatStyle.MEDIUM, MinguoChronology.INSTANCE, Locale.CHINESE, "ah:mm:ss"}, 212 // {null, FormatStyle.SHORT, MinguoChronology.INSTANCE, Locale.CHINESE, "ah:mm"}, 213 {FormatStyle.SHORT, null, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy/M/d"}, 214 {null, FormatStyle.FULL, MinguoChronology.INSTANCE, Locale.CHINESE, "zzzz HH:mm:ss"}, 215 {null, FormatStyle.LONG, MinguoChronology.INSTANCE, Locale.CHINESE, "z HH:mm:ss"}, 216 {null, FormatStyle.MEDIUM, MinguoChronology.INSTANCE, Locale.CHINESE, "HH:mm:ss"}, 217 {null, FormatStyle.SHORT, MinguoChronology.INSTANCE, Locale.CHINESE, "HH:mm"}, 218 }; 219 } 220 221 @Test(dataProvider="localePatterns") test_getLocalizedDateTimePattern(FormatStyle dateStyle, FormatStyle timeStyle, Chronology chrono, Locale locale, String expected)222 public void test_getLocalizedDateTimePattern(FormatStyle dateStyle, FormatStyle timeStyle, 223 Chronology chrono, Locale locale, String expected) { 224 // Android-added: The ICU data is different before ICU version 70. http://b/229960530 225 if (VersionInfo.ICU_VERSION.getMajor() < 70) { 226 return; 227 } 228 String actual = DateTimeFormatterBuilder.getLocalizedDateTimePattern(dateStyle, timeStyle, chrono, locale); 229 assertEquals(actual, expected, "Pattern " + convertNonAscii(actual)); 230 } 231 232 /** 233 * Returns a string that includes non-ascii characters after expanding 234 * the non-ascii characters to their Java language \\uxxxx form. 235 * @param input an input string 236 * @return the encoded string. 237 */ convertNonAscii(String input)238 private String convertNonAscii(String input) { 239 StringBuilder sb = new StringBuilder(input.length() * 6); 240 for (int i = 0; i < input.length(); i++) { 241 char ch = input.charAt(i); 242 if (ch < 255) { 243 sb.append(ch); 244 } else { 245 sb.append("\\u"); 246 sb.append(Integer.toHexString(ch)); 247 } 248 } 249 return sb.toString(); 250 } 251 date(int y, int m, int d)252 private static Temporal date(int y, int m, int d) { 253 return LocalDate.of(y, m, d); 254 } 255 } 256