1 /* 2 * Copyright (c) 2012, 2021, 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 {FormatStyle.MEDIUM, FormatStyle.MEDIUM, IsoChronology.INSTANCE, Locale.FRENCH, "d MMM y, HH:mm:ss"}, 167 {FormatStyle.SHORT, FormatStyle.SHORT, IsoChronology.INSTANCE, Locale.FRENCH, "dd/MM/y HH:mm"}, 168 {FormatStyle.FULL, null, IsoChronology.INSTANCE, Locale.FRENCH, "EEEE d MMMM y"}, 169 {FormatStyle.LONG, null, IsoChronology.INSTANCE, Locale.FRENCH, "d MMMM y"}, 170 {FormatStyle.MEDIUM, null, IsoChronology.INSTANCE, Locale.FRENCH, "d MMM y"}, 171 {FormatStyle.SHORT, null, IsoChronology.INSTANCE, Locale.FRENCH, "dd/MM/y"}, 172 {null, FormatStyle.FULL, IsoChronology.INSTANCE, Locale.FRENCH, "HH:mm:ss zzzz"}, 173 {null, FormatStyle.LONG, IsoChronology.INSTANCE, Locale.FRENCH, "HH:mm:ss z"}, 174 {null, FormatStyle.MEDIUM, IsoChronology.INSTANCE, Locale.FRENCH, "HH:mm:ss"}, 175 {null, FormatStyle.SHORT, IsoChronology.INSTANCE, Locale.FRENCH, "HH:mm"}, 176 177 // Japanese Locale and JapaneseChronology 178 {FormatStyle.FULL, FormatStyle.FULL, JapaneseChronology.INSTANCE, Locale.JAPANESE, "Gy\u5e74M\u6708d\u65e5EEEE H\u6642mm\u5206ss\u79d2 zzzz"}, 179 {FormatStyle.LONG, FormatStyle.LONG, JapaneseChronology.INSTANCE, Locale.JAPANESE, "Gy\u5e74M\u6708d\u65e5 H:mm:ss z"}, 180 {FormatStyle.MEDIUM, FormatStyle.MEDIUM, JapaneseChronology.INSTANCE, Locale.JAPANESE, "Gy\u5e74M\u6708d\u65e5 H:mm:ss"}, 181 {FormatStyle.SHORT, FormatStyle.SHORT, JapaneseChronology.INSTANCE, Locale.JAPANESE, "GGGGGy/M/d H:mm"}, 182 {FormatStyle.FULL, null, JapaneseChronology.INSTANCE, Locale.JAPANESE, "Gy\u5e74M\u6708d\u65e5EEEE"}, 183 {FormatStyle.LONG, null, JapaneseChronology.INSTANCE, Locale.JAPANESE, "Gy\u5e74M\u6708d\u65e5"}, 184 {FormatStyle.MEDIUM, null, JapaneseChronology.INSTANCE, Locale.JAPANESE, "Gy\u5e74M\u6708d\u65e5"}, 185 {FormatStyle.SHORT, null, JapaneseChronology.INSTANCE, Locale.JAPANESE, "GGGGGy/M/d"}, 186 {null, FormatStyle.FULL, JapaneseChronology.INSTANCE, Locale.JAPANESE, "H\u6642mm\u5206ss\u79d2 zzzz"}, 187 {null, FormatStyle.LONG, JapaneseChronology.INSTANCE, Locale.JAPANESE, "H:mm:ss z"}, 188 {null, FormatStyle.MEDIUM, JapaneseChronology.INSTANCE, Locale.JAPANESE, "H:mm:ss"}, 189 {null, FormatStyle.SHORT, JapaneseChronology.INSTANCE, Locale.JAPANESE, "H:mm"}, 190 191 // Chinese Local and Chronology 192 // Android-changed: Since ICU 70, use 24-hour time format 193 // {FormatStyle.FULL, FormatStyle.FULL, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5EEEE zzzz ah:mm:ss"}, 194 // {FormatStyle.LONG, FormatStyle.LONG, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5 z ah:mm:ss"}, 195 // {FormatStyle.MEDIUM, FormatStyle.MEDIUM, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5 ah:mm:ss"}, 196 {FormatStyle.FULL, FormatStyle.FULL, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5EEEE zzzz HH:mm:ss"}, 197 {FormatStyle.LONG, FormatStyle.LONG, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5 z HH:mm:ss"}, 198 {FormatStyle.MEDIUM, FormatStyle.MEDIUM, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5 HH:mm:ss"}, 199 // Android-changed: Since ICU 70, use 24-hour time format 200 // {FormatStyle.SHORT, FormatStyle.SHORT, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy/M/d ah:mm"}, 201 {FormatStyle.SHORT, FormatStyle.SHORT, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy/M/d HH:mm"}, 202 {FormatStyle.FULL, null, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5EEEE"}, 203 {FormatStyle.LONG, null, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5"}, 204 {FormatStyle.MEDIUM, null, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5"}, 205 // Android-changed: Since ICU 68, use single 'y' to represent year in short form like other format styles 206 // {FormatStyle.SHORT, null, MinguoChronology.INSTANCE, Locale.CHINESE, "Gyy/M/d"}, 207 // {null, FormatStyle.FULL, MinguoChronology.INSTANCE, Locale.CHINESE, "zzzz ah:mm:ss"}, 208 // {null, FormatStyle.LONG, MinguoChronology.INSTANCE, Locale.CHINESE, "z ah:mm:ss"}, 209 // {null, FormatStyle.MEDIUM, MinguoChronology.INSTANCE, Locale.CHINESE, "ah:mm:ss"}, 210 // {null, FormatStyle.SHORT, MinguoChronology.INSTANCE, Locale.CHINESE, "ah:mm"}, 211 {FormatStyle.SHORT, null, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy/M/d"}, 212 {null, FormatStyle.FULL, MinguoChronology.INSTANCE, Locale.CHINESE, "zzzz HH:mm:ss"}, 213 {null, FormatStyle.LONG, MinguoChronology.INSTANCE, Locale.CHINESE, "z HH:mm:ss"}, 214 {null, FormatStyle.MEDIUM, MinguoChronology.INSTANCE, Locale.CHINESE, "HH:mm:ss"}, 215 {null, FormatStyle.SHORT, MinguoChronology.INSTANCE, Locale.CHINESE, "HH:mm"}, 216 }; 217 } 218 219 @Test(dataProvider="localePatterns") test_getLocalizedDateTimePattern(FormatStyle dateStyle, FormatStyle timeStyle, Chronology chrono, Locale locale, String expected)220 public void test_getLocalizedDateTimePattern(FormatStyle dateStyle, FormatStyle timeStyle, 221 Chronology chrono, Locale locale, String expected) { 222 // Android-added: The ICU data is different before ICU version 70. http://b/229960530 223 if (VersionInfo.ICU_VERSION.getMajor() < 70) { 224 return; 225 } 226 String actual = DateTimeFormatterBuilder.getLocalizedDateTimePattern(dateStyle, timeStyle, chrono, locale); 227 assertEquals(actual, expected, "Pattern " + convertNonAscii(actual)); 228 } 229 230 /** 231 * Returns a string that includes non-ascii characters after expanding 232 * the non-ascii characters to their Java language \\uxxxx form. 233 * @param input an input string 234 * @return the encoded string. 235 */ convertNonAscii(String input)236 private String convertNonAscii(String input) { 237 StringBuilder sb = new StringBuilder(input.length() * 6); 238 for (int i = 0; i < input.length(); i++) { 239 char ch = input.charAt(i); 240 if (ch < 255) { 241 sb.append(ch); 242 } else { 243 sb.append("\\u"); 244 sb.append(Integer.toHexString(ch)); 245 } 246 } 247 return sb.toString(); 248 } 249 date(int y, int m, int d)250 private static Temporal date(int y, int m, int d) { 251 return LocalDate.of(y, m, d); 252 } 253 } 254