use of android.icu.util.TimeZone in project android_frameworks_opt_telephony by LineageOS.
the class TimeZoneLookupHelper method lookupByCountry.
/**
* Returns information about the time zones used in a country at a given time.
*
* {@code null} can be returned if a problem occurs during lookup, e.g. if the country code is
* unrecognized, if the country is uninhabited, or if there is a problem with the data.
*/
@VisibleForTesting
@Nullable
public CountryResult lookupByCountry(@NonNull String isoCountryCode, long whenMillis) {
CountryTimeZones countryTimeZones = getCountryTimeZones(isoCountryCode);
if (countryTimeZones == null) {
// Unknown country code.
return null;
}
TimeZone countryDefaultZone = countryTimeZones.getDefaultTimeZone();
if (countryDefaultZone == null) {
// This is not expected: the country default should have been validated before.
return null;
}
String debugInfo;
int matchQuality;
if (countryTimeZones.isDefaultTimeZoneBoosted()) {
matchQuality = CountryResult.QUALITY_DEFAULT_BOOSTED;
debugInfo = "Country default is boosted";
} else {
List<TimeZoneMapping> effectiveTimeZoneMappings = countryTimeZones.getEffectiveTimeZoneMappingsAt(whenMillis);
if (effectiveTimeZoneMappings.isEmpty()) {
// This should never happen unless there's been an error loading the data.
// Treat it the same as a low quality answer.
matchQuality = QUALITY_MULTIPLE_ZONES_DIFFERENT_OFFSETS;
debugInfo = "No effective time zones found at whenMillis=" + whenMillis;
} else if (effectiveTimeZoneMappings.size() == 1) {
// The default is the only zone so it's a good candidate.
matchQuality = CountryResult.QUALITY_SINGLE_ZONE;
debugInfo = "One effective time zone found at whenMillis=" + whenMillis;
} else {
boolean countryUsesDifferentOffsets = countryUsesDifferentOffsets(whenMillis, effectiveTimeZoneMappings, countryDefaultZone);
matchQuality = countryUsesDifferentOffsets ? QUALITY_MULTIPLE_ZONES_DIFFERENT_OFFSETS : QUALITY_MULTIPLE_ZONES_SAME_OFFSET;
debugInfo = "countryUsesDifferentOffsets=" + countryUsesDifferentOffsets + " at" + " whenMillis=" + whenMillis;
}
}
return new CountryResult(countryDefaultZone.getID(), matchQuality, debugInfo);
}
use of android.icu.util.TimeZone in project android_packages_apps_Settings by omnirom.
the class RegionZonePicker method getRegionTimeZoneInfo.
/**
* Returns a list of {@link TimeZoneInfo} objects. The returned list will be sorted properly for
* display in the locale.It may be smaller than the input collection, if equivalent IDs are
* passed in.
*
* @param timeZoneIds a list of Olson IDs.
*/
public List<TimeZoneInfo> getRegionTimeZoneInfo(Collection<String> timeZoneIds) {
final TimeZoneInfo.Formatter formatter = new TimeZoneInfo.Formatter(getLocale(), new Date());
final TreeSet<TimeZoneInfo> timeZoneInfos = new TreeSet<>(new TimeZoneInfoComparator(Collator.getInstance(getLocale()), new Date()));
for (final String timeZoneId : timeZoneIds) {
final TimeZone timeZone = TimeZone.getFrozenTimeZone(timeZoneId);
// Skip time zone ICU isn't aware.
if (timeZone.getID().equals(TimeZone.UNKNOWN_ZONE_ID)) {
continue;
}
timeZoneInfos.add(formatter.format(timeZone));
}
return Collections.unmodifiableList(new ArrayList<>(timeZoneInfos));
}
use of android.icu.util.TimeZone 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.TimeZone 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.TimeZone in project j2objc by google.
the class TimeZoneGenericNames method formatGenericNonLocationName.
/**
* 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;
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.getFrozenTimeZone(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;
}
Aggregations