ICU-8342 TimeZoneNames/TimeZoneFormat APIs as 4.8 technology preview. Also, including Output<T> (#8475), GMT zero format support, fallback region format support.
X-SVN-Rev: 29982
This commit is contained in:
parent
04435370a7
commit
49c4bc01ff
6
.gitattributes
vendored
6
.gitattributes
vendored
@ -244,6 +244,12 @@ icu4j/main/classes/core/.project -text
|
||||
icu4j/main/classes/core/.settings/org.eclipse.core.resources.prefs -text
|
||||
icu4j/main/classes/core/.settings/org.eclipse.jdt.core.prefs -text
|
||||
icu4j/main/classes/core/manifest.stub -text
|
||||
icu4j/main/classes/core/src/com/ibm/icu/impl/TimeZoneGenericNames.java -text
|
||||
icu4j/main/classes/core/src/com/ibm/icu/impl/TimeZoneNamesFactoryImpl.java -text
|
||||
icu4j/main/classes/core/src/com/ibm/icu/impl/TimeZoneNamesImpl.java -text
|
||||
icu4j/main/classes/core/src/com/ibm/icu/text/TimeZoneFormat.java -text
|
||||
icu4j/main/classes/core/src/com/ibm/icu/text/TimeZoneNames.java -text
|
||||
icu4j/main/classes/core/src/com/ibm/icu/util/Output.java -text
|
||||
icu4j/main/classes/core/src/com/ibm/icu/util/Region.java -text
|
||||
icu4j/main/classes/currdata/.externalToolBuilders/copy-data-currdata.launch -text
|
||||
icu4j/main/classes/currdata/.settings/org.eclipse.core.resources.prefs -text
|
||||
|
@ -22,12 +22,6 @@ com.ibm.icu.util.TimeZone.DefaultTimeZoneType = ICU
|
||||
#
|
||||
com.ibm.icu.text.DecimalFormat.SkipExtendedSeparatorParsing = false
|
||||
|
||||
# Sets the default MessageFormat apostrophe-quoting behavior.
|
||||
# See the com.ibm.icu.text.MessagePattern.ApostropheMode enum documentation.
|
||||
# Values: DOUBLE_OPTIONAL or DOUBLE_REQUIRED.
|
||||
# This is new in ICU 4.8.
|
||||
# DOUBLE_OPTIONAL is the ICU default behavior.
|
||||
com.ibm.icu.text.MessagePattern.ApostropheMode = DOUBLE_OPTIONAL
|
||||
|
||||
#
|
||||
# [Internal Use Only]
|
||||
@ -35,3 +29,9 @@ com.ibm.icu.text.MessagePattern.ApostropheMode = DOUBLE_OPTIONAL
|
||||
# at run time.
|
||||
#
|
||||
com.ibm.icu.impl.ICUResourceBundle.skipRuntimeLocaleResourceScan = false
|
||||
|
||||
#
|
||||
# [Internal Use Only]
|
||||
# Time zone names service factory
|
||||
#
|
||||
# com.ibm.icu.text.TimeZoneNames.Factory.impl = com.ibm.icu.impl.TimeZoneNamesFactoryImpl
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2007-2010, International Business Machines
|
||||
* Copyright (C) 2007-2011, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
*******************************************************************************
|
||||
*/
|
||||
@ -11,6 +11,7 @@ import java.io.ObjectInputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.text.FieldPosition;
|
||||
import java.text.ParsePosition;
|
||||
import java.util.Arrays;
|
||||
import java.util.MissingResourceException;
|
||||
|
||||
import com.ibm.icu.lang.UCharacter;
|
||||
@ -77,9 +78,11 @@ public final class DateNumberFormat extends NumberFormat {
|
||||
elems[10] = minusString.charAt(0);
|
||||
CACHE.put(loc, elems);
|
||||
}
|
||||
|
||||
|
||||
digits = new char[10];
|
||||
System.arraycopy(elems, 0, digits, 0, 10);
|
||||
zeroDigit = digits[0];
|
||||
|
||||
minusSign = elems[10];
|
||||
}
|
||||
|
||||
@ -105,24 +108,22 @@ public final class DateNumberFormat extends NumberFormat {
|
||||
}
|
||||
|
||||
public char getZeroDigit() {
|
||||
if ( digits != null ) {
|
||||
return digits[0];
|
||||
} else {
|
||||
return zeroDigit;
|
||||
}
|
||||
return zeroDigit;
|
||||
}
|
||||
|
||||
public void setZeroDigit(char zero) {
|
||||
if ( digits == null ) {
|
||||
zeroDigit = zero;
|
||||
if (digits == null) {
|
||||
digits = new char[10];
|
||||
}
|
||||
digits[0] = zero;
|
||||
if (Character.digit(zero,10) == 0) {
|
||||
for ( int i = 1 ; i < 10 ; i++ ) {
|
||||
digits[i] = (char)(zero+i);
|
||||
}
|
||||
for ( int i = 1 ; i < 10 ; i++ ) {
|
||||
digits[i] = (char)(zero+i);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public char[] getDigits() {
|
||||
return digits;
|
||||
}
|
||||
|
||||
public StringBuffer format(double number, StringBuffer toAppendTo,
|
||||
@ -136,6 +137,7 @@ public final class DateNumberFormat extends NumberFormat {
|
||||
if (numberL < 0) {
|
||||
// negative
|
||||
toAppendTo.append(minusSign);
|
||||
numberL = -numberL;
|
||||
}
|
||||
|
||||
// Note: NumberFormat used by DateFormat only uses int numbers.
|
||||
@ -235,33 +237,18 @@ public final class DateNumberFormat extends NumberFormat {
|
||||
return false;
|
||||
}
|
||||
DateNumberFormat other = (DateNumberFormat)obj;
|
||||
|
||||
for (int i = 0 ; i < 10 ; i++) {
|
||||
char check1, check2;
|
||||
if ( digits != null ) {
|
||||
check1 = digits[i];
|
||||
} else {
|
||||
check1 = (char)(zeroDigit+i);
|
||||
}
|
||||
if ( other.digits != null ) {
|
||||
check2 = other.digits[i];
|
||||
} else {
|
||||
check2 = (char)(other.zeroDigit+i);
|
||||
}
|
||||
|
||||
if (check1 != check2) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return (this.maxIntDigits == other.maxIntDigits
|
||||
&& this.minIntDigits == other.minIntDigits
|
||||
&& this.minusSign == other.minusSign
|
||||
&& this.positiveOnly == other.positiveOnly);
|
||||
&& this.positiveOnly == other.positiveOnly
|
||||
&& Arrays.equals(this.digits, other.digits));
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
|
||||
stream.defaultReadObject();
|
||||
if (digits == null) {
|
||||
setZeroDigit(zeroDigit);
|
||||
}
|
||||
// re-allocate the work buffer
|
||||
decimalBuf = new char[20];
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2005-2010, International Business Machines Corporation and *
|
||||
* Copyright (C) 2005-2011, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
@ -401,6 +401,26 @@ public class OlsonTimeZone extends BasicTimeZone {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the canonical ID of this system time zone
|
||||
*/
|
||||
public String getCanonicalID() {
|
||||
if (canonicalID == null) {
|
||||
synchronized(this) {
|
||||
if (canonicalID == null) {
|
||||
canonicalID = getCanonicalID(getID());
|
||||
|
||||
assert(canonicalID != null);
|
||||
if (canonicalID == null) {
|
||||
// This should never happen...
|
||||
canonicalID = getID();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return canonicalID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a GMT+0 zone with no transitions. This is done when a
|
||||
* constructor fails so the resultant object is well-behaved.
|
||||
@ -589,7 +609,21 @@ public class OlsonTimeZone extends BasicTimeZone {
|
||||
super.setID(id);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.ibm.icu.util.TimeZone#setID(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void setID(String id){
|
||||
// Before updating the ID, preserve the original ID's canonical ID.
|
||||
if (canonicalID == null) {
|
||||
canonicalID = getCanonicalID(getID());
|
||||
assert(canonicalID != null);
|
||||
if (canonicalID == null) {
|
||||
// This should never happen...
|
||||
canonicalID = getID();
|
||||
}
|
||||
}
|
||||
|
||||
if (finalZone != null){
|
||||
finalZone.setID(id);
|
||||
}
|
||||
@ -796,6 +830,12 @@ public class OlsonTimeZone extends BasicTimeZone {
|
||||
*/
|
||||
private SimpleTimeZone finalZone = null; // owned, may be NULL
|
||||
|
||||
/**
|
||||
* The canonical ID of this zone. Initialized when {@link #getCanonicalID()}
|
||||
* is invoked first time, or {@link #setID(String)} is called.
|
||||
*/
|
||||
private volatile String canonicalID = null;
|
||||
|
||||
private static final String ZONEINFORES = "zoneinfo64";
|
||||
|
||||
private static final boolean DEBUG = ICUDebug.enabled("olson");
|
||||
|
@ -1,49 +1,46 @@
|
||||
/*
|
||||
* ********************************************************************************
|
||||
* Copyright (C) 2007-2009, International Business Machines Corporation and others.
|
||||
* Copyright (C) 2007-2011, International Business Machines Corporation and others.
|
||||
* All Rights Reserved.
|
||||
* ********************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
import com.ibm.icu.lang.UCharacter;
|
||||
import com.ibm.icu.text.UTF16;
|
||||
|
||||
/**
|
||||
* TextTrieMap is a trie implementation for supporting
|
||||
* fast prefix match for the key.
|
||||
*/
|
||||
public class TextTrieMap<V> {
|
||||
|
||||
private Node _root = new Node();
|
||||
boolean _ignoreCase;
|
||||
|
||||
/**
|
||||
* Constructs a TextTrieMap object.
|
||||
*
|
||||
* @param ignoreCase true to use case insensitive match
|
||||
* @param ignoreCase true to use simple case insensitive match
|
||||
*/
|
||||
public TextTrieMap(boolean ignoreCase) {
|
||||
this.ignoreCase = ignoreCase;
|
||||
_ignoreCase = ignoreCase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the text key and its associated object in this object.
|
||||
*
|
||||
* @param text The text.
|
||||
* @param o The object associated with the text.
|
||||
* @param val The value object associated with the text.
|
||||
*/
|
||||
public synchronized void put(String text, V o) {
|
||||
CharacterNode node = root;
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
int ch = UTF16.charAt(text, i);
|
||||
node = node.addChildNode(ch);
|
||||
if (UTF16.getCharCount(ch) == 2) {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
node.addObject(o);
|
||||
public TextTrieMap<V> put(CharSequence text, V val) {
|
||||
CharIterator chitr = new CharIterator(text, 0, _ignoreCase);
|
||||
_root.add(chitr, val);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -71,8 +68,15 @@ public class TextTrieMap<V> {
|
||||
* matching entry is found.
|
||||
*/
|
||||
public Iterator<V> get(String text, int start) {
|
||||
return get(text, start, null);
|
||||
}
|
||||
|
||||
public Iterator<V> get(String text, int start, int[] matchLen) {
|
||||
LongestMatchHandler<V> handler = new LongestMatchHandler<V>();
|
||||
find(text, start, handler);
|
||||
if (matchLen != null && matchLen.length > 0) {
|
||||
matchLen[0] = handler.getMatchLength();
|
||||
}
|
||||
return handler.getMatches();
|
||||
}
|
||||
|
||||
@ -80,164 +84,94 @@ public class TextTrieMap<V> {
|
||||
find(text, 0, handler);
|
||||
}
|
||||
|
||||
public void find(String text, int start, ResultHandler<V> handler) {
|
||||
find(root, text, start, start, handler);
|
||||
public void find(String text, int offset, ResultHandler<V> handler) {
|
||||
CharIterator chitr = new CharIterator(text, offset, _ignoreCase);
|
||||
find(_root, chitr, handler);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find an iterator of the objects associated with the
|
||||
* longest prefix matching string key under the specified node.
|
||||
*
|
||||
* @param node The character node in this trie.
|
||||
* @param text The text to be matched with prefixes.
|
||||
* @param start The start index within the text.
|
||||
* @param index The current index within the text.
|
||||
* @param handler The result handler, ResultHandler#handlePrefixMatch
|
||||
* is called when any prefix match is found.
|
||||
*/
|
||||
private synchronized void find(CharacterNode node, String text,
|
||||
int start, int index, ResultHandler<V> handler) {
|
||||
Iterator<V> itr = node.iterator();
|
||||
if (itr != null) {
|
||||
if (!handler.handlePrefixMatch(index - start, itr)) {
|
||||
private synchronized void find(Node node, CharIterator chitr, ResultHandler<V> handler) {
|
||||
Iterator<V> values = node.values();
|
||||
if (values != null) {
|
||||
if (!handler.handlePrefixMatch(chitr.processedLength(), values)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (index < text.length()) {
|
||||
List<CharacterNode> childNodes = node.getChildNodes();
|
||||
if (childNodes == null) {
|
||||
return;
|
||||
}
|
||||
int ch = UTF16.charAt(text, index);
|
||||
int chLen = UTF16.getCharCount(ch);
|
||||
for (int i = 0; i < childNodes.size(); i++) {
|
||||
CharacterNode child = childNodes.get(i);
|
||||
if (compare(ch, child.getCharacter())) {
|
||||
find(child, text, start, index + chLen, handler);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Node nextMatch = node.findMatch(chitr);
|
||||
if (nextMatch != null) {
|
||||
find(nextMatch, chitr, handler);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A private method used for comparing two characters.
|
||||
*
|
||||
* @param ch1 The first character.
|
||||
* @param ch2 The second character.
|
||||
* @return true if the first character matches the second.
|
||||
*/
|
||||
private boolean compare(int ch1, int ch2) {
|
||||
if (ch1 == ch2) {
|
||||
|
||||
public static class CharIterator implements Iterator<Character> {
|
||||
private boolean _ignoreCase;
|
||||
private CharSequence _text;
|
||||
private int _nextIdx;
|
||||
private int _startIdx;
|
||||
|
||||
private Character _remainingChar;
|
||||
|
||||
CharIterator(CharSequence text, int offset, boolean ignoreCase) {
|
||||
_text = text;
|
||||
_nextIdx = _startIdx = offset;
|
||||
_ignoreCase = ignoreCase;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.util.Iterator#hasNext()
|
||||
*/
|
||||
public boolean hasNext() {
|
||||
if (_nextIdx == _text.length() && _remainingChar == null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (ignoreCase) {
|
||||
if (UCharacter.toLowerCase(ch1) == UCharacter.toLowerCase(ch2)) {
|
||||
return true;
|
||||
}
|
||||
else if (UCharacter.toUpperCase(ch1) == UCharacter.toUpperCase(ch2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// The root node of this trie
|
||||
private CharacterNode root = new CharacterNode(0);
|
||||
|
||||
// Character matching option
|
||||
boolean ignoreCase;
|
||||
|
||||
/**
|
||||
* Inner class representing a character node in the trie.
|
||||
*/
|
||||
private class CharacterNode {
|
||||
int character;
|
||||
List<CharacterNode> children;
|
||||
List<V> objlist;
|
||||
|
||||
/**
|
||||
* Constructs a node for the character.
|
||||
*
|
||||
* @param ch The character associated with this node.
|
||||
/* (non-Javadoc)
|
||||
* @see java.util.Iterator#next()
|
||||
*/
|
||||
public CharacterNode(int ch) {
|
||||
character = ch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the character associated with this node.
|
||||
*
|
||||
* @return The character
|
||||
*/
|
||||
public int getCharacter() {
|
||||
return character;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the object to the node.
|
||||
*
|
||||
* @param obj The object set in the leaf node.
|
||||
*/
|
||||
public void addObject(V obj) {
|
||||
if (objlist == null) {
|
||||
objlist = new LinkedList<V>();
|
||||
}
|
||||
objlist.add(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an iterator of the objects associated with
|
||||
* the leaf node.
|
||||
*
|
||||
* @return The iterator or null if no objects are
|
||||
* associated with this node.
|
||||
*/
|
||||
public Iterator<V> iterator() {
|
||||
if (objlist == null) {
|
||||
public Character next() {
|
||||
if (_nextIdx == _text.length() && _remainingChar == null) {
|
||||
return null;
|
||||
}
|
||||
return objlist.iterator();
|
||||
Character next;
|
||||
if (_remainingChar != null) {
|
||||
next = _remainingChar;
|
||||
_remainingChar = null;
|
||||
} else {
|
||||
if (_ignoreCase) {
|
||||
int cp = UCharacter.foldCase(Character.codePointAt(_text, _nextIdx), true);
|
||||
_nextIdx = _nextIdx + Character.charCount(cp);
|
||||
|
||||
char[] chars = Character.toChars(cp);
|
||||
next = chars[0];
|
||||
if (chars.length == 2) {
|
||||
_remainingChar = chars[1];
|
||||
}
|
||||
} else {
|
||||
next = _text.charAt(_nextIdx);
|
||||
_nextIdx++;
|
||||
}
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a child node for the character under this character
|
||||
* node in the trie. When the matching child node already
|
||||
* exists, the reference of the existing child node is
|
||||
* returned.
|
||||
*
|
||||
* @param ch The character associated with a child node.
|
||||
* @return The child node.
|
||||
/* (non-Javadoc)
|
||||
* @see java.util.Iterator#remove()
|
||||
*/
|
||||
public CharacterNode addChildNode(int ch) {
|
||||
if (children == null) {
|
||||
children = new ArrayList<CharacterNode>();
|
||||
CharacterNode newNode = new CharacterNode(ch);
|
||||
children.add(newNode);
|
||||
return newNode;
|
||||
}
|
||||
CharacterNode node = null;
|
||||
for (int i = 0; i < children.size(); i++) {
|
||||
CharacterNode cur = children.get(i);
|
||||
if (compare(ch, cur.getCharacter())) {
|
||||
node = cur;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (node == null) {
|
||||
node = new CharacterNode(ch);
|
||||
children.add(node);
|
||||
}
|
||||
return node;
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("remove() not supproted");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of child nodes under this node.
|
||||
*
|
||||
* @return The list of child nodes.
|
||||
*/
|
||||
public List<CharacterNode> getChildNodes() {
|
||||
return children;
|
||||
public int nextIndex() {
|
||||
return _nextIdx;
|
||||
}
|
||||
|
||||
public int processedLength() {
|
||||
if (_remainingChar != null) {
|
||||
throw new IllegalStateException("In the middle of surrogate pair");
|
||||
}
|
||||
return _nextIdx - _startIdx;
|
||||
}
|
||||
}
|
||||
|
||||
@ -271,5 +205,181 @@ public class TextTrieMap<V> {
|
||||
public Iterator<V> getMatches() {
|
||||
return matches;
|
||||
}
|
||||
|
||||
public int getMatchLength() {
|
||||
return length;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class representing a text node in the trie.
|
||||
*/
|
||||
private class Node {
|
||||
private char[] _text;
|
||||
private List<V> _values;
|
||||
private List<Node> _children;
|
||||
|
||||
private Node() {
|
||||
}
|
||||
|
||||
private Node(char[] text, List<V> values, List<Node> children) {
|
||||
_text = text;
|
||||
_values = values;
|
||||
_children = children;
|
||||
}
|
||||
|
||||
public Iterator<V> values() {
|
||||
if (_values == null) {
|
||||
return null;
|
||||
}
|
||||
return _values.iterator();
|
||||
}
|
||||
|
||||
public void add(CharIterator chitr, V value) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
while (chitr.hasNext()) {
|
||||
buf.append(chitr.next());
|
||||
}
|
||||
add(toCharArray(buf), 0, value);
|
||||
}
|
||||
|
||||
public Node findMatch(CharIterator chitr) {
|
||||
if (_children == null) {
|
||||
return null;
|
||||
}
|
||||
if (!chitr.hasNext()) {
|
||||
return null;
|
||||
}
|
||||
Node match = null;
|
||||
Character ch = chitr.next();
|
||||
for (Node child : _children) {
|
||||
if (ch < child._text[0]) {
|
||||
break;
|
||||
}
|
||||
if (ch == child._text[0]) {
|
||||
if (child.matchFollowing(chitr)) {
|
||||
match = child;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
private void add(char[] text, int offset, V value) {
|
||||
if (text.length == offset) {
|
||||
_values = addValue(_values, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_children == null) {
|
||||
_children = new LinkedList<Node>();
|
||||
Node child = new Node(subArray(text, offset), addValue(null, value), null);
|
||||
_children.add(child);
|
||||
return;
|
||||
}
|
||||
|
||||
// walk through children
|
||||
ListIterator<Node> litr = _children.listIterator();
|
||||
while (litr.hasNext()) {
|
||||
Node next = litr.next();
|
||||
if (text[offset] < next._text[0]) {
|
||||
litr.previous();
|
||||
break;
|
||||
}
|
||||
if (text[offset] == next._text[0]) {
|
||||
int matchLen = next.lenMatches(text, offset);
|
||||
if (matchLen == next._text.length) {
|
||||
// full match
|
||||
next.add(text, offset + matchLen, value);
|
||||
} else {
|
||||
// partial match, create a branch
|
||||
next.split(matchLen);
|
||||
next.add(text, offset + matchLen, value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
// add a new child to this node
|
||||
litr.add(new Node(subArray(text, offset), addValue(null, value), null));
|
||||
}
|
||||
|
||||
private boolean matchFollowing(CharIterator chitr) {
|
||||
boolean matched = true;
|
||||
int idx = 1;
|
||||
while (idx < _text.length) {
|
||||
if(!chitr.hasNext()) {
|
||||
matched = false;
|
||||
break;
|
||||
}
|
||||
Character ch = chitr.next();
|
||||
if (ch != _text[idx]) {
|
||||
matched = false;
|
||||
break;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
return matched;
|
||||
}
|
||||
|
||||
private int lenMatches(char[] text, int offset) {
|
||||
int textLen = text.length - offset;
|
||||
int limit = _text.length < textLen ? _text.length : textLen;
|
||||
int len = 0;
|
||||
while (len < limit) {
|
||||
if (_text[len] != text[offset + len]) {
|
||||
break;
|
||||
}
|
||||
len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
private void split(int offset) {
|
||||
// split the current node at the offset
|
||||
char[] childText = subArray(_text, offset);
|
||||
_text = subArray(_text, 0, offset);
|
||||
|
||||
// add the Node representing after the offset as a child
|
||||
Node child = new Node(childText, _values, _children);
|
||||
_values = null;
|
||||
|
||||
_children = new LinkedList<Node>();
|
||||
_children.add(child);
|
||||
}
|
||||
|
||||
private List<V> addValue(List<V> list, V value) {
|
||||
if (list == null) {
|
||||
list = new LinkedList<V>();
|
||||
}
|
||||
list.add(value);
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
private static char[] toCharArray(CharSequence text) {
|
||||
char[] array = new char[text.length()];
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
array[i] = text.charAt(i);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
private static char[] subArray(char[] array, int start) {
|
||||
if (start == 0) {
|
||||
return array;
|
||||
}
|
||||
char[] sub = new char[array.length - start];
|
||||
System.arraycopy(array, start, sub, 0, sub.length);
|
||||
return sub;
|
||||
}
|
||||
|
||||
private static char[] subArray(char[] array, int start, int limit) {
|
||||
if (start == 0 && limit == array.length) {
|
||||
return array;
|
||||
}
|
||||
char[] sub = new char[limit - start];
|
||||
System.arraycopy(array, start, sub, 0, limit - start);
|
||||
return sub;
|
||||
}
|
||||
}
|
||||
|
@ -1,94 +0,0 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2010, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.impl;
|
||||
|
||||
import com.ibm.icu.util.TimeZone;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
|
||||
/**
|
||||
* @author JCEmmons
|
||||
*
|
||||
*/
|
||||
public class TimeZoneFormat {
|
||||
|
||||
public ZoneStringFormat zsf;
|
||||
|
||||
public static TimeZoneFormat createInstance ( ULocale loc ) {
|
||||
TimeZoneFormat tzf = new TimeZoneFormat();
|
||||
tzf.zsf = ZoneStringFormat.getInstance(loc);
|
||||
return tzf;
|
||||
}
|
||||
|
||||
public String format ( TimeZone tz, long date, int style ) {
|
||||
String result = null;
|
||||
switch ( style ) {
|
||||
case TimeZone.SHORT :
|
||||
case TimeZone.SHORT_COMMONLY_USED :
|
||||
result = zsf.getSpecificShortString(tz, date, style == TimeZone.SHORT_COMMONLY_USED);
|
||||
break;
|
||||
case TimeZone.LONG :
|
||||
result = zsf.getSpecificLongString(tz, date);
|
||||
break;
|
||||
case TimeZone.SHORT_GENERIC :
|
||||
result = zsf.getGenericShortString(tz, date, true);
|
||||
break;
|
||||
case TimeZone.LONG_GENERIC :
|
||||
result = zsf.getGenericLongString(tz, date);
|
||||
break;
|
||||
case TimeZone.SHORT_GMT :
|
||||
result = zsf.getShortGMTString(tz,date);
|
||||
break;
|
||||
case TimeZone.LONG_GMT :
|
||||
result = zsf.getLongGMTString(tz, date);
|
||||
break;
|
||||
case TimeZone.GENERIC_LOCATION :
|
||||
result = zsf.getGenericLocationString(tz, date);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public String format ( TimeZone tz, int style, boolean daylight ) {
|
||||
return format (tz, System.currentTimeMillis(), style, daylight);
|
||||
}
|
||||
|
||||
public String format ( TimeZone tz, long date , int style , boolean daylight ) {
|
||||
String result = null;
|
||||
switch ( style ) {
|
||||
case TimeZone.LONG :
|
||||
if ( daylight ) {
|
||||
result = zsf.getLongDaylight(tz.getID(), date);
|
||||
} else {
|
||||
result = zsf.getLongStandard(tz.getID(),date);
|
||||
}
|
||||
break;
|
||||
|
||||
case TimeZone.SHORT :
|
||||
case TimeZone.SHORT_COMMONLY_USED :
|
||||
if ( daylight ) {
|
||||
result = zsf.getShortDaylight(tz.getID(), date, style == TimeZone.SHORT_COMMONLY_USED);
|
||||
} else {
|
||||
result = zsf.getShortStandard(tz.getID(), date, style == TimeZone.SHORT_COMMONLY_USED);
|
||||
}
|
||||
break;
|
||||
|
||||
case TimeZone.SHORT_GMT :
|
||||
result = zsf.getShortGMTString(tz, date, daylight);
|
||||
break;
|
||||
|
||||
case TimeZone.LONG_GMT:
|
||||
result = zsf.getLongGMTString(tz, date, daylight);
|
||||
break;
|
||||
|
||||
default :
|
||||
result = format ( tz, date, style );
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,903 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2011, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.Serializable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.ibm.icu.impl.TextTrieMap.ResultHandler;
|
||||
import com.ibm.icu.text.LocaleDisplayNames;
|
||||
import com.ibm.icu.text.TimeZoneFormat.TimeType;
|
||||
import com.ibm.icu.text.TimeZoneNames;
|
||||
import com.ibm.icu.text.TimeZoneNames.MatchInfo;
|
||||
import com.ibm.icu.text.TimeZoneNames.NameType;
|
||||
import com.ibm.icu.util.BasicTimeZone;
|
||||
import com.ibm.icu.util.Freezable;
|
||||
import com.ibm.icu.util.TimeZone;
|
||||
import com.ibm.icu.util.TimeZone.SystemTimeZoneType;
|
||||
import com.ibm.icu.util.TimeZoneTransition;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
|
||||
/**
|
||||
* This class interact with TimeZoneNames and LocaleDisplayNames
|
||||
* to format and parse time zone's generic display names.
|
||||
* It is not recommended to use this class directly, instead
|
||||
* use com.ibm.icu.text.TimeZoneFormat.
|
||||
*/
|
||||
public class TimeZoneGenericNames implements Serializable, Freezable<TimeZoneGenericNames> {
|
||||
|
||||
private static final long serialVersionUID = 2729910342063468417L;
|
||||
|
||||
/**
|
||||
* Generic name type enum
|
||||
*/
|
||||
public enum GenericNameType {
|
||||
LOCATION ("LONG", "SHORT"),
|
||||
LONG (),
|
||||
SHORT ();
|
||||
|
||||
String[] _fallbackTypeOf;
|
||||
GenericNameType(String... fallbackTypeOf) {
|
||||
_fallbackTypeOf = fallbackTypeOf;
|
||||
}
|
||||
|
||||
public boolean isFallbackTypeOf(GenericNameType type) {
|
||||
String typeStr = type.toString();
|
||||
for (String t : _fallbackTypeOf) {
|
||||
if (t.equals(typeStr)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format pattern enum used for composing location and partial location names
|
||||
*/
|
||||
public enum Pattern {
|
||||
// The format pattern such as "{0} Time", where {0} is the country.
|
||||
REGION_FORMAT("regionFormat", "({0})"),
|
||||
|
||||
// The format pattern such as "{1} Time ({0})", where {1} is the country and {0} is a city.
|
||||
FALLBACK_REGION_FORMAT("fallbackRegionFormat", "{1} ({0})"),
|
||||
|
||||
// The format pattern such as "{1} ({0})", where {1} is the metazone, and {0} is the country or city.
|
||||
FALLBACK_FORMAT("fallbackFormat", "{1} ({0})");
|
||||
|
||||
String _key;
|
||||
String _defaultVal;
|
||||
|
||||
Pattern(String key, String defaultVal) {
|
||||
_key = key;
|
||||
_defaultVal = defaultVal;
|
||||
}
|
||||
|
||||
String key() {
|
||||
return _key;
|
||||
}
|
||||
|
||||
String defaultValue() {
|
||||
return _defaultVal;
|
||||
}
|
||||
}
|
||||
|
||||
private ULocale _locale;
|
||||
private TimeZoneNames _tznames;
|
||||
|
||||
private transient boolean _frozen;
|
||||
private transient String _region;
|
||||
private transient WeakReference<LocaleDisplayNames> _localeDisplayNamesRef;
|
||||
private transient MessageFormat[] _patternFormatters;
|
||||
|
||||
private transient ConcurrentHashMap<String, String> _genericLocationNamesMap;
|
||||
private transient ConcurrentHashMap<String, String> _genericPartialLocationNamesMap;
|
||||
private transient TextTrieMap<NameInfo> _gnamesTrie;
|
||||
private transient boolean _gnamesTrieFullyLoaded;
|
||||
|
||||
private static Cache GENERIC_NAMES_CACHE = new Cache();
|
||||
|
||||
// Window size used for DST check for a zone in a metazone (about a half year)
|
||||
private static final long DST_CHECK_RANGE = 184L*(24*60*60*1000);
|
||||
|
||||
private static final NameType[] GENERIC_NON_LOCATION_TYPES =
|
||||
{NameType.LONG_GENERIC, NameType.SHORT_GENERIC};
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a <code>TimeZoneGenericNames</code> with the given locale
|
||||
* and the <code>TimeZoneNames</code>.
|
||||
* @param locale the locale
|
||||
* @param tznames the TimeZoneNames
|
||||
*/
|
||||
public TimeZoneGenericNames(ULocale locale, TimeZoneNames tznames) {
|
||||
_locale = locale;
|
||||
_tznames = tznames;
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Private method initializing the instance of <code>TimeZoneGenericName</code>.
|
||||
* This method should be called from a constructor and readObject.
|
||||
*/
|
||||
private void init() {
|
||||
if (_tznames == null) {
|
||||
_tznames = TimeZoneNames.getInstance(_locale);
|
||||
}
|
||||
_genericLocationNamesMap = new ConcurrentHashMap<String, String>();
|
||||
_genericPartialLocationNamesMap = new ConcurrentHashMap<String, String>();
|
||||
|
||||
_gnamesTrie = new TextTrieMap<NameInfo>(true);
|
||||
_gnamesTrieFullyLoaded = false;
|
||||
|
||||
// Preload zone strings for the default time zone
|
||||
TimeZone tz = TimeZone.getDefault();
|
||||
String tzCanonicalID = ZoneMeta.getCanonicalCLDRID(tz);
|
||||
if (tzCanonicalID != null) {
|
||||
loadStrings(tzCanonicalID);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a <code>TimeZoneGenericNames</code> with the given locale.
|
||||
* This constructor is private and called from {@link #getInstance(ULocale)}.
|
||||
* @param locale the locale
|
||||
*/
|
||||
private TimeZoneGenericNames(ULocale locale) {
|
||||
this(locale, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* The factory method of <code>TimeZoneGenericNames</code>. This static method
|
||||
* returns a frozen instance of cached <code>TimeZoneGenericNames</code>.
|
||||
* @param locale the locale
|
||||
* @return A frozen <code>TimeZoneGenericNames</code>.
|
||||
*/
|
||||
public static TimeZoneGenericNames getInstance(ULocale locale) {
|
||||
String key = locale.getBaseName();
|
||||
return GENERIC_NAMES_CACHE.getInstance(key, locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the display name of the time zone for the given name type
|
||||
* at the given date, or null if the display name is not available.
|
||||
*
|
||||
* @param tz the time zone
|
||||
* @param type the generic name type - see {@link GenericNameType}
|
||||
* @param date the date
|
||||
* @return the display name of the time zone for the given name type
|
||||
* at the given date, or null.
|
||||
*/
|
||||
public String getDisplayName(TimeZone tz, GenericNameType type, long date) {
|
||||
String name = null;
|
||||
String tzCanonicalID = null;
|
||||
switch (type) {
|
||||
case LOCATION:
|
||||
tzCanonicalID = ZoneMeta.getCanonicalCLDRID(tz);
|
||||
if (tzCanonicalID != null) {
|
||||
name = getGenericLocationName(tzCanonicalID);
|
||||
}
|
||||
break;
|
||||
case LONG:
|
||||
case SHORT:
|
||||
name = formatGenericNonLocationName(tz, type, date);
|
||||
if (name == null) {
|
||||
tzCanonicalID = ZoneMeta.getCanonicalCLDRID(tz);
|
||||
if (tzCanonicalID != null) {
|
||||
name = getGenericLocationName(tzCanonicalID);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the generic location name for the given canonical time zone ID.
|
||||
*
|
||||
* @param canonicalTzID the canonical time zone ID
|
||||
* @return the generic location name for the given canonical time zone ID.
|
||||
*/
|
||||
public String getGenericLocationName(String canonicalTzID) {
|
||||
if (canonicalTzID == null || canonicalTzID.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
String name = _genericLocationNamesMap.get(canonicalTzID);
|
||||
if (name != null) {
|
||||
if (name.length() == 0) {
|
||||
// empty string to indicate the name is not available
|
||||
return null;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
String countryCode = ZoneMeta.getCanonicalCountry(canonicalTzID);
|
||||
if (countryCode != null) {
|
||||
String country = getLocaleDisplayNames().regionDisplayName(countryCode);
|
||||
if (ZoneMeta.getSingleCountry(canonicalTzID) != null) {
|
||||
// If the zone is only one zone in the country, do not add city
|
||||
name = formatPattern(Pattern.REGION_FORMAT, country);
|
||||
} else {
|
||||
// getExemplarLocationName should return non-empty String
|
||||
// if the time zone is associated with a location
|
||||
String city = _tznames.getExemplarLocationName(canonicalTzID);
|
||||
name = formatPattern(Pattern.FALLBACK_REGION_FORMAT, city, country);
|
||||
}
|
||||
}
|
||||
|
||||
if (name == null) {
|
||||
_genericLocationNamesMap.putIfAbsent(canonicalTzID.intern(), "");
|
||||
} else {
|
||||
synchronized (this) { // we have to sync the name map and the trie
|
||||
canonicalTzID = canonicalTzID.intern();
|
||||
String tmp = _genericLocationNamesMap.putIfAbsent(canonicalTzID, name.intern());
|
||||
if (tmp == null) {
|
||||
// Also put the name info the to trie
|
||||
NameInfo info = new NameInfo();
|
||||
info.tzID = canonicalTzID;
|
||||
info.type = GenericNameType.LOCATION;
|
||||
_gnamesTrie.put(name, info);
|
||||
} else {
|
||||
name = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pattern string for the pattern type.
|
||||
* Note: This method is designed for CLDR ST - not for common use.
|
||||
* @param patType the pattern type
|
||||
* @param patStr the pattern string
|
||||
* @return this object.
|
||||
*/
|
||||
public TimeZoneGenericNames setFormatPattern(Pattern patType, String patStr) {
|
||||
if (isFrozen()) {
|
||||
throw new UnsupportedOperationException("Attempt to modify frozen object");
|
||||
}
|
||||
|
||||
// Changing pattern will invalidates cached names
|
||||
if (!_genericLocationNamesMap.isEmpty()) {
|
||||
_genericLocationNamesMap = new ConcurrentHashMap<String, String>();
|
||||
}
|
||||
if (!_genericPartialLocationNamesMap.isEmpty()) {
|
||||
_genericPartialLocationNamesMap = new ConcurrentHashMap<String, String>();
|
||||
}
|
||||
_gnamesTrie = null;
|
||||
_gnamesTrieFullyLoaded = false;
|
||||
|
||||
if (_patternFormatters == null) {
|
||||
_patternFormatters = new MessageFormat[Pattern.values().length];
|
||||
}
|
||||
_patternFormatters[patType.ordinal()] = new MessageFormat(patStr);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Private method to get a generic string, with fallback logics involved,
|
||||
* that is,
|
||||
*
|
||||
* 1. If a generic non-location string is available for the zone, return it.
|
||||
* 2. If a generic non-location string is associated with a meta zone and
|
||||
* the zone never use daylight time around the given date, use the standard
|
||||
* string (if available).
|
||||
* 3. If a generic non-location string is associated with a meta zone and
|
||||
* the offset at the given time is different from the preferred zone for the
|
||||
* current locale, then return the generic partial location string (if available)
|
||||
* 4. If a generic non-location string is not available, use generic location
|
||||
* string.
|
||||
*
|
||||
* @param tz the requested time zone
|
||||
* @param date the date
|
||||
* @param type the generic name type, either LONG or SHORT
|
||||
* @return the name used for a generic name type, which could be the
|
||||
* generic name, or the standard name (if the zone does not observes DST
|
||||
* around the date), or the partial location name.
|
||||
*/
|
||||
private String formatGenericNonLocationName(TimeZone tz, GenericNameType type, long date) {
|
||||
assert(type == GenericNameType.LONG || type == GenericNameType.SHORT);
|
||||
String tzID = ZoneMeta.getCanonicalCLDRID(tz);
|
||||
|
||||
if (tzID == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Try to get a name from time zone first
|
||||
NameType nameType = (type == GenericNameType.LONG) ? NameType.LONG_GENERIC : NameType.SHORT_GENERIC;
|
||||
String name = _tznames.getTimeZoneDisplayName(tzID, nameType);
|
||||
|
||||
if (name != null) {
|
||||
return name;
|
||||
}
|
||||
|
||||
// Try meta zone
|
||||
String mzID = _tznames.getMetaZoneID(tzID, date);
|
||||
if (mzID != null) {
|
||||
boolean useStandard = false;
|
||||
int[] offsets = {0, 0};
|
||||
tz.getOffset(date, false, offsets);
|
||||
|
||||
if (offsets[1] == 0) {
|
||||
useStandard = true;
|
||||
// Check if the zone actually uses daylight saving time around the time
|
||||
if (tz instanceof BasicTimeZone) {
|
||||
BasicTimeZone btz = (BasicTimeZone)tz;
|
||||
TimeZoneTransition before = btz.getPreviousTransition(date, true);
|
||||
if (before != null
|
||||
&& (date - before.getTime() < DST_CHECK_RANGE)
|
||||
&& before.getFrom().getDSTSavings() != 0) {
|
||||
useStandard = false;
|
||||
} else {
|
||||
TimeZoneTransition after = btz.getNextTransition(date, false);
|
||||
if (after != null
|
||||
&& (after.getTime() - date < DST_CHECK_RANGE)
|
||||
&& after.getTo().getDSTSavings() != 0) {
|
||||
useStandard = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If not BasicTimeZone... only if the instance is not an ICU's implementation.
|
||||
// We may get a wrong answer in edge case, but it should practically work OK.
|
||||
int[] tmpOffsets = new int[2];
|
||||
tz.getOffset(date - DST_CHECK_RANGE, false, tmpOffsets);
|
||||
if (tmpOffsets[1] != 0) {
|
||||
useStandard = false;
|
||||
} else {
|
||||
tz.getOffset(date + DST_CHECK_RANGE, false, tmpOffsets);
|
||||
if (tmpOffsets[1] != 0){
|
||||
useStandard = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (useStandard) {
|
||||
NameType stdNameType = (nameType == NameType.LONG_GENERIC) ?
|
||||
NameType.LONG_STANDARD : NameType.SHORT_STANDARD_COMMONLY_USED;
|
||||
String stdName = _tznames.getDisplayName(tzID, stdNameType, date);
|
||||
if (stdName != null) {
|
||||
name = stdName;
|
||||
|
||||
// TODO: revisit this issue later
|
||||
// In CLDR, a same display name is used for both generic and standard
|
||||
// for some meta zones in some locales. This looks like a data bugs.
|
||||
// For now, we check if the standard name is different from its generic
|
||||
// name below.
|
||||
String mzGenericName = _tznames.getMetaZoneDisplayName(mzID, nameType);
|
||||
if (stdName.equalsIgnoreCase(mzGenericName)) {
|
||||
name = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (name == null) {
|
||||
// Get a name from meta zone
|
||||
String mzName = _tznames.getMetaZoneDisplayName(mzID, nameType);
|
||||
if (mzName != null) {
|
||||
// Check if we need to use a partial location format.
|
||||
// This check is done by comparing offset with the meta zone's
|
||||
// golden zone at the given date.
|
||||
String goldenID = _tznames.getReferenceZoneID(mzID, getTargetRegion());
|
||||
if (goldenID != null && !goldenID.equals(tzID)) {
|
||||
TimeZone goldenZone = TimeZone.getTimeZone(goldenID);
|
||||
int[] offsets1 = {0, 0};
|
||||
|
||||
// Check offset in the golden zone with wall time.
|
||||
// With getOffset(date, false, offsets1),
|
||||
// you may get incorrect results because of time overlap at DST->STD
|
||||
// transition.
|
||||
goldenZone.getOffset(date + offsets[0] + offsets[1], true, offsets1);
|
||||
|
||||
if (offsets[0] != offsets1[0] || offsets[1] != offsets1[1]) {
|
||||
// Now we need to use a partial location format.
|
||||
name = getPartialLocationName(tzID, mzID, (nameType == NameType.LONG_GENERIC), mzName);
|
||||
} else {
|
||||
name = mzName;
|
||||
}
|
||||
} else {
|
||||
name = mzName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Private simple pattern formatter used for formatting generic location names
|
||||
* and partial location names. We intentionally use JDK MessageFormat
|
||||
* for performance reason.
|
||||
*
|
||||
* @param pat the message pattern enum
|
||||
* @param args the format argument(s)
|
||||
* @return the formatted string
|
||||
*/
|
||||
private synchronized String formatPattern(Pattern pat, String... args) {
|
||||
if (_patternFormatters == null) {
|
||||
_patternFormatters = new MessageFormat[Pattern.values().length];
|
||||
}
|
||||
|
||||
int idx = pat.ordinal();
|
||||
if (_patternFormatters[idx] == null) {
|
||||
String patText;
|
||||
try {
|
||||
ICUResourceBundle bundle = (ICUResourceBundle) ICUResourceBundle.getBundleInstance(
|
||||
ICUResourceBundle.ICU_ZONE_BASE_NAME, _locale);
|
||||
patText = bundle.getStringWithFallback("zoneStrings/" + pat.key());
|
||||
} catch (MissingResourceException e) {
|
||||
patText = pat.defaultValue();
|
||||
}
|
||||
|
||||
_patternFormatters[idx] = new MessageFormat(patText);
|
||||
}
|
||||
return _patternFormatters[idx].format(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Private method returning LocaleDisplayNames instance for the locale of this
|
||||
* instance. Because LocaleDisplayNames is only used for generic
|
||||
* location formant and partial location format, the LocaleDisplayNames
|
||||
* is instantiated lazily.
|
||||
*
|
||||
* @return the instance of LocaleDisplayNames for the locale of this object.
|
||||
*/
|
||||
private synchronized LocaleDisplayNames getLocaleDisplayNames() {
|
||||
LocaleDisplayNames locNames = null;
|
||||
if (_localeDisplayNamesRef != null) {
|
||||
locNames = _localeDisplayNamesRef.get();
|
||||
}
|
||||
if (locNames == null) {
|
||||
locNames = LocaleDisplayNames.getInstance(_locale);
|
||||
_localeDisplayNamesRef = new WeakReference<LocaleDisplayNames>(locNames);
|
||||
}
|
||||
return locNames;
|
||||
}
|
||||
|
||||
private synchronized void loadStrings(String tzCanonicalID) {
|
||||
if (tzCanonicalID == null || tzCanonicalID.length() == 0) {
|
||||
return;
|
||||
}
|
||||
// getGenericLocationName() formats a name and put it into the trie
|
||||
getGenericLocationName(tzCanonicalID);
|
||||
|
||||
// Generic partial location format
|
||||
Set<String> mzIDs = _tznames.getAvailableMetaZoneIDs(tzCanonicalID);
|
||||
for (String mzID : mzIDs) {
|
||||
// if this time zone is not the golden zone of the meta zone,
|
||||
// partial location name (such as "PT (Los Angeles)") might be
|
||||
// available.
|
||||
String goldenID = _tznames.getReferenceZoneID(mzID, getTargetRegion());
|
||||
if (!tzCanonicalID.equals(goldenID)) {
|
||||
for (NameType genNonLocType : GENERIC_NON_LOCATION_TYPES) {
|
||||
String mzGenName = _tznames.getMetaZoneDisplayName(mzID, genNonLocType);
|
||||
if (mzGenName != null) {
|
||||
// getPartialLocationName() formats a name and put it into the trie
|
||||
getPartialLocationName(tzCanonicalID, mzID, (genNonLocType == NameType.LONG_GENERIC), mzGenName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Private method returning the target region. The target regions is determined by
|
||||
* the locale of this instance. When a generic name is coming from
|
||||
* a meta zone, this region is used for checking if the time zone
|
||||
* is a reference zone of the meta zone.
|
||||
*
|
||||
* @return the target region
|
||||
*/
|
||||
private synchronized String getTargetRegion() {
|
||||
if (_region == null) {
|
||||
_region = _locale.getCountry();
|
||||
if (_region.length() == 0) {
|
||||
ULocale tmp = ULocale.addLikelySubtags(_locale);
|
||||
_region = tmp.getCountry();
|
||||
if (_region.length() == 0) {
|
||||
_region = "001";
|
||||
}
|
||||
}
|
||||
}
|
||||
return _region;
|
||||
}
|
||||
|
||||
/**
|
||||
* Private method for formatting partial location names. This format
|
||||
* is used when a generic name of a meta zone is available, but the given
|
||||
* time zone is not a reference zone (golden zone) of the meta zone.
|
||||
*
|
||||
* @param tzID the canonical time zone ID
|
||||
* @param mzID the meta zone ID
|
||||
* @param isLong true when long generic name
|
||||
* @param mzDisplayName the meta zone generic display name
|
||||
* @return the partial location format string
|
||||
*/
|
||||
private String getPartialLocationName(String tzID, String mzID, boolean isLong, String mzDisplayName) {
|
||||
String letter = isLong ? "L" : "S";
|
||||
String key = tzID + "&" + mzID + "#" + letter;
|
||||
String name = _genericPartialLocationNamesMap.get(key);
|
||||
if (name != null) {
|
||||
return name;
|
||||
}
|
||||
String location = null;
|
||||
String countryCode = ZoneMeta.getSingleCountry(tzID);
|
||||
if (countryCode != null) {
|
||||
location = getLocaleDisplayNames().regionDisplayName(countryCode);
|
||||
} else {
|
||||
location = _tznames.getExemplarLocationName(tzID);
|
||||
if (location == null) {
|
||||
// This could happen when the time zone is not associated with a country,
|
||||
// and its ID is not hierarchical, for example, CST6CDT.
|
||||
// We use the canonical ID itself as the location for this case.
|
||||
location = tzID;
|
||||
}
|
||||
}
|
||||
name = formatPattern(Pattern.FALLBACK_FORMAT, location, mzDisplayName);
|
||||
synchronized (this) { // we have to sync the name map and the trie
|
||||
String tmp = _genericPartialLocationNamesMap.putIfAbsent(key.intern(), name.intern());
|
||||
if (tmp == null) {
|
||||
NameInfo info = new NameInfo();
|
||||
info.tzID = tzID.intern();
|
||||
info.type = isLong ? GenericNameType.LONG : GenericNameType.SHORT;
|
||||
_gnamesTrie.put(name, info);
|
||||
} else {
|
||||
name = tmp;
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* A private class used for storing the name information in the local trie.
|
||||
*/
|
||||
private static class NameInfo {
|
||||
String tzID;
|
||||
GenericNameType type;
|
||||
}
|
||||
|
||||
/**
|
||||
* A class used for returning the name search result used by
|
||||
* {@link TimeZoneGenericNames#find(String, int, EnumSet)}.
|
||||
*/
|
||||
public static class GenericMatchInfo {
|
||||
GenericNameType nameType;
|
||||
String tzID;
|
||||
int matchLength;
|
||||
TimeType timeType = TimeType.UNKNOWN;
|
||||
|
||||
public GenericNameType nameType() {
|
||||
return nameType;
|
||||
}
|
||||
|
||||
public String tzID() {
|
||||
return tzID;
|
||||
}
|
||||
|
||||
public TimeType timeType() {
|
||||
return timeType;
|
||||
}
|
||||
|
||||
public int matchLength() {
|
||||
return matchLength;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A private class implementing the search callback interface in
|
||||
* <code>TextTrieMap</code> for collecting match results.
|
||||
*/
|
||||
private static class GenericNameSearchHandler implements ResultHandler<NameInfo> {
|
||||
private EnumSet<GenericNameType> _types;
|
||||
private Collection<GenericMatchInfo> _matches;
|
||||
private int _maxMatchLen;
|
||||
|
||||
GenericNameSearchHandler(EnumSet<GenericNameType> types) {
|
||||
_types = types;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.ibm.icu.impl.TextTrieMap.ResultHandler#handlePrefixMatch(int, java.util.Iterator)
|
||||
*/
|
||||
public boolean handlePrefixMatch(int matchLength, Iterator<NameInfo> values) {
|
||||
while (values.hasNext()) {
|
||||
NameInfo info = values.next();
|
||||
if (_types != null && !_types.contains(info.type)) {
|
||||
continue;
|
||||
}
|
||||
GenericMatchInfo matchInfo = new GenericMatchInfo();
|
||||
matchInfo.tzID = info.tzID;
|
||||
matchInfo.nameType = info.type;
|
||||
matchInfo.matchLength = matchLength;
|
||||
//matchInfo.timeType = TimeType.UNKNOWN;
|
||||
if (_matches == null) {
|
||||
_matches = new LinkedList<GenericMatchInfo>();
|
||||
}
|
||||
_matches.add(matchInfo);
|
||||
if (matchLength > _maxMatchLen) {
|
||||
_maxMatchLen = matchLength;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the match results
|
||||
* @return the match results
|
||||
*/
|
||||
public Collection<GenericMatchInfo> getMatches() {
|
||||
return _matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum match length, or 0 if no match was found
|
||||
* @return the maximum match length
|
||||
*/
|
||||
public int getMaxMatchLen() {
|
||||
return _maxMatchLen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the match results
|
||||
*/
|
||||
public void resetResults() {
|
||||
_matches = null;
|
||||
_maxMatchLen = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the best match of time zone display name for the specified types in the
|
||||
* given text at the given offset.
|
||||
* @param text the text
|
||||
* @param start the start offset in the text
|
||||
* @param genericTypes the set of name types.
|
||||
* @return the best matching name info.
|
||||
*/
|
||||
public GenericMatchInfo findBestMatch(String text, int start, EnumSet<GenericNameType> genericTypes) {
|
||||
if (text == null || text.length() == 0 || start < 0 || start >= text.length()) {
|
||||
throw new IllegalArgumentException("bad input text or range");
|
||||
}
|
||||
GenericMatchInfo bestMatch = null;
|
||||
|
||||
// Find matches in the TimeZoneNames first
|
||||
Collection<MatchInfo> tznamesMatches = findTimeZoneNames(text, start, genericTypes);
|
||||
if (tznamesMatches != null) {
|
||||
MatchInfo longestMatch = null;
|
||||
for (MatchInfo match : tznamesMatches) {
|
||||
if (longestMatch == null || match.matchLength() > longestMatch.matchLength()) {
|
||||
longestMatch = match;
|
||||
}
|
||||
}
|
||||
if (longestMatch != null) {
|
||||
bestMatch = createGenericMatchInfo(longestMatch);
|
||||
if (bestMatch.matchLength() == (text.length() - start)) {
|
||||
// Full match
|
||||
//return bestMatch;
|
||||
|
||||
// TODO Some time zone uses a same name for the long standard name
|
||||
// and the location name. When the match is a long standard name,
|
||||
// then we need to check if the name is same with the location name.
|
||||
// This is probably a data error or a design bug.
|
||||
if (bestMatch.nameType != GenericNameType.LONG || bestMatch.timeType != TimeType.STANDARD) {
|
||||
return bestMatch;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find matches in the local trie
|
||||
Collection<GenericMatchInfo> localMatches = findLocal(text, start, genericTypes);
|
||||
if (localMatches != null) {
|
||||
for (GenericMatchInfo match : localMatches) {
|
||||
// TODO See the above TODO. We use match.matchLength() >= bestMatch.matcheLength()
|
||||
// for the reason described above.
|
||||
//if (bestMatch == null || match.matchLength() > bestMatch.matchLength()) {
|
||||
if (bestMatch == null || match.matchLength() >= bestMatch.matchLength()) {
|
||||
bestMatch = match;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bestMatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a collection of time zone display name matches for the specified types in the
|
||||
* given text at the given offset.
|
||||
* @param text the text
|
||||
* @param start the start offset in the text
|
||||
* @param genericTypes the set of name types.
|
||||
* @return A collection of match info.
|
||||
*/
|
||||
public Collection<GenericMatchInfo> find(String text, int start, EnumSet<GenericNameType> genericTypes) {
|
||||
if (text == null || text.length() == 0 || start < 0 || start >= text.length()) {
|
||||
throw new IllegalArgumentException("bad input text or range");
|
||||
}
|
||||
// Find matches in the local trie
|
||||
Collection<GenericMatchInfo> results = findLocal(text, start, genericTypes);
|
||||
|
||||
// Also find matches in the TimeZoneNames
|
||||
Collection<MatchInfo> tznamesMatches = findTimeZoneNames(text, start, genericTypes);
|
||||
if (tznamesMatches != null) {
|
||||
// transform matches and append them to local matches
|
||||
for (MatchInfo match : tznamesMatches) {
|
||||
if (results == null) {
|
||||
results = new LinkedList<GenericMatchInfo>();
|
||||
}
|
||||
results.add(createGenericMatchInfo(match));
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a <code>GenericMatchInfo</code> for the given <code>MatchInfo</code>.
|
||||
* @param matchInfo the MatchInfo
|
||||
* @return A GenericMatchInfo
|
||||
*/
|
||||
private GenericMatchInfo createGenericMatchInfo(MatchInfo matchInfo) {
|
||||
GenericNameType nameType = null;
|
||||
TimeType timeType = TimeType.UNKNOWN;
|
||||
switch (matchInfo.nameType()) {
|
||||
case LONG_STANDARD:
|
||||
timeType = TimeType.STANDARD;
|
||||
//$FALL-THROUGH$
|
||||
case LONG_GENERIC:
|
||||
nameType = GenericNameType.LONG;
|
||||
break;
|
||||
case SHORT_STANDARD_COMMONLY_USED:
|
||||
timeType = TimeType.STANDARD;
|
||||
//$FALL-THROUGH$
|
||||
case SHORT_GENERIC:
|
||||
nameType = GenericNameType.SHORT;
|
||||
break;
|
||||
}
|
||||
assert(nameType != null);
|
||||
|
||||
String tzID = matchInfo.tzID();
|
||||
if (tzID == null) {
|
||||
String mzID = matchInfo.mzID();
|
||||
assert(mzID != null);
|
||||
tzID = _tznames.getReferenceZoneID(mzID, getTargetRegion());
|
||||
}
|
||||
assert(tzID != null);
|
||||
|
||||
GenericMatchInfo gmatch = new GenericMatchInfo();
|
||||
gmatch.nameType = nameType;
|
||||
gmatch.tzID = tzID;
|
||||
gmatch.matchLength = matchInfo.matchLength();
|
||||
gmatch.timeType = timeType;
|
||||
|
||||
return gmatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a collection of time zone display name matches for the specified types in the
|
||||
* given text at the given offset. This method only finds matches from the TimeZoneNames
|
||||
* used by this object.
|
||||
* @param text the text
|
||||
* @param start the start offset in the text
|
||||
* @param types the set of name types.
|
||||
* @return A collection of match info.
|
||||
*/
|
||||
private Collection<MatchInfo> findTimeZoneNames(String text, int start, EnumSet<GenericNameType> types) {
|
||||
Collection<MatchInfo> tznamesMatches = null;
|
||||
|
||||
// Check if the target name type is really in the TimeZoneNames
|
||||
EnumSet<NameType> nameTypes = EnumSet.noneOf(NameType.class);
|
||||
if (types.contains(GenericNameType.LONG)) {
|
||||
nameTypes.add(NameType.LONG_GENERIC);
|
||||
nameTypes.add(NameType.LONG_STANDARD);
|
||||
}
|
||||
if (types.contains(GenericNameType.SHORT)) {
|
||||
nameTypes.add(NameType.SHORT_GENERIC);
|
||||
nameTypes.add(NameType.SHORT_STANDARD_COMMONLY_USED);
|
||||
}
|
||||
|
||||
if (!nameTypes.isEmpty()) {
|
||||
// Find matches in the TimeZoneNames
|
||||
tznamesMatches = _tznames.find(text, start, nameTypes);
|
||||
}
|
||||
return tznamesMatches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a collection of time zone display name matches for the specified types in the
|
||||
* given text at the given offset. This method only finds matches from the local trie,
|
||||
* that contains 1) generic location names and 2) long/short generic partial location names,
|
||||
* used by this object.
|
||||
* @param text the text
|
||||
* @param start the start offset in the text
|
||||
* @param types the set of name types.
|
||||
* @return A collection of match info.
|
||||
*/
|
||||
private synchronized Collection<GenericMatchInfo> findLocal(String text, int start, EnumSet<GenericNameType> types) {
|
||||
GenericNameSearchHandler handler = new GenericNameSearchHandler(types);
|
||||
_gnamesTrie.find(text, start, handler);
|
||||
if (handler.getMaxMatchLen() == (text.length() - start) || _gnamesTrieFullyLoaded) {
|
||||
// perfect match
|
||||
return handler.getMatches();
|
||||
}
|
||||
|
||||
// All names are not yet loaded into the local trie.
|
||||
// Load all available names into the trie. This could be very heavy.
|
||||
|
||||
Set<String> tzIDs = TimeZone.getAvailableIDs(SystemTimeZoneType.CANONICAL, null, null);
|
||||
for (String tzID : tzIDs) {
|
||||
loadStrings(tzID);
|
||||
}
|
||||
_gnamesTrieFullyLoaded = true;
|
||||
|
||||
// now, try it again
|
||||
handler.resetResults();
|
||||
_gnamesTrie.find(text, start, handler);
|
||||
return handler.getMatches();
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>TimeZoneGenericNames</code> cache implementation.
|
||||
*/
|
||||
private static class Cache extends SoftCache<String, TimeZoneGenericNames, ULocale> {
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.ibm.icu.impl.CacheBase#createInstance(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
protected TimeZoneGenericNames createInstance(String key, ULocale data) {
|
||||
return new TimeZoneGenericNames(data).freeze();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* The custom deserialization method.
|
||||
* This implementation only read locale used by the object.
|
||||
*/
|
||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
in.defaultReadObject();
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isFrozen() {
|
||||
return _frozen;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public TimeZoneGenericNames freeze() {
|
||||
_frozen = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public TimeZoneGenericNames cloneAsThawed() {
|
||||
TimeZoneGenericNames copy = null;
|
||||
try {
|
||||
copy = (TimeZoneGenericNames)super.clone();
|
||||
copy._frozen = false;
|
||||
} catch (Throwable t) {
|
||||
// This should never happen
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2011, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.impl;
|
||||
|
||||
import com.ibm.icu.text.TimeZoneNames;
|
||||
import com.ibm.icu.text.TimeZoneNames.Factory;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
|
||||
/**
|
||||
* The implementation class of <code>TimeZoneNames.Factory</code>
|
||||
*/
|
||||
public class TimeZoneNamesFactoryImpl extends Factory {
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.ibm.icu.text.TimeZoneNames.Factory#getTimeZoneNames(com.ibm.icu.util.ULocale)
|
||||
*/
|
||||
@Override
|
||||
public TimeZoneNames getTimeZoneNames(ULocale locale) {
|
||||
return new TimeZoneNamesImpl(locale);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,728 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2011, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.ibm.icu.impl.TextTrieMap.ResultHandler;
|
||||
import com.ibm.icu.text.TimeZoneNames;
|
||||
import com.ibm.icu.util.TimeZone;
|
||||
import com.ibm.icu.util.TimeZone.SystemTimeZoneType;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
import com.ibm.icu.util.UResourceBundle;
|
||||
|
||||
/**
|
||||
* The standard ICU implementation of TimeZoneNames
|
||||
*/
|
||||
public class TimeZoneNamesImpl extends TimeZoneNames {
|
||||
|
||||
private static final long serialVersionUID = -2179814848495897472L;
|
||||
|
||||
private static final String ZONE_STRINGS_BUNDLE = "zoneStrings";
|
||||
private static final String MZ_PREFIX = "meta:";
|
||||
|
||||
private static Set<String> METAZONE_IDS;
|
||||
private static final TZ2MZsCache TZ_TO_MZS_CACHE = new TZ2MZsCache();
|
||||
private static final MZ2TZsCache MZ_TO_TZS_CACHE = new MZ2TZsCache();
|
||||
|
||||
private transient ICUResourceBundle _zoneStrings;
|
||||
|
||||
|
||||
// These are hard cache. We create only one TimeZoneNamesImpl per locale
|
||||
// and it's stored in SoftCache, so we do not need to worry about the
|
||||
// footprint much.
|
||||
private transient ConcurrentHashMap<String, ZNames> _mzNamesMap;
|
||||
private transient ConcurrentHashMap<String, TZNames> _tzNamesMap;
|
||||
|
||||
private transient TextTrieMap<NameInfo> _namesTrie;
|
||||
private transient boolean _namesTrieFullyLoaded;
|
||||
|
||||
public TimeZoneNamesImpl(ULocale locale) {
|
||||
initialize(locale);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.ibm.icu.text.TimeZoneNames#getAvailableMetaZoneIDs()
|
||||
*/
|
||||
@Override
|
||||
public synchronized Set<String> getAvailableMetaZoneIDs() {
|
||||
if (METAZONE_IDS == null) {
|
||||
try {
|
||||
UResourceBundle bundle = UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, "metaZones");
|
||||
UResourceBundle mapTimezones = bundle.get("mapTimezones");
|
||||
Set<String> keys = mapTimezones.keySet();
|
||||
METAZONE_IDS = Collections.unmodifiableSet(keys);
|
||||
} catch (MissingResourceException e) {
|
||||
METAZONE_IDS = Collections.emptySet();
|
||||
}
|
||||
}
|
||||
return METAZONE_IDS;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.ibm.icu.text.TimeZoneNames#getAvailableMetaZoneIDs(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public Set<String> getAvailableMetaZoneIDs(String tzID) {
|
||||
if (tzID == null || tzID.length() == 0) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
List<MZMapEntry> maps = TZ_TO_MZS_CACHE.getInstance(tzID, tzID);
|
||||
if (maps.isEmpty()) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
Set<String> mzIDs = new HashSet<String>(maps.size());
|
||||
for (MZMapEntry map : maps) {
|
||||
mzIDs.add(map.mzID());
|
||||
}
|
||||
// make it unmodifiable because of the API contract. We may cache the results in futre.
|
||||
return Collections.unmodifiableSet(mzIDs);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.ibm.icu.text.TimeZoneNames#getMetaZoneID(java.lang.String, long)
|
||||
*/
|
||||
@Override
|
||||
public String getMetaZoneID(String tzID, long date) {
|
||||
if (tzID == null || tzID.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
String mzID = null;
|
||||
List<MZMapEntry> maps = TZ_TO_MZS_CACHE.getInstance(tzID, tzID);
|
||||
for (MZMapEntry map : maps) {
|
||||
if (date >= map.from() && date < map.to()) {
|
||||
mzID = map.mzID();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return mzID;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.ibm.icu.text.TimeZoneNames#getReferenceZoneID(java.lang.String, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public String getReferenceZoneID(String mzID, String region) {
|
||||
if (mzID == null || mzID.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
String refID = null;
|
||||
Map<String, String> regionTzMap = MZ_TO_TZS_CACHE.getInstance(mzID, mzID);
|
||||
if (!regionTzMap.isEmpty()) {
|
||||
refID = regionTzMap.get(region);
|
||||
if (refID == null) {
|
||||
refID = regionTzMap.get("001");
|
||||
}
|
||||
}
|
||||
return refID;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.ibm.icu.text.TimeZoneNames#getMetaZoneDisplayName(java.lang.String, com.ibm.icu.text.TimeZoneNames.NameType)
|
||||
*/
|
||||
@Override
|
||||
public String getMetaZoneDisplayName(String mzID, NameType type) {
|
||||
if (mzID == null || mzID.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
return loadMetaZoneNames(mzID).getName(type);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.ibm.icu.text.TimeZoneNames#getTimeZoneDisplayName(java.lang.String, com.ibm.icu.text.TimeZoneNames.NameType)
|
||||
*/
|
||||
@Override
|
||||
public String getTimeZoneDisplayName(String tzID, NameType type) {
|
||||
if (tzID == null || tzID.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
return loadTimeZoneNames(tzID).getName(type);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.ibm.icu.text.TimeZoneNames#getExemplarLocationName(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public String getExemplarLocationName(String tzID) {
|
||||
if (tzID == null || tzID.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
String locName = loadTimeZoneNames(tzID).getLocationName();
|
||||
if (locName == null) {
|
||||
locName = super.getExemplarLocationName(tzID);
|
||||
}
|
||||
return locName;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.ibm.icu.text.TimeZoneNames#find(java.lang.String, int, java.util.Set)
|
||||
*/
|
||||
@Override
|
||||
public synchronized Collection<MatchInfo> find(String text, int start, EnumSet<NameType> nameTypes) {
|
||||
if (text == null || text.length() == 0 || start < 0 || start >= text.length()) {
|
||||
throw new IllegalArgumentException("bad input text or range");
|
||||
}
|
||||
NameSearchHandler handler = new NameSearchHandler(nameTypes);
|
||||
_namesTrie.find(text, start, handler);
|
||||
if (handler.getMaxMatchLen() == (text.length() - start) || _namesTrieFullyLoaded) {
|
||||
// perfect match
|
||||
return handler.getMatches();
|
||||
}
|
||||
|
||||
// All names are not yet loaded into the trie
|
||||
|
||||
// time zone names
|
||||
Set<String> tzIDs = TimeZone.getAvailableIDs(SystemTimeZoneType.CANONICAL, null, null);
|
||||
for (String tzID : tzIDs) {
|
||||
loadTimeZoneNames(tzID);
|
||||
}
|
||||
|
||||
// meta zone names
|
||||
Set<String> mzIDs = getAvailableMetaZoneIDs();
|
||||
for (String mzID : mzIDs) {
|
||||
loadMetaZoneNames(mzID);
|
||||
}
|
||||
_namesTrieFullyLoaded = true;
|
||||
|
||||
// now, try it again
|
||||
handler.resetResults();
|
||||
_namesTrie.find(text, start, handler);
|
||||
return handler.getMatches();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the transient fields, called from the constructor and
|
||||
* readObject.
|
||||
*
|
||||
* @param locale The locale
|
||||
*/
|
||||
private void initialize(ULocale locale) {
|
||||
if (locale == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ICUResourceBundle bundle = (ICUResourceBundle)ICUResourceBundle.getBundleInstance(
|
||||
ICUResourceBundle.ICU_ZONE_BASE_NAME, locale);
|
||||
_zoneStrings = (ICUResourceBundle)bundle.get(ZONE_STRINGS_BUNDLE);
|
||||
} catch (MissingResourceException mre) {
|
||||
_zoneStrings = null;
|
||||
}
|
||||
|
||||
_tzNamesMap = new ConcurrentHashMap<String, TZNames>();
|
||||
_mzNamesMap = new ConcurrentHashMap<String, ZNames>();
|
||||
|
||||
_namesTrie = new TextTrieMap<NameInfo>(true);
|
||||
_namesTrieFullyLoaded = false;
|
||||
|
||||
// Preload zone strings for the default time zone
|
||||
TimeZone tz = TimeZone.getDefault();
|
||||
String tzCanonicalID = ZoneMeta.getCanonicalCLDRID(tz);
|
||||
if (tzCanonicalID != null) {
|
||||
loadStrings(tzCanonicalID);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all strings used by the specified time zone.
|
||||
* This is called from the initializer to load default zone's
|
||||
* strings.
|
||||
* @param tzCanonicalID the canonical time zone ID
|
||||
*/
|
||||
private synchronized void loadStrings(String tzCanonicalID) {
|
||||
if (tzCanonicalID == null || tzCanonicalID.length() == 0) {
|
||||
return;
|
||||
}
|
||||
loadTimeZoneNames(tzCanonicalID);
|
||||
|
||||
Set<String> mzIDs = getAvailableMetaZoneIDs(tzCanonicalID);
|
||||
for (String mzID : mzIDs) {
|
||||
loadMetaZoneNames(mzID);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The custom serialization method.
|
||||
* This implementation only preserve locale used for the names.
|
||||
*/
|
||||
private void writeObject(ObjectOutputStream out) throws IOException {
|
||||
ULocale locale = _zoneStrings == null ? null : _zoneStrings.getULocale();
|
||||
out.writeObject(locale);
|
||||
}
|
||||
|
||||
/*
|
||||
* The custom deserialization method.
|
||||
* This implementation only read locale used by the object.
|
||||
*/
|
||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
ULocale locale = (ULocale)in.readObject();
|
||||
initialize(locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of names for the given meta zone ID. This method loads
|
||||
* the set of names into the internal map and trie for future references.
|
||||
* @param mzID the meta zone ID
|
||||
* @return An instance of ZNames that includes a set of meta zone display names.
|
||||
*/
|
||||
private synchronized ZNames loadMetaZoneNames(String mzID) {
|
||||
ZNames znames = _mzNamesMap.get(mzID);
|
||||
if (znames == null) {
|
||||
znames = ZNames.getInstance(_zoneStrings, MZ_PREFIX + mzID);
|
||||
// put names into the trie
|
||||
mzID = mzID.intern();
|
||||
for (NameType t : NameType.values()) {
|
||||
String name = znames.getName(t);
|
||||
if (name != null) {
|
||||
NameInfo info = new NameInfo();
|
||||
info.mzID = mzID;
|
||||
info.type = t;
|
||||
_namesTrie.put(name, info);
|
||||
}
|
||||
}
|
||||
_mzNamesMap.put(mzID, znames);
|
||||
}
|
||||
return znames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of names for the given time zone ID. This method loads
|
||||
* the set of names into the internal map and trie for future references.
|
||||
* @param tzID the canonical time zone ID
|
||||
* @return An instance of TZNames that includes a set of time zone display names.
|
||||
*/
|
||||
private synchronized TZNames loadTimeZoneNames(String tzID) {
|
||||
TZNames tznames = _tzNamesMap.get(tzID);
|
||||
if (tznames == null) {
|
||||
tznames = TZNames.getInstance(_zoneStrings, tzID.replace('/', ':'));
|
||||
// put names into the trie
|
||||
tzID = tzID.intern();
|
||||
for (NameType t : NameType.values()) {
|
||||
String name = tznames.getName(t);
|
||||
if (name != null) {
|
||||
NameInfo info = new NameInfo();
|
||||
info.tzID = tzID;
|
||||
info.type = t;
|
||||
_namesTrie.put(name, info);
|
||||
}
|
||||
}
|
||||
_tzNamesMap.put(tzID, tznames);
|
||||
}
|
||||
return tznames;
|
||||
}
|
||||
|
||||
/**
|
||||
* An instance of NameInfo is stored in the zone names trie.
|
||||
*/
|
||||
private static class NameInfo {
|
||||
String tzID;
|
||||
String mzID;
|
||||
NameType type;
|
||||
}
|
||||
|
||||
/**
|
||||
* NameSearchHandler is used for collecting name matches.
|
||||
*/
|
||||
private static class NameSearchHandler implements ResultHandler<NameInfo> {
|
||||
private EnumSet<NameType> _nameTypes;
|
||||
private Collection<MatchInfo> _matches;
|
||||
private int _maxMatchLen;
|
||||
|
||||
NameSearchHandler(EnumSet<NameType> nameTypes) {
|
||||
_nameTypes = nameTypes;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.ibm.icu.impl.TextTrieMap.ResultHandler#handlePrefixMatch(int, java.util.Iterator)
|
||||
*/
|
||||
public boolean handlePrefixMatch(int matchLength, Iterator<NameInfo> values) {
|
||||
while (values.hasNext()) {
|
||||
NameInfo ninfo = values.next();
|
||||
if (_nameTypes != null && !_nameTypes.contains(ninfo.type)) {
|
||||
continue;
|
||||
}
|
||||
MatchInfo minfo;
|
||||
if (ninfo.tzID != null) {
|
||||
minfo = new MatchInfo(ninfo.type, ninfo.tzID, null, matchLength);
|
||||
} else {
|
||||
assert(ninfo.mzID != null);
|
||||
minfo = new MatchInfo(ninfo.type, null, ninfo.mzID, matchLength);
|
||||
}
|
||||
if (_matches == null) {
|
||||
_matches = new LinkedList<MatchInfo>();
|
||||
}
|
||||
_matches.add(minfo);
|
||||
if (matchLength > _maxMatchLen) {
|
||||
_maxMatchLen = matchLength;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the match results
|
||||
* @return the match results
|
||||
*/
|
||||
public Collection<MatchInfo> getMatches() {
|
||||
if (_matches == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return _matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum match length, or 0 if no match was found
|
||||
* @return the maximum match length
|
||||
*/
|
||||
public int getMaxMatchLen() {
|
||||
return _maxMatchLen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the match results
|
||||
*/
|
||||
public void resetResults() {
|
||||
_matches = null;
|
||||
_maxMatchLen = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class stores name data for a meta zone
|
||||
*/
|
||||
private static class ZNames {
|
||||
private static final ZNames EMPTY_ZNAMES = new ZNames(null, false);
|
||||
|
||||
private String[] _names;
|
||||
private boolean _shortCommonlyUsed;
|
||||
|
||||
private static final String[] KEYS = {"lg", "ls", "ld", "sg", "ss", "sd"};
|
||||
|
||||
protected ZNames(String[] names, boolean shortCommonlyUsed) {
|
||||
_names = names;
|
||||
_shortCommonlyUsed = shortCommonlyUsed;
|
||||
}
|
||||
|
||||
public static ZNames getInstance(ICUResourceBundle zoneStrings, String key) {
|
||||
boolean[] cu = new boolean[1];
|
||||
String[] names = loadData(zoneStrings, key, cu);
|
||||
if (names == null) {
|
||||
return EMPTY_ZNAMES;
|
||||
}
|
||||
return new ZNames(names, cu[0]);
|
||||
}
|
||||
|
||||
public String getName(NameType type) {
|
||||
if (_names == null) {
|
||||
return null;
|
||||
}
|
||||
String name = null;
|
||||
switch (type) {
|
||||
case LONG_GENERIC:
|
||||
name = _names[0];
|
||||
break;
|
||||
case LONG_STANDARD:
|
||||
name = _names[1];
|
||||
break;
|
||||
case LONG_DAYLIGHT:
|
||||
name = _names[2];
|
||||
break;
|
||||
case SHORT_GENERIC:
|
||||
if (_shortCommonlyUsed) {
|
||||
name = _names[3];
|
||||
}
|
||||
break;
|
||||
case SHORT_STANDARD:
|
||||
name = _names[4];
|
||||
break;
|
||||
case SHORT_DAYLIGHT:
|
||||
name = _names[5];
|
||||
break;
|
||||
case SHORT_STANDARD_COMMONLY_USED:
|
||||
if (_shortCommonlyUsed) {
|
||||
name = _names[4];
|
||||
}
|
||||
break;
|
||||
case SHORT_DAYLIGHT_COMMONLY_USED:
|
||||
if (_shortCommonlyUsed) {
|
||||
name = _names[5];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
protected static String[] loadData(ICUResourceBundle zoneStrings, String key, boolean[] shortCommonlyUsed) {
|
||||
if (zoneStrings == null || key == null || key.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
shortCommonlyUsed[0] = false;
|
||||
ICUResourceBundle table = null;
|
||||
try {
|
||||
table = zoneStrings.getWithFallback(key);
|
||||
} catch (MissingResourceException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean isEmpty = true;
|
||||
String[] names = new String[KEYS.length];
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
try {
|
||||
names[i] = table.getStringWithFallback(KEYS[i]);
|
||||
isEmpty = false;
|
||||
} catch (MissingResourceException e) {
|
||||
names[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
ICUResourceBundle cuRes = table.getWithFallback("cu");
|
||||
int cu = cuRes.getInt();
|
||||
shortCommonlyUsed[0] = (cu != 0);
|
||||
} catch (MissingResourceException e) {
|
||||
// cu is optional
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class stores name data for a single time zone
|
||||
*/
|
||||
private static class TZNames extends ZNames {
|
||||
private String _locationName;
|
||||
|
||||
private static final TZNames EMPTY_TZNAMES = new TZNames(null, false, null);
|
||||
|
||||
public static TZNames getInstance(ICUResourceBundle zoneStrings, String key) {
|
||||
if (zoneStrings == null || key == null || key.length() == 0) {
|
||||
return EMPTY_TZNAMES;
|
||||
}
|
||||
|
||||
ICUResourceBundle table = null;
|
||||
try {
|
||||
table = zoneStrings.getWithFallback(key);
|
||||
} catch (MissingResourceException e) {
|
||||
return EMPTY_TZNAMES;
|
||||
}
|
||||
|
||||
String locationName = null;
|
||||
try {
|
||||
locationName = table.getStringWithFallback("ec");
|
||||
} catch (MissingResourceException e) {
|
||||
// location name is optional
|
||||
}
|
||||
|
||||
boolean[] cu = new boolean[1];
|
||||
String[] names = loadData(zoneStrings, key, cu);
|
||||
|
||||
if (locationName == null && names == null) {
|
||||
return EMPTY_TZNAMES;
|
||||
}
|
||||
return new TZNames(names, cu[0], locationName);
|
||||
}
|
||||
|
||||
public String getLocationName() {
|
||||
return _locationName;
|
||||
}
|
||||
|
||||
private TZNames(String[] names, boolean shortCommonlyUsed, String locationName) {
|
||||
super(names, shortCommonlyUsed);
|
||||
_locationName = locationName;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Canonical time zone ID -> meta zone ID
|
||||
//
|
||||
|
||||
private static class MZMapEntry {
|
||||
private String _mzID;
|
||||
private long _from;
|
||||
private long _to;
|
||||
|
||||
MZMapEntry(String mzID, long from, long to) {
|
||||
_mzID = mzID;
|
||||
_from = from;
|
||||
_to = to;
|
||||
}
|
||||
|
||||
String mzID() {
|
||||
return _mzID;
|
||||
}
|
||||
|
||||
long from() {
|
||||
return _from;
|
||||
}
|
||||
|
||||
long to() {
|
||||
return _to;
|
||||
}
|
||||
}
|
||||
|
||||
private static class TZ2MZsCache extends SoftCache<String, List<MZMapEntry>, String> {
|
||||
/* (non-Javadoc)
|
||||
* @see com.ibm.icu.impl.CacheBase#createInstance(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
protected List<MZMapEntry> createInstance(String key, String data) {
|
||||
List<MZMapEntry> mzMaps = null;
|
||||
try {
|
||||
UResourceBundle bundle = UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, "metaZones");
|
||||
UResourceBundle metazoneInfoBundle = bundle.get("metazoneInfo");
|
||||
|
||||
String tzkey = data.replace('/', ':');
|
||||
UResourceBundle zoneBundle = metazoneInfoBundle.get(tzkey);
|
||||
|
||||
mzMaps = new ArrayList<MZMapEntry>(zoneBundle.getSize());
|
||||
for (int idx = 0; idx < zoneBundle.getSize(); idx++) {
|
||||
UResourceBundle mz = zoneBundle.get(idx);
|
||||
String mzid = mz.getString(0);
|
||||
String fromStr = "1970-01-01 00:00";
|
||||
String toStr = "9999-12-31 23:59";
|
||||
if (mz.getSize() == 3) {
|
||||
fromStr = mz.getString(1);
|
||||
toStr = mz.getString(2);
|
||||
}
|
||||
long from, to;
|
||||
from = parseDate(fromStr);
|
||||
to = parseDate(toStr);
|
||||
mzMaps.add(new MZMapEntry(mzid, from, to));
|
||||
}
|
||||
|
||||
} catch (MissingResourceException mre) {
|
||||
// fall through
|
||||
}
|
||||
if (mzMaps == null) {
|
||||
mzMaps = Collections.emptyList();
|
||||
}
|
||||
return mzMaps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Private static method parsing the date text used by meta zone to
|
||||
* time zone mapping data in locale resource.
|
||||
*
|
||||
* @param text the UTC date text in the format of "yyyy-MM-dd HH:mm",
|
||||
* for example - "1970-01-01 00:00"
|
||||
* @return the date
|
||||
*/
|
||||
private static long parseDate (String text) {
|
||||
int year = 0, month = 0, day = 0, hour = 0, min = 0;
|
||||
int idx;
|
||||
int n;
|
||||
|
||||
// "yyyy" (0 - 3)
|
||||
for (idx = 0; idx <= 3; idx++) {
|
||||
n = text.charAt(idx) - '0';
|
||||
if (n >= 0 && n < 10) {
|
||||
year = 10*year + n;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Bad year");
|
||||
}
|
||||
}
|
||||
// "MM" (5 - 6)
|
||||
for (idx = 5; idx <= 6; idx++) {
|
||||
n = text.charAt(idx) - '0';
|
||||
if (n >= 0 && n < 10) {
|
||||
month = 10*month + n;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Bad month");
|
||||
}
|
||||
}
|
||||
// "dd" (8 - 9)
|
||||
for (idx = 8; idx <= 9; idx++) {
|
||||
n = text.charAt(idx) - '0';
|
||||
if (n >= 0 && n < 10) {
|
||||
day = 10*day + n;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Bad day");
|
||||
}
|
||||
}
|
||||
// "HH" (11 - 12)
|
||||
for (idx = 11; idx <= 12; idx++) {
|
||||
n = text.charAt(idx) - '0';
|
||||
if (n >= 0 && n < 10) {
|
||||
hour = 10*hour + n;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Bad hour");
|
||||
}
|
||||
}
|
||||
// "mm" (14 - 15)
|
||||
for (idx = 14; idx <= 15; idx++) {
|
||||
n = text.charAt(idx) - '0';
|
||||
if (n >= 0 && n < 10) {
|
||||
min = 10*min + n;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Bad minute");
|
||||
}
|
||||
}
|
||||
|
||||
long date = Grego.fieldsToDay(year, month - 1, day) * Grego.MILLIS_PER_DAY
|
||||
+ hour * Grego.MILLIS_PER_HOUR + min * Grego.MILLIS_PER_MINUTE;
|
||||
return date;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Meta zone ID -> time zone ID
|
||||
//
|
||||
|
||||
private static class MZ2TZsCache extends SoftCache<String, Map<String, String>, String> {
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.ibm.icu.impl.CacheBase#createInstance(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
protected Map<String, String> createInstance(String key, String data) {
|
||||
Map<String, String> map = null;
|
||||
try {
|
||||
UResourceBundle bundle = UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, "metaZones");
|
||||
UResourceBundle mapTimezones = bundle.get("mapTimezones");
|
||||
UResourceBundle regionMap = mapTimezones.get(key);
|
||||
|
||||
Set<String> regions = regionMap.keySet();
|
||||
map = new HashMap<String, String>(regions.size());
|
||||
|
||||
for (String region : regions) {
|
||||
String tzID = regionMap.getString(region).intern();
|
||||
map.put(region.intern(), tzID);
|
||||
}
|
||||
} catch (MissingResourceException e) {
|
||||
// fall through
|
||||
}
|
||||
if (map == null) {
|
||||
map = Collections.emptyMap();
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
}
|
@ -13,20 +13,15 @@ package com.ibm.icu.impl;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.text.ParsePosition;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Locale;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import com.ibm.icu.text.MessageFormat;
|
||||
import com.ibm.icu.text.NumberFormat;
|
||||
import com.ibm.icu.util.SimpleTimeZone;
|
||||
import com.ibm.icu.util.TimeZone;
|
||||
import com.ibm.icu.util.TimeZone.SystemTimeZoneType;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
import com.ibm.icu.util.UResourceBundle;
|
||||
|
||||
/**
|
||||
@ -179,12 +174,16 @@ public final class ZoneMeta {
|
||||
return baseSet;
|
||||
}
|
||||
|
||||
if (region != null) {
|
||||
region = region.toUpperCase(Locale.US);
|
||||
}
|
||||
|
||||
// Filter by region/rawOffset
|
||||
Set<String> result = new TreeSet<String>();
|
||||
for (String id : baseSet) {
|
||||
if (region != null) {
|
||||
String r = getRegion(id);
|
||||
if (!region.equalsIgnoreCase(r)) {
|
||||
if (!region.equals(r)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -336,11 +335,17 @@ public final class ZoneMeta {
|
||||
return zoneIdx;
|
||||
}
|
||||
|
||||
|
||||
private static ICUCache<String, String> CANONICAL_ID_CACHE = new SimpleCache<String, String>();
|
||||
private static ICUCache<String, String> REGION_CACHE = new SimpleCache<String, String>();
|
||||
private static ICUCache<String, Boolean> SINGLE_COUNTRY_CACHE = new SimpleCache<String, Boolean>();
|
||||
|
||||
public static String getCanonicalCLDRID(TimeZone tz) {
|
||||
if (tz instanceof OlsonTimeZone) {
|
||||
return ((OlsonTimeZone)tz).getCanonicalID();
|
||||
}
|
||||
return getCanonicalCLDRID(tz.getID());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the canonical id for this tzid defined by CLDR, which might be
|
||||
* the id itself. If the given tzid is not known, return null.
|
||||
@ -449,101 +454,6 @@ public final class ZoneMeta {
|
||||
return country;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a time zone location(region) format string defined by UTR#35.
|
||||
* e.g. "Italy Time", "United States (Los Angeles) Time"
|
||||
*/
|
||||
public static String getLocationFormat(String tzid, String city, ULocale locale) {
|
||||
String country_code = getCanonicalCountry(tzid);
|
||||
if (country_code == null) {
|
||||
// no location is associated
|
||||
return null;
|
||||
}
|
||||
|
||||
String country = null;
|
||||
try {
|
||||
ICUResourceBundle rb =
|
||||
(ICUResourceBundle)UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_REGION_BASE_NAME, locale);
|
||||
//
|
||||
// TODO: There is a design bug in UResourceBundle and getLoadingStatus() does not work well.
|
||||
//
|
||||
// if (rb.getLoadingStatus() != ICUResourceBundle.FROM_ROOT && rb.getLoadingStatus() != ICUResourceBundle.FROM_DEFAULT) {
|
||||
// country = ULocale.getDisplayCountry("xx_" + country_code, locale);
|
||||
// }
|
||||
// START WORKAROUND
|
||||
ULocale rbloc = rb.getULocale();
|
||||
if (!rbloc.equals(ULocale.ROOT) && rbloc.getLanguage().equals(locale.getLanguage())) {
|
||||
country = ULocale.getDisplayCountry("xx_" + country_code, locale);
|
||||
}
|
||||
// END WORKAROUND
|
||||
} catch (MissingResourceException e) {
|
||||
// fall through
|
||||
}
|
||||
if (country == null || country.length() == 0) {
|
||||
country = country_code;
|
||||
}
|
||||
|
||||
// This is not behavior specified in tr35, but behavior added by Mark.
|
||||
// TR35 says to display the country _only_ if there is a localization.
|
||||
if (getSingleCountry(tzid) != null) { // single country
|
||||
String regPat = getTZLocalizationInfo(locale, REGION_FORMAT);
|
||||
if (regPat == null) {
|
||||
regPat = DEF_REGION_FORMAT;
|
||||
}
|
||||
MessageFormat mf = new MessageFormat(regPat);
|
||||
return mf.format(new Object[] { country });
|
||||
}
|
||||
|
||||
if (city == null) {
|
||||
city = tzid.substring(tzid.lastIndexOf('/')+1).replace('_',' ');
|
||||
}
|
||||
|
||||
String flbPat = getTZLocalizationInfo(locale, FALLBACK_FORMAT);
|
||||
if (flbPat == null) {
|
||||
flbPat = DEF_FALLBACK_FORMAT;
|
||||
}
|
||||
MessageFormat mf = new MessageFormat(flbPat);
|
||||
|
||||
return mf.format(new Object[] { city, country });
|
||||
}
|
||||
|
||||
private static final String DEF_REGION_FORMAT = "{0}";
|
||||
private static final String DEF_FALLBACK_FORMAT = "{1} ({0})";
|
||||
|
||||
public static final String
|
||||
HOUR = "hourFormat",
|
||||
GMT = "gmtFormat",
|
||||
REGION_FORMAT = "regionFormat",
|
||||
FALLBACK_FORMAT = "fallbackFormat",
|
||||
ZONE_STRINGS = "zoneStrings",
|
||||
FORWARD_SLASH = "/";
|
||||
|
||||
/**
|
||||
* Get the index'd tz datum for this locale. Index must be one of the
|
||||
* values PREFIX, HOUR, GMT, REGION_FORMAT, FALLBACK_FORMAT
|
||||
*/
|
||||
public static String getTZLocalizationInfo(ULocale locale, String format) {
|
||||
String result = null;
|
||||
try {
|
||||
ICUResourceBundle bundle = (ICUResourceBundle) ICUResourceBundle.getBundleInstance(
|
||||
ICUResourceBundle.ICU_ZONE_BASE_NAME, locale);
|
||||
result = bundle.getStringWithFallback(ZONE_STRINGS+FORWARD_SLASH+format);
|
||||
} catch (MissingResourceException e) {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// private static Set getValidIDs() {
|
||||
// // Construct list of time zones that are valid, according
|
||||
// // to the current underlying core JDK. We have to do this
|
||||
// // at runtime since we don't know what we're running on.
|
||||
// Set valid = new TreeSet();
|
||||
// valid.addAll(Arrays.asList(java.util.TimeZone.getAvailableIDs()));
|
||||
// return valid;
|
||||
// }
|
||||
|
||||
|
||||
/**
|
||||
* Given an ID and the top-level resource of the zoneinfo resource,
|
||||
* open the appropriate resource for the given time zone.
|
||||
@ -827,182 +737,4 @@ public final class ZoneMeta {
|
||||
}
|
||||
return zid.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a CLDR metazone ID for the given Olson tzid and time.
|
||||
*/
|
||||
public static String getMetazoneID(String olsonID, long date) {
|
||||
String mzid = null;
|
||||
List<OlsonToMetaMappingEntry> mappings = getOlsonToMatazones(olsonID);
|
||||
if (mappings != null) {
|
||||
for (int i = 0; i < mappings.size(); i++) {
|
||||
OlsonToMetaMappingEntry mzm = mappings.get(i);
|
||||
if (date >= mzm.from && date < mzm.to) {
|
||||
mzid = mzm.mzid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return mzid;
|
||||
}
|
||||
|
||||
private static ICUCache<String, List<OlsonToMetaMappingEntry>> OLSON_TO_META_CACHE =
|
||||
new SimpleCache<String, List<OlsonToMetaMappingEntry>>();
|
||||
|
||||
static class OlsonToMetaMappingEntry {
|
||||
String mzid;
|
||||
long from;
|
||||
long to;
|
||||
}
|
||||
|
||||
static List<OlsonToMetaMappingEntry> getOlsonToMatazones(String tzid) {
|
||||
List<OlsonToMetaMappingEntry> mzMappings = OLSON_TO_META_CACHE.get(tzid);
|
||||
if (mzMappings == null) {
|
||||
try {
|
||||
UResourceBundle bundle = UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, "metaZones");
|
||||
UResourceBundle metazoneInfoBundle = bundle.get("metazoneInfo");
|
||||
|
||||
String canonicalID = getCanonicalCLDRID(tzid);
|
||||
if (canonicalID == null) {
|
||||
return null;
|
||||
}
|
||||
String tzkey = canonicalID.replace('/', ':');
|
||||
UResourceBundle zoneBundle = metazoneInfoBundle.get(tzkey);
|
||||
|
||||
mzMappings = new LinkedList<OlsonToMetaMappingEntry>();
|
||||
|
||||
for (int idx = 0; idx < zoneBundle.getSize(); idx++) {
|
||||
UResourceBundle mz = zoneBundle.get(idx);
|
||||
String mzid = mz.getString(0);
|
||||
String from = "1970-01-01 00:00";
|
||||
String to = "9999-12-31 23:59";
|
||||
if (mz.getSize() == 3) {
|
||||
from = mz.getString(1);
|
||||
to = mz.getString(2);
|
||||
}
|
||||
OlsonToMetaMappingEntry mzmap = new OlsonToMetaMappingEntry();
|
||||
mzmap.mzid = mzid.intern();
|
||||
try {
|
||||
mzmap.from = parseDate(from);
|
||||
mzmap.to = parseDate(to);
|
||||
} catch (IllegalArgumentException baddate) {
|
||||
// skip this
|
||||
continue;
|
||||
}
|
||||
// Add this mapping to the list
|
||||
mzMappings.add(mzmap);
|
||||
}
|
||||
|
||||
} catch (MissingResourceException mre) {
|
||||
// fall through
|
||||
}
|
||||
if (mzMappings != null) {
|
||||
OLSON_TO_META_CACHE.put(tzid, mzMappings);
|
||||
}
|
||||
}
|
||||
return mzMappings;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a date string used by metazone mappings to long.
|
||||
* The format used by CLDR metazone mapping is "yyyy-MM-dd HH:mm".
|
||||
* We do not want to use SimpleDateFormat to parse the metazone
|
||||
* mapping range strings in createOlsonToMeta, because it might be
|
||||
* called from SimpleDateFormat initialization code.
|
||||
*/
|
||||
static long parseDate (String text) throws IllegalArgumentException {
|
||||
int year = 0, month = 0, day = 0, hour = 0, min = 0;
|
||||
int idx;
|
||||
int n;
|
||||
|
||||
// "yyyy" (0 - 3)
|
||||
for (idx = 0; idx <= 3; idx++) {
|
||||
n = text.charAt(idx) - '0';
|
||||
if (n >= 0 && n < 10) {
|
||||
year = 10*year + n;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Bad year");
|
||||
}
|
||||
}
|
||||
// "MM" (5 - 6)
|
||||
for (idx = 5; idx <= 6; idx++) {
|
||||
n = text.charAt(idx) - '0';
|
||||
if (n >= 0 && n < 10) {
|
||||
month = 10*month + n;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Bad month");
|
||||
}
|
||||
}
|
||||
// "dd" (8 - 9)
|
||||
for (idx = 8; idx <= 9; idx++) {
|
||||
n = text.charAt(idx) - '0';
|
||||
if (n >= 0 && n < 10) {
|
||||
day = 10*day + n;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Bad day");
|
||||
}
|
||||
}
|
||||
// "HH" (11 - 12)
|
||||
for (idx = 11; idx <= 12; idx++) {
|
||||
n = text.charAt(idx) - '0';
|
||||
if (n >= 0 && n < 10) {
|
||||
hour = 10*hour + n;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Bad hour");
|
||||
}
|
||||
}
|
||||
// "mm" (14 - 15)
|
||||
for (idx = 14; idx <= 15; idx++) {
|
||||
n = text.charAt(idx) - '0';
|
||||
if (n >= 0 && n < 10) {
|
||||
min = 10*min + n;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Bad minute");
|
||||
}
|
||||
}
|
||||
|
||||
long date = Grego.fieldsToDay(year, month - 1, day) * Grego.MILLIS_PER_DAY
|
||||
+ hour * Grego.MILLIS_PER_HOUR + min * Grego.MILLIS_PER_MINUTE;
|
||||
return date;
|
||||
}
|
||||
|
||||
private static ICUCache<String, Map<String, String>> META_TO_OLSON_CACHE =
|
||||
new SimpleCache<String, Map<String, String>>();
|
||||
|
||||
/**
|
||||
* Returns an Olson ID for the ginve metazone and region
|
||||
*/
|
||||
public static String getZoneIdByMetazone(String metazoneID, String region) {
|
||||
String tzid = null;
|
||||
|
||||
// look up in the cache first
|
||||
Map<String, String> zoneMap = META_TO_OLSON_CACHE.get(metazoneID);
|
||||
if (zoneMap == null) {
|
||||
try {
|
||||
// Create zone mappings for the metazone
|
||||
UResourceBundle bundle = UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, "metaZones");
|
||||
UResourceBundle mapTimezones = bundle.get("mapTimezones");
|
||||
UResourceBundle territoryMap = mapTimezones.get(metazoneID);
|
||||
zoneMap = new HashMap<String, String>();
|
||||
Set<String> territories = territoryMap.keySet();
|
||||
for (String territory : territories) {
|
||||
String zone = territoryMap.getString(territory);
|
||||
zoneMap.put(territory, zone);
|
||||
}
|
||||
// cache this
|
||||
META_TO_OLSON_CACHE.put(metazoneID, zoneMap);
|
||||
} catch (MissingResourceException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
if (zoneMap != null) {
|
||||
tzid = zoneMap.get(region);
|
||||
if (tzid == null) {
|
||||
tzid = zoneMap.get(kWorld); // use the mapping for world as fallback
|
||||
}
|
||||
}
|
||||
|
||||
return tzid;
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -19,11 +19,10 @@ import com.ibm.icu.impl.CalendarUtil;
|
||||
import com.ibm.icu.impl.ICUCache;
|
||||
import com.ibm.icu.impl.ICUResourceBundle;
|
||||
import com.ibm.icu.impl.SimpleCache;
|
||||
import com.ibm.icu.impl.TimeZoneFormat;
|
||||
import com.ibm.icu.impl.Utility;
|
||||
import com.ibm.icu.impl.ZoneMeta;
|
||||
import com.ibm.icu.impl.ZoneStringFormat;
|
||||
import com.ibm.icu.text.TimeZoneNames.NameType;
|
||||
import com.ibm.icu.util.Calendar;
|
||||
import com.ibm.icu.util.TimeZone;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
import com.ibm.icu.util.UResourceBundle;
|
||||
|
||||
@ -409,23 +408,6 @@ public class DateFormatSymbols implements Serializable, Cloneable {
|
||||
*/
|
||||
String standaloneQuarters[] = null;
|
||||
|
||||
/**
|
||||
* Pattern string used for localized time zone GMT format. For example, "GMT{0}"
|
||||
* @serial
|
||||
*/
|
||||
String gmtFormat = null;
|
||||
|
||||
/**
|
||||
* Pattern strings used for formatting zone offset in a localized time zone GMT string.
|
||||
* This is 2x2 String array holding followings
|
||||
* [0][0] Negative H + m + s
|
||||
* [0][1] Negative H + m
|
||||
* [1][0] Positive H + m + s
|
||||
* [1][1] Positive H + m
|
||||
* @serial
|
||||
*/
|
||||
String gmtHourFormats[][] = null;
|
||||
|
||||
/**
|
||||
* Localized names of time zones in this locale. This is a
|
||||
* two-dimensional array of strings of size <em>n</em> by <em>m</em>,
|
||||
@ -461,14 +443,6 @@ public class DateFormatSymbols implements Serializable, Cloneable {
|
||||
*/
|
||||
private String zoneStrings[][] = null;
|
||||
|
||||
/**
|
||||
* Since ICU 3.8.1, we use ZoneStringFormat to access localized
|
||||
* zone names. This field remains null unless setZoneStrings is
|
||||
* called.
|
||||
*/
|
||||
private transient ZoneStringFormat zsformat = null;
|
||||
private transient TimeZoneFormat tzformat = null;
|
||||
|
||||
/**
|
||||
* Unlocalized date-time pattern characters. For example: 'y', 'd', etc.
|
||||
* All locales use the same unlocalized pattern characters.
|
||||
@ -864,28 +838,74 @@ public class DateFormatSymbols implements Serializable, Cloneable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns timezone strings.
|
||||
* @return the timezone strings.
|
||||
* Returns time zone strings.
|
||||
* <p>
|
||||
* The array returned by this API is a two dimensional String array and
|
||||
* each row contains at least following strings:
|
||||
* <ul>
|
||||
* <li>ZoneStrings[n][0] - System time zone ID
|
||||
* <li>ZoneStrings[n][1] - Long standard time display name
|
||||
* <li>ZoneStrings[n][2] - Short standard time display name
|
||||
* <li>ZoneStrings[n][3] - Long daylight saving time display name
|
||||
* <li>ZoneStrings[n][4] - Short daylight saving time display name
|
||||
* </ul>
|
||||
* When a localized display name is not available, the corresponding
|
||||
* array element will be <code>null</code>.
|
||||
* <p>
|
||||
* <b>Note</b>: ICU implements time zone display name formatting algorithm
|
||||
* specified by <a href="http://www.unicode.org/reports/tr35/">UTS#35 Unicode
|
||||
* Locale Data Markup Language(LDML)</a>. The algorithm supports historic
|
||||
* display name changes and various different type of names not available in
|
||||
* JDK. For accessing the full set of time zone string data used by ICU implementation,
|
||||
* you should use {@link TimeZoneNames} APIs instead.
|
||||
*
|
||||
* @return the time zone strings.
|
||||
* @stable ICU 2.0
|
||||
*/
|
||||
public String[][] getZoneStrings() {
|
||||
if (zoneStrings != null) {
|
||||
return duplicate(zoneStrings);
|
||||
}
|
||||
return ZoneStringFormat.getInstance(requestedLocale).getZoneStrings();
|
||||
|
||||
String[] tzIDs = TimeZone.getAvailableIDs();
|
||||
TimeZoneNames tznames = TimeZoneNames.getInstance(validLocale);
|
||||
long now = System.currentTimeMillis();
|
||||
String[][] array = new String[tzIDs.length][5];
|
||||
for (int i = 0; i < tzIDs.length; i++) {
|
||||
String canonicalID = TimeZone.getCanonicalID(tzIDs[i]);
|
||||
if (canonicalID == null) {
|
||||
canonicalID = tzIDs[i];
|
||||
}
|
||||
|
||||
array[i][0] = tzIDs[i];
|
||||
array[i][1] = tznames.getDisplayName(canonicalID, NameType.LONG_STANDARD, now);
|
||||
array[i][2] = tznames.getDisplayName(canonicalID, NameType.SHORT_STANDARD, now);
|
||||
array[i][3] = tznames.getDisplayName(canonicalID, NameType.LONG_DAYLIGHT, now);
|
||||
array[i][4] = tznames.getDisplayName(canonicalID, NameType.SHORT_DAYLIGHT, now);
|
||||
}
|
||||
|
||||
zoneStrings = array;
|
||||
return zoneStrings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets timezone strings.
|
||||
* @param newZoneStrings the new timezone strings.
|
||||
* Sets time zone strings.
|
||||
* <p>
|
||||
* <b>Note</b>: {@link SimpleDateFormat} no longer uses the
|
||||
* zone strings stored in a <code>DateFormatSymbols</code>.
|
||||
* Therefore, the time zone strings set by this method have
|
||||
* no effects in an instance of <code>SimpleDateFormat</code>
|
||||
* for formatting time zones. If you want to customize time
|
||||
* zone display names formatted by <code>SimpleDateFormat</code>,
|
||||
* you should customize {@link TimeZoneFormat} and set the
|
||||
* instance by {@link SimpleDateFormat#setTimeZoneFormat(TimeZoneFormat)}
|
||||
* instead.
|
||||
*
|
||||
* @param newZoneStrings the new time zone strings.
|
||||
* @stable ICU 2.0
|
||||
*/
|
||||
public void setZoneStrings(String[][] newZoneStrings) {
|
||||
zoneStrings = duplicate(newZoneStrings);
|
||||
if ( tzformat == null ) {
|
||||
tzformat = TimeZoneFormat.createInstance(requestedLocale);
|
||||
}
|
||||
tzformat.zsf = new ZoneStringFormat(zoneStrings);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -961,8 +981,6 @@ public class DateFormatSymbols implements Serializable, Cloneable {
|
||||
&& Utility.arrayEquals(standaloneShortWeekdays, that.standaloneShortWeekdays)
|
||||
&& Utility.arrayEquals(standaloneNarrowWeekdays, that.standaloneNarrowWeekdays)
|
||||
&& Utility.arrayEquals(ampms, that.ampms)
|
||||
&& gmtFormat.equals(that.gmtFormat)
|
||||
&& arrayOfArrayEquals(gmtHourFormats, that.gmtHourFormats)
|
||||
&& arrayOfArrayEquals(zoneStrings, that.zoneStrings)
|
||||
// getDiplayName maps deprecated country and language codes to the current ones
|
||||
// too bad there is no way to get the current codes!
|
||||
@ -1033,9 +1051,6 @@ public class DateFormatSymbols implements Serializable, Cloneable {
|
||||
this.standaloneShortQuarters = dfs.standaloneShortQuarters;
|
||||
this.standaloneQuarters = dfs.standaloneQuarters;
|
||||
|
||||
this.gmtFormat = dfs.gmtFormat;
|
||||
this.gmtHourFormats = dfs.gmtHourFormats;
|
||||
|
||||
this.zoneStrings = dfs.zoneStrings; // always null at initialization time for now
|
||||
this.localPatternChars = dfs.localPatternChars;
|
||||
|
||||
@ -1123,9 +1138,6 @@ public class DateFormatSymbols implements Serializable, Cloneable {
|
||||
standaloneQuarters = calData.getStringArray("quarters", "stand-alone", "wide");
|
||||
standaloneShortQuarters = calData.getStringArray("quarters", "stand-alone", "abbreviated");
|
||||
|
||||
// Initialize localized GMT format patterns
|
||||
initializeGMTFormat(desiredLocale);
|
||||
|
||||
requestedLocale = desiredLocale;
|
||||
|
||||
ICUResourceBundle rb =
|
||||
@ -1144,60 +1156,6 @@ public class DateFormatSymbols implements Serializable, Cloneable {
|
||||
setLocale(uloc, uloc);
|
||||
}
|
||||
|
||||
static final String DEFAULT_GMT_PATTERN = "GMT{0}";
|
||||
static final String[][] DEFAULT_GMT_HOUR_PATTERNS = {
|
||||
{"-HH:mm:ss", "-HH:mm"},
|
||||
{"+HH:mm:ss", "+HH:mm"}
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes localized GMT format patterns
|
||||
*/
|
||||
private void initializeGMTFormat(ULocale desiredLocale) {
|
||||
// TimeZone format localization is not included in CalendarData
|
||||
gmtFormat = ZoneMeta.getTZLocalizationInfo(desiredLocale, ZoneMeta.GMT);
|
||||
if (gmtFormat == null) {
|
||||
gmtFormat = DEFAULT_GMT_PATTERN;
|
||||
}
|
||||
|
||||
try {
|
||||
String offsetHM = ZoneMeta.getTZLocalizationInfo(desiredLocale, ZoneMeta.HOUR);
|
||||
gmtHourFormats = new String[2][2];
|
||||
int sepIdx = offsetHM.indexOf(';');
|
||||
if (sepIdx != -1) {
|
||||
gmtHourFormats[OFFSET_POSITIVE][OFFSET_HM] = offsetHM.substring(0, sepIdx);
|
||||
gmtHourFormats[OFFSET_NEGATIVE][OFFSET_HM] = offsetHM.substring(sepIdx + 1);
|
||||
} else {
|
||||
gmtHourFormats[OFFSET_POSITIVE][OFFSET_HM] = "+HH:mm";
|
||||
gmtHourFormats[OFFSET_NEGATIVE][OFFSET_HM] = "-HH:mm";
|
||||
}
|
||||
// CLDR 1.5 does not have GMT offset pattern including second field.
|
||||
// For now, append "ss" to the end.
|
||||
if (gmtHourFormats[OFFSET_POSITIVE][OFFSET_HM].indexOf(':') != -1) {
|
||||
gmtHourFormats[OFFSET_POSITIVE][OFFSET_HMS] =
|
||||
gmtHourFormats[OFFSET_POSITIVE][OFFSET_HM] + ":ss";
|
||||
} else if (gmtHourFormats[OFFSET_POSITIVE][OFFSET_HM].indexOf('.') != -1) {
|
||||
gmtHourFormats[OFFSET_POSITIVE][OFFSET_HMS] =
|
||||
gmtHourFormats[OFFSET_POSITIVE][OFFSET_HM] + ".ss";
|
||||
} else {
|
||||
gmtHourFormats[OFFSET_POSITIVE][OFFSET_HMS] =
|
||||
gmtHourFormats[OFFSET_POSITIVE][OFFSET_HM] + "ss";
|
||||
}
|
||||
if (gmtHourFormats[OFFSET_NEGATIVE][OFFSET_HM].indexOf(':') != -1) {
|
||||
gmtHourFormats[OFFSET_NEGATIVE][OFFSET_HMS] =
|
||||
gmtHourFormats[OFFSET_NEGATIVE][OFFSET_HM] + ":ss";
|
||||
} else if (gmtHourFormats[OFFSET_NEGATIVE][OFFSET_HM].indexOf('.') != -1) {
|
||||
gmtHourFormats[OFFSET_NEGATIVE][OFFSET_HMS] =
|
||||
gmtHourFormats[OFFSET_NEGATIVE][OFFSET_HM] + ".ss";
|
||||
} else {
|
||||
gmtHourFormats[OFFSET_NEGATIVE][OFFSET_HMS] =
|
||||
gmtHourFormats[OFFSET_NEGATIVE][OFFSET_HM] + "ss";
|
||||
}
|
||||
} catch (MissingResourceException e) {
|
||||
gmtHourFormats = DEFAULT_GMT_HOUR_PATTERNS;
|
||||
}
|
||||
}
|
||||
|
||||
private static final boolean arrayOfArrayEquals(Object[][] aa1, Object[][]aa2) {
|
||||
if (aa1 == aa2) { // both are null
|
||||
return true;
|
||||
@ -1218,57 +1176,6 @@ public class DateFormatSymbols implements Serializable, Cloneable {
|
||||
return equal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Package local method (for now) to get localized GMT format pattern.
|
||||
*/
|
||||
String getGmtFormat() {
|
||||
return gmtFormat;
|
||||
}
|
||||
|
||||
static final int OFFSET_HMS = 0;
|
||||
static final int OFFSET_HM = 1;
|
||||
static final int OFFSET_NEGATIVE = 0;
|
||||
static final int OFFSET_POSITIVE = 1;
|
||||
|
||||
/*
|
||||
* Package local method (for now) to get hour format pattern used by localized
|
||||
* GMT string.
|
||||
*/
|
||||
String getGmtHourFormat(int sign, int width) {
|
||||
return gmtHourFormats[sign][width];
|
||||
}
|
||||
|
||||
/*
|
||||
* Package local method to access ZoneStringFormat used by this
|
||||
* DateFormatSymbols instance.
|
||||
*/
|
||||
ZoneStringFormat getZoneStringFormat() {
|
||||
if (zsformat != null) {
|
||||
return zsformat;
|
||||
}
|
||||
if (zoneStrings != null) {
|
||||
zsformat = new ZoneStringFormat(zoneStrings);
|
||||
return zsformat;
|
||||
}
|
||||
// We do not want to hold the reference to an instance of
|
||||
// ZoneStringFormat. An instance of ZoneStringFormat for
|
||||
// a locale is shared and cached in ZoneStringFormat class
|
||||
// itself.
|
||||
return ZoneStringFormat.getInstance(requestedLocale);
|
||||
}
|
||||
TimeZoneFormat getTimeZoneFormat() {
|
||||
if (tzformat != null) {
|
||||
return tzformat;
|
||||
}
|
||||
|
||||
tzformat = TimeZoneFormat.createInstance(requestedLocale);
|
||||
|
||||
if (zoneStrings != null) {
|
||||
tzformat.zsf = new ZoneStringFormat(zoneStrings);
|
||||
}
|
||||
|
||||
return tzformat;
|
||||
}
|
||||
/*
|
||||
* save the input locale
|
||||
*/
|
||||
@ -1638,8 +1545,5 @@ public class DateFormatSymbols implements Serializable, Cloneable {
|
||||
*/
|
||||
private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
|
||||
stream.defaultReadObject();
|
||||
if (gmtFormat == null) {
|
||||
initializeGMTFormat(requestedLocale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ package com.ibm.icu.text;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.text.AttributedCharacterIterator;
|
||||
import java.text.AttributedString;
|
||||
import java.text.FieldPosition;
|
||||
@ -28,13 +27,13 @@ import com.ibm.icu.impl.DateNumberFormat;
|
||||
import com.ibm.icu.impl.ICUCache;
|
||||
import com.ibm.icu.impl.PatternProps;
|
||||
import com.ibm.icu.impl.SimpleCache;
|
||||
import com.ibm.icu.impl.ZoneMeta;
|
||||
import com.ibm.icu.impl.ZoneStringFormat.ZoneStringInfo;
|
||||
import com.ibm.icu.lang.UCharacter;
|
||||
import com.ibm.icu.text.TimeZoneFormat.Style;
|
||||
import com.ibm.icu.text.TimeZoneFormat.TimeType;
|
||||
import com.ibm.icu.util.BasicTimeZone;
|
||||
import com.ibm.icu.util.Calendar;
|
||||
import com.ibm.icu.util.GregorianCalendar;
|
||||
import com.ibm.icu.util.HebrewCalendar;
|
||||
import com.ibm.icu.util.Output;
|
||||
import com.ibm.icu.util.TimeZone;
|
||||
import com.ibm.icu.util.TimeZoneTransition;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
@ -346,8 +345,6 @@ public class SimpleDateFormat extends DateFormat {
|
||||
private transient int tztype = TZTYPE_UNK;
|
||||
|
||||
private static final int millisPerHour = 60 * 60 * 1000;
|
||||
private static final int millisPerMinute = 60 * 1000;
|
||||
private static final int millisPerSecond = 1000;
|
||||
|
||||
// When possessing ISO format, the ERA may be ommitted is the
|
||||
// year specifier is a negative number.
|
||||
@ -364,6 +361,11 @@ public class SimpleDateFormat extends DateFormat {
|
||||
*/
|
||||
private transient boolean useFastFormat;
|
||||
|
||||
/*
|
||||
* The time zone sub-formatter, introduced in ICU 4.8
|
||||
*/
|
||||
private volatile TimeZoneFormat tzFormat;
|
||||
|
||||
/**
|
||||
* Constructs a SimpleDateFormat using the default pattern for the default
|
||||
* locale. <b>Note:</b> Not all locales support SimpleDateFormat; for full
|
||||
@ -509,7 +511,7 @@ public class SimpleDateFormat extends DateFormat {
|
||||
}
|
||||
if (numberFormat == null) {
|
||||
NumberingSystem ns = NumberingSystem.getInstance(locale);
|
||||
if ( ns.isAlgorithmic() ) {
|
||||
if (ns.isAlgorithmic()) {
|
||||
numberFormat = NumberFormat.getInstance(locale);
|
||||
} else {
|
||||
String digitString = ns.getDescription();
|
||||
@ -531,6 +533,46 @@ public class SimpleDateFormat extends DateFormat {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Private method lazily instantiate the TimeZoneFormat field
|
||||
* @param bForceUpdate when true, check if tzFormat is synchronized with
|
||||
* the current numberFormat and update its digits if necessary. When false,
|
||||
* this check is skipped.
|
||||
*/
|
||||
private synchronized void initializeTimeZoneFormat(boolean bForceUpdate) {
|
||||
if (bForceUpdate || tzFormat == null) {
|
||||
tzFormat = TimeZoneFormat.getInstance(locale);
|
||||
|
||||
String digits = null;
|
||||
if (numberFormat instanceof DecimalFormat) {
|
||||
DecimalFormatSymbols decsym = ((DecimalFormat) numberFormat).getDecimalFormatSymbols();
|
||||
digits = new String(decsym.getDigits());
|
||||
} else if (numberFormat instanceof DateNumberFormat) {
|
||||
digits = new String(((DateNumberFormat)numberFormat).getDigits());
|
||||
}
|
||||
|
||||
if (digits != null) {
|
||||
if (!tzFormat.getGMTOffsetDigits().equals(digits)) {
|
||||
if (tzFormat.isFrozen()) {
|
||||
tzFormat = tzFormat.cloneAsThawed();
|
||||
}
|
||||
tzFormat.setGMTOffsetDigits(digits);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Private method, returns non-null TimeZoneFormat.
|
||||
* @return the TimeZoneFormat used by this formatter.
|
||||
*/
|
||||
private TimeZoneFormat tzFormat() {
|
||||
if (tzFormat == null) {
|
||||
initializeTimeZoneFormat(false);
|
||||
}
|
||||
return tzFormat;
|
||||
}
|
||||
|
||||
// privates for the default pattern
|
||||
private static ULocale cachedDefaultLocale = null;
|
||||
private static String cachedDefaultPattern = null;
|
||||
@ -958,67 +1000,32 @@ public class SimpleDateFormat extends DateFormat {
|
||||
case 17: // 'z' - ZONE_OFFSET
|
||||
if (count < 4) {
|
||||
// "z", "zz", "zzz"
|
||||
result = formatData.getTimeZoneFormat().format(tz, date, TimeZone.SHORT_COMMONLY_USED);
|
||||
result = tzFormat().format(Style.SPECIFIC_SHORT_COMMONLY_USED, tz, date);
|
||||
} else {
|
||||
result = formatData.getTimeZoneFormat().format(tz, date, TimeZone.LONG);
|
||||
result = tzFormat().format(Style.SPECIFIC_LONG, tz, date);
|
||||
}
|
||||
if ( result == null )
|
||||
result = formatData.getTimeZoneFormat().format(tz, date, TimeZone.LONG_GMT);
|
||||
buf.append(result);
|
||||
break;
|
||||
case 23: // 'Z' - TIMEZONE_RFC
|
||||
{
|
||||
if (count < 4) {
|
||||
// RFC822 format, must use ASCII digits
|
||||
int val = (cal.get(Calendar.ZONE_OFFSET) + cal.get(Calendar.DST_OFFSET));
|
||||
char sign = '+';
|
||||
if (val < 0) {
|
||||
val = -val;
|
||||
sign = '-';
|
||||
}
|
||||
buf.append(sign);
|
||||
|
||||
int offsetH = val / millisPerHour;
|
||||
val = val % millisPerHour;
|
||||
int offsetM = val / millisPerMinute;
|
||||
val = val % millisPerMinute;
|
||||
int offsetS = val / millisPerSecond;
|
||||
|
||||
int num = 0, denom = 0;
|
||||
if (offsetS == 0) {
|
||||
val = offsetH*100 + offsetM; // HHmm
|
||||
num = val % 10000;
|
||||
denom = 1000;
|
||||
} else {
|
||||
val = offsetH*10000 + offsetM*100 + offsetS; // HHmmss
|
||||
num = val % 1000000;
|
||||
denom = 100000;
|
||||
}
|
||||
while (denom >= 1) {
|
||||
char digit = (char)((num / denom) + '0');
|
||||
buf.append(digit);
|
||||
num = num % denom;
|
||||
denom /= 10;
|
||||
}
|
||||
// RFC822 format
|
||||
result = tzFormat().format(Style.RFC822, tz, date);
|
||||
} else {
|
||||
// long form, localized GMT pattern
|
||||
result = formatData.getTimeZoneFormat().format(tz, date, TimeZone.LONG_GMT);
|
||||
buf.append(result);
|
||||
result = tzFormat().format(Style.LOCALIZED_GMT, tz, date);
|
||||
}
|
||||
buf.append(result);
|
||||
break;
|
||||
|
||||
}
|
||||
case 24: // 'v' - TIMEZONE_GENERIC
|
||||
if (count == 1) {
|
||||
// "v"
|
||||
result = formatData.getTimeZoneFormat().format(tz, date, TimeZone.SHORT_GENERIC);
|
||||
result = tzFormat().format(Style.GENERIC_SHORT, tz, date);
|
||||
} else if (count == 4) {
|
||||
// "vvvv"
|
||||
result = formatData.getTimeZoneFormat().format(tz, date, TimeZone.LONG_GENERIC);
|
||||
result = tzFormat().format(Style.GENERIC_LONG, tz, date);
|
||||
}
|
||||
|
||||
if ( result == null ) {
|
||||
result = formatData.getTimeZoneFormat().format(tz, date, TimeZone.LONG_GMT);
|
||||
}
|
||||
|
||||
buf.append(result);
|
||||
break;
|
||||
|
||||
@ -1070,13 +1077,10 @@ public class SimpleDateFormat extends DateFormat {
|
||||
case 29: // 'V' - TIMEZONE_SPECIAL
|
||||
if (count == 1) {
|
||||
// "V"
|
||||
result = formatData.getTimeZoneFormat().format(tz, date, TimeZone.SHORT);
|
||||
result = tzFormat().format(Style.SPECIFIC_SHORT, tz, date);
|
||||
} else if (count == 4) {
|
||||
// "VVVV"
|
||||
result = formatData.getTimeZoneFormat().format(tz, date, TimeZone.GENERIC_LOCATION);
|
||||
}
|
||||
if ( result == null ) {
|
||||
result = formatData.getTimeZoneFormat().format(tz, date, TimeZone.LONG_GMT);
|
||||
result = tzFormat().format(Style.GENERIC_LOCATION, tz, date);
|
||||
}
|
||||
buf.append(result);
|
||||
break;
|
||||
@ -1222,276 +1226,6 @@ public class SimpleDateFormat extends DateFormat {
|
||||
return patternItems;
|
||||
}
|
||||
|
||||
/*
|
||||
* Time zone localized GMT format stuffs
|
||||
*/
|
||||
private static final String STR_GMT = "GMT";
|
||||
private static final String STR_UT = "UT";
|
||||
private static final String STR_UTC = "UTC";
|
||||
private static final int STR_GMT_LEN = 3;
|
||||
private static final int STR_UT_LEN = 2;
|
||||
private static final int STR_UTC_LEN = 3;
|
||||
private static final char PLUS = '+';
|
||||
private static final char MINUS = '-';
|
||||
private static final char COLON = ':';
|
||||
|
||||
|
||||
private Integer parseGMT(String text, ParsePosition pos, NumberFormat currentNumberFormat) {
|
||||
if (!isDefaultGMTFormat()) {
|
||||
int start = pos.getIndex();
|
||||
String gmtPattern = formatData.gmtFormat;
|
||||
|
||||
// Quick check
|
||||
boolean prefixMatch = false;
|
||||
int prefixLen = gmtPattern.indexOf('{');
|
||||
if (prefixLen > 0 && text.regionMatches(start, gmtPattern, 0, prefixLen)) {
|
||||
prefixMatch = true;
|
||||
}
|
||||
|
||||
if (prefixMatch) {
|
||||
// Prefix matched
|
||||
MessageFormat fmt;
|
||||
Object[] parsedObjects;
|
||||
int offset;
|
||||
|
||||
// Try negative Hms
|
||||
fmt = getGMTFormatter(DateFormatSymbols.OFFSET_NEGATIVE,
|
||||
DateFormatSymbols.OFFSET_HMS);
|
||||
parsedObjects = fmt.parse(text, pos);
|
||||
if ((parsedObjects != null) && (parsedObjects[0] instanceof Date)
|
||||
&& (pos.getIndex() - start) >= getGMTFormatMinHMSLen(
|
||||
DateFormatSymbols.OFFSET_NEGATIVE)) {
|
||||
offset = (int)((Date)parsedObjects[0]).getTime();
|
||||
return new Integer(-offset /* negative */);
|
||||
}
|
||||
|
||||
// Reset ParsePosition
|
||||
pos.setIndex(start);
|
||||
pos.setErrorIndex(-1);
|
||||
|
||||
// Try positive Hms
|
||||
fmt = getGMTFormatter(DateFormatSymbols.OFFSET_POSITIVE,
|
||||
DateFormatSymbols.OFFSET_HMS);
|
||||
parsedObjects = fmt.parse(text, pos);
|
||||
if ((parsedObjects != null) && (parsedObjects[0] instanceof Date)
|
||||
&& (pos.getIndex() - start) >= getGMTFormatMinHMSLen(
|
||||
DateFormatSymbols.OFFSET_POSITIVE)) {
|
||||
offset = (int)((Date)parsedObjects[0]).getTime();
|
||||
return new Integer(offset);
|
||||
}
|
||||
|
||||
// Reset ParsePosition
|
||||
pos.setIndex(start);
|
||||
pos.setErrorIndex(-1);
|
||||
|
||||
// Try negative Hm
|
||||
fmt = getGMTFormatter(DateFormatSymbols.OFFSET_NEGATIVE,
|
||||
DateFormatSymbols.OFFSET_HM);
|
||||
parsedObjects = fmt.parse(text, pos);
|
||||
if ((parsedObjects != null) && (parsedObjects[0] instanceof Date)) {
|
||||
offset = (int)((Date)parsedObjects[0]).getTime();
|
||||
return new Integer(-offset /* negative */);
|
||||
}
|
||||
|
||||
// Reset ParsePosition
|
||||
pos.setIndex(start);
|
||||
pos.setErrorIndex(-1);
|
||||
|
||||
// Try positive Hm
|
||||
fmt = getGMTFormatter(DateFormatSymbols.OFFSET_POSITIVE,
|
||||
DateFormatSymbols.OFFSET_HM);
|
||||
parsedObjects = fmt.parse(text, pos);
|
||||
if ((parsedObjects != null) && (parsedObjects[0] instanceof Date)) {
|
||||
offset = (int)((Date)parsedObjects[0]).getTime();
|
||||
return new Integer(offset);
|
||||
}
|
||||
|
||||
// Reset ParsePosition
|
||||
pos.setIndex(start);
|
||||
pos.setErrorIndex(-1);
|
||||
}
|
||||
}
|
||||
|
||||
return parseGMTDefault(text, pos, currentNumberFormat);
|
||||
}
|
||||
|
||||
private Integer parseGMTDefault(String text, ParsePosition pos,
|
||||
NumberFormat currentNumberFormat) {
|
||||
int start = pos.getIndex();
|
||||
|
||||
if (start + STR_UT_LEN + 1 >= text.length()) {
|
||||
pos.setErrorIndex(start);
|
||||
return null;
|
||||
}
|
||||
|
||||
int cur = start;
|
||||
// "GMT"
|
||||
if (text.regionMatches(true, start, STR_GMT, 0, STR_GMT_LEN)) {
|
||||
cur += STR_GMT_LEN;
|
||||
} else if (text.regionMatches(true, start, STR_UT, 0, STR_UT_LEN)) {
|
||||
cur += STR_UT_LEN;
|
||||
} else {
|
||||
pos.setErrorIndex(start);
|
||||
return null;
|
||||
}
|
||||
// Sign
|
||||
boolean negative = false;
|
||||
if (text.charAt(cur) == MINUS) {
|
||||
negative = true;
|
||||
} else if (text.charAt(cur) != PLUS) {
|
||||
pos.setErrorIndex(cur);
|
||||
return null;
|
||||
}
|
||||
cur++;
|
||||
|
||||
// Numbers
|
||||
int numLen;
|
||||
pos.setIndex(cur);
|
||||
|
||||
Number n = parseInt(text, 6, pos, false,currentNumberFormat);
|
||||
numLen = pos.getIndex() - cur;
|
||||
|
||||
if (n == null || numLen <= 0 || numLen > 6) {
|
||||
pos.setIndex(start);
|
||||
pos.setErrorIndex(cur);
|
||||
return null;
|
||||
}
|
||||
|
||||
int numVal = n.intValue();
|
||||
|
||||
int hour = 0;
|
||||
int min = 0;
|
||||
int sec = 0;
|
||||
|
||||
if (numLen <= 2) {
|
||||
// H[H][:mm[:ss]]
|
||||
hour = numVal;
|
||||
cur += numLen;
|
||||
if (cur + 2 < text.length() && text.charAt(cur) == COLON) {
|
||||
cur++;
|
||||
pos.setIndex(cur);
|
||||
n = parseInt(text, 2, pos, false,currentNumberFormat);
|
||||
numLen = pos.getIndex() - cur;
|
||||
if (n != null && numLen == 2) {
|
||||
// got minute field
|
||||
min = n.intValue();
|
||||
cur += numLen;
|
||||
if (cur + 2 < text.length() && text.charAt(cur) == COLON) {
|
||||
cur++;
|
||||
pos.setIndex(cur);
|
||||
n = parseInt(text, 2, pos, false,currentNumberFormat);
|
||||
numLen = pos.getIndex() - cur;
|
||||
if (n != null && numLen == 2) {
|
||||
// got second field
|
||||
sec = n.intValue();
|
||||
} else {
|
||||
// reset position
|
||||
pos.setIndex(cur - 1);
|
||||
pos.setErrorIndex(-1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// reset postion
|
||||
pos.setIndex(cur - 1);
|
||||
pos.setErrorIndex(-1);
|
||||
}
|
||||
}
|
||||
} else if (numLen == 3 || numLen == 4) {
|
||||
// Hmm or HHmm
|
||||
hour = numVal / 100;
|
||||
min = numVal % 100;
|
||||
} else { // numLen == 5 || numLen == 6
|
||||
// Hmmss or HHmmss
|
||||
hour = numVal / 10000;
|
||||
min = (numVal % 10000) / 100;
|
||||
sec = numVal % 100;
|
||||
}
|
||||
|
||||
int offset = ((hour*60 + min)*60 + sec)*1000;
|
||||
if (negative) {
|
||||
offset = -offset;
|
||||
}
|
||||
return new Integer(offset);
|
||||
}
|
||||
|
||||
transient private WeakReference<MessageFormat>[] gmtfmtCache;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private MessageFormat getGMTFormatter(int sign, int width) {
|
||||
MessageFormat fmt = null;
|
||||
if (gmtfmtCache == null) {
|
||||
gmtfmtCache = new WeakReference[4];
|
||||
}
|
||||
int cacheIdx = sign*2 + width;
|
||||
if (gmtfmtCache[cacheIdx] != null) {
|
||||
fmt = gmtfmtCache[cacheIdx].get();
|
||||
}
|
||||
if (fmt == null) {
|
||||
fmt = new MessageFormat(formatData.gmtFormat);
|
||||
GregorianCalendar gcal = new GregorianCalendar(TimeZone.getTimeZone("Etc/UTC"));
|
||||
SimpleDateFormat sdf = (SimpleDateFormat)this.clone();
|
||||
sdf.setCalendar(gcal);
|
||||
sdf.applyPattern(formatData.getGmtHourFormat(sign, width));
|
||||
fmt.setFormat(0, sdf);
|
||||
gmtfmtCache[cacheIdx] = new WeakReference<MessageFormat>(fmt);
|
||||
}
|
||||
return fmt;
|
||||
}
|
||||
|
||||
transient private int[] gmtFormatHmsMinLen = null;
|
||||
|
||||
private int getGMTFormatMinHMSLen(int sign) {
|
||||
if (gmtFormatHmsMinLen == null) {
|
||||
gmtFormatHmsMinLen = new int[2];
|
||||
Long offset = new Long(60*60*1000); // 1 hour
|
||||
|
||||
StringBuffer buf = new StringBuffer();
|
||||
MessageFormat fmtNeg = getGMTFormatter(DateFormatSymbols.OFFSET_NEGATIVE, DateFormatSymbols.OFFSET_HMS);
|
||||
fmtNeg.format(new Object[] {offset}, buf, null);
|
||||
gmtFormatHmsMinLen[0] = buf.length();
|
||||
|
||||
buf.setLength(0);
|
||||
MessageFormat fmtPos = getGMTFormatter(DateFormatSymbols.OFFSET_POSITIVE, DateFormatSymbols.OFFSET_HMS);
|
||||
fmtPos.format(new Object[] {offset}, buf, null);
|
||||
gmtFormatHmsMinLen[1] = buf.length();
|
||||
}
|
||||
return gmtFormatHmsMinLen[(sign < 0 ? 0 : 1)];
|
||||
}
|
||||
|
||||
private boolean isDefaultGMTFormat() {
|
||||
// GMT pattern
|
||||
if (!DateFormatSymbols.DEFAULT_GMT_PATTERN.equals(formatData.getGmtFormat())) {
|
||||
return false;
|
||||
}
|
||||
// GMT offset hour patters
|
||||
boolean res = true;
|
||||
for (int sign = 0; sign < 2 && res; sign++) {
|
||||
for (int width = 0; width < 2; width++) {
|
||||
if (!DateFormatSymbols.DEFAULT_GMT_HOUR_PATTERNS[sign][width]
|
||||
.equals(formatData.getGmtHourFormat(sign, width))) {
|
||||
|
||||
res = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal method. Returns null if the value of an array is empty, or if the
|
||||
* index is out of bounds
|
||||
*/
|
||||
/* private String getZoneArrayValue(String[] zs, int ix) {
|
||||
if (ix >= 0 && ix < zs.length) {
|
||||
String result = zs[ix];
|
||||
if (result != null && result.length() != 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Internal high-speed method. Reuses a StringBuffer for results
|
||||
* instead of creating a String on the heap for each call.
|
||||
@ -1500,7 +1234,10 @@ public class SimpleDateFormat extends DateFormat {
|
||||
*/
|
||||
protected void zeroPaddingNumber(NumberFormat nf,StringBuffer buf, int value,
|
||||
int minDigits, int maxDigits) {
|
||||
if (useLocalZeroPaddingNumberFormat) {
|
||||
// Note: Indian calendar uses negative value for a calendar
|
||||
// field. fastZeroPaddingNumber cannot handle negative numbers.
|
||||
// BTW, it looks like a design bug in the Indian calendar...
|
||||
if (useLocalZeroPaddingNumberFormat && value >= 0) {
|
||||
fastZeroPaddingNumber(buf, value, minDigits, maxDigits);
|
||||
} else {
|
||||
nf.setMinimumIntegerDigits(minDigits);
|
||||
@ -1517,14 +1254,15 @@ public class SimpleDateFormat extends DateFormat {
|
||||
// Override this method to update local zero padding number formatter
|
||||
super.setNumberFormat(newNumberFormat);
|
||||
initLocalZeroPaddingNumberFormat();
|
||||
initializeTimeZoneFormat(true);
|
||||
}
|
||||
|
||||
private void initLocalZeroPaddingNumberFormat() {
|
||||
if (numberFormat instanceof DecimalFormat) {
|
||||
zeroDigit = ((DecimalFormat)numberFormat).getDecimalFormatSymbols().getZeroDigit();
|
||||
decDigits = ((DecimalFormat)numberFormat).getDecimalFormatSymbols().getDigits();
|
||||
useLocalZeroPaddingNumberFormat = true;
|
||||
} else if (numberFormat instanceof DateNumberFormat) {
|
||||
zeroDigit = ((DateNumberFormat)numberFormat).getZeroDigit();
|
||||
decDigits = ((DateNumberFormat)numberFormat).getDigits();
|
||||
useLocalZeroPaddingNumberFormat = true;
|
||||
} else {
|
||||
useLocalZeroPaddingNumberFormat = false;
|
||||
@ -1537,7 +1275,7 @@ public class SimpleDateFormat extends DateFormat {
|
||||
|
||||
// If true, use local version of zero padding number format
|
||||
private transient boolean useLocalZeroPaddingNumberFormat;
|
||||
private transient char zeroDigit;
|
||||
private transient char[] decDigits;
|
||||
private transient char[] decimalBuf;
|
||||
|
||||
/*
|
||||
@ -1555,7 +1293,7 @@ public class SimpleDateFormat extends DateFormat {
|
||||
int limit = decimalBuf.length < maxDigits ? decimalBuf.length : maxDigits;
|
||||
int index = limit - 1;
|
||||
while (true) {
|
||||
decimalBuf[index] = (char)((value % 10) + zeroDigit);
|
||||
decimalBuf[index] = decDigits[(value % 10)];
|
||||
value /= 10;
|
||||
if (index == 0 || value == 0) {
|
||||
break;
|
||||
@ -1564,13 +1302,13 @@ public class SimpleDateFormat extends DateFormat {
|
||||
}
|
||||
int padding = minDigits - (limit - index);
|
||||
while (padding > 0 && index > 0) {
|
||||
decimalBuf[--index] = zeroDigit;
|
||||
decimalBuf[--index] = decDigits[0];
|
||||
padding--;
|
||||
}
|
||||
while (padding > 0) {
|
||||
// when pattern width is longer than decimalBuf, need extra
|
||||
// leading zeros - ticke#7595
|
||||
buf.append(zeroDigit);
|
||||
buf.append(decDigits[0]);
|
||||
padding--;
|
||||
}
|
||||
buf.append(decimalBuf, index, limit - index);
|
||||
@ -2300,157 +2038,71 @@ public class SimpleDateFormat extends DateFormat {
|
||||
cal.set(Calendar.HOUR, value);
|
||||
return pos.getIndex();
|
||||
case 17: // 'z' - ZONE_OFFSET
|
||||
case 23: // 'Z' - TIMEZONE_RFC
|
||||
case 24: // 'v' - TIMEZONE_GENERIC
|
||||
case 29: // 'V' - TIMEZONE_SPECIAL
|
||||
{
|
||||
TimeZone tz = null;
|
||||
int offset = 0;
|
||||
boolean parsed = false;
|
||||
|
||||
// Step 1
|
||||
// Check if this is a long GMT offset string (either localized or default)
|
||||
Integer gmtoff = parseGMT(text, pos, currentNumberFormat);
|
||||
if (gmtoff != null) {
|
||||
offset = gmtoff.intValue();
|
||||
parsed = true;
|
||||
}
|
||||
|
||||
if (!parsed) {
|
||||
// Step 2
|
||||
// Check if this is an RFC822 time zone offset.
|
||||
// ICU supports the standard RFC822 format [+|-]HHmm
|
||||
// and its extended form [+|-]HHmmSS.
|
||||
|
||||
do {
|
||||
int sign = 0;
|
||||
char signChar = text.charAt(start);
|
||||
if (signChar == '+') {
|
||||
sign = 1;
|
||||
} else if (signChar == '-') {
|
||||
sign = -1;
|
||||
} else {
|
||||
// Not an RFC822 offset string
|
||||
break;
|
||||
}
|
||||
|
||||
// Parse digits
|
||||
int orgPos = start + 1;
|
||||
pos.setIndex(orgPos);
|
||||
number = parseInt(text, 6, pos, false,currentNumberFormat);
|
||||
int numLen = pos.getIndex() - orgPos;
|
||||
if (numLen <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Followings are possible format (excluding sign char)
|
||||
// HHmmSS
|
||||
// HmmSS
|
||||
// HHmm
|
||||
// Hmm
|
||||
// HH
|
||||
// H
|
||||
int val = number.intValue();
|
||||
int hour = 0, min = 0, sec = 0;
|
||||
switch(numLen) {
|
||||
case 1: // H
|
||||
case 2: // HH
|
||||
hour = val;
|
||||
break;
|
||||
case 3: // Hmm
|
||||
case 4: // HHmm
|
||||
hour = val / 100;
|
||||
min = val % 100;
|
||||
break;
|
||||
case 5: // Hmmss
|
||||
case 6: // HHmmss
|
||||
hour = val / 10000;
|
||||
min = (val % 10000) / 100;
|
||||
sec = val % 100;
|
||||
break;
|
||||
}
|
||||
if (hour > 23 || min > 59 || sec > 59) {
|
||||
// Invalid value range
|
||||
break;
|
||||
}
|
||||
offset = (((hour * 60) + min) * 60 + sec) * 1000 * sign;
|
||||
parsed = true;
|
||||
} while (false);
|
||||
|
||||
if (!parsed) {
|
||||
// Failed to parse. Reset the position.
|
||||
pos.setIndex(start);
|
||||
Output<TimeType> tzTimeType = new Output<TimeType>();
|
||||
Style style = (count < 4) ? Style.SPECIFIC_SHORT_COMMONLY_USED : Style.SPECIFIC_LONG;
|
||||
TimeZone tz = tzFormat().parse(style, text, pos, tzTimeType);
|
||||
if (tz != null) {
|
||||
if (tzTimeType.value == TimeType.STANDARD) {
|
||||
tztype = TZTYPE_STD;
|
||||
} else if (tzTimeType.value == TimeType.DAYLIGHT) {
|
||||
tztype = TZTYPE_DST;
|
||||
}
|
||||
}
|
||||
|
||||
if (parsed) {
|
||||
// offset was successfully parsed as either a long GMT string or
|
||||
// RFC822 zone offset string. Create normalized zone ID for the
|
||||
// offset.
|
||||
tz = ZoneMeta.getCustomTimeZone(offset);
|
||||
cal.setTimeZone(tz);
|
||||
return pos.getIndex();
|
||||
}
|
||||
|
||||
// Step 3
|
||||
// At this point, check for named time zones by looking through
|
||||
// the locale data from the DateFormatZoneData strings.
|
||||
// Want to be able to parse both short and long forms.
|
||||
// optimize for calendar's current time zone
|
||||
ZoneStringInfo zsinfo = null;
|
||||
switch (patternCharIndex) {
|
||||
case 17: // 'z' - ZONE_OFFSET
|
||||
if (count < 4) {
|
||||
zsinfo = formatData.getZoneStringFormat().findSpecificShort(text, start);
|
||||
} else {
|
||||
zsinfo = formatData.getZoneStringFormat().findSpecificLong(text, start);
|
||||
return -start;
|
||||
}
|
||||
break;
|
||||
case 24: // 'v' - TIMEZONE_GENERIC
|
||||
if (count == 1) {
|
||||
zsinfo = formatData.getZoneStringFormat().findGenericShort(text, start);
|
||||
} else if (count == 4) {
|
||||
zsinfo = formatData.getZoneStringFormat().findGenericLong(text, start);
|
||||
}
|
||||
break;
|
||||
case 29: // 'V' - TIMEZONE_SPECIAL
|
||||
if (count == 1) {
|
||||
zsinfo = formatData.getZoneStringFormat().findSpecificShort(text, start);
|
||||
} else if (count == 4) {
|
||||
zsinfo = formatData.getZoneStringFormat().findGenericLocation(text, start);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (zsinfo != null) {
|
||||
if (zsinfo.isStandard()) {
|
||||
case 23: // 'Z' - TIMEZONE_RFC
|
||||
{
|
||||
Output<TimeType> tzTimeType = new Output<TimeType>();
|
||||
Style style = (count < 4) ? Style.RFC822 : Style.LOCALIZED_GMT;
|
||||
TimeZone tz = tzFormat().parse(style, text, pos, tzTimeType);
|
||||
if (tz != null) {
|
||||
if (tzTimeType.value == TimeType.STANDARD) {
|
||||
tztype = TZTYPE_STD;
|
||||
} else if (zsinfo.isDaylight()) {
|
||||
} else if (tzTimeType.value == TimeType.DAYLIGHT) {
|
||||
tztype = TZTYPE_DST;
|
||||
}
|
||||
tz = TimeZone.getTimeZone(zsinfo.getID());
|
||||
cal.setTimeZone(tz);
|
||||
return start + zsinfo.getString().length();
|
||||
return pos.getIndex();
|
||||
}
|
||||
return -start;
|
||||
}
|
||||
// Step 4
|
||||
// Final attempt - is this standalone GMT/UT/UTC?
|
||||
int gmtLen = 0;
|
||||
if (text.regionMatches(true, start, STR_GMT, 0, STR_GMT_LEN)) {
|
||||
gmtLen = STR_GMT_LEN;
|
||||
} else if (text.regionMatches(true, start, STR_UTC, 0, STR_UTC_LEN)) {
|
||||
gmtLen = STR_UTC_LEN;
|
||||
} else if (text.regionMatches(true, start, STR_UT, 0, STR_UT_LEN)) {
|
||||
gmtLen = STR_UT_LEN;
|
||||
}
|
||||
if (gmtLen > 0) {
|
||||
tz = TimeZone.getTimeZone("Etc/GMT");
|
||||
case 24: // 'v' - TIMEZONE_GENERIC
|
||||
{
|
||||
Output<TimeType> tzTimeType = new Output<TimeType>();
|
||||
// Note: 'v' only supports count 1 and 4
|
||||
Style style = (count < 4) ? Style.GENERIC_SHORT : Style.GENERIC_LONG;
|
||||
TimeZone tz = tzFormat().parse(style, text, pos, tzTimeType);
|
||||
if (tz != null) {
|
||||
if (tzTimeType.value == TimeType.STANDARD) {
|
||||
tztype = TZTYPE_STD;
|
||||
} else if (tzTimeType.value == TimeType.DAYLIGHT) {
|
||||
tztype = TZTYPE_DST;
|
||||
}
|
||||
cal.setTimeZone(tz);
|
||||
return start + gmtLen;
|
||||
return pos.getIndex();
|
||||
}
|
||||
return -start;
|
||||
}
|
||||
case 29: // 'V' - TIMEZONE_SPECIAL
|
||||
{
|
||||
Output<TimeType> tzTimeType = new Output<TimeType>();
|
||||
// Note: 'v' only supports count 1 and 4
|
||||
Style style = (count < 4) ? Style.SPECIFIC_SHORT : Style.GENERIC_LOCATION;
|
||||
TimeZone tz = tzFormat().parse(style, text, pos, tzTimeType);
|
||||
if (tz != null) {
|
||||
if (tzTimeType.value == TimeType.STANDARD) {
|
||||
tztype = TZTYPE_STD;
|
||||
} else if (tzTimeType.value == TimeType.DAYLIGHT) {
|
||||
tztype = TZTYPE_DST;
|
||||
}
|
||||
cal.setTimeZone(tz);
|
||||
return pos.getIndex();
|
||||
}
|
||||
|
||||
// complete failure
|
||||
return -start;
|
||||
}
|
||||
|
||||
case 27: // 'Q' - QUARTER
|
||||
if (count <= 2) { // i.e., Q or QQ.
|
||||
// Don't want to parse the quarter if it is a string
|
||||
@ -2676,7 +2328,6 @@ public class SimpleDateFormat extends DateFormat {
|
||||
public void setDateFormatSymbols(DateFormatSymbols newFormatSymbols)
|
||||
{
|
||||
this.formatData = (DateFormatSymbols)newFormatSymbols.clone();
|
||||
gmtfmtCache = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2687,6 +2338,36 @@ public class SimpleDateFormat extends DateFormat {
|
||||
return formatData;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@icu} Gets the time zone formatter which this date/time
|
||||
* formatter uses to format and parse a time zone.
|
||||
*
|
||||
* @return the time zone formatter which this date/time
|
||||
* formatter uses.
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
public TimeZoneFormat getTimeZoneFormat() {
|
||||
return tzFormat().freeze();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@icu} Allows you to set the time zoen formatter.
|
||||
*
|
||||
* @param tzfmt the new time zone formatter
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
public void setTimeZoneFormat(TimeZoneFormat tzfmt) {
|
||||
if (tzfmt.isFrozen()) {
|
||||
// If frozen, use it as is.
|
||||
tzFormat = tzfmt;
|
||||
} else {
|
||||
// If not frozen, clone and freeze.
|
||||
tzFormat = tzfmt.cloneAsThawed().freeze();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides Cloneable
|
||||
* @stable ICU 2.0
|
||||
@ -2729,6 +2410,7 @@ public class SimpleDateFormat extends DateFormat {
|
||||
// calculate and set value before serialization.
|
||||
initializeDefaultCenturyStart(defaultCenturyBase);
|
||||
}
|
||||
initializeTimeZoneFormat(false);
|
||||
stream.defaultWriteObject();
|
||||
}
|
||||
|
||||
@ -2751,6 +2433,12 @@ public class SimpleDateFormat extends DateFormat {
|
||||
}
|
||||
serialVersionOnStream = currentSerialVersion;
|
||||
locale = getLocale(ULocale.VALID_LOCALE);
|
||||
if (locale == null) {
|
||||
// ICU4J 3.6 or older versions did not have UFormat locales
|
||||
// in the serialized data. This is just for preventing the
|
||||
// worst case scenario...
|
||||
locale = ULocale.getDefault();
|
||||
}
|
||||
|
||||
initLocalZeroPaddingNumberFormat();
|
||||
}
|
||||
|
2085
icu4j/main/classes/core/src/com/ibm/icu/text/TimeZoneFormat.java
Normal file
2085
icu4j/main/classes/core/src/com/ibm/icu/text/TimeZoneFormat.java
Normal file
File diff suppressed because it is too large
Load Diff
572
icu4j/main/classes/core/src/com/ibm/icu/text/TimeZoneNames.java
Normal file
572
icu4j/main/classes/core/src/com/ibm/icu/text/TimeZoneNames.java
Normal file
@ -0,0 +1,572 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2011, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.text;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.ibm.icu.impl.ICUConfig;
|
||||
import com.ibm.icu.impl.SoftCache;
|
||||
import com.ibm.icu.util.TimeZone;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
|
||||
/**
|
||||
* <code>TimeZoneNames</code> is an abstract class representing the time zone display name data model defined
|
||||
* by <a href="http://www.unicode.org/reports/tr35/">UTS#35 Unicode Locale Data Markup Language (LDML)</a>.
|
||||
* The model defines meta zone, which is used for storing a set of display names. A meta zone can be shared
|
||||
* by multiple time zones. Also a time zone may have multiple meta zone historic mappings.
|
||||
* <p>
|
||||
* For example, people in the United States refer the zone used by the east part of North America as "Eastern Time".
|
||||
* The tz database contains multiple time zones "America/New_York", "America/Detroit", "America/Montreal" and some
|
||||
* others that belong to "Eastern Time". However, assigning different display names to these time zones does not make
|
||||
* much sense for most of people.
|
||||
* <p>
|
||||
* In <a href="http://cldr.unicode.org/">CLDR</a> (which uses LDML for representing locale data), the display name
|
||||
* "Eastern Time" is stored as long generic display name of a meta zone identified by the ID "America_Eastern".
|
||||
* Then, there is another table maintaining the historic mapping to meta zones for each time zone. The time zones in
|
||||
* the above example ("America/New_York", "America/Detroit"...) are mapped to the meta zone "America_Eastern".
|
||||
* <p>
|
||||
* Sometimes, a time zone is mapped to a different time zone in the past. For example, "America/Indiana/Knox"
|
||||
* had been moving "Eastern Time" and "Central Time" back and forth. Therefore, it is necessary that time zone
|
||||
* to meta zones mapping data are stored by date range.
|
||||
*
|
||||
* <p><b>Note:</b>
|
||||
* <p>
|
||||
* {@link TimeZoneFormat} assumes an instance of <code>TimeZoneNames</code> is immutable. If you want to provide
|
||||
* your own <code>TimeZoneNames</code> implementation and use it with {@link TimeZoneFormat}, you must follow
|
||||
* the contract.
|
||||
* <p>
|
||||
* The methods in this class assume that time zone IDs are already canonicalized. For example, you may not get proper
|
||||
* result returned by a method with time zone ID "America/Indiana/Indianapolis", because it's not a canonical time zone
|
||||
* ID (the canonical time zone ID for the time zone is "America/Indianapolis". See
|
||||
* {@link TimeZone#getCanonicalID(String)} about ICU canonical time zone IDs.
|
||||
*
|
||||
* <p>
|
||||
* In CLDR, most of time zone display names except location names are provided through meta zones. But a time zone may
|
||||
* have a specific name that is not shared with other time zones.
|
||||
*
|
||||
* For example, time zone "Europe/London" has English long name for standard time "Greenwich Mean Time", which is also
|
||||
* shared with other time zones. However, the long name for daylight saving time is "British Summer Time", which is only
|
||||
* used for "Europe/London".
|
||||
*
|
||||
* <p>
|
||||
* {@link #getTimeZoneDisplayName(String, NameType)} is designed for accessing a name only used by a single time zone.
|
||||
* But is not necessarily mean that a subclass implementation use the same model with CLDR. A subclass implementation
|
||||
* may provide time zone names only through {@link #getTimeZoneDisplayName(String, NameType)}, or only through
|
||||
* {@link #getMetaZoneDisplayName(String, NameType)}, or both.
|
||||
*
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
public abstract class TimeZoneNames implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -9180227029248969153L;
|
||||
|
||||
/**
|
||||
* Time zone display name types
|
||||
*
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
public enum NameType {
|
||||
/**
|
||||
* Long display name, such as "Eastern Time".
|
||||
*
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
LONG_GENERIC,
|
||||
/**
|
||||
* Long display name for standard time, such as "Eastern Standard Time".
|
||||
*
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
LONG_STANDARD,
|
||||
/**
|
||||
* Long display name for daylight saving time, such as "Eastern Daylight Time".
|
||||
*
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
LONG_DAYLIGHT,
|
||||
/**
|
||||
* Short display name, such as "ET".
|
||||
*
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
SHORT_GENERIC,
|
||||
/**
|
||||
* Short display name for standard time, such as "EST".
|
||||
*
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
SHORT_STANDARD,
|
||||
/**
|
||||
* Short display name for daylight saving time, such as "EDT".
|
||||
*
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
SHORT_DAYLIGHT,
|
||||
/**
|
||||
* Short display name for standard time, such as "EST".
|
||||
* <p><b>Note:</b> The short abbreviation might not be well understood by people not familiar with the zone.
|
||||
* Unlike {@link #SHORT_STANDARD}, this type excludes short standard names not commonly used by the region.
|
||||
*
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
SHORT_STANDARD_COMMONLY_USED,
|
||||
/**
|
||||
* Short display name for daylight saving time, such as "EDT".
|
||||
* <p><b>Note:</b> The short abbreviation might not be well understood by people not familiar with the zone.
|
||||
* Unlike {@link #SHORT_DAYLIGHT}, this type excludes short daylight names not commonly used by the region.
|
||||
*
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
SHORT_DAYLIGHT_COMMONLY_USED
|
||||
}
|
||||
|
||||
private static Cache TZNAMES_CACHE = new Cache();
|
||||
|
||||
private static final Factory TZNAMES_FACTORY;
|
||||
private static final String FACTORY_NAME_PROP = "com.ibm.icu.text.TimeZoneNames.Factory.impl";
|
||||
private static final String DEFAULT_FACTORY_CLASS = "com.ibm.icu.impl.TimeZoneNamesFactoryImpl";
|
||||
private static final Pattern LOC_EXCLUSION_PATTERN = Pattern.compile("Etc/.*|SystemV/.*|.*/Riyadh8[7-9]");
|
||||
|
||||
static {
|
||||
Factory factory = null;
|
||||
String classname = ICUConfig.get(FACTORY_NAME_PROP, DEFAULT_FACTORY_CLASS);
|
||||
while (true) {
|
||||
try {
|
||||
factory = (Factory) Class.forName(classname).newInstance();
|
||||
break;
|
||||
} catch (ClassNotFoundException cnfe) {
|
||||
// fall through
|
||||
} catch (IllegalAccessException iae) {
|
||||
// fall through
|
||||
} catch (InstantiationException ie) {
|
||||
// fall through
|
||||
}
|
||||
if (classname.equals(DEFAULT_FACTORY_CLASS)) {
|
||||
break;
|
||||
}
|
||||
classname = DEFAULT_FACTORY_CLASS;
|
||||
}
|
||||
|
||||
if (factory == null) {
|
||||
factory = new DefaultTimeZoneNames.FactoryImpl();
|
||||
}
|
||||
TZNAMES_FACTORY = factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of <code>TimeZoneDisplayNames</code> for the specified locale.
|
||||
*
|
||||
* @param locale
|
||||
* The locale.
|
||||
* @return An instance of <code>TimeZoneDisplayNames</code>
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
public static TimeZoneNames getInstance(ULocale locale) {
|
||||
String key = locale.getBaseName();
|
||||
return TZNAMES_CACHE.getInstance(key, locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an immutable set of all available meta zone IDs.
|
||||
* @return An immutable set of all available meta zone IDs.
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
public abstract Set<String> getAvailableMetaZoneIDs();
|
||||
|
||||
/**
|
||||
* Returns an immutable set of all available meta zone IDs used by the given time zone.
|
||||
*
|
||||
* @param tzID
|
||||
* The canonical time zone ID.
|
||||
* @return An immutable set of all available meta zone IDs used by the given time zone.
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
public abstract Set<String> getAvailableMetaZoneIDs(String tzID);
|
||||
|
||||
/**
|
||||
* Returns the meta zone ID for the given canonical time zone ID at the given date.
|
||||
*
|
||||
* @param tzID
|
||||
* The canonical time zone ID.
|
||||
* @param date
|
||||
* The date.
|
||||
* @return The meta zone ID for the given time zone ID at the given date. If the time zone does not have a
|
||||
* corresponding meta zone at the given date or the implementation does not support meta zones, null is
|
||||
* returned.
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
public abstract String getMetaZoneID(String tzID, long date);
|
||||
|
||||
/**
|
||||
* Returns the reference zone ID for the given meta zone ID for the region.
|
||||
*
|
||||
* @param mzID
|
||||
* The meta zone ID.
|
||||
* @param region
|
||||
* The region.
|
||||
* @return The reference zone ID ("golden zone" in the LDML specification) for the given time zone ID for the
|
||||
* region. If the meta zone is unknown or the implementation does not support meta zones, null is returned.
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
public abstract String getReferenceZoneID(String mzID, String region);
|
||||
|
||||
/**
|
||||
* Returns the display name of the meta zone.
|
||||
*
|
||||
* @param mzID
|
||||
* The meta zone ID.
|
||||
* @param type
|
||||
* The display name type. See {@link TimeZoneNames.NameType}.
|
||||
* @return The display name of the meta zone. When this object does not have a localized display name for the given
|
||||
* meta zone with the specified type or the implementation does not provide any display names associated
|
||||
* with meta zones, null is returned.
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
public abstract String getMetaZoneDisplayName(String mzID, NameType type);
|
||||
|
||||
/**
|
||||
* Returns the display name of the time zone at the given date.
|
||||
*
|
||||
* <p>
|
||||
* <b>Note:</b> This method calls the subclass's {@link #getTimeZoneDisplayName(String, NameType)} first. When the
|
||||
* result is null, this method calls {@link #getMetaZoneID(String, long)} to get the meta zone ID mapped from the
|
||||
* time zone, then calls {@link #getMetaZoneDisplayName(String, NameType)}.
|
||||
*
|
||||
* @param tzID
|
||||
* The canonical time zone ID.
|
||||
* @param type
|
||||
* The display name type. See {@link TimeZoneNames.NameType}.
|
||||
* @param date
|
||||
* The date
|
||||
* @return The display name for the time zone at the given date. When this object does not have a localized display
|
||||
* name for the time zone with the specified type and date, null is returned.
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
public final String getDisplayName(String tzID, NameType type, long date) {
|
||||
String name = getTimeZoneDisplayName(tzID, type);
|
||||
if (name == null) {
|
||||
String mzID = getMetaZoneID(tzID, date);
|
||||
name = getMetaZoneDisplayName(mzID, type);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the display name of the time zone. Unlike {@link #getDisplayName(String, NameType, long)},
|
||||
* this method does not get a name from a meta zone used by the time zone.
|
||||
*
|
||||
* @param tzID
|
||||
* The canonical time zone ID.
|
||||
* @param type
|
||||
* The display name type. See {@link TimeZoneNames.NameType}.
|
||||
* @return The display name for the time zone. When this object does not have a localized display name for the given
|
||||
* time zone with the specified type, null is returned.
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
public abstract String getTimeZoneDisplayName(String tzID, NameType type);
|
||||
|
||||
/**
|
||||
* Returns the exemplar location name for the given time zone. When this object does not have a localized location
|
||||
* name, the default implementation may still returns a programmatically generated name with the logic described
|
||||
* below.
|
||||
* <ol>
|
||||
* <li>Check if the ID contains "/". If not, return null.
|
||||
* <li>Check if the ID does not start with "Etc/" or "SystemV/". If it does, return null.
|
||||
* <li>Extract a substring after the last occurrence of "/".
|
||||
* <li>Replace "_" with " ".
|
||||
* </ol>
|
||||
* For example, "New York" is returned for the time zone ID "America/New_York" when this object does not have the
|
||||
* localized location name.
|
||||
*
|
||||
* @param tzID
|
||||
* The canonical time zone ID
|
||||
* @return The exemplar location name for the given time zone, or null when a localized location name is not
|
||||
* available and the fallback logic described above cannot extract location from the ID.
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
public String getExemplarLocationName(String tzID) {
|
||||
if (tzID == null || tzID.length() == 0 || LOC_EXCLUSION_PATTERN.matcher(tzID).matches()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String location = null;
|
||||
int sep = tzID.lastIndexOf('/');
|
||||
if (sep > 0 && sep + 1 < tzID.length()) {
|
||||
location = tzID.substring(sep + 1).replace('_', ' ');
|
||||
}
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds time zone name prefix matches for the input text at the
|
||||
* given offset and returns a collection of the matches.
|
||||
*
|
||||
* @param text the text.
|
||||
* @param start the starting offset within the text.
|
||||
* @param types the set of name types, or <code>null</code> for all name types.
|
||||
* @return A collection of matches.
|
||||
* @see NameType
|
||||
* @see MatchInfo
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
public Collection<MatchInfo> find(String text, int start, EnumSet<NameType> types) {
|
||||
throw new UnsupportedOperationException("The method is not implemented in TimeZoneNames base class.");
|
||||
}
|
||||
|
||||
/**
|
||||
* A <code>MatchInfo</code> represents a time zone name match used by
|
||||
* {@link TimeZoneNames#find(String, int, EnumSet)}.
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
public static class MatchInfo {
|
||||
private NameType _nameType;
|
||||
private String _tzID;
|
||||
private String _mzID;
|
||||
private int _matchLength;
|
||||
|
||||
/**
|
||||
* Constructing a <code>MatchInfo</code>.
|
||||
*
|
||||
* @param nameType the name type enum.
|
||||
* @param tzID the time zone ID, or null
|
||||
* @param mzID the meta zone ID, or null
|
||||
* @param matchLength the match length.
|
||||
* @throws IllegalArgumentException when 1) <code>nameType</code> is <code>null</code>,
|
||||
* or 2) both <code>tzID</code> and <code>mzID</code> are <code>null</code>,
|
||||
* or 3) <code>matchLength</code> is 0 or smaller.
|
||||
* @see NameType
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
public MatchInfo(NameType nameType, String tzID, String mzID, int matchLength) {
|
||||
if (nameType == null) {
|
||||
throw new IllegalArgumentException("nameType is null");
|
||||
}
|
||||
if (tzID == null && mzID == null) {
|
||||
throw new IllegalArgumentException("Either tzID or mzID must be available");
|
||||
}
|
||||
if (matchLength <= 0) {
|
||||
throw new IllegalArgumentException("matchLength must be positive value");
|
||||
}
|
||||
_nameType = nameType;
|
||||
_tzID = tzID;
|
||||
_mzID = mzID;
|
||||
_matchLength = matchLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time zone ID, or <code>null</code> if not available.
|
||||
*
|
||||
* <p><b>Note</b>: A <code>MatchInfo</code> must have either a time zone ID
|
||||
* or a meta zone ID.
|
||||
*
|
||||
* @return the time zone ID, or <code>null</code>.
|
||||
* @see #mzID()
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
public String tzID() {
|
||||
return _tzID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the meta zone ID, or <code>null</code> if not available.
|
||||
*
|
||||
* <p><b>Note</b>: A <code>MatchInfo</code> must have either a time zone ID
|
||||
* or a meta zone ID.
|
||||
*
|
||||
* @return the meta zone ID, or <code>null</code>.
|
||||
* @see #tzID()
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
public String mzID() {
|
||||
return _mzID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time zone name type.
|
||||
* @return the time zone name type enum.
|
||||
* @see NameType
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
public NameType nameType() {
|
||||
return _nameType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the match length.
|
||||
* @return the match length.
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
public int matchLength() {
|
||||
return _matchLength;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sole constructor for invocation by subclass constructors.
|
||||
*
|
||||
* @internal ICU 4.8 technology preview
|
||||
* @deprecated This API might change or be removed in a future release.
|
||||
*/
|
||||
protected TimeZoneNames() {
|
||||
}
|
||||
|
||||
/**
|
||||
* The super class of <code>TimeZoneNames</code> service factory classes.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static abstract class Factory {
|
||||
/**
|
||||
* The factory method of <code>TimeZoneNames</code>.
|
||||
*
|
||||
* @param locale
|
||||
* The display locale
|
||||
* @return An instance of <code>TimeZoneNames</code>.
|
||||
* @internal
|
||||
*/
|
||||
public abstract TimeZoneNames getTimeZoneNames(ULocale locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* TimeZoneNames cache used by {@link TimeZoneNames#getInstance(ULocale)}
|
||||
*/
|
||||
private static class Cache extends SoftCache<String, TimeZoneNames, ULocale> {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.icu.impl.CacheBase#createInstance(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
protected TimeZoneNames createInstance(String key, ULocale data) {
|
||||
return TZNAMES_FACTORY.getTimeZoneNames(data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The default implementation of <code>TimeZoneNames</code> used by {@link TimeZoneNames#getInstance(ULocale)} when
|
||||
* the ICU4J tznamedata component is not available.
|
||||
*/
|
||||
private static class DefaultTimeZoneNames extends TimeZoneNames {
|
||||
|
||||
private static final long serialVersionUID = -995672072494349071L;
|
||||
|
||||
public static final DefaultTimeZoneNames INSTANCE = new DefaultTimeZoneNames();
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.ibm.icu.text.TimeZoneNames#getAvailableMetaZoneIDs()
|
||||
*/
|
||||
@Override
|
||||
public Set<String> getAvailableMetaZoneIDs() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.ibm.icu.text.TimeZoneNames#getAvailableMetaZoneIDs(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public Set<String> getAvailableMetaZoneIDs(String tzID) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.icu.text.TimeZoneNames#getMetaZoneID (java.lang.String, long)
|
||||
*/
|
||||
@Override
|
||||
public String getMetaZoneID(String tzID, long date) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.icu.text.TimeZoneNames#getReferenceZoneID(java.lang.String, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public String getReferenceZoneID(String mzID, String region) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.ibm.icu.text.TimeZoneNames#getMetaZoneDisplayName(java.lang.String, com.ibm.icu.text.TimeZoneNames.NameType)
|
||||
*/
|
||||
@Override
|
||||
public String getMetaZoneDisplayName(String mzID, NameType type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.ibm.icu.text.TimeZoneNames#getTimeZoneDisplayName(java.lang.String, com.ibm.icu.text.TimeZoneNames.NameType)
|
||||
*/
|
||||
@Override
|
||||
public String getTimeZoneDisplayName(String tzID, NameType type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.ibm.icu.text.TimeZoneNames#find(java.lang.String, int, com.ibm.icu.text.TimeZoneNames.NameType[])
|
||||
*/
|
||||
@Override
|
||||
public Collection<MatchInfo> find(String text, int start, EnumSet<NameType> nameTypes) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* The default <code>TimeZoneNames</code> factory called from {@link TimeZoneNames#getInstance(ULocale)} when
|
||||
* the ICU4J tznamedata component is not available.
|
||||
*/
|
||||
public static class FactoryImpl extends Factory {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see com.ibm.icu.text.TimeZoneNames.Factory#getTimeZoneNames (com.ibm.icu.util.ULocale)
|
||||
*/
|
||||
@Override
|
||||
public TimeZoneNames getTimeZoneNames(ULocale locale) {
|
||||
return DefaultTimeZoneNames.INSTANCE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
50
icu4j/main/classes/core/src/com/ibm/icu/util/Output.java
Normal file
50
icu4j/main/classes/core/src/com/ibm/icu/util/Output.java
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2011, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
package com.ibm.icu.util;
|
||||
|
||||
/**
|
||||
* Simple struct-like class for output parameters.
|
||||
* @param <T> The type of the parameter.
|
||||
* @draft ICU 4.8
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public class Output<T> {
|
||||
/**
|
||||
* The value field
|
||||
* @draft ICU 4.8
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public T value;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @draft ICU 4.8
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public String toString() {
|
||||
return value == null ? "null" : value.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an empty <code>Output</code>
|
||||
* @draft ICU 4.8
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public Output() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an <code>Output</code> withe the given value.
|
||||
* @param value the initial value
|
||||
* @draft ICU 4.8
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public Output(T value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
@ -14,14 +14,16 @@ import java.util.MissingResourceException;
|
||||
import java.util.Set;
|
||||
|
||||
import com.ibm.icu.impl.Grego;
|
||||
import com.ibm.icu.impl.ICUCache;
|
||||
import com.ibm.icu.impl.ICUConfig;
|
||||
import com.ibm.icu.impl.ICULogger;
|
||||
import com.ibm.icu.impl.JavaTimeZone;
|
||||
import com.ibm.icu.impl.SimpleCache;
|
||||
import com.ibm.icu.impl.TimeZoneAdapter;
|
||||
import com.ibm.icu.impl.TimeZoneFormat;
|
||||
import com.ibm.icu.impl.ZoneMeta;
|
||||
import com.ibm.icu.text.TimeZoneFormat;
|
||||
import com.ibm.icu.text.TimeZoneFormat.Style;
|
||||
import com.ibm.icu.text.TimeZoneFormat.TimeType;
|
||||
import com.ibm.icu.text.TimeZoneNames;
|
||||
import com.ibm.icu.text.TimeZoneNames.NameType;
|
||||
|
||||
/**
|
||||
* {@icuenhanced java.util.TimeZone}.{@icu _usage_}
|
||||
@ -206,7 +208,7 @@ abstract public class TimeZone implements Serializable, Cloneable {
|
||||
* @see #getTimeZone(String)
|
||||
*
|
||||
* @draft ICU 4.8
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public static final String UNKNOWN_ZONE_ID = "Etc/Unknown";
|
||||
|
||||
@ -215,37 +217,31 @@ abstract public class TimeZone implements Serializable, Cloneable {
|
||||
* {@link TimeZone#getAvailableIDs(SystemTimeZoneType, String, Integer)}
|
||||
*
|
||||
* @draft ICU 4.8
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public enum SystemTimeZoneType {
|
||||
/**
|
||||
* Any system zones.
|
||||
* @draft ICU 4.8
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
ANY,
|
||||
|
||||
/**
|
||||
* Canonical system zones.
|
||||
* @draft ICU 4.8
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
CANONICAL,
|
||||
|
||||
/**
|
||||
* Canonical system zones associated with actual locations.
|
||||
* @draft ICU 4.8
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
CANONICAL_LOCATION,
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache to hold the SimpleDateFormat objects for a Locale.
|
||||
*/
|
||||
private static ICUCache<ULocale, TimeZoneFormat> cachedLocaleData =
|
||||
new SimpleCache<ULocale, TimeZoneFormat>();
|
||||
|
||||
/**
|
||||
* Gets the time zone offset, for current date, modified in case of
|
||||
* daylight savings. This is the offset to add *to* UTC to get local time.
|
||||
@ -385,7 +381,7 @@ abstract public class TimeZone implements Serializable, Cloneable {
|
||||
* @stable ICU 2.0
|
||||
*/
|
||||
public final String getDisplayName() {
|
||||
return _getDisplayName(false, false, LONG_GENERIC, ULocale.getDefault());
|
||||
return _getDisplayName(LONG_GENERIC, false, ULocale.getDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -400,7 +396,7 @@ abstract public class TimeZone implements Serializable, Cloneable {
|
||||
* @stable ICU 2.0
|
||||
*/
|
||||
public final String getDisplayName(Locale locale) {
|
||||
return _getDisplayName(false, false, LONG_GENERIC, ULocale.forLocale(locale));
|
||||
return _getDisplayName(LONG_GENERIC, false, ULocale.forLocale(locale));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -415,15 +411,15 @@ abstract public class TimeZone implements Serializable, Cloneable {
|
||||
* @stable ICU 3.2
|
||||
*/
|
||||
public final String getDisplayName(ULocale locale) {
|
||||
return _getDisplayName(false, false, LONG_GENERIC, locale);
|
||||
return _getDisplayName(LONG_GENERIC, false, locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a name of this time zone suitable for presentation to the user
|
||||
* in the default locale.
|
||||
* If the display name is not available for the locale,
|
||||
* then this method returns a string in the format
|
||||
* <code>GMT[+-]hh:mm</code>.
|
||||
* then this method returns a string in the localized GMT offset format
|
||||
* such as <code>GMT[+-]HH:mm</code>.
|
||||
* @param daylight if true, return the daylight savings name.
|
||||
* @param style the output style of the display name. Valid styles are
|
||||
* <code>SHORT</code>, <code>LONG</code>, <code>SHORT_GENERIC</code>,
|
||||
@ -440,8 +436,8 @@ abstract public class TimeZone implements Serializable, Cloneable {
|
||||
* Returns a name of this time zone suitable for presentation to the user
|
||||
* in the specified locale.
|
||||
* If the display name is not available for the locale,
|
||||
* then this method returns a string in the format
|
||||
* <code>GMT[+-]hh:mm</code>.
|
||||
* then this method returns a string in the localized GMT offset format
|
||||
* such as <code>GMT[+-]HH:mm</code>.
|
||||
* @param daylight if true, return the daylight savings name.
|
||||
* @param style the output style of the display name. Valid styles are
|
||||
* <code>SHORT</code>, <code>LONG</code>, <code>SHORT_GENERIC</code>,
|
||||
@ -461,8 +457,8 @@ abstract public class TimeZone implements Serializable, Cloneable {
|
||||
* Returns a name of this time zone suitable for presentation to the user
|
||||
* in the specified locale.
|
||||
* If the display name is not available for the locale,
|
||||
* then this method returns a string in the format
|
||||
* <code>GMT[+-]hh:mm</code>.
|
||||
* then this method returns a string in the localized GMT offset format
|
||||
* such as <code>GMT[+-]HH:mm</code>.
|
||||
* @param daylight if true, return the daylight savings name.
|
||||
* @param style the output style of the display name. Valid styles are
|
||||
* <code>SHORT</code>, <code>LONG</code>, <code>SHORT_GENERIC</code>,
|
||||
@ -479,7 +475,7 @@ abstract public class TimeZone implements Serializable, Cloneable {
|
||||
throw new IllegalArgumentException("Illegal style: " + style);
|
||||
}
|
||||
|
||||
return _getDisplayName(daylight, true, style, locale);
|
||||
return _getDisplayName(style, daylight, locale);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -487,37 +483,81 @@ abstract public class TimeZone implements Serializable, Cloneable {
|
||||
* SHORT, LONG, SHORT_GENERIC, LONG_GENERIC, SHORT_GMT, LONG_GMT,
|
||||
* SHORT_COMMONLY_USED and GENERIC_LOCATION.
|
||||
*/
|
||||
private String _getDisplayName(boolean daylight, boolean daylightRequested, int style, ULocale locale) {
|
||||
private String _getDisplayName(int style, boolean daylight, ULocale locale) {
|
||||
if (locale == null) {
|
||||
throw new NullPointerException("locale is null");
|
||||
}
|
||||
|
||||
// We keep a cache, indexed by locale.
|
||||
TimeZoneFormat tzf = null;
|
||||
tzf = cachedLocaleData.get(locale);
|
||||
if (tzf == null) {
|
||||
tzf = TimeZoneFormat.createInstance(locale);
|
||||
cachedLocaleData.put(locale, tzf);
|
||||
}
|
||||
String result = null;
|
||||
|
||||
String result;
|
||||
if ( daylightRequested ) {
|
||||
result = tzf.format(this, style, daylight);
|
||||
if ( result == null) {
|
||||
result = tzf.format( this, LONG_GMT, daylight);
|
||||
if (style == GENERIC_LOCATION || style == LONG_GENERIC || style == SHORT_GENERIC) {
|
||||
// Generic format
|
||||
TimeZoneFormat tzfmt = TimeZoneFormat.getInstance(locale);
|
||||
long date = System.currentTimeMillis();
|
||||
Output<TimeType> timeType = new Output<TimeType>(TimeType.UNKNOWN);
|
||||
|
||||
switch (style) {
|
||||
case GENERIC_LOCATION:
|
||||
result = tzfmt.format(Style.GENERIC_LOCATION, this, date, timeType);
|
||||
break;
|
||||
case LONG_GENERIC:
|
||||
result = tzfmt.format(Style.GENERIC_LONG, this, date, timeType);
|
||||
break;
|
||||
case SHORT_GENERIC:
|
||||
result = tzfmt.format(Style.GENERIC_SHORT, this, date, timeType);
|
||||
break;
|
||||
}
|
||||
|
||||
// Generic format many use Localized GMT as the final fallback.
|
||||
// When Localized GMT format is used, the result might not be
|
||||
// appropriate for the requested daylight value.
|
||||
if (daylight && timeType.value == TimeType.STANDARD ||
|
||||
!daylight && timeType.value == TimeType.DAYLIGHT) {
|
||||
int offset = daylight ? getRawOffset() + getDSTSavings() : getRawOffset();
|
||||
result = tzfmt.formatOffsetLocalizedGMT(offset);
|
||||
}
|
||||
|
||||
} else if (style == LONG_GMT || style == SHORT_GMT) {
|
||||
// Offset format
|
||||
TimeZoneFormat tzfmt = TimeZoneFormat.getInstance(locale);
|
||||
int offset = daylight && useDaylightTime() ? getRawOffset() + getDSTSavings() : getRawOffset();
|
||||
switch (style) {
|
||||
case LONG_GMT:
|
||||
result = tzfmt.formatOffsetLocalizedGMT(offset);
|
||||
break;
|
||||
case SHORT_GMT:
|
||||
result = tzfmt.formatOffsetRFC822(offset);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
long now = System.currentTimeMillis();
|
||||
result = tzf.format(this, now, style);
|
||||
if ( result == null) {
|
||||
result = tzf.format( this, now, LONG_GMT);
|
||||
// Specific format
|
||||
assert(style == LONG || style == SHORT || style == SHORT_COMMONLY_USED);
|
||||
|
||||
// Gets the name directly from TimeZoneNames
|
||||
long date = System.currentTimeMillis();
|
||||
TimeZoneNames tznames = TimeZoneNames.getInstance(locale);
|
||||
NameType nameType = null;
|
||||
switch (style) {
|
||||
case LONG:
|
||||
nameType = daylight ? NameType.LONG_DAYLIGHT : NameType.LONG_STANDARD;
|
||||
break;
|
||||
case SHORT:
|
||||
nameType = daylight ? NameType.SHORT_DAYLIGHT : NameType.SHORT_STANDARD;
|
||||
break;
|
||||
case SHORT_COMMONLY_USED:
|
||||
nameType = daylight ? NameType.SHORT_DAYLIGHT_COMMONLY_USED : NameType.SHORT_STANDARD_COMMONLY_USED;
|
||||
break;
|
||||
}
|
||||
result = tznames.getDisplayName(ZoneMeta.getCanonicalCLDRID(this), nameType, date);
|
||||
if (result == null) {
|
||||
// Fallback to localized GMT
|
||||
TimeZoneFormat tzfmt = TimeZoneFormat.getInstance(locale);
|
||||
int offset = daylight && useDaylightTime() ? getRawOffset() + getDSTSavings() : getRawOffset();
|
||||
result = tzfmt.formatOffsetLocalizedGMT(offset);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( result == null )
|
||||
result = tzf.format(this, LONG_GMT, daylight);
|
||||
|
||||
assert(result != null);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -604,7 +644,7 @@ abstract public class TimeZone implements Serializable, Cloneable {
|
||||
result = new JavaTimeZone(ID);
|
||||
} else {
|
||||
/* We first try to lookup the zone ID in our system list. If this
|
||||
* fails, we try to parse it as a custom string GMT[+-]hh:mm. If
|
||||
* fails, we try to parse it as a custom string GMT[+-]HH:mm. If
|
||||
* all else fails, we return GMT, which is probably not what the
|
||||
* user wants, but at least is a functioning TimeZone object.
|
||||
*
|
||||
@ -667,7 +707,7 @@ abstract public class TimeZone implements Serializable, Cloneable {
|
||||
* @see SystemTimeZoneType
|
||||
*
|
||||
* @draft ICU 4.8
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public static Set<String> getAvailableIDs(SystemTimeZoneType zoneType,
|
||||
String region, Integer rawOffset) {
|
||||
@ -954,7 +994,7 @@ abstract public class TimeZone implements Serializable, Cloneable {
|
||||
* @see #getAvailableIDs(String)
|
||||
*
|
||||
* @draft ICU 4.8
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
* @provisional This API might change or be removed in a future release.
|
||||
*/
|
||||
public static String getRegion(String id) {
|
||||
String region = null;
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 2001-2010, International Business Machines Corporation and *
|
||||
* Copyright (C) 2001-2011, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*/
|
||||
@ -32,6 +32,7 @@ import com.ibm.icu.text.DateFormat;
|
||||
import com.ibm.icu.text.DateFormatSymbols;
|
||||
import com.ibm.icu.text.NumberFormat;
|
||||
import com.ibm.icu.text.SimpleDateFormat;
|
||||
import com.ibm.icu.text.TimeZoneFormat;
|
||||
import com.ibm.icu.util.BuddhistCalendar;
|
||||
import com.ibm.icu.util.Calendar;
|
||||
import com.ibm.icu.util.ChineseCalendar;
|
||||
@ -308,7 +309,7 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
|
||||
"Anno Domini", "1997", "August", "0013", "0014", "0014", "0034", "0012", "5130",
|
||||
"Wednesday", "0225", "0002", "0033", "0002", "PM", "0002", "0002", "Pacific Daylight Time", "1997",
|
||||
"Wednesday", "1997", "2450674", "52452513", "GMT-07:00", "Pacific Time","Wednesday","August", "3rd quarter", "3rd quarter","United States (Los Angeles)",
|
||||
"Wednesday", "1997", "2450674", "52452513", "GMT-07:00", "Pacific Time","Wednesday","August", "3rd quarter", "3rd quarter","United States Time (Los Angeles)",
|
||||
};
|
||||
|
||||
assertTrue("data size", EXPECTED.length == COUNT * DateFormat.FIELD_COUNT);
|
||||
@ -552,7 +553,7 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
// TODO: Revisit after 3.8
|
||||
//"y/M/d H:mm vvvv", "pf", "2004/10/31 1:30 Argentina Time", "2004 10 30 21:30 PDT", "2004/10/31 1:30 Argentina Time",
|
||||
};
|
||||
expect(ZDATA, en);
|
||||
expect(ZDATA, en, true);
|
||||
|
||||
logln("cross format/parse tests");
|
||||
final String basepat = "yy/MM/dd H:mm ";
|
||||
@ -564,6 +565,16 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
};
|
||||
|
||||
final SimpleDateFormat univ = new SimpleDateFormat("yyyy MM dd HH:mm zzz", en);
|
||||
|
||||
// To allow cross pattern parsing, we need setParseAllStyles(true) since 4.8
|
||||
TimeZoneFormat tzfmt = univ.getTimeZoneFormat().cloneAsThawed();
|
||||
tzfmt.setParseAllStyles(true);
|
||||
tzfmt.freeze();
|
||||
univ.setTimeZoneFormat(tzfmt);
|
||||
for (SimpleDateFormat sdf : formats) {
|
||||
sdf.setTimeZoneFormat(tzfmt);
|
||||
}
|
||||
|
||||
final String[] times = { "2004 01 02 03:04 PST", "2004 07 08 09:10 PDT" };
|
||||
for (int i = 0; i < times.length; ++i) {
|
||||
try {
|
||||
@ -626,7 +637,7 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
"vvvv y/M/d H:mm", "pf", "PT 2004/7/1 1:00", "2004 07 01 01:00 PDT", "Pacific Time 2004/7/1 1:00",
|
||||
};
|
||||
Locale en = new Locale("en", "", "");
|
||||
expect(XDATA, en);
|
||||
expect(XDATA, en, true);
|
||||
}
|
||||
|
||||
public void TestTimeZoneDisplayName() {
|
||||
@ -675,7 +686,7 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
{ "en", "America/Los_Angeles", "2004-07-15T00:00:00Z", "zzzz", "Pacific Daylight Time", "America/Los_Angeles" },
|
||||
{ "en", "America/Los_Angeles", "2004-07-15T00:00:00Z", "v", "PT", "America/Los_Angeles" },
|
||||
{ "en", "America/Los_Angeles", "2004-07-15T00:00:00Z", "vvvv", "Pacific Time", "America/Los_Angeles" },
|
||||
{ "en", "America/Los_Angeles", "2004-07-15T00:00:00Z", "VVVV", "United States (Los Angeles)", "America/Los_Angeles" },
|
||||
{ "en", "America/Los_Angeles", "2004-07-15T00:00:00Z", "VVVV", "United States Time (Los Angeles)", "America/Los_Angeles" },
|
||||
{ "en_GB", "America/Los_Angeles", "2004-01-15T12:00:00Z", "z", "PST", "America/Los_Angeles" },
|
||||
{ "en", "America/Phoenix", "2004-01-15T00:00:00Z", "Z", "-0700", "-7:00" },
|
||||
{ "en", "America/Phoenix", "2004-01-15T00:00:00Z", "ZZZZ", "GMT-07:00", "-7:00" },
|
||||
@ -689,7 +700,7 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
{ "en", "America/Phoenix", "2004-07-15T00:00:00Z", "zzzz", "Mountain Standard Time", "America/Phoenix" },
|
||||
{ "en", "America/Phoenix", "2004-07-15T00:00:00Z", "v", "MST", "America/Phoenix" },
|
||||
{ "en", "America/Phoenix", "2004-07-15T00:00:00Z", "vvvv", "Mountain Standard Time", "America/Phoenix" },
|
||||
{ "en", "America/Phoenix", "2004-07-15T00:00:00Z", "VVVV", "United States (Phoenix)", "America/Phoenix" },
|
||||
{ "en", "America/Phoenix", "2004-07-15T00:00:00Z", "VVVV", "United States Time (Phoenix)", "America/Phoenix" },
|
||||
|
||||
{ "en", "America/Argentina/Buenos_Aires", "2004-01-15T00:00:00Z", "Z", "-0300", "-3:00" },
|
||||
{ "en", "America/Argentina/Buenos_Aires", "2004-01-15T00:00:00Z", "ZZZZ", "GMT-03:00", "-3:00" },
|
||||
@ -701,9 +712,9 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
{ "en", "America/Argentina/Buenos_Aires", "2004-07-15T00:00:00Z", "z", "GMT-03:00", "-3:00" },
|
||||
{ "en", "America/Argentina/Buenos_Aires", "2004-07-15T00:00:00Z", "V", "ART", "-3:00" },
|
||||
{ "en", "America/Argentina/Buenos_Aires", "2004-07-15T00:00:00Z", "zzzz", "Argentina Time", "-3:00" },
|
||||
{ "en", "America/Argentina/Buenos_Aires", "2004-07-15T00:00:00Z", "v", "Argentina (Buenos Aires)", "America/Buenos_Aires" },
|
||||
{ "en", "America/Argentina/Buenos_Aires", "2004-07-15T00:00:00Z", "v", "Argentina Time (Buenos Aires)", "America/Buenos_Aires" },
|
||||
{ "en", "America/Argentina/Buenos_Aires", "2004-07-15T00:00:00Z", "vvvv", "Argentina Time", "America/Buenos_Aires" },
|
||||
{ "en", "America/Argentina/Buenos_Aires", "2004-07-15T00:00:00Z", "VVVV", "Argentina (Buenos Aires)", "America/Buenos_Aires" },
|
||||
{ "en", "America/Argentina/Buenos_Aires", "2004-07-15T00:00:00Z", "VVVV", "Argentina Time (Buenos Aires)", "America/Buenos_Aires" },
|
||||
|
||||
{ "en", "America/Buenos_Aires", "2004-01-15T00:00:00Z", "Z", "-0300", "-3:00" },
|
||||
{ "en", "America/Buenos_Aires", "2004-01-15T00:00:00Z", "ZZZZ", "GMT-03:00", "-3:00" },
|
||||
@ -715,9 +726,9 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
{ "en", "America/Buenos_Aires", "2004-07-15T00:00:00Z", "z", "GMT-03:00", "-3:00" },
|
||||
{ "en", "America/Buenos_Aires", "2004-07-15T00:00:00Z", "V", "ART", "-3:00" },
|
||||
{ "en", "America/Buenos_Aires", "2004-07-15T00:00:00Z", "zzzz", "Argentina Time", "-3:00" },
|
||||
{ "en", "America/Buenos_Aires", "2004-07-15T00:00:00Z", "v", "Argentina (Buenos Aires)", "America/Buenos_Aires" },
|
||||
{ "en", "America/Buenos_Aires", "2004-07-15T00:00:00Z", "v", "Argentina Time (Buenos Aires)", "America/Buenos_Aires" },
|
||||
{ "en", "America/Buenos_Aires", "2004-07-15T00:00:00Z", "vvvv", "Argentina Time", "America/Buenos_Aires" },
|
||||
{ "en", "America/Buenos_Aires", "2004-07-15T00:00:00Z", "VVVV", "Argentina (Buenos Aires)", "America/Buenos_Aires" },
|
||||
{ "en", "America/Buenos_Aires", "2004-07-15T00:00:00Z", "VVVV", "Argentina Time (Buenos Aires)", "America/Buenos_Aires" },
|
||||
|
||||
{ "en", "America/Havana", "2004-01-15T00:00:00Z", "Z", "-0500", "-5:00" },
|
||||
{ "en", "America/Havana", "2004-01-15T00:00:00Z", "ZZZZ", "GMT-05:00", "-5:00" },
|
||||
@ -743,9 +754,9 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
{ "en", "Australia/ACT", "2004-07-15T00:00:00Z", "z", "GMT+10:00", "+10:00" },
|
||||
{ "en", "Australia/ACT", "2004-07-15T00:00:00Z", "V", "AEST", "+10:00" },
|
||||
{ "en", "Australia/ACT", "2004-07-15T00:00:00Z", "zzzz", "Australian Eastern Standard Time", "+10:00" },
|
||||
{ "en", "Australia/ACT", "2004-07-15T00:00:00Z", "v", "Australia (Sydney)", "Australia/Sydney" },
|
||||
{ "en", "Australia/ACT", "2004-07-15T00:00:00Z", "v", "Australia Time (Sydney)", "Australia/Sydney" },
|
||||
{ "en", "Australia/ACT", "2004-07-15T00:00:00Z", "vvvv", "Eastern Australia Time", "Australia/Sydney" },
|
||||
{ "en", "Australia/ACT", "2004-07-15T00:00:00Z", "VVVV", "Australia (Sydney)", "Australia/Sydney" },
|
||||
{ "en", "Australia/ACT", "2004-07-15T00:00:00Z", "VVVV", "Australia Time (Sydney)", "Australia/Sydney" },
|
||||
|
||||
{ "en", "Australia/Sydney", "2004-01-15T00:00:00Z", "Z", "+1100", "+11:00" },
|
||||
{ "en", "Australia/Sydney", "2004-01-15T00:00:00Z", "ZZZZ", "GMT+11:00", "+11:00" },
|
||||
@ -757,12 +768,12 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
{ "en", "Australia/Sydney", "2004-07-15T00:00:00Z", "z", "GMT+10:00", "+10:00" },
|
||||
{ "en", "Australia/Sydney", "2004-07-15T00:00:00Z", "V", "AEST", "+10:00" },
|
||||
{ "en", "Australia/Sydney", "2004-07-15T00:00:00Z", "zzzz", "Australian Eastern Standard Time", "+10:00" },
|
||||
{ "en", "Australia/Sydney", "2004-07-15T00:00:00Z", "v", "Australia (Sydney)", "Australia/Sydney" },
|
||||
{ "en", "Australia/Sydney", "2004-07-15T00:00:00Z", "v", "Australia Time (Sydney)", "Australia/Sydney" },
|
||||
{ "en", "Australia/Sydney", "2004-07-15T00:00:00Z", "vvvv", "Eastern Australia Time", "Australia/Sydney" },
|
||||
{ "en", "Australia/Sydney", "2004-07-15T00:00:00Z", "VVVV", "Australia (Sydney)", "Australia/Sydney" },
|
||||
{ "en", "Australia/Sydney", "2004-07-15T00:00:00Z", "VVVV", "Australia Time (Sydney)", "Australia/Sydney" },
|
||||
|
||||
{ "en", "Europe/London", "2004-01-15T00:00:00Z", "Z", "+0000", "+0:00" },
|
||||
{ "en", "Europe/London", "2004-01-15T00:00:00Z", "ZZZZ", "GMT+00:00", "+0:00" },
|
||||
{ "en", "Europe/London", "2004-01-15T00:00:00Z", "ZZZZ", "GMT", "+0:00" },
|
||||
{ "en", "Europe/London", "2004-01-15T00:00:00Z", "z", "GMT", "+0:00" },
|
||||
{ "en", "Europe/London", "2004-01-15T00:00:00Z", "V", "GMT", "+0:00" },
|
||||
{ "en", "Europe/London", "2004-01-15T00:00:00Z", "zzzz", "Greenwich Mean Time", "+0:00" },
|
||||
@ -873,9 +884,9 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
{ "de", "Australia/Sydney", "2004-07-15T00:00:00Z", "vvvv", "Australien (Sydney)", "Australia/Sydney" },
|
||||
|
||||
{ "de", "Europe/London", "2004-01-15T00:00:00Z", "Z", "+0000", "+0:00" },
|
||||
{ "de", "Europe/London", "2004-01-15T00:00:00Z", "ZZZZ", "GMT+00:00", "+0:00" },
|
||||
{ "de", "Europe/London", "2004-01-15T00:00:00Z", "z", "GMT+00:00", "+0:00" },
|
||||
{ "de", "Europe/London", "2004-01-15T00:00:00Z", "zzzz", "GMT+00:00", "+0:00" },
|
||||
{ "de", "Europe/London", "2004-01-15T00:00:00Z", "ZZZZ", "GMT", "+0:00" },
|
||||
{ "de", "Europe/London", "2004-01-15T00:00:00Z", "z", "GMT", "+0:00" },
|
||||
{ "de", "Europe/London", "2004-01-15T00:00:00Z", "zzzz", "GMT", "+0:00" },
|
||||
{ "de", "Europe/London", "2004-07-15T00:00:00Z", "Z", "+0100", "+1:00" },
|
||||
{ "de", "Europe/London", "2004-07-15T00:00:00Z", "ZZZZ", "GMT+01:00", "+1:00" },
|
||||
{ "de", "Europe/London", "2004-07-15T00:00:00Z", "z", "GMT+01:00", "+1:00" },
|
||||
@ -977,8 +988,8 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
{ "zh", "Australia/Sydney", "2004-07-15T00:00:00Z", "vvvv", "\u6fb3\u5927\u5229\u4e9a\u4e1c\u90e8\u65f6\u95f4", "Australia/Sydney" },
|
||||
|
||||
{ "zh", "Europe/London", "2004-01-15T00:00:00Z", "Z", "+0000", "+0:00" },
|
||||
{ "zh", "Europe/London", "2004-01-15T00:00:00Z", "ZZZZ", GMT_ZH+"+0000", "+0:00" },
|
||||
{ "zh", "Europe/London", "2004-01-15T00:00:00Z", "z", GMT_ZH+"+0000", "+0:00" },
|
||||
{ "zh", "Europe/London", "2004-01-15T00:00:00Z", "ZZZZ", GMT_ZH, "+0:00" },
|
||||
{ "zh", "Europe/London", "2004-01-15T00:00:00Z", "z", GMT_ZH, "+0:00" },
|
||||
{ "zh", "Europe/London", "2004-01-15T00:00:00Z", "V", "GMT", "+0:00" },
|
||||
{ "zh", "Europe/London", "2004-01-15T00:00:00Z", "zzzz", "\u683C\u6797\u5C3C\u6CBB\u6807\u51C6\u65F6\u95F4", "+0:00" },
|
||||
{ "zh", "Europe/London", "2004-07-15T00:00:00Z", "Z", "+0100", "+1:00" },
|
||||
@ -1082,9 +1093,9 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
{ "hi", "Australia/Sydney", "2004-07-15T00:00:00Z", "vvvv", "\u0911\u0938\u094d\u091f\u094d\u0930\u0947\u0932\u093f\u092f\u093e (\u0938\u093f\u0921\u0928\u0940)", "Australia/Sydney" },
|
||||
|
||||
{ "hi", "Europe/London", "2004-01-15T00:00:00Z", "Z", "+0000", "+0:00" },
|
||||
{ "hi", "Europe/London", "2004-01-15T00:00:00Z", "ZZZZ", "GMT+\u0966\u0966:\u0966\u0966", "+0:00" },
|
||||
{ "hi", "Europe/London", "2004-01-15T00:00:00Z", "z", "GMT+\u0966\u0966:\u0966\u0966", "+0:00" },
|
||||
{ "hi", "Europe/London", "2004-01-15T00:00:00Z", "zzzz", "GMT+\u0966\u0966:\u0966\u0966", "+0:00" },
|
||||
{ "hi", "Europe/London", "2004-01-15T00:00:00Z", "ZZZZ", "GMT", "+0:00" },
|
||||
{ "hi", "Europe/London", "2004-01-15T00:00:00Z", "z", "GMT", "+0:00" },
|
||||
{ "hi", "Europe/London", "2004-01-15T00:00:00Z", "zzzz", "GMT", "+0:00" },
|
||||
{ "hi", "Europe/London", "2004-07-15T00:00:00Z", "Z", "+0100", "+1:00" },
|
||||
{ "hi", "Europe/London", "2004-07-15T00:00:00Z", "ZZZZ", "GMT+\u0966\u0967:\u0966\u0966", "+1:00" },
|
||||
{ "hi", "Europe/London", "2004-07-15T00:00:00Z", "z", "GMT+\u0966\u0967:\u0966\u0966", "+1:00" },
|
||||
@ -1188,8 +1199,8 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
{ "bg", "Australia/Sydney", "2004-07-15T00:00:00Z", "vvvv", "\u0410\u0432\u0441\u0442\u0440\u0430\u043b\u0438\u044f (\u0421\u0438\u0434\u043D\u0438)", "Australia/Sydney" },
|
||||
|
||||
{ "bg", "Europe/London", "2004-01-15T00:00:00Z", "Z", "+0000", "+0:00" },
|
||||
{ "bg", "Europe/London", "2004-01-15T00:00:00Z", "ZZZZ", GMT_BG+"+0000", "+0:00" },
|
||||
{ "bg", "Europe/London", "2004-01-15T00:00:00Z", "z", GMT_BG+"+0000", "+0:00" },
|
||||
{ "bg", "Europe/London", "2004-01-15T00:00:00Z", "ZZZZ", GMT_BG, "+0:00" },
|
||||
{ "bg", "Europe/London", "2004-01-15T00:00:00Z", "z", GMT_BG, "+0:00" },
|
||||
{ "bg", "Europe/London", "2004-01-15T00:00:00Z", "zzzz", "\u0427\u0430\u0441\u043E\u0432\u0430 \u0437\u043E\u043D\u0430 \u0413\u0440\u0438\u043D\u0443\u0438\u0447", "+0:00" },
|
||||
{ "bg", "Europe/London", "2004-07-15T00:00:00Z", "Z", "+0100", "+1:00" },
|
||||
{ "bg", "Europe/London", "2004-07-15T00:00:00Z", "ZZZZ", GMT_BG+"+0100", "+1:00" },
|
||||
@ -1297,8 +1308,8 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
{ "ja", "Australia/Sydney", "2004-07-15T00:00:00Z", "vvvv", "\u30aa\u30fc\u30b9\u30c8\u30e9\u30ea\u30a2 (\u30b7\u30c9\u30cb\u30fc)", "Australia/Sydney" },
|
||||
|
||||
{ "ja", "Europe/London", "2004-01-15T00:00:00Z", "Z", "+0000", "+0:00" },
|
||||
{ "ja", "Europe/London", "2004-01-15T00:00:00Z", "ZZZZ", "GMT+00:00", "+0:00" },
|
||||
{ "ja", "Europe/London", "2004-01-15T00:00:00Z", "z", "GMT+00:00", "+0:00" },
|
||||
{ "ja", "Europe/London", "2004-01-15T00:00:00Z", "ZZZZ", "GMT", "+0:00" },
|
||||
{ "ja", "Europe/London", "2004-01-15T00:00:00Z", "z", "GMT", "+0:00" },
|
||||
{ "ja", "Europe/London", "2004-01-15T00:00:00Z", "V", "GMT", "+0:00" },
|
||||
{ "ja", "Europe/London", "2004-01-15T00:00:00Z", "zzzz", "\u30B0\u30EA\u30CB\u30C3\u30B8\u6A19\u6E96\u6642", "+0:00" },
|
||||
{ "ja", "Europe/London", "2004-07-15T00:00:00Z", "Z", "+0100", "+1:00" },
|
||||
@ -1402,9 +1413,9 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
{ "si", "Australia/Sydney", "2004-07-15T00:00:00Z", "vvvv", "AU (Sydney)", "Australia/Sydney" },
|
||||
|
||||
{ "si", "Europe/London", "2004-01-15T00:00:00Z", "Z", "+0000", "+0:00" },
|
||||
{ "si", "Europe/London", "2004-01-15T00:00:00Z", "ZZZZ", "GMT+00:00", "+0:00" },
|
||||
{ "si", "Europe/London", "2004-01-15T00:00:00Z", "z", "GMT+00:00", "+0:00" },
|
||||
{ "si", "Europe/London", "2004-01-15T00:00:00Z", "zzzz", "GMT+00:00", "+0:00" },
|
||||
{ "si", "Europe/London", "2004-01-15T00:00:00Z", "ZZZZ", "GMT", "+0:00" },
|
||||
{ "si", "Europe/London", "2004-01-15T00:00:00Z", "z", "GMT", "+0:00" },
|
||||
{ "si", "Europe/London", "2004-01-15T00:00:00Z", "zzzz", "GMT", "+0:00" },
|
||||
{ "si", "Europe/London", "2004-07-15T00:00:00Z", "Z", "+0100", "+1:00" },
|
||||
{ "si", "Europe/London", "2004-07-15T00:00:00Z", "ZZZZ", "GMT+01:00", "+1:00" },
|
||||
{ "si", "Europe/London", "2004-07-15T00:00:00Z", "z", "GMT+01:00", "+1:00" },
|
||||
@ -1867,11 +1878,11 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
Date greenwichdate = greenwichcalendar.getTime();
|
||||
// format every way
|
||||
String DATA[] = {
|
||||
"simple format: ", "04/04/97 23:00 GMT+00:00",
|
||||
"simple format: ", "04/04/97 23:00 GMT",
|
||||
"MM/dd/yy HH:mm zzz", "full format: ",
|
||||
"Friday, April 4, 1997 11:00:00 o'clock PM GMT+00:00",
|
||||
"Friday, April 4, 1997 11:00:00 o'clock PM GMT",
|
||||
"EEEE, MMMM d, yyyy h:mm:ss 'o''clock' a zzz",
|
||||
"long format: ", "April 4, 1997 11:00:00 PM GMT+00:00",
|
||||
"long format: ", "April 4, 1997 11:00:00 PM GMT",
|
||||
"MMMM d, yyyy h:mm:ss a z", "default format: ",
|
||||
"04-Apr-97 11:00:00 PM", "dd-MMM-yy h:mm:ss a",
|
||||
"short format: ", "4/4/97 11:00 PM",
|
||||
@ -2511,28 +2522,6 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
TimeZone.setDefault(oldtz);
|
||||
throw new IllegalStateException(e.getMessage());
|
||||
}
|
||||
|
||||
// create DFS that recognizes our bogus time zone, sortof
|
||||
DateFormatSymbols xsym = new DateFormatSymbols();
|
||||
String[][] tzids = xsym.getZoneStrings();
|
||||
if (tzids.length > 0) { // let's hope!
|
||||
tzids[0][1] = "DBDY"; // change a local name
|
||||
logln("replaced '" + tzids[0][0] + "' with DBDY");
|
||||
|
||||
xsym.setZoneStrings(tzids);
|
||||
fmt.setDateFormatSymbols(xsym);
|
||||
|
||||
try {
|
||||
fmt.parse(text);
|
||||
logln("we parsed DBDY (as GMT, but still...)");
|
||||
}
|
||||
catch (ParseException e) {
|
||||
errln("hey, still didn't recognize DBDY");
|
||||
}
|
||||
finally {
|
||||
TimeZone.setDefault(oldtz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
@ -2893,7 +2882,7 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
"HH:mm:ss zzzz", "10:20:30 UTC", "10:20:30 +0000", // standalone "UTC"
|
||||
"ZZZZ HH:mm:ss", "UT 10:20:30", "10:20:30 +0000",
|
||||
"V HH:mm:ss", "UT+0130 10:20:30", "10:20:30 +0130",
|
||||
"V HH:mm:ss", "UTC+0130 10:20:30", null, // UTC+0130 is not a supported pattern
|
||||
"V HH:mm:ss", "UTC+0130 10:20:30", "10:20:30 +0130",
|
||||
"HH mm Z ss", "10 20 GMT-1100 30", "10:20:30 -1100",
|
||||
};
|
||||
expectParse(DATA, new Locale("en", "", ""));
|
||||
@ -3066,6 +3055,10 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
* -- compare r0 and A, fail if not equal
|
||||
*/
|
||||
void expect(String[] data, Locale loc) {
|
||||
expect(data, loc, false);
|
||||
}
|
||||
|
||||
void expect(String[] data, Locale loc, boolean parseAllTZStyles) {
|
||||
int i = 1;
|
||||
SimpleDateFormat univ = new SimpleDateFormat("EE G yyyy MM dd HH:mm:ss.SSS zzz", loc);
|
||||
String currentPat = null;
|
||||
@ -3073,6 +3066,13 @@ public class DateFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
|
||||
while (i<data.length) {
|
||||
SimpleDateFormat fmt = new SimpleDateFormat("", loc);
|
||||
|
||||
if (parseAllTZStyles) {
|
||||
TimeZoneFormat tzfmt = fmt.getTimeZoneFormat().cloneAsThawed();
|
||||
tzfmt.setParseAllStyles(true).freeze();
|
||||
fmt.setTimeZoneFormat(tzfmt);
|
||||
}
|
||||
|
||||
String pattern = data[i++];
|
||||
if (pattern != null) {
|
||||
fmt.applyPattern(pattern);
|
||||
|
@ -109,7 +109,7 @@ public class DateTimeGeneratorTest extends TestFmwk {
|
||||
{"EyyyyMMMddhhmmss", "Thu, Oct 14, 1999 6:58:59 AM"}, // (fixed expected result per ticket 6872<-7180)
|
||||
{"hmm", "6:58 AM"},
|
||||
{"hhmm", "6:58 AM"}, // (fixed expected result per ticket 6872<-7180)
|
||||
{"hhmmVVVV", "6:58 AM GMT+00:00"}, // (fixed expected result per ticket 6872<-7180)
|
||||
{"hhmmVVVV", "6:58 AM GMT"}, // (fixed expected result per ticket 6872<-7180)
|
||||
};
|
||||
for (int i = 0; i < tests.length; ++i) {
|
||||
final String testSkeleton = tests[i][0];
|
||||
|
@ -14,6 +14,7 @@ import java.util.Set;
|
||||
|
||||
import com.ibm.icu.lang.UCharacter;
|
||||
import com.ibm.icu.text.SimpleDateFormat;
|
||||
import com.ibm.icu.text.TimeZoneFormat;
|
||||
import com.ibm.icu.util.BasicTimeZone;
|
||||
import com.ibm.icu.util.Calendar;
|
||||
import com.ibm.icu.util.SimpleTimeZone;
|
||||
@ -29,6 +30,7 @@ public class TimeZoneFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
}
|
||||
|
||||
private static final String[] PATTERNS = {"z", "zzzz", "Z", "ZZZZ", "v", "vvvv", "V", "VVVV"};
|
||||
boolean REALLY_VERBOSE_LOG = false;
|
||||
|
||||
/*
|
||||
* Test case for checking if a TimeZone is properly set in the result calendar
|
||||
@ -82,7 +84,12 @@ public class TimeZoneFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
|
||||
// Run the roundtrip test
|
||||
for (int locidx = 0; locidx < LOCALES.length; locidx++) {
|
||||
logln("Locale: " + LOCALES[locidx].toString());
|
||||
|
||||
String localGMTString = TimeZoneFormat.getInstance(LOCALES[locidx]).formatOffsetLocalizedGMT(0);
|
||||
|
||||
for (int patidx = 0; patidx < PATTERNS.length; patidx++) {
|
||||
logln(" pattern: " + PATTERNS[patidx]);
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(PATTERNS[patidx], LOCALES[locidx]);
|
||||
|
||||
for (int tzidx = 0; tzidx < tzids.length; tzidx++) {
|
||||
@ -115,13 +122,13 @@ public class TimeZoneFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
if (PATTERNS[patidx].equals("VVVV")) {
|
||||
// Location: time zone rule must be preserved except
|
||||
// zones not actually associated with a specific location.
|
||||
// Time zones in this category do not have "/" in its ID.
|
||||
String canonicalID = TimeZone.getCanonicalID(tzids[tzidx]);
|
||||
boolean hasNoLocation = TimeZone.getRegion(tzids[tzidx]).equals("001");
|
||||
if (canonicalID != null && !outtz.getID().equals(canonicalID)) {
|
||||
// Canonical ID did not match - check the rules
|
||||
boolean bFailure = false;
|
||||
if ((tz instanceof BasicTimeZone) && (outtz instanceof BasicTimeZone)) {
|
||||
bFailure = !(canonicalID.indexOf('/') == -1)
|
||||
bFailure = !hasNoLocation
|
||||
&& !((BasicTimeZone)outtz).hasEquivalentTransitions(tz, low, high);
|
||||
}
|
||||
if (bFailure) {
|
||||
@ -129,7 +136,7 @@ public class TimeZoneFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
+ ", locale=" + LOCALES[locidx] + ", pattern=" + PATTERNS[patidx]
|
||||
+ ", time=" + DATES[datidx].getTime() + ", str=" + tzstr
|
||||
+ ", outtz=" + outtz.getID());
|
||||
} else {
|
||||
} else if (REALLY_VERBOSE_LOG) {
|
||||
logln("Canonical round trip failed (as expected); tz=" + tzids[tzidx]
|
||||
+ ", locale=" + LOCALES[locidx] + ", pattern=" + PATTERNS[patidx]
|
||||
+ ", time=" + DATES[datidx].getTime() + ", str=" + tzstr
|
||||
@ -145,7 +152,7 @@ public class TimeZoneFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
}
|
||||
}
|
||||
|
||||
if (numDigits >= 3) {
|
||||
if (tzstr.equals(localGMTString) || numDigits >= 3) {
|
||||
// Localized GMT or RFC: total offset (raw + dst) must be preserved.
|
||||
int inOffset = inOffsets[0] + inOffsets[1];
|
||||
int outOffset = outOffsets[0] + outOffsets[1];
|
||||
@ -162,10 +169,12 @@ public class TimeZoneFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
&& tzids[tzidx].startsWith("SystemV/")) {
|
||||
// JDK uses rule SystemV for these zones while
|
||||
// ICU handles these zones as aliases of existing time zones
|
||||
logln("Raw offset round trip failed; tz=" + tzids[tzidx]
|
||||
+ ", locale=" + LOCALES[locidx] + ", pattern=" + PATTERNS[patidx]
|
||||
+ ", time=" + DATES[datidx].getTime() + ", str=" + tzstr
|
||||
+ ", inRawOffset=" + inOffsets[0] + ", outRawOffset=" + outOffsets[0]);
|
||||
if (REALLY_VERBOSE_LOG) {
|
||||
logln("Raw offset round trip failed; tz=" + tzids[tzidx]
|
||||
+ ", locale=" + LOCALES[locidx] + ", pattern=" + PATTERNS[patidx]
|
||||
+ ", time=" + DATES[datidx].getTime() + ", str=" + tzstr
|
||||
+ ", inRawOffset=" + inOffsets[0] + ", outRawOffset=" + outOffsets[0]);
|
||||
}
|
||||
|
||||
} else {
|
||||
errln("Raw offset round trip failed; tz=" + tzids[tzidx]
|
||||
@ -217,7 +226,6 @@ public class TimeZoneFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
final String BASEPATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS";
|
||||
|
||||
ULocale[] LOCALES = null;
|
||||
boolean REALLY_VERBOSE = false;
|
||||
|
||||
// timer for performance analysis
|
||||
long[] times = new long[PATTERNS.length];
|
||||
@ -317,7 +325,7 @@ public class TimeZoneFormatTest extends com.ibm.icu.dev.test.TestFmwk {
|
||||
.append(", diff=").append(restime - testTimes[testidx]);
|
||||
if (expectedRoundTrip[testidx]) {
|
||||
errln("FAIL: " + msg.toString());
|
||||
} else if (REALLY_VERBOSE) {
|
||||
} else if (REALLY_VERBOSE_LOG) {
|
||||
logln(msg.toString());
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (c) 2004-2010, International Business Machines
|
||||
* Copyright (c) 2004-2011, International Business Machines
|
||||
* Corporation and others. All Rights Reserved.
|
||||
*******************************************************************************
|
||||
*
|
||||
@ -13,6 +13,9 @@ import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
|
||||
import com.ibm.icu.impl.DateNumberFormat;
|
||||
import com.ibm.icu.impl.TimeZoneGenericNames;
|
||||
import com.ibm.icu.impl.TimeZoneGenericNames.GenericNameType;
|
||||
import com.ibm.icu.impl.Utility;
|
||||
import com.ibm.icu.text.ChineseDateFormat;
|
||||
import com.ibm.icu.text.ChineseDateFormatSymbols;
|
||||
import com.ibm.icu.text.CurrencyPluralInfo;
|
||||
@ -31,11 +34,15 @@ import com.ibm.icu.text.RuleBasedNumberFormat;
|
||||
import com.ibm.icu.text.SelectFormat;
|
||||
import com.ibm.icu.text.SimpleDateFormat;
|
||||
import com.ibm.icu.text.TimeUnitFormat;
|
||||
import com.ibm.icu.text.TimeZoneFormat;
|
||||
import com.ibm.icu.text.TimeZoneFormat.Style;
|
||||
import com.ibm.icu.text.TimeZoneNames;
|
||||
import com.ibm.icu.util.Calendar;
|
||||
import com.ibm.icu.util.DateInterval;
|
||||
import com.ibm.icu.util.GregorianCalendar;
|
||||
import com.ibm.icu.util.TimeUnit;
|
||||
import com.ibm.icu.util.TimeUnitAmount;
|
||||
import com.ibm.icu.util.TimeZone;
|
||||
import com.ibm.icu.util.ULocale;
|
||||
|
||||
/**
|
||||
@ -2184,6 +2191,119 @@ public class FormatTests
|
||||
}
|
||||
}
|
||||
|
||||
public static class TimeZoneNamesHandler implements SerializableTest.Handler {
|
||||
public Object[] getTestObjects() {
|
||||
return new Object[] {
|
||||
TimeZoneNames.getInstance(ULocale.ENGLISH),
|
||||
TimeZoneNames.getInstance(ULocale.JAPAN)
|
||||
};
|
||||
}
|
||||
public boolean hasSameBehavior(Object a, Object b) {
|
||||
TimeZoneNames tzna = (TimeZoneNames)a;
|
||||
TimeZoneNames tznb = (TimeZoneNames)b;
|
||||
|
||||
final String tzid = "America/Los_Angeles";
|
||||
|
||||
String eca = tzna.getExemplarLocationName(tzid);
|
||||
String ecb = tznb.getExemplarLocationName(tzid);
|
||||
|
||||
if (!eca.equals(ecb)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final String mzID = "America_Pacific";
|
||||
final String region = "US";
|
||||
|
||||
String refza = tzna.getReferenceZoneID(mzID, region);
|
||||
String refzb = tznb.getReferenceZoneID(mzID, region);
|
||||
|
||||
if (!refza.equals(refzb)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TimeZoneGenericNamesHandler implements SerializableTest.Handler {
|
||||
public Object[] getTestObjects() {
|
||||
return new Object[] {
|
||||
TimeZoneGenericNames.getInstance(ULocale.ENGLISH),
|
||||
TimeZoneGenericNames.getInstance(ULocale.JAPAN)
|
||||
};
|
||||
}
|
||||
public boolean hasSameBehavior(Object a, Object b) {
|
||||
TimeZoneGenericNames tzgna = (TimeZoneGenericNames)a;
|
||||
TimeZoneGenericNames tzgnb = (TimeZoneGenericNames)b;
|
||||
|
||||
final String[] TZIDS = {
|
||||
"America/Los_Angeles",
|
||||
"America/Argentina/Buenos_Aires",
|
||||
"Etc/GMT"
|
||||
};
|
||||
|
||||
final long[] DATES = {
|
||||
1277942400000L, // 2010-07-01 00:00:00 GMT
|
||||
1293840000000L, // 2011-01-01 00:00:00 GMT
|
||||
};
|
||||
|
||||
for (String tzid : TZIDS) {
|
||||
TimeZone tz = TimeZone.getTimeZone(tzid);
|
||||
for (GenericNameType nt : GenericNameType.values()) {
|
||||
for (long date : DATES) {
|
||||
String nameA = tzgna.getDisplayName(tz, nt, date);
|
||||
String nameB = tzgnb.getDisplayName(tz, nt, date);
|
||||
if (!Utility.objectEquals(nameA, nameB)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TimeZoneFormatHandler implements SerializableTest.Handler {
|
||||
static final String CUSTOM_GMT_PATTERN = "Offset {0} from UTC";
|
||||
|
||||
public Object[] getTestObjects() {
|
||||
TimeZoneFormat tzfmt = TimeZoneFormat.getInstance(ULocale.ENGLISH).cloneAsThawed();
|
||||
tzfmt.setGMTPattern(CUSTOM_GMT_PATTERN);
|
||||
|
||||
return new Object[] {tzfmt};
|
||||
}
|
||||
public boolean hasSameBehavior(Object a, Object b) {
|
||||
TimeZoneFormat tzfa = (TimeZoneFormat)a;
|
||||
TimeZoneFormat tzfb = (TimeZoneFormat)b;
|
||||
|
||||
if (!tzfa.getGMTPattern().equals(tzfb.getGMTPattern())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int offset = -5 * 60 * 60 * 1000;
|
||||
|
||||
String gmta = tzfa.formatOffsetLocalizedGMT(offset);
|
||||
String gmtb = tzfb.formatOffsetLocalizedGMT(offset);
|
||||
|
||||
if (!gmta.equals(gmtb)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
|
||||
|
||||
String genloca = tzfa.format(Style.GENERIC_LOCATION, tz, now);
|
||||
String genlocb = tzfb.format(Style.GENERIC_LOCATION, tz, now);
|
||||
|
||||
if (!genloca.equals(genlocb)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
// nothing needed...
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* Copyright (C) 1996-2009, International Business Machines Corporation and *
|
||||
* Copyright (C) 1996-2011, International Business Machines Corporation and *
|
||||
* others. All Rights Reserved. *
|
||||
*******************************************************************************
|
||||
*
|
||||
@ -672,6 +672,9 @@ public class SerializableTest extends TestFmwk.TestGroup
|
||||
map.put("com.ibm.icu.text.PluralRules", new FormatTests.PluralRulesHandler());
|
||||
map.put("com.ibm.icu.text.TimeUnitFormat", new FormatTests.TimeUnitFormatHandler());
|
||||
map.put("com.ibm.icu.text.SelectFormat", new FormatTests.SelectFormatHandler());
|
||||
map.put("com.ibm.icu.impl.TimeZoneNamesImpl", new FormatTests.TimeZoneNamesHandler());
|
||||
map.put("com.ibm.icu.text.TimeZoneFormat", new FormatTests.TimeZoneFormatHandler());
|
||||
map.put("com.ibm.icu.impl.TimeZoneGenericNames", new FormatTests.TimeZoneGenericNamesHandler());
|
||||
|
||||
map.put("com.ibm.icu.util.Calendar", new CalendarTests.CalendarHandler());
|
||||
map.put("com.ibm.icu.util.BuddhistCalendar", new CalendarTests.BuddhistCalendarHandler());
|
||||
|
@ -378,8 +378,8 @@ public class TimeZoneTest extends TestFmwk
|
||||
// V and VVVV
|
||||
Boolean.FALSE, new Integer(TimeZone.SHORT_COMMONLY_USED), "PST",
|
||||
Boolean.TRUE, new Integer(TimeZone.SHORT_COMMONLY_USED), "PDT",
|
||||
Boolean.FALSE, new Integer(TimeZone.GENERIC_LOCATION), "United States (Los Angeles)",
|
||||
Boolean.TRUE, new Integer(TimeZone.GENERIC_LOCATION), "United States (Los Angeles)",
|
||||
Boolean.FALSE, new Integer(TimeZone.GENERIC_LOCATION), "United States Time (Los Angeles)",
|
||||
Boolean.TRUE, new Integer(TimeZone.GENERIC_LOCATION), "United States Time (Los Angeles)",
|
||||
};
|
||||
|
||||
for (int i=0; i<DATA.length; i+=3) {
|
||||
@ -476,14 +476,24 @@ public class TimeZoneTest extends TestFmwk
|
||||
ULocale locale = new ULocale(locales[j]);
|
||||
for (int i = 0; i < timezones.length; ++i) {
|
||||
TimeZone tz = TimeZone.getTimeZone(timezones[i]);
|
||||
String displayName0 = tz.getDisplayName(locale); // doesn't work???
|
||||
String displayName0 = tz.getDisplayName(locale);
|
||||
SimpleDateFormat dt = new SimpleDateFormat("vvvv", locale);
|
||||
dt.setTimeZone(tz);
|
||||
String displayName1 = dt.format(now); // date value _does_ matter if we fallback to GMT
|
||||
logln(locale.getDisplayName() + ", " + tz.getID() + ": " + displayName0);
|
||||
if (!displayName1.equals(displayName0)) {
|
||||
errln(locale.getDisplayName() + ", " + tz.getID() +
|
||||
": expected " + displayName1 + " but got: " + displayName0);
|
||||
// This could happen when the date used is in DST,
|
||||
// because TimeZone.getDisplayName(ULocale) may use
|
||||
// localized GMT format for the time zone's standard
|
||||
// time.
|
||||
if (tz.inDaylightTime(now)) {
|
||||
// Try getDisplayName with daylight argument
|
||||
displayName0 = tz.getDisplayName(true, TimeZone.LONG_GENERIC, locale);
|
||||
}
|
||||
if (!displayName1.equals(displayName0)) {
|
||||
errln(locale.getDisplayName() + ", " + tz.getID() +
|
||||
": expected " + displayName1 + " but got: " + displayName0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user