8218948: SimpleDateFormat :: format - Zone Names are not reflected correctly during run time
Reviewed-by: lancea, rgoel
This commit is contained in:
parent
f391d9141b
commit
473a26aebd
@ -73,31 +73,28 @@ public class CLDRTimeZoneNameProviderImpl extends TimeZoneNameProviderImpl {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String[] getDisplayNameArray(String id, Locale locale) {
|
protected String[] getDisplayNameArray(String id, Locale locale) {
|
||||||
String tzid = TimeZoneNameUtility.canonicalTZID(id).orElse(id);
|
// Use English for the ROOT locale
|
||||||
String[] namesSuper = super.getDisplayNameArray(tzid, locale);
|
locale = locale.equals(Locale.ROOT) ? Locale.ENGLISH : locale;
|
||||||
|
String[] namesSuper = super.getDisplayNameArray(id, locale);
|
||||||
|
|
||||||
if (Objects.nonNull(namesSuper)) {
|
if (namesSuper == null) {
|
||||||
|
// try canonical id instead
|
||||||
|
namesSuper = super.getDisplayNameArray(
|
||||||
|
TimeZoneNameUtility.canonicalTZID(id).orElse(id),
|
||||||
|
locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (namesSuper != null) {
|
||||||
// CLDR's resource bundle has an translated entry for this id.
|
// CLDR's resource bundle has an translated entry for this id.
|
||||||
// Fix up names if needed, either missing or no-inheritance
|
// Fix up names if needed, either missing or no-inheritance
|
||||||
namesSuper[INDEX_TZID] = id;
|
namesSuper[INDEX_TZID] = id;
|
||||||
|
|
||||||
// Check if standard long name exists. If not, try to retrieve the name
|
|
||||||
// from language only locale resources. E.g., "Europe/London"
|
|
||||||
// for en-GB only contains DST names
|
|
||||||
if (!exists(namesSuper, INDEX_STD_LONG) && !locale.getCountry().isEmpty()) {
|
|
||||||
String[] names =
|
|
||||||
getDisplayNameArray(id, Locale.forLanguageTag(locale.getLanguage()));
|
|
||||||
if (exists(names, INDEX_STD_LONG)) {
|
|
||||||
namesSuper[INDEX_STD_LONG] = names[INDEX_STD_LONG];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int i = INDEX_STD_LONG; i < namesSuper.length; i++) { // index 0 is the 'id' itself
|
for(int i = INDEX_STD_LONG; i < namesSuper.length; i++) { // index 0 is the 'id' itself
|
||||||
switch (namesSuper[i]) {
|
switch (namesSuper[i]) {
|
||||||
case "":
|
case "":
|
||||||
// Fill in empty elements
|
// Fill in empty elements
|
||||||
deriveFallbackName(namesSuper, i, locale,
|
deriveFallbackName(namesSuper, i, locale,
|
||||||
namesSuper[INDEX_DST_LONG].isEmpty());
|
!exists(namesSuper, INDEX_DST_LONG));
|
||||||
break;
|
break;
|
||||||
case NO_INHERITANCE_MARKER:
|
case NO_INHERITANCE_MARKER:
|
||||||
// CLDR's "no inheritance marker"
|
// CLDR's "no inheritance marker"
|
||||||
@ -141,16 +138,19 @@ public class CLDRTimeZoneNameProviderImpl extends TimeZoneNameProviderImpl {
|
|||||||
|
|
||||||
// Derive fallback time zone name according to LDML's logic
|
// Derive fallback time zone name according to LDML's logic
|
||||||
private void deriveFallbackNames(String[] names, Locale locale) {
|
private void deriveFallbackNames(String[] names, Locale locale) {
|
||||||
|
boolean noDST = !exists(names, INDEX_DST_LONG);
|
||||||
for (int i = INDEX_STD_LONG; i <= INDEX_GEN_SHORT; i++) {
|
for (int i = INDEX_STD_LONG; i <= INDEX_GEN_SHORT; i++) {
|
||||||
deriveFallbackName(names, i, locale, false);
|
deriveFallbackName(names, i, locale, noDST);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deriveFallbackName(String[] names, int index, Locale locale, boolean noDST) {
|
private void deriveFallbackName(String[] names, int index, Locale locale, boolean noDST) {
|
||||||
|
String id = names[INDEX_TZID];
|
||||||
|
|
||||||
if (exists(names, index)) {
|
if (exists(names, index)) {
|
||||||
if (names[index].equals(NO_INHERITANCE_MARKER)) {
|
if (names[index].equals(NO_INHERITANCE_MARKER)) {
|
||||||
// CLDR's "no inheritance marker"
|
// CLDR's "no inheritance marker"
|
||||||
names[index] = toGMTFormat(names[INDEX_TZID],
|
names[index] = toGMTFormat(id,
|
||||||
index == INDEX_DST_LONG || index == INDEX_DST_SHORT,
|
index == INDEX_DST_LONG || index == INDEX_DST_SHORT,
|
||||||
index % 2 != 0, locale);
|
index % 2 != 0, locale);
|
||||||
}
|
}
|
||||||
@ -160,8 +160,8 @@ public class CLDRTimeZoneNameProviderImpl extends TimeZoneNameProviderImpl {
|
|||||||
// Check if COMPAT can substitute the name
|
// Check if COMPAT can substitute the name
|
||||||
if (LocaleProviderAdapter.getAdapterPreference().contains(Type.JRE)) {
|
if (LocaleProviderAdapter.getAdapterPreference().contains(Type.JRE)) {
|
||||||
String[] compatNames = (String[])LocaleProviderAdapter.forJRE()
|
String[] compatNames = (String[])LocaleProviderAdapter.forJRE()
|
||||||
.getLocaleResources(locale)
|
.getLocaleResources(mapChineseLocale(locale))
|
||||||
.getTimeZoneNames(names[INDEX_TZID]);
|
.getTimeZoneNames(id);
|
||||||
if (compatNames != null) {
|
if (compatNames != null) {
|
||||||
for (int i = INDEX_STD_LONG; i <= INDEX_GEN_SHORT; i++) {
|
for (int i = INDEX_STD_LONG; i <= INDEX_GEN_SHORT; i++) {
|
||||||
// Assumes COMPAT has no empty slots
|
// Assumes COMPAT has no empty slots
|
||||||
@ -184,9 +184,8 @@ public class CLDRTimeZoneNameProviderImpl extends TimeZoneNameProviderImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// last resort
|
// last resort
|
||||||
String id = names[INDEX_TZID].toUpperCase(Locale.ROOT);
|
if (!id.toUpperCase(Locale.ROOT).startsWith("UT")) {
|
||||||
if (!id.startsWith("UT")) {
|
names[index] = toGMTFormat(id,
|
||||||
names[index] = toGMTFormat(names[INDEX_TZID],
|
|
||||||
index == INDEX_DST_LONG || index == INDEX_DST_SHORT,
|
index == INDEX_DST_LONG || index == INDEX_DST_SHORT,
|
||||||
index % 2 != 0,
|
index % 2 != 0,
|
||||||
locale);
|
locale);
|
||||||
@ -290,4 +289,33 @@ public class CLDRTimeZoneNameProviderImpl extends TimeZoneNameProviderImpl {
|
|||||||
String.format(l, hourFormat, offset / 60, offset % 60));
|
String.format(l, hourFormat, offset / 60, offset % 60));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mapping CLDR's Simplified/Traditional Chinese resources
|
||||||
|
// to COMPAT's zh-CN/TW
|
||||||
|
private Locale mapChineseLocale(Locale locale) {
|
||||||
|
if (locale.getLanguage() == "zh") {
|
||||||
|
switch (locale.getScript()) {
|
||||||
|
case "Hans":
|
||||||
|
return Locale.CHINA;
|
||||||
|
case "Hant":
|
||||||
|
return Locale.TAIWAN;
|
||||||
|
case "":
|
||||||
|
// no script, guess from country code.
|
||||||
|
switch (locale.getCountry()) {
|
||||||
|
case "":
|
||||||
|
case "CN":
|
||||||
|
case "SG":
|
||||||
|
return Locale.CHINA;
|
||||||
|
case "HK":
|
||||||
|
case "MO":
|
||||||
|
case "TW":
|
||||||
|
return Locale.TAIWAN;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no need to map
|
||||||
|
return locale;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -56,7 +56,6 @@ import java.util.TimeZone;
|
|||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
import sun.security.action.GetPropertyAction;
|
import sun.security.action.GetPropertyAction;
|
||||||
import sun.util.calendar.ZoneInfo;
|
|
||||||
import sun.util.resources.LocaleData;
|
import sun.util.resources.LocaleData;
|
||||||
import sun.util.resources.OpenListResourceBundle;
|
import sun.util.resources.OpenListResourceBundle;
|
||||||
import sun.util.resources.ParallelListResourceBundle;
|
import sun.util.resources.ParallelListResourceBundle;
|
||||||
@ -271,17 +270,31 @@ public class LocaleResources {
|
|||||||
|
|
||||||
if (Objects.isNull(data) || Objects.isNull(val = data.get())) {
|
if (Objects.isNull(data) || Objects.isNull(val = data.get())) {
|
||||||
TimeZoneNamesBundle tznb = localeData.getTimeZoneNames(locale);
|
TimeZoneNamesBundle tznb = localeData.getTimeZoneNames(locale);
|
||||||
if (tznb.containsKey(key)) {
|
|
||||||
if (key.startsWith(TZNB_EXCITY_PREFIX)) {
|
if (key.startsWith(TZNB_EXCITY_PREFIX)) {
|
||||||
|
if (tznb.containsKey(key)) {
|
||||||
val = tznb.getString(key);
|
val = tznb.getString(key);
|
||||||
assert val instanceof String;
|
assert val instanceof String;
|
||||||
trace("tznb: %s key: %s, val: %s\n", tznb, key, val);
|
trace("tznb: %s key: %s, val: %s\n", tznb, key, val);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
String[] names = tznb.getStringArray(key);
|
String[] names = null;
|
||||||
|
if (tznb.containsKey(key)) {
|
||||||
|
names = tznb.getStringArray(key);
|
||||||
|
} else {
|
||||||
|
var tz = TimeZoneNameUtility.canonicalTZID(key).orElse(key);
|
||||||
|
if (tznb.containsKey(tz)) {
|
||||||
|
names = tznb.getStringArray(tz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (names != null) {
|
||||||
|
names[0] = key;
|
||||||
trace("tznb: %s key: %s, names: %s, %s, %s, %s, %s, %s, %s\n", tznb, key,
|
trace("tznb: %s key: %s, names: %s, %s, %s, %s, %s, %s, %s\n", tznb, key,
|
||||||
names[0], names[1], names[2], names[3], names[4], names[5], names[6]);
|
names[0], names[1], names[2], names[3], names[4], names[5], names[6]);
|
||||||
val = names;
|
val = names;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (val != null) {
|
||||||
cache.put(cacheKey,
|
cache.put(cacheKey,
|
||||||
new ResourceReference(cacheKey, val, referenceQueue));
|
new ResourceReference(cacheKey, val, referenceQueue));
|
||||||
}
|
}
|
||||||
@ -321,8 +334,6 @@ public class LocaleResources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (type == LocaleProviderAdapter.Type.CLDR) {
|
if (type == LocaleProviderAdapter.Type.CLDR) {
|
||||||
// Add aliases data for CLDR
|
|
||||||
Map<String, String> aliases = ZoneInfo.getAliasTable();
|
|
||||||
// Note: TimeZoneNamesBundle creates a String[] on each getStringArray call.
|
// Note: TimeZoneNamesBundle creates a String[] on each getStringArray call.
|
||||||
|
|
||||||
// Add timezones which are not present in this keyset,
|
// Add timezones which are not present in this keyset,
|
||||||
@ -335,9 +346,10 @@ public class LocaleResources {
|
|||||||
if (keyset.contains(tzid)) {
|
if (keyset.contains(tzid)) {
|
||||||
val = rb.getStringArray(tzid);
|
val = rb.getStringArray(tzid);
|
||||||
} else {
|
} else {
|
||||||
String tz = aliases.get(tzid);
|
var canonID = TimeZoneNameUtility.canonicalTZID(tzid)
|
||||||
if (keyset.contains(tz)) {
|
.orElse(tzid);
|
||||||
val = rb.getStringArray(tz);
|
if (keyset.contains(canonID)) {
|
||||||
|
val = rb.getStringArray(canonID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val[0] = tzid;
|
val[0] = tzid;
|
||||||
|
368
test/jdk/java/text/Format/DateFormat/SDFTCKZoneNamesTest.java
Normal file
368
test/jdk/java/text/Format/DateFormat/SDFTCKZoneNamesTest.java
Normal file
@ -0,0 +1,368 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @bug 8218948
|
||||||
|
* @summary TCK tests that check the time zone names between DFS.getZoneStrings()
|
||||||
|
* and SDF.format("z*")
|
||||||
|
* @run main SDFTCKZoneNamesTest
|
||||||
|
*/
|
||||||
|
import java.text.*;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
public class SDFTCKZoneNamesTest {
|
||||||
|
|
||||||
|
StringBuffer myFormat(Date date, SimpleDateFormat sdf) {
|
||||||
|
String pattern = sdf.toPattern();
|
||||||
|
StringBuffer toAppendTo = new StringBuffer("");
|
||||||
|
boolean inQuote = false;
|
||||||
|
char prevCh = 0;
|
||||||
|
char ch;
|
||||||
|
int count = 0;
|
||||||
|
for (int i = 0; i < pattern.length(); i++) {
|
||||||
|
ch = pattern.charAt(i);
|
||||||
|
if (inQuote) {
|
||||||
|
if (ch == '\'') {
|
||||||
|
inQuote = false;
|
||||||
|
if (count == 0) toAppendTo.append(ch);
|
||||||
|
else count = 0;
|
||||||
|
} else {
|
||||||
|
toAppendTo.append(ch);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
} else { // not inQuote
|
||||||
|
if (ch == '\'') {
|
||||||
|
inQuote = true;
|
||||||
|
if (count > 0) {
|
||||||
|
toAppendTo.append(subFormat(prevCh, count, date, sdf));
|
||||||
|
count = 0;
|
||||||
|
prevCh = 0;
|
||||||
|
}
|
||||||
|
} else if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z') {
|
||||||
|
if (ch != prevCh && count > 0) {
|
||||||
|
toAppendTo.append(subFormat(prevCh, count, date, sdf));
|
||||||
|
prevCh = ch;
|
||||||
|
count = 1;
|
||||||
|
} else {
|
||||||
|
if (ch != prevCh) prevCh = ch;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
} else if (count > 0) {
|
||||||
|
toAppendTo.append(subFormat(prevCh, count, date, sdf));
|
||||||
|
toAppendTo.append(ch);
|
||||||
|
prevCh = 0;
|
||||||
|
count = 0;
|
||||||
|
} else toAppendTo.append(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (count > 0) {
|
||||||
|
toAppendTo.append(subFormat(prevCh, count, date, sdf));
|
||||||
|
}
|
||||||
|
return toAppendTo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String subFormat(char ch, int count, Date date, SimpleDateFormat sdf)
|
||||||
|
throws IllegalArgumentException {
|
||||||
|
int value = 0;
|
||||||
|
int patternCharIndex = -1;
|
||||||
|
int maxIntCount = 10;
|
||||||
|
String current = "";
|
||||||
|
DateFormatSymbols formatData = sdf.getDateFormatSymbols();
|
||||||
|
Calendar calendar = sdf.getCalendar();
|
||||||
|
calendar.setTime(date);
|
||||||
|
NumberFormat nf = sdf.getNumberFormat();
|
||||||
|
nf.setGroupingUsed(false);
|
||||||
|
|
||||||
|
if ((patternCharIndex = "GyMdkHmsSEDFwWahKz".indexOf(ch)) == -1)
|
||||||
|
throw new IllegalArgumentException("Illegal pattern character " +
|
||||||
|
"'" + ch + "'");
|
||||||
|
switch (patternCharIndex) {
|
||||||
|
case 0: // 'G' - ERA
|
||||||
|
value = calendar.get(Calendar.ERA);
|
||||||
|
current = formatData.getEras()[value];
|
||||||
|
break;
|
||||||
|
case 1: // 'y' - YEAR
|
||||||
|
value = calendar.get(Calendar.YEAR);
|
||||||
|
|
||||||
|
if (count == 2) {
|
||||||
|
// For formatting, if the number of pattern letters is 2,
|
||||||
|
// the year is truncated to 2 digits;
|
||||||
|
current = zeroPaddingNumber(value, 2, 2, nf);
|
||||||
|
} else {
|
||||||
|
// otherwise it is interpreted as a number.
|
||||||
|
current = zeroPaddingNumber(value, count, maxIntCount, nf);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 2: // 'M' - MONTH
|
||||||
|
value = calendar.get(Calendar.MONTH);
|
||||||
|
if (count >= 4)
|
||||||
|
// DateFormatSymbols::getMonths spec: "If the language requires different forms for formatting
|
||||||
|
// and stand-alone usages, this method returns month names in the formatting form."
|
||||||
|
// Because of that only formatting cases patterns may be tested. Like, "MMMM yyyy". Wrong
|
||||||
|
// pattern: "MMMM".
|
||||||
|
current = formatData.getMonths()[value];
|
||||||
|
else if (count == 3)
|
||||||
|
// DateFormatSymbols::getShortMonths spec: "If the language requires different forms for formatting
|
||||||
|
// and stand-alone usages, This method returns short month names in the formatting form."
|
||||||
|
// Because of that only formatting cases patterns may be tested. Like, "MMM yyyy". Wrong pattern:
|
||||||
|
// "MMM".
|
||||||
|
current = formatData.getShortMonths()[value];
|
||||||
|
else
|
||||||
|
current = zeroPaddingNumber(value + 1, count, maxIntCount, nf);
|
||||||
|
break;
|
||||||
|
case 3: // 'd' - DATE
|
||||||
|
value = calendar.get(Calendar.DATE);
|
||||||
|
current = zeroPaddingNumber(value, count, maxIntCount, nf);
|
||||||
|
break;
|
||||||
|
case 4: // 'k' - HOUR_OF_DAY: 1-based. eg, 23:59 + 1 hour =>> 24:59
|
||||||
|
if ((value = calendar.get(Calendar.HOUR_OF_DAY)) == 0)
|
||||||
|
current = zeroPaddingNumber(
|
||||||
|
calendar.getMaximum(Calendar.HOUR_OF_DAY) + 1,
|
||||||
|
count, maxIntCount, nf);
|
||||||
|
else
|
||||||
|
current = zeroPaddingNumber(value, count, maxIntCount, nf);
|
||||||
|
break;
|
||||||
|
case 5: // 'H' - HOUR_OF_DAY:0-based. eg, 23:59 + 1 hour =>> 00:59
|
||||||
|
value = calendar.get(Calendar.HOUR_OF_DAY);
|
||||||
|
current = zeroPaddingNumber(value, count, maxIntCount, nf);
|
||||||
|
break;
|
||||||
|
case 6: // 'm' - MINUTE
|
||||||
|
value = calendar.get(Calendar.MINUTE);
|
||||||
|
current = zeroPaddingNumber(value, count, maxIntCount, nf);
|
||||||
|
break;
|
||||||
|
case 7: // 's' - SECOND
|
||||||
|
value = calendar.get(Calendar.SECOND);
|
||||||
|
current = zeroPaddingNumber(value, count, maxIntCount, nf);
|
||||||
|
break;
|
||||||
|
case 8: // 'S' - MILLISECOND
|
||||||
|
value = calendar.get(Calendar.MILLISECOND);
|
||||||
|
/*
|
||||||
|
if (count > 3)
|
||||||
|
value = value * (int) Math.pow(10, count - 3);
|
||||||
|
else if (count == 2)
|
||||||
|
value = (value + 5) / 10;
|
||||||
|
else if (count == 1)
|
||||||
|
value = (value + 50) / 100;
|
||||||
|
*/
|
||||||
|
current = zeroPaddingNumber(value, count, maxIntCount, nf);
|
||||||
|
break;
|
||||||
|
case 9: // 'E' - DAY_OF_WEEK
|
||||||
|
value = calendar.get(Calendar.DAY_OF_WEEK);
|
||||||
|
if (count >= 4)
|
||||||
|
current = formatData.getWeekdays()[value];
|
||||||
|
else // count < 4, use abbreviated form if exists
|
||||||
|
current = formatData.getShortWeekdays()[value];
|
||||||
|
break;
|
||||||
|
case 10: // 'D' - DAY_OF_YEAR
|
||||||
|
value = calendar.get(Calendar.DAY_OF_YEAR);
|
||||||
|
current = zeroPaddingNumber(value, count, maxIntCount, nf);
|
||||||
|
break;
|
||||||
|
case 11: // 'F' - DAY_OF_WEEK_IN_MONTH
|
||||||
|
value = calendar.get(Calendar.DAY_OF_WEEK_IN_MONTH);
|
||||||
|
current = zeroPaddingNumber(value, count, maxIntCount, nf);
|
||||||
|
break;
|
||||||
|
case 12: // 'w' - WEEK_OF_YEAR
|
||||||
|
value = calendar.get(Calendar.WEEK_OF_YEAR);
|
||||||
|
current = zeroPaddingNumber(value, count, maxIntCount, nf);
|
||||||
|
break;
|
||||||
|
case 13: // 'W' - WEEK_OF_MONTH
|
||||||
|
value = calendar.get(Calendar.WEEK_OF_MONTH);
|
||||||
|
current = zeroPaddingNumber(value, count, maxIntCount, nf);
|
||||||
|
break;
|
||||||
|
case 14: // 'a' - AM_PM
|
||||||
|
value = calendar.get(Calendar.AM_PM);
|
||||||
|
current = formatData.getAmPmStrings()[value];
|
||||||
|
break;
|
||||||
|
case 15: // 'h' - HOUR:1-based. eg, 11PM + 1 hour =>> 12 AM
|
||||||
|
if ((value = calendar.get(Calendar.HOUR)) == 0)
|
||||||
|
current = zeroPaddingNumber(
|
||||||
|
calendar.getLeastMaximum(Calendar.HOUR) + 1,
|
||||||
|
count, maxIntCount, nf);
|
||||||
|
else
|
||||||
|
current = zeroPaddingNumber(value, count, maxIntCount, nf);
|
||||||
|
break;
|
||||||
|
case 16: // 'K' - HOUR: 0-based. eg, 11PM + 1 hour =>> 0 AM
|
||||||
|
value = calendar.get(Calendar.HOUR);
|
||||||
|
current = zeroPaddingNumber(value, count, maxIntCount, nf);
|
||||||
|
break;
|
||||||
|
case 17: // 'z' - ZONE_OFFSET
|
||||||
|
int zoneIndex = getZoneIndex(calendar.getTimeZone().getID(), formatData);
|
||||||
|
if (zoneIndex == -1) {
|
||||||
|
StringBuffer zoneString = new StringBuffer();
|
||||||
|
value = calendar.get(Calendar.ZONE_OFFSET)
|
||||||
|
+ calendar.get(Calendar.DST_OFFSET);
|
||||||
|
if (value < 0) {
|
||||||
|
zoneString.append("GMT-");
|
||||||
|
value = -value; // suppress the '-' sign for text display.
|
||||||
|
} else
|
||||||
|
zoneString.append("GMT+");
|
||||||
|
zoneString.append(
|
||||||
|
zeroPaddingNumber((int) (value / (60 * 60 * 1000)), 2, 2, nf));
|
||||||
|
zoneString.append(':');
|
||||||
|
zoneString.append(
|
||||||
|
zeroPaddingNumber(
|
||||||
|
(int) ((value % (60 * 60 * 1000)) / (60 * 1000)), 2, 2, nf));
|
||||||
|
current = zoneString.toString();
|
||||||
|
} else if (calendar.get(Calendar.DST_OFFSET) != 0) {
|
||||||
|
if (count >= 4)
|
||||||
|
current = formatData.getZoneStrings()[zoneIndex][3];
|
||||||
|
else
|
||||||
|
// count < 4, use abbreviated form if exists
|
||||||
|
current = formatData.getZoneStrings()[zoneIndex][4];
|
||||||
|
} else {
|
||||||
|
if (count >= 4)
|
||||||
|
current = formatData.getZoneStrings()[zoneIndex][1];
|
||||||
|
else
|
||||||
|
current = formatData.getZoneStrings()[zoneIndex][2];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String zeroPaddingNumber(long value, int minDigits, int maxDigits,
|
||||||
|
NumberFormat nf) {
|
||||||
|
nf.setMinimumIntegerDigits(minDigits);
|
||||||
|
nf.setMaximumIntegerDigits(maxDigits);
|
||||||
|
return nf.format(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int getZoneIndex(String ID, DateFormatSymbols dfs) {
|
||||||
|
String[][] zoneStrings = dfs.getZoneStrings();
|
||||||
|
|
||||||
|
for (int index = 0; index < zoneStrings.length; index++) {
|
||||||
|
if (ID.equalsIgnoreCase(zoneStrings[index][0])) return index;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
final int second = 1000;
|
||||||
|
final int minute = 60 * second;
|
||||||
|
final int hour = 60 * minute;
|
||||||
|
final int day = 24 * hour;
|
||||||
|
final int month = 30 * day;
|
||||||
|
final int year = 365 * day;
|
||||||
|
final int someday = 30 * year + 3 * month + 19 * day + 5 * hour;
|
||||||
|
|
||||||
|
|
||||||
|
/* standalone interface */
|
||||||
|
public static void main(String argv[]) {
|
||||||
|
Locale defaultLocale = Locale.getDefault();
|
||||||
|
SDFTCKZoneNamesTest test = new SDFTCKZoneNamesTest();
|
||||||
|
|
||||||
|
try {
|
||||||
|
List.of(Locale.ROOT,
|
||||||
|
Locale.CHINA,
|
||||||
|
Locale.forLanguageTag("es-419"),
|
||||||
|
Locale.GERMANY,
|
||||||
|
Locale.forLanguageTag("hi-IN"),
|
||||||
|
Locale.JAPAN,
|
||||||
|
Locale.TAIWAN,
|
||||||
|
Locale.UK,
|
||||||
|
Locale.US,
|
||||||
|
Locale.forLanguageTag("uz-Cyrl-UZ"),
|
||||||
|
Locale.forLanguageTag("zh-SG"),
|
||||||
|
Locale.forLanguageTag("zh-HK"),
|
||||||
|
Locale.forLanguageTag("zh-MO")).stream()
|
||||||
|
.forEach(l -> {
|
||||||
|
System.out.printf("Testing locale: %s%n", l);
|
||||||
|
Locale.setDefault(l);
|
||||||
|
test.SimpleDateFormat0062();
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
Locale.setDefault(defaultLocale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Equivalence class partitioning
|
||||||
|
* with state, input and output values orientation
|
||||||
|
* for public StringBuffer format(Date date, StringBuffer result, FieldPosition fp),
|
||||||
|
* <br><b>pre-conditions</b>: patterns: { "'s0mething'z mm::hh,yyyy zz",
|
||||||
|
* "zzzz",
|
||||||
|
* "z"} (each pattern contains letter for TIMEZONE_FIELD),
|
||||||
|
* <br><b>date</b>: a Date object
|
||||||
|
* <br><b>result</b>: a string
|
||||||
|
* <br><b>fp</b>: a FieldPosition object with TIMEZONE_FIELD field
|
||||||
|
* <br><b>output</b>: formatted date as expected.
|
||||||
|
*/
|
||||||
|
public void SimpleDateFormat0062() {
|
||||||
|
boolean passed = true;
|
||||||
|
String patterns[] = {"'s0mething'z mm::hh,yyyy zz",
|
||||||
|
"zzzz",
|
||||||
|
"z"};
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat();
|
||||||
|
Date date = new Date(1234567890);
|
||||||
|
for (String[] tz : sdf.getDateFormatSymbols().getZoneStrings()) {
|
||||||
|
sdf.setTimeZone(TimeZone.getTimeZone(tz[0]));
|
||||||
|
for (int i = 0; i < patterns.length && passed; i++) {
|
||||||
|
StringBuffer result = new StringBuffer("qwerty");
|
||||||
|
FieldPosition fp = new FieldPosition(DateFormat.TIMEZONE_FIELD);
|
||||||
|
sdf.applyPattern(patterns[i]);
|
||||||
|
String expected = new
|
||||||
|
StringBuffer("qwerty").append(myFormat(date,
|
||||||
|
sdf)).toString();
|
||||||
|
String formatted = sdf.format(date, result, fp).toString();
|
||||||
|
|
||||||
|
if (!expected.equals(formatted)) {
|
||||||
|
System.out.println(
|
||||||
|
"method format(date, StringBuffer, FieldPosition) formats wrong");
|
||||||
|
System.out.println(" pattern: " + patterns[i]);
|
||||||
|
System.out.println(" time zone ID: " + tz[0]);
|
||||||
|
System.out.println(" expected result: " + expected);
|
||||||
|
System.out.println(" formatted result: " + formatted);
|
||||||
|
passed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (passed && !expected.equals(result.toString())) {
|
||||||
|
System.out.println(
|
||||||
|
"method format(Date date, StringBuffer toAppendTo, FieldPosition fp) toAppendTo is not " +
|
||||||
|
"equal to output");
|
||||||
|
System.out.println(" pattern: " + patterns[i]);
|
||||||
|
System.out.println(" time zone ID: " + tz[0]);
|
||||||
|
System.out.println(" toAppendTo : " + result);
|
||||||
|
System.out.println(" formatted date: " + formatted);
|
||||||
|
passed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(passed)
|
||||||
|
{
|
||||||
|
System.out.println("PASSED : OKAY");
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
throw new RuntimeException("FAILED");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user