use of android.icu.util.Output in project j2objc by google.
the class TimeZoneFormat method parse.
/**
* Returns a <code>TimeZone</code> by parsing the time zone string according to
* the parse position, the style and the parse options.
*
* @param text the text contains a time zone string at the position.
* @param style the format style.
* @param pos the position.
* @param options the parse options.
* @param timeType The output argument for receiving the time type (standard/daylight/unknown),
* or specify null if the information is not necessary.
* @return A <code>TimeZone</code>, or null if the input could not be parsed.
* @see Style
* @see #format(Style, TimeZone, long, Output)
*/
public TimeZone parse(Style style, String text, ParsePosition pos, EnumSet<ParseOption> options, Output<TimeType> timeType) {
if (timeType == null) {
timeType = new Output<TimeType>(TimeType.UNKNOWN);
} else {
timeType.value = TimeType.UNKNOWN;
}
int startIdx = pos.getIndex();
int maxPos = text.length();
int offset;
// Styles using localized GMT format as fallback
boolean fallbackLocalizedGMT = (style == Style.SPECIFIC_LONG || style == Style.GENERIC_LONG || style == Style.GENERIC_LOCATION);
boolean fallbackShortLocalizedGMT = (style == Style.SPECIFIC_SHORT || style == Style.GENERIC_SHORT);
// bit flags representing already evaluated styles
int evaluated = 0;
ParsePosition tmpPos = new ParsePosition(startIdx);
// stores successfully parsed offset for later use
int parsedOffset = UNKNOWN_OFFSET;
// stores successfully parsed offset position for later use
int parsedPos = -1;
// Try localized GMT format first if necessary
if (fallbackLocalizedGMT || fallbackShortLocalizedGMT) {
Output<Boolean> hasDigitOffset = new Output<Boolean>(false);
offset = parseOffsetLocalizedGMT(text, tmpPos, fallbackShortLocalizedGMT, hasDigitOffset);
if (tmpPos.getErrorIndex() == -1) {
// 2) The input text was not completely processed
if (tmpPos.getIndex() == maxPos || hasDigitOffset.value) {
pos.setIndex(tmpPos.getIndex());
return getTimeZoneForOffset(offset);
}
parsedOffset = offset;
parsedPos = tmpPos.getIndex();
}
// Note: For now, no distinction between long/short localized GMT format in the parser.
// This might be changed in future.
// evaluated |= (fallbackLocalizedGMT ? Style.LOCALIZED_GMT.flag : Style.LOCALIZED_GMT_SHORT.flag);
evaluated |= (Style.LOCALIZED_GMT.flag | Style.LOCALIZED_GMT_SHORT.flag);
}
boolean parseTZDBAbbrev = (options == null) ? getDefaultParseOptions().contains(ParseOption.TZ_DATABASE_ABBREVIATIONS) : options.contains(ParseOption.TZ_DATABASE_ABBREVIATIONS);
// Try the specified style
switch(style) {
case LOCALIZED_GMT:
{
tmpPos.setIndex(startIdx);
tmpPos.setErrorIndex(-1);
offset = parseOffsetLocalizedGMT(text, tmpPos);
if (tmpPos.getErrorIndex() == -1) {
pos.setIndex(tmpPos.getIndex());
return getTimeZoneForOffset(offset);
}
// Note: For now, no distinction between long/short localized GMT format in the parser.
// This might be changed in future.
evaluated |= Style.LOCALIZED_GMT_SHORT.flag;
break;
}
case LOCALIZED_GMT_SHORT:
{
tmpPos.setIndex(startIdx);
tmpPos.setErrorIndex(-1);
offset = parseOffsetShortLocalizedGMT(text, tmpPos);
if (tmpPos.getErrorIndex() == -1) {
pos.setIndex(tmpPos.getIndex());
return getTimeZoneForOffset(offset);
}
// Note: For now, no distinction between long/short localized GMT format in the parser.
// This might be changed in future.
evaluated |= Style.LOCALIZED_GMT.flag;
break;
}
case ISO_BASIC_SHORT:
case ISO_BASIC_FIXED:
case ISO_BASIC_FULL:
case ISO_EXTENDED_FIXED:
case ISO_EXTENDED_FULL:
{
tmpPos.setIndex(startIdx);
tmpPos.setErrorIndex(-1);
offset = parseOffsetISO8601(text, tmpPos);
if (tmpPos.getErrorIndex() == -1) {
pos.setIndex(tmpPos.getIndex());
return getTimeZoneForOffset(offset);
}
break;
}
case ISO_BASIC_LOCAL_SHORT:
case ISO_BASIC_LOCAL_FIXED:
case ISO_BASIC_LOCAL_FULL:
case ISO_EXTENDED_LOCAL_FIXED:
case ISO_EXTENDED_LOCAL_FULL:
{
tmpPos.setIndex(startIdx);
tmpPos.setErrorIndex(-1);
// Exclude the case of UTC Indicator "Z" here
Output<Boolean> hasDigitOffset = new Output<Boolean>(false);
offset = parseOffsetISO8601(text, tmpPos, false, hasDigitOffset);
if (tmpPos.getErrorIndex() == -1 && hasDigitOffset.value) {
pos.setIndex(tmpPos.getIndex());
return getTimeZoneForOffset(offset);
}
break;
}
case SPECIFIC_LONG:
case SPECIFIC_SHORT:
{
// Specific styles
EnumSet<NameType> nameTypes = null;
if (style == Style.SPECIFIC_LONG) {
nameTypes = EnumSet.of(NameType.LONG_STANDARD, NameType.LONG_DAYLIGHT);
} else {
assert style == Style.SPECIFIC_SHORT;
nameTypes = EnumSet.of(NameType.SHORT_STANDARD, NameType.SHORT_DAYLIGHT);
}
Collection<MatchInfo> specificMatches = _tznames.find(text, startIdx, nameTypes);
if (specificMatches != null) {
MatchInfo specificMatch = null;
for (MatchInfo match : specificMatches) {
if (startIdx + match.matchLength() > parsedPos) {
specificMatch = match;
parsedPos = startIdx + match.matchLength();
}
}
if (specificMatch != null) {
timeType.value = getTimeType(specificMatch.nameType());
pos.setIndex(parsedPos);
return TimeZone.getTimeZone(getTimeZoneID(specificMatch.tzID(), specificMatch.mzID()));
}
}
if (parseTZDBAbbrev && style == Style.SPECIFIC_SHORT) {
assert nameTypes.contains(NameType.SHORT_STANDARD);
assert nameTypes.contains(NameType.SHORT_DAYLIGHT);
Collection<MatchInfo> tzdbNameMatches = getTZDBTimeZoneNames().find(text, startIdx, nameTypes);
if (tzdbNameMatches != null) {
MatchInfo tzdbNameMatch = null;
for (MatchInfo match : tzdbNameMatches) {
if (startIdx + match.matchLength() > parsedPos) {
tzdbNameMatch = match;
parsedPos = startIdx + match.matchLength();
}
}
if (tzdbNameMatch != null) {
timeType.value = getTimeType(tzdbNameMatch.nameType());
pos.setIndex(parsedPos);
return TimeZone.getTimeZone(getTimeZoneID(tzdbNameMatch.tzID(), tzdbNameMatch.mzID()));
}
}
}
break;
}
case GENERIC_LONG:
case GENERIC_SHORT:
case GENERIC_LOCATION:
{
EnumSet<GenericNameType> genericNameTypes = null;
switch(style) {
case GENERIC_LOCATION:
genericNameTypes = EnumSet.of(GenericNameType.LOCATION);
break;
case GENERIC_LONG:
genericNameTypes = EnumSet.of(GenericNameType.LONG, GenericNameType.LOCATION);
break;
case GENERIC_SHORT:
genericNameTypes = EnumSet.of(GenericNameType.SHORT, GenericNameType.LOCATION);
break;
default:
// style cannot be other than above cases
assert false;
break;
}
GenericMatchInfo bestGeneric = getTimeZoneGenericNames().findBestMatch(text, startIdx, genericNameTypes);
if (bestGeneric != null && (startIdx + bestGeneric.matchLength() > parsedPos)) {
timeType.value = bestGeneric.timeType();
pos.setIndex(startIdx + bestGeneric.matchLength());
return TimeZone.getTimeZone(bestGeneric.tzID());
}
break;
}
case ZONE_ID:
{
tmpPos.setIndex(startIdx);
tmpPos.setErrorIndex(-1);
String id = parseZoneID(text, tmpPos);
if (tmpPos.getErrorIndex() == -1) {
pos.setIndex(tmpPos.getIndex());
return TimeZone.getTimeZone(id);
}
break;
}
case ZONE_ID_SHORT:
{
tmpPos.setIndex(startIdx);
tmpPos.setErrorIndex(-1);
String id = parseShortZoneID(text, tmpPos);
if (tmpPos.getErrorIndex() == -1) {
pos.setIndex(tmpPos.getIndex());
return TimeZone.getTimeZone(id);
}
break;
}
case EXEMPLAR_LOCATION:
{
tmpPos.setIndex(startIdx);
tmpPos.setErrorIndex(-1);
String id = parseExemplarLocation(text, tmpPos);
if (tmpPos.getErrorIndex() == -1) {
pos.setIndex(tmpPos.getIndex());
return TimeZone.getTimeZone(id);
}
break;
}
}
evaluated |= style.flag;
if (parsedPos > startIdx) {
// GMT format.
assert parsedOffset != UNKNOWN_OFFSET;
pos.setIndex(parsedPos);
return getTimeZoneForOffset(parsedOffset);
}
// Failed to parse the input text as the time zone format in the specified style.
// Check the longest match among other styles below.
// stores successfully parsed zone ID for later use
String parsedID = null;
// stores successfully parsed time type for later use
TimeType parsedTimeType = TimeType.UNKNOWN;
assert parsedPos < 0;
assert parsedOffset == UNKNOWN_OFFSET;
// ISO 8601
if (parsedPos < maxPos && ((evaluated & ISO_Z_STYLE_FLAG) == 0 || (evaluated & ISO_LOCAL_STYLE_FLAG) == 0)) {
tmpPos.setIndex(startIdx);
tmpPos.setErrorIndex(-1);
Output<Boolean> hasDigitOffset = new Output<Boolean>(false);
offset = parseOffsetISO8601(text, tmpPos, false, hasDigitOffset);
if (tmpPos.getErrorIndex() == -1) {
if (tmpPos.getIndex() == maxPos || hasDigitOffset.value) {
pos.setIndex(tmpPos.getIndex());
return getTimeZoneForOffset(offset);
}
// may collide with other names. In this case, we need to evaluate other names.
if (parsedPos < tmpPos.getIndex()) {
parsedOffset = offset;
parsedID = null;
parsedTimeType = TimeType.UNKNOWN;
parsedPos = tmpPos.getIndex();
// only when "Z" is used
assert parsedPos == startIdx + 1;
}
}
}
// Localized GMT format
if (parsedPos < maxPos && (evaluated & Style.LOCALIZED_GMT.flag) == 0) {
tmpPos.setIndex(startIdx);
tmpPos.setErrorIndex(-1);
Output<Boolean> hasDigitOffset = new Output<Boolean>(false);
offset = parseOffsetLocalizedGMT(text, tmpPos, false, hasDigitOffset);
if (tmpPos.getErrorIndex() == -1) {
if (tmpPos.getIndex() == maxPos || hasDigitOffset.value) {
pos.setIndex(tmpPos.getIndex());
return getTimeZoneForOffset(offset);
}
// Evaluate other names - see the comment earlier in this method.
if (parsedPos < tmpPos.getIndex()) {
parsedOffset = offset;
parsedID = null;
parsedTimeType = TimeType.UNKNOWN;
parsedPos = tmpPos.getIndex();
}
}
}
if (parsedPos < maxPos && (evaluated & Style.LOCALIZED_GMT_SHORT.flag) == 0) {
tmpPos.setIndex(startIdx);
tmpPos.setErrorIndex(-1);
Output<Boolean> hasDigitOffset = new Output<Boolean>(false);
offset = parseOffsetLocalizedGMT(text, tmpPos, true, hasDigitOffset);
if (tmpPos.getErrorIndex() == -1) {
if (tmpPos.getIndex() == maxPos || hasDigitOffset.value) {
pos.setIndex(tmpPos.getIndex());
return getTimeZoneForOffset(offset);
}
// Evaluate other names - see the comment earlier in this method.
if (parsedPos < tmpPos.getIndex()) {
parsedOffset = offset;
parsedID = null;
parsedTimeType = TimeType.UNKNOWN;
parsedPos = tmpPos.getIndex();
}
}
}
// When ParseOption.ALL_STYLES is available, we also try to look all possible display names and IDs.
// For example, when style is GENERIC_LONG, "EST" (SPECIFIC_SHORT) is never
// used for America/New_York. With parseAllStyles true, this code parses "EST"
// as America/New_York.
// Note: Adding all possible names into the trie used by the implementation is quite heavy operation,
// which we want to avoid normally (note that we cache the trie, so this is applicable to the
// first time only as long as the cache does not expire).
boolean parseAllStyles = (options == null) ? getDefaultParseOptions().contains(ParseOption.ALL_STYLES) : options.contains(ParseOption.ALL_STYLES);
if (parseAllStyles) {
// Try all specific names and exemplar location names
if (parsedPos < maxPos) {
Collection<MatchInfo> specificMatches = _tznames.find(text, startIdx, ALL_SIMPLE_NAME_TYPES);
MatchInfo specificMatch = null;
int matchPos = -1;
if (specificMatches != null) {
for (MatchInfo match : specificMatches) {
if (startIdx + match.matchLength() > matchPos) {
specificMatch = match;
matchPos = startIdx + match.matchLength();
}
}
}
if (parsedPos < matchPos) {
parsedPos = matchPos;
parsedID = getTimeZoneID(specificMatch.tzID(), specificMatch.mzID());
parsedTimeType = getTimeType(specificMatch.nameType());
parsedOffset = UNKNOWN_OFFSET;
}
}
if (parseTZDBAbbrev && parsedPos < maxPos && (evaluated & Style.SPECIFIC_SHORT.flag) == 0) {
Collection<MatchInfo> tzdbNameMatches = getTZDBTimeZoneNames().find(text, startIdx, ALL_SIMPLE_NAME_TYPES);
MatchInfo tzdbNameMatch = null;
int matchPos = -1;
if (tzdbNameMatches != null) {
for (MatchInfo match : tzdbNameMatches) {
if (startIdx + match.matchLength() > matchPos) {
tzdbNameMatch = match;
matchPos = startIdx + match.matchLength();
}
}
if (parsedPos < matchPos) {
parsedPos = matchPos;
parsedID = getTimeZoneID(tzdbNameMatch.tzID(), tzdbNameMatch.mzID());
parsedTimeType = getTimeType(tzdbNameMatch.nameType());
parsedOffset = UNKNOWN_OFFSET;
}
}
}
// Try generic names
if (parsedPos < maxPos) {
GenericMatchInfo genericMatch = getTimeZoneGenericNames().findBestMatch(text, startIdx, ALL_GENERIC_NAME_TYPES);
if (genericMatch != null && parsedPos < startIdx + genericMatch.matchLength()) {
parsedPos = startIdx + genericMatch.matchLength();
parsedID = genericMatch.tzID();
parsedTimeType = genericMatch.timeType();
parsedOffset = UNKNOWN_OFFSET;
}
}
// Try time zone ID
if (parsedPos < maxPos && (evaluated & Style.ZONE_ID.flag) == 0) {
tmpPos.setIndex(startIdx);
tmpPos.setErrorIndex(-1);
String id = parseZoneID(text, tmpPos);
if (tmpPos.getErrorIndex() == -1 && parsedPos < tmpPos.getIndex()) {
parsedPos = tmpPos.getIndex();
parsedID = id;
parsedTimeType = TimeType.UNKNOWN;
parsedOffset = UNKNOWN_OFFSET;
}
}
// Try short time zone ID
if (parsedPos < maxPos && (evaluated & Style.ZONE_ID_SHORT.flag) == 0) {
tmpPos.setIndex(startIdx);
tmpPos.setErrorIndex(-1);
String id = parseShortZoneID(text, tmpPos);
if (tmpPos.getErrorIndex() == -1 && parsedPos < tmpPos.getIndex()) {
parsedPos = tmpPos.getIndex();
parsedID = id;
parsedTimeType = TimeType.UNKNOWN;
parsedOffset = UNKNOWN_OFFSET;
}
}
}
if (parsedPos > startIdx) {
// Parsed successfully
TimeZone parsedTZ = null;
if (parsedID != null) {
parsedTZ = TimeZone.getTimeZone(parsedID);
} else {
assert parsedOffset != UNKNOWN_OFFSET;
parsedTZ = getTimeZoneForOffset(parsedOffset);
}
timeType.value = parsedTimeType;
pos.setIndex(parsedPos);
return parsedTZ;
}
pos.setErrorIndex(startIdx);
return null;
}
use of android.icu.util.Output in project j2objc by google.
the class SimpleDateFormat method parse.
/**
* Overrides DateFormat
* @see DateFormat
*/
@Override
public void parse(String text, Calendar cal, ParsePosition parsePos) {
TimeZone backupTZ = null;
Calendar resultCal = null;
if (cal != calendar && !cal.getType().equals(calendar.getType())) {
// Different calendar type
// We use the time/zone from the input calendar, but
// do not use the input calendar for field calculation.
calendar.setTimeInMillis(cal.getTimeInMillis());
backupTZ = calendar.getTimeZone();
calendar.setTimeZone(cal.getTimeZone());
resultCal = cal;
cal = calendar;
}
int pos = parsePos.getIndex();
if (pos < 0) {
parsePos.setErrorIndex(0);
return;
}
int start = pos;
// Hold the day period until everything else is parsed, because we need
// the hour to interpret time correctly.
// Using an one-element array for output parameter.
Output<DayPeriodRules.DayPeriod> dayPeriod = new Output<DayPeriodRules.DayPeriod>(null);
Output<TimeType> tzTimeType = new Output<TimeType>(TimeType.UNKNOWN);
boolean[] ambiguousYear = { false };
// item index for the first numeric field within a contiguous numeric run
int numericFieldStart = -1;
// item length for the first numeric field within a contiguous numeric run
int numericFieldLength = 0;
// start index of numeric text run in the input text
int numericStartPos = 0;
MessageFormat numericLeapMonthFormatter = null;
if (formatData.leapMonthPatterns != null && formatData.leapMonthPatterns.length >= DateFormatSymbols.DT_MONTH_PATTERN_COUNT) {
numericLeapMonthFormatter = new MessageFormat(formatData.leapMonthPatterns[DateFormatSymbols.DT_LEAP_MONTH_PATTERN_NUMERIC], locale);
}
Object[] items = getPatternItems();
int i = 0;
while (i < items.length) {
if (items[i] instanceof PatternItem) {
// Handle pattern field
PatternItem field = (PatternItem) items[i];
if (field.isNumeric) {
// try 4/2/2, 3/2/2, 2/2/2, and finally 1/2/2.
if (numericFieldStart == -1) {
// check if this field is followed by abutting another numeric field
if ((i + 1) < items.length && (items[i + 1] instanceof PatternItem) && ((PatternItem) items[i + 1]).isNumeric) {
// record the first numeric field within a numeric text run
numericFieldStart = i;
numericFieldLength = field.length;
numericStartPos = pos;
}
}
}
if (numericFieldStart != -1) {
// Handle a numeric field within abutting numeric fields
int len = field.length;
if (numericFieldStart == i) {
len = numericFieldLength;
}
// Parse a numeric field
pos = subParse(text, pos, field.type, len, true, false, ambiguousYear, cal, numericLeapMonthFormatter, tzTimeType);
if (pos < 0) {
// If the parse fails anywhere in the numeric run, back up to the
// start of the run and use shorter pattern length for the first
// numeric field.
--numericFieldLength;
if (numericFieldLength == 0) {
// can not make shorter any more
parsePos.setIndex(start);
parsePos.setErrorIndex(pos);
if (backupTZ != null) {
calendar.setTimeZone(backupTZ);
}
return;
}
i = numericFieldStart;
pos = numericStartPos;
continue;
}
} else if (field.type != 'l') {
// (SMALL LETTER L) obsolete pattern char just gets ignored
// Handle a non-numeric field or a non-abutting numeric field
numericFieldStart = -1;
int s = pos;
pos = subParse(text, pos, field.type, field.length, false, true, ambiguousYear, cal, numericLeapMonthFormatter, tzTimeType, dayPeriod);
if (pos < 0) {
if (pos == ISOSpecialEra) {
// era not present, in special cases allow this to continue
pos = s;
if (i + 1 < items.length) {
String patl = null;
// if it will cause a class cast exception to String, we can't use it
try {
patl = (String) items[i + 1];
} catch (ClassCastException cce) {
parsePos.setIndex(start);
parsePos.setErrorIndex(s);
if (backupTZ != null) {
calendar.setTimeZone(backupTZ);
}
return;
}
// get next item in pattern
if (patl == null)
patl = (String) items[i + 1];
int plen = patl.length();
int idx = 0;
// Skip contiguous white spaces.
while (idx < plen) {
char pch = patl.charAt(idx);
if (PatternProps.isWhiteSpace(pch))
idx++;
else
break;
}
// if next item in pattern is all whitespace, skip it
if (idx == plen) {
i++;
}
}
} else {
parsePos.setIndex(start);
parsePos.setErrorIndex(s);
if (backupTZ != null) {
calendar.setTimeZone(backupTZ);
}
return;
}
}
}
} else {
// Handle literal pattern text literal
numericFieldStart = -1;
boolean[] complete = new boolean[1];
pos = matchLiteral(text, pos, items, i, complete);
if (!complete[0]) {
// Set the position of mismatch
parsePos.setIndex(start);
parsePos.setErrorIndex(pos);
if (backupTZ != null) {
calendar.setTimeZone(backupTZ);
}
return;
}
}
++i;
}
// Special hack for trailing "." after non-numeric field.
if (pos < text.length()) {
char extra = text.charAt(pos);
if (extra == '.' && getBooleanAttribute(DateFormat.BooleanAttribute.PARSE_ALLOW_WHITESPACE) && items.length != 0) {
// only do if the last field is not numeric
Object lastItem = items[items.length - 1];
if (lastItem instanceof PatternItem && !((PatternItem) lastItem).isNumeric) {
// skip the extra "."
pos++;
}
}
}
// If dayPeriod is set, use it in conjunction with hour-of-day to determine am/pm.
if (dayPeriod.value != null) {
DayPeriodRules ruleSet = DayPeriodRules.getInstance(getLocale());
if (!cal.isSet(Calendar.HOUR) && !cal.isSet(Calendar.HOUR_OF_DAY)) {
// If hour is not set, set time to the midpoint of current day period, overwriting
// minutes if it's set.
double midPoint = ruleSet.getMidPointForDayPeriod(dayPeriod.value);
// Truncate midPoint toward zero to get the hour.
// Any leftover means it was a half-hour.
int midPointHour = (int) midPoint;
int midPointMinute = (midPoint - midPointHour) > 0 ? 30 : 0;
// No need to set am/pm because hour-of-day is set last therefore takes precedence.
cal.set(Calendar.HOUR_OF_DAY, midPointHour);
cal.set(Calendar.MINUTE, midPointMinute);
} else {
int hourOfDay;
if (cal.isSet(Calendar.HOUR_OF_DAY)) {
// Hour is parsed in 24-hour format.
hourOfDay = cal.get(Calendar.HOUR_OF_DAY);
} else {
// Hour is parsed in 12-hour format.
hourOfDay = cal.get(Calendar.HOUR);
// so 0 unambiguously means a 24-hour time from above.
if (hourOfDay == 0) {
hourOfDay = 12;
}
}
assert (0 <= hourOfDay && hourOfDay <= 23);
// If hour-of-day is 0 or 13 thru 23 then input time in unambiguously in 24-hour format.
if (hourOfDay == 0 || (13 <= hourOfDay && hourOfDay <= 23)) {
// Make hour-of-day take precedence over (hour + am/pm) by setting it again.
cal.set(Calendar.HOUR_OF_DAY, hourOfDay);
} else {
// - cal.get(MINUTE) will return 0 if MINUTE is unset, which works.
if (hourOfDay == 12) {
hourOfDay = 0;
}
double currentHour = hourOfDay + cal.get(Calendar.MINUTE) / 60.0;
double midPointHour = ruleSet.getMidPointForDayPeriod(dayPeriod.value);
double hoursAheadMidPoint = currentHour - midPointHour;
// Assume current time is in the AM.
if (-6 <= hoursAheadMidPoint && hoursAheadMidPoint < 6) {
// Assumption holds; set time as such.
cal.set(Calendar.AM_PM, 0);
} else {
cal.set(Calendar.AM_PM, 1);
}
}
}
}
// At this point the fields of Calendar have been set. Calendar
// will fill in default values for missing fields when the time
// is computed.
parsePos.setIndex(pos);
// the year correctly to start with in other cases -- see subParse().
try {
TimeType tztype = tzTimeType.value;
if (ambiguousYear[0] || tztype != TimeType.UNKNOWN) {
// We need a copy of the fields, and we need to avoid triggering a call to
// complete(), which will recalculate the fields. Since we can't access
// the fields[] array in Calendar, we clone the entire object. This will
// stop working if Calendar.clone() is ever rewritten to call complete().
Calendar copy;
if (ambiguousYear[0]) {
// the two-digit year == the default start year
copy = (Calendar) cal.clone();
Date parsedDate = copy.getTime();
if (parsedDate.before(getDefaultCenturyStart())) {
// We can't use add here because that does a complete() first.
cal.set(Calendar.YEAR, getDefaultCenturyStartYear() + 100);
}
}
if (tztype != TimeType.UNKNOWN) {
copy = (Calendar) cal.clone();
TimeZone tz = copy.getTimeZone();
BasicTimeZone btz = null;
if (tz instanceof BasicTimeZone) {
btz = (BasicTimeZone) tz;
}
// Get local millis
copy.set(Calendar.ZONE_OFFSET, 0);
copy.set(Calendar.DST_OFFSET, 0);
long localMillis = copy.getTimeInMillis();
// Make sure parsed time zone type (Standard or Daylight)
// matches the rule used by the parsed time zone.
int[] offsets = new int[2];
if (btz != null) {
if (tztype == TimeType.STANDARD) {
btz.getOffsetFromLocal(localMillis, BasicTimeZone.LOCAL_STD, BasicTimeZone.LOCAL_STD, offsets);
} else {
btz.getOffsetFromLocal(localMillis, BasicTimeZone.LOCAL_DST, BasicTimeZone.LOCAL_DST, offsets);
}
} else {
// No good way to resolve ambiguous time at transition,
// but following code work in most case.
tz.getOffset(localMillis, true, offsets);
if (tztype == TimeType.STANDARD && offsets[1] != 0 || tztype == TimeType.DAYLIGHT && offsets[1] == 0) {
// Roll back one day and try it again.
// Note: This code assumes 1. timezone transition only happens
// once within 24 hours at max
// 2. the difference of local offsets at the transition is
// less than 24 hours.
tz.getOffset(localMillis - (24 * 60 * 60 * 1000), true, offsets);
}
}
// Now, compare the results with parsed type, either standard or
// daylight saving time
int resolvedSavings = offsets[1];
if (tztype == TimeType.STANDARD) {
if (offsets[1] != 0) {
// Override DST_OFFSET = 0 in the result calendar
resolvedSavings = 0;
}
} else {
// tztype == TZTYPE_DST
if (offsets[1] == 0) {
if (btz != null) {
long time = localMillis + offsets[0];
// We use the nearest daylight saving time rule.
TimeZoneTransition beforeTrs, afterTrs;
long beforeT = time, afterT = time;
int beforeSav = 0, afterSav = 0;
// Search for DST rule before or on the time
while (true) {
beforeTrs = btz.getPreviousTransition(beforeT, true);
if (beforeTrs == null) {
break;
}
beforeT = beforeTrs.getTime() - 1;
beforeSav = beforeTrs.getFrom().getDSTSavings();
if (beforeSav != 0) {
break;
}
}
// Search for DST rule after the time
while (true) {
afterTrs = btz.getNextTransition(afterT, false);
if (afterTrs == null) {
break;
}
afterT = afterTrs.getTime();
afterSav = afterTrs.getTo().getDSTSavings();
if (afterSav != 0) {
break;
}
}
if (beforeTrs != null && afterTrs != null) {
if (time - beforeT > afterT - time) {
resolvedSavings = afterSav;
} else {
resolvedSavings = beforeSav;
}
} else if (beforeTrs != null && beforeSav != 0) {
resolvedSavings = beforeSav;
} else if (afterTrs != null && afterSav != 0) {
resolvedSavings = afterSav;
} else {
resolvedSavings = btz.getDSTSavings();
}
} else {
resolvedSavings = tz.getDSTSavings();
}
if (resolvedSavings == 0) {
// Final fallback
resolvedSavings = millisPerHour;
}
}
}
cal.set(Calendar.ZONE_OFFSET, offsets[0]);
cal.set(Calendar.DST_OFFSET, resolvedSavings);
}
}
}// if any fields are out of range, e.g., MONTH == 17.
catch (IllegalArgumentException e) {
parsePos.setErrorIndex(pos);
parsePos.setIndex(start);
if (backupTZ != null) {
calendar.setTimeZone(backupTZ);
}
return;
}
// instead of the input calendar
if (resultCal != null) {
resultCal.setTimeZone(cal.getTimeZone());
resultCal.setTimeInMillis(cal.getTimeInMillis());
}
// Restore the original time zone if required
if (backupTZ != null) {
calendar.setTimeZone(backupTZ);
}
}
use of android.icu.util.Output in project j2objc by google.
the class CompactDecimalFormat method format.
/* INTERNALS */
private StringBuffer format(double number, Currency curr, StringBuffer toAppendTo, FieldPosition pos) {
if (curr != null && style == CompactStyle.LONG) {
throw new UnsupportedOperationException("CompactDecimalFormat does not support LONG style for currency.");
}
// Compute the scaled amount, prefix, and suffix appropriate for the number's magnitude.
Output<Unit> currencyUnit = new Output<Unit>();
Amount amount = toAmount(number, curr, currencyUnit);
Unit unit = amount.getUnit();
// Note that currencyUnit is a remnant. In almost all cases, it will be null.
StringBuffer prefix = new StringBuffer();
StringBuffer suffix = new StringBuffer();
if (currencyUnit.value != null) {
currencyUnit.value.writePrefix(prefix);
}
unit.writePrefix(prefix);
unit.writeSuffix(suffix);
if (currencyUnit.value != null) {
currencyUnit.value.writeSuffix(suffix);
}
if (curr == null) {
// Prevent locking when not formatting a currency number.
toAppendTo.append(escape(prefix.toString()));
super.format(amount.getQty(), toAppendTo, pos);
toAppendTo.append(escape(suffix.toString()));
} else {
// This has to be synchronized since this information is held in the state of the DecimalFormat object.
synchronized (this) {
String originalPattern = this.toPattern();
Currency originalCurrency = this.getCurrency();
StringBuffer newPattern = new StringBuffer();
// Write prefixes and suffixes to the pattern. Note that we have to apply it to both halves of a
// positive/negative format (separated by ';')
int semicolonPos = originalPattern.indexOf(';');
newPattern.append(prefix);
if (semicolonPos != -1) {
newPattern.append(originalPattern, 0, semicolonPos);
newPattern.append(suffix);
newPattern.append(';');
newPattern.append(prefix);
}
newPattern.append(originalPattern, semicolonPos + 1, originalPattern.length());
newPattern.append(suffix);
// Overwrite the pattern and currency.
setCurrency(curr);
applyPattern(newPattern.toString());
// Actually perform the formatting.
super.format(amount.getQty(), toAppendTo, pos);
// Reset the pattern and currency.
setCurrency(originalCurrency);
applyPattern(originalPattern);
}
}
return toAppendTo;
}
use of android.icu.util.Output in project j2objc by google.
the class PluralRulesTest method TestSerialization.
@Test
public void TestSerialization() {
Output<Integer> size = new Output<Integer>();
int max = 0;
for (ULocale locale : PluralRules.getAvailableULocales()) {
PluralRules item = PluralRules.forLocale(locale);
PluralRules item2 = serializeAndDeserialize(item, size);
logln(locale + "\tsize:\t" + size.value);
max = Math.max(max, size.value);
if (!assertEquals(locale + "\tPlural rules before and after serialization", item, item2)) {
// for debugging
PluralRules item3 = serializeAndDeserialize(item, size);
item.equals(item3);
}
}
logln("max \tsize:\t" + max);
}
use of android.icu.util.Output in project j2objc by google.
the class PluralRulesTest method TestKeywords.
@Test
public void TestKeywords() {
Set<String> possibleKeywords = new LinkedHashSet(Arrays.asList("zero", "one", "two", "few", "many", "other"));
Object[][][] tests = { // format is locale, explicits, then triples of keyword, status, unique value.
{ { "en", null }, { "one", KeywordStatus.UNIQUE, 1.0d }, { "other", KeywordStatus.UNBOUNDED, null } }, { { "pl", null }, { "one", KeywordStatus.UNIQUE, 1.0d }, { "few", KeywordStatus.UNBOUNDED, null }, { "many", KeywordStatus.UNBOUNDED, null }, // note that it is
{ "other", KeywordStatus.SUPPRESSED, null, KeywordStatus.UNBOUNDED, null } // suppressed in
// INTEGER but not
// DECIMAL
}, { // check that 1 is suppressed
{ "en", new HashSet<Double>(Arrays.asList(1.0d)) }, { "one", KeywordStatus.SUPPRESSED, null }, { "other", KeywordStatus.UNBOUNDED, null } } };
Output<Double> uniqueValue = new Output<Double>();
for (Object[][] test : tests) {
ULocale locale = new ULocale((String) test[0][0]);
// NumberType numberType = (NumberType) test[1];
Set<Double> explicits = (Set<Double>) test[0][1];
PluralRules pluralRules = factory.forLocale(locale);
LinkedHashSet<String> remaining = new LinkedHashSet(possibleKeywords);
for (int i = 1; i < test.length; ++i) {
Object[] row = test[i];
String keyword = (String) row[0];
KeywordStatus statusExpected = (KeywordStatus) row[1];
Double uniqueExpected = (Double) row[2];
remaining.remove(keyword);
KeywordStatus status = pluralRules.getKeywordStatus(keyword, 0, explicits, uniqueValue);
assertEquals(getAssertMessage("Unique Value", locale, pluralRules, keyword), uniqueExpected, uniqueValue.value);
assertEquals(getAssertMessage("Keyword Status", locale, pluralRules, keyword), statusExpected, status);
if (row.length > 3) {
statusExpected = (KeywordStatus) row[3];
uniqueExpected = (Double) row[4];
status = pluralRules.getKeywordStatus(keyword, 0, explicits, uniqueValue, SampleType.DECIMAL);
assertEquals(getAssertMessage("Unique Value - decimal", locale, pluralRules, keyword), uniqueExpected, uniqueValue.value);
assertEquals(getAssertMessage("Keyword Status - decimal", locale, pluralRules, keyword), statusExpected, status);
}
}
for (String keyword : remaining) {
KeywordStatus status = pluralRules.getKeywordStatus(keyword, 0, null, uniqueValue);
assertEquals("Invalid keyword " + keyword, status, KeywordStatus.INVALID);
assertNull("Invalid keyword " + keyword, uniqueValue.value);
}
}
}
Aggregations