Skip to content

Instantly share code, notes, and snippets.

@kleisauke
Last active October 23, 2016 23:08
Show Gist options
  • Save kleisauke/5a74ab95269f5df3ae37 to your computer and use it in GitHub Desktop.
Save kleisauke/5a74ab95269f5df3ae37 to your computer and use it in GitHub Desktop.
Fixed TimeZoneInfo.java for Android M. (Because in SDK 23 TimeZone.mTransition is now long[])
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.timezonepicker;
import android.content.Context;
import android.os.Build;
import android.text.Spannable;
import android.text.Spannable.Factory;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.util.SparseArray;
import java.lang.reflect.Field;
import java.text.DateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Formatter;
import java.util.Locale;
import java.util.TimeZone;
public class TimeZoneInfo implements Comparable<TimeZoneInfo> {
private static final int GMT_TEXT_COLOR = TimeZonePickerUtils.GMT_TEXT_COLOR;
private static final int DST_SYMBOL_COLOR = TimeZonePickerUtils.DST_SYMBOL_COLOR;
private static final char SEPARATOR = ',';
private static final String TAG = null;
public static int NUM_OF_TRANSITIONS = 6;
public static long time = System.currentTimeMillis() / 1000;
public static boolean is24HourFormat;
private static final Factory mSpannableFactory = Spannable.Factory.getInstance();
TimeZone mTz;
public String mTzId;
int mRawoffset;
public long[] mTransitions; // may have trailing 0's.
public String mCountry;
public int groupId;
public String mDisplayName;
private Time recycledTime = new Time();
private static StringBuilder mSB = new StringBuilder(50);
private static Formatter mFormatter = new Formatter(mSB, Locale.getDefault());
public TimeZoneInfo(TimeZone tz, String country) {
mTz = tz;
mTzId = tz.getID();
mCountry = country;
mRawoffset = tz.getRawOffset();
try {
mTransitions = getTransitions(tz, time);
} catch (NoSuchFieldException ignored) {
} catch (IllegalAccessException ignored) {
ignored.printStackTrace();
}
}
SparseArray<String> mLocalTimeCache = new SparseArray<String>();
long mLocalTimeCacheReferenceTime = 0;
static private long mGmtDisplayNameUpdateTime;
static private SparseArray<CharSequence> mGmtDisplayNameCache =
new SparseArray<CharSequence>();
public String getLocalTime(long referenceTime) {
recycledTime.timezone = TimeZone.getDefault().getID();
recycledTime.set(referenceTime);
int currYearDay = recycledTime.year * 366 + recycledTime.yearDay;
recycledTime.timezone = mTzId;
recycledTime.set(referenceTime);
String localTimeStr = null;
int hourMinute = recycledTime.hour * 60 +
recycledTime.minute;
if (mLocalTimeCacheReferenceTime != referenceTime) {
mLocalTimeCacheReferenceTime = referenceTime;
mLocalTimeCache.clear();
} else {
localTimeStr = mLocalTimeCache.get(hourMinute);
}
if (localTimeStr == null) {
String format = "%I:%M %p";
if (currYearDay != (recycledTime.year * 366 + recycledTime.yearDay)) {
if (is24HourFormat) {
format = "%b %d %H:%M";
} else {
format = "%b %d %I:%M %p";
}
} else if (is24HourFormat) {
format = "%H:%M";
}
// format = "%Y-%m-%d %H:%M";
localTimeStr = recycledTime.format(format);
mLocalTimeCache.put(hourMinute, localTimeStr);
}
return localTimeStr;
}
public int getLocalHr(long referenceTime) {
recycledTime.timezone = mTzId;
recycledTime.set(referenceTime);
return recycledTime.hour;
}
public int getNowOffsetMillis() {
return mTz.getOffset(System.currentTimeMillis());
}
/*
* The method is synchronized because there's one mSB, which is used by
* mFormatter, per instance. If there are multiple callers for
* getGmtDisplayName, the output may be mangled.
*/
public synchronized CharSequence getGmtDisplayName(Context context) {
// TODO Note: The local time is shown in current time (current GMT
// offset) which may be different from the time specified by
// mTimeMillis
final long nowMinute = System.currentTimeMillis() / DateUtils.MINUTE_IN_MILLIS;
final long now = nowMinute * DateUtils.MINUTE_IN_MILLIS;
final int gmtOffset = mTz.getOffset(now);
int cacheKey;
boolean hasFutureDST = mTz.useDaylightTime();
if (hasFutureDST) {
cacheKey = (int) (gmtOffset + 36 * DateUtils.HOUR_IN_MILLIS);
} else {
cacheKey = (int) (gmtOffset - 36 * DateUtils.HOUR_IN_MILLIS);
}
CharSequence displayName = null;
if (mGmtDisplayNameUpdateTime != nowMinute) {
mGmtDisplayNameUpdateTime = nowMinute;
mGmtDisplayNameCache.clear();
} else {
displayName = mGmtDisplayNameCache.get(cacheKey);
}
if (displayName == null) {
mSB.setLength(0);
int flags = DateUtils.FORMAT_ABBREV_ALL;
flags |= DateUtils.FORMAT_SHOW_TIME;
if (TimeZoneInfo.is24HourFormat) {
flags |= DateUtils.FORMAT_24HOUR;
}
// mFormatter writes to mSB
DateUtils.formatDateRange(context, mFormatter, now, now, flags, mTzId);
mSB.append(" ");
int gmtStart = mSB.length();
TimeZonePickerUtils.appendGmtOffset(mSB, gmtOffset);
int gmtEnd = mSB.length();
int symbolStart = 0;
int symbolEnd = 0;
if (hasFutureDST) {
mSB.append(' ');
symbolStart = mSB.length();
mSB.append(TimeZonePickerUtils.getDstSymbol()); // Sun symbol
symbolEnd = mSB.length();
}
// Set the gray colors.
Spannable spannableText = mSpannableFactory.newSpannable(mSB);
spannableText.setSpan(new ForegroundColorSpan(GMT_TEXT_COLOR),
gmtStart, gmtEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
if (hasFutureDST) {
spannableText.setSpan(new ForegroundColorSpan(DST_SYMBOL_COLOR),
symbolStart, symbolEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
displayName = spannableText;
mGmtDisplayNameCache.put(cacheKey, displayName);
}
return displayName;
}
private static long[] getTransitions(TimeZone tz, long time) throws IllegalAccessException, NoSuchFieldException {
Class<?> zoneInfoClass = tz.getClass();
Field mTransitionsField = zoneInfoClass.getDeclaredField("mTransitions");
mTransitionsField.setAccessible(true);
long[] objTransitions;
long[] transitions = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
objTransitions = (long[]) mTransitionsField.get(tz);
} else {
objTransitions = copyFromIntArray((int[]) mTransitionsField.get(tz));
}
if (objTransitions.length != 0) {
transitions = new long[NUM_OF_TRANSITIONS];
int numOfTransitions = 0;
for (int i = 0; i < objTransitions.length; ++i) {
if (objTransitions[i] < time) {
continue;
}
transitions[numOfTransitions++] = objTransitions[i];
if (numOfTransitions == NUM_OF_TRANSITIONS) {
break;
}
}
}
return transitions;
}
public static long[] copyFromIntArray(int[] source) {
if (source == null) {
return new long[0];
}
long[] dest = new long[source.length];
for (int i = 0; i < source.length; i++) {
dest[i] = source[i];
}
return dest;
}
public boolean hasSameRules(TimeZoneInfo tzi) {
// this.mTz.hasSameRules(tzi.mTz)
return this.mRawoffset == tzi.mRawoffset
&& Arrays.equals(this.mTransitions, tzi.mTransitions);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
final String country = this.mCountry;
final TimeZone tz = this.mTz;
sb.append(mTzId);
sb.append(SEPARATOR);
sb.append(tz.getDisplayName(false /* daylightTime */, TimeZone.LONG));
sb.append(SEPARATOR);
sb.append(tz.getDisplayName(false /* daylightTime */, TimeZone.SHORT));
sb.append(SEPARATOR);
if (tz.useDaylightTime()) {
sb.append(tz.getDisplayName(true, TimeZone.LONG));
sb.append(SEPARATOR);
sb.append(tz.getDisplayName(true, TimeZone.SHORT));
} else {
sb.append(SEPARATOR);
}
sb.append(SEPARATOR);
sb.append(tz.getRawOffset() / 3600000f);
sb.append(SEPARATOR);
sb.append(tz.getDSTSavings() / 3600000f);
sb.append(SEPARATOR);
sb.append(country);
sb.append(SEPARATOR);
// 1-1-2013 noon GMT
sb.append(getLocalTime(1357041600000L));
sb.append(SEPARATOR);
// 3-15-2013 noon GMT
sb.append(getLocalTime(1363348800000L));
sb.append(SEPARATOR);
// 7-1-2013 noon GMT
sb.append(getLocalTime(1372680000000L));
sb.append(SEPARATOR);
// 11-01-2013 noon GMT
sb.append(getLocalTime(1383307200000L));
sb.append(SEPARATOR);
// if (this.mTransitions != null && this.mTransitions.length != 0) {
// sb.append('"');
// DateFormat df = new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss Z",
// Locale.US);
// df.setTimeZone(tz);
// DateFormat weekdayFormat = new SimpleDateFormat("EEEE", Locale.US);
// weekdayFormat.setTimeZone(tz);
// Formatter f = new Formatter(sb);
// for (int i = 0; i < this.mTransitions.length; ++i) {
// if (this.mTransitions[i] < time) {
// continue;
// }
//
// String fromTime = formatTime(df, this.mTransitions[i] - 1);
// String toTime = formatTime(df, this.mTransitions[i]);
// f.format("%s -> %s (%d)", fromTime, toTime, this.mTransitions[i]);
//
// String weekday = weekdayFormat.format(new Date(1000L *
// this.mTransitions[i]));
// if (!weekday.equals("Sunday")) {
// f.format(" -- %s", weekday);
// }
// sb.append("##");
// }
// sb.append('"');
// }
// sb.append(SEPARATOR);
sb.append('\n');
return sb.toString();
}
private static String formatTime(DateFormat df, int s) {
long ms = s * 1000L;
return df.format(new Date(ms));
}
/*
* Returns a negative integer if this instance is less than the other; a
* positive integer if this instance is greater than the other; 0 if this
* instance has the same order as the other.
*/
@Override
public int compareTo(TimeZoneInfo other) {
if (this.getNowOffsetMillis() != other.getNowOffsetMillis()) {
return (other.getNowOffsetMillis() < this.getNowOffsetMillis()) ? -1 : 1;
}
// By country
if (this.mCountry == null) {
if (other.mCountry != null) {
return 1;
}
}
if (other.mCountry == null) {
return -1;
} else {
int diff = this.mCountry.compareTo(other.mCountry);
if (diff != 0) {
return diff;
}
}
if (Arrays.equals(this.mTransitions, other.mTransitions)) {
Log.e(TAG, "Not expected to be comparing tz with the same country, same offset," +
" same dst, same transitions:\n" + this.toString() + "\n" + other.toString());
}
// Finally diff by display name
if (mDisplayName != null && other.mDisplayName != null) {
return this.mDisplayName.compareTo(other.mDisplayName);
}
return this.mTz.getDisplayName(Locale.getDefault()).compareTo(
other.mTz.getDisplayName(Locale.getDefault()));
}
}
@androcraze-git
Copy link

When I try and apply this fix, I get a build fail with AOSP 6.0.1_r72. Do I need to do something in addition to applying the fix? This is my output:

target thumb C++: libbccCore <= frameworks/compile/libbcc/lib/Core/BCCContext.cpp
target thumb C++: libbccCore <= frameworks/compile/libbcc/lib/Core/BCCContextImpl.cpp
target thumb C++: libbccCore <= frameworks/compile/libbcc/lib/Core/Compiler.cpp
target thumb C++: libbccCore <= frameworks/compile/libbcc/lib/Core/Script.cpp
target thumb C++: libbccCore <= frameworks/compile/libbcc/lib/Core/Source.cpp
bc lib: libclcore.bc (out/target/product/hammerhead/obj/SHARED_LIBRARIES/libclcore.bc_intermediates/libclcore.bc)
WARNING: Linking two modules of different data layouts: 'out/target/product/hammerhead/obj/SHARED_LIBRARIES/libclcore.bc_intermediates/ll32/allocation.bc' is 'e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:64:128-a0:0:64-n32-S64' whereas 'llvm-link' is 'e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64'

WARNING: Linking two modules of different data layouts: 'out/target/product/hammerhead/obj/SHARED_LIBRARIES/libclcore.bc_intermediates/ll32/math.bc' is 'e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:64:128-a0:0:64-n32-S64' whereas 'llvm-link' is 'e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64'

bc lib: libclcore_debug.bc (out/target/product/hammerhead/obj/SHARED_LIBRARIES/libclcore_debug.bc_intermediates/libclcore_debug.bc)
WARNING: Linking two modules of different data layouts: 'out/target/product/hammerhead/obj/SHARED_LIBRARIES/libclcore_debug.bc_intermediates/ll32/allocation.bc' is 'e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:64:128-a0:0:64-n32-S64' whereas 'llvm-link' is 'e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64'

WARNING: Linking two modules of different data layouts: 'out/target/product/hammerhead/obj/SHARED_LIBRARIES/libclcore_debug.bc_intermediates/ll32/math.bc' is 'e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:64:128-a0:0:64-n32-S64' whereas 'llvm-link' is 'e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64'

bc lib: libclcore_neon.bc (out/target/product/hammerhead/obj/SHARED_LIBRARIES/libclcore_neon.bc_intermediates/libclcore_neon.bc)
target R.java/Manifest.java: BluetoothMidiService (out/target/common/obj/APPS/BluetoothMidiService_intermediates/src/R.stamp)
WARNING: Linking two modules of different data layouts: 'out/target/product/hammerhead/obj/SHARED_LIBRARIES/libclcore_neon.bc_intermediates/ll32/allocation.bc' is 'e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:64:128-a0:0:64-n32-S64' whereas 'llvm-link' is 'e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64'

WARNING: Linking two modules of different data layouts: 'out/target/product/hammerhead/obj/SHARED_LIBRARIES/libclcore_neon.bc_intermediates/ll32/math.bc' is 'e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:64:128-a0:0:64-n32-S64' whereas 'llvm-link' is 'e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64'

target R.java/Manifest.java: Calendar (out/target/common/obj/APPS/Calendar_intermediates/src/R.stamp)
Building with Jack: out/target/common/obj/JAVA_LIBRARIES/android-common_intermediates/classes.jack
WARNING: Linking two modules of different data layouts: 'out/target/product/hammerhead/obj/SHARED_LIBRARIES/libclcore_neon.bc_intermediates/arch/neon.bc' is 'e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:64:128-a0:0:64-n32-S64' whereas 'llvm-link' is 'e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64'

Launching background server java -Dfile.encoding=UTF-8 -Xms2560m -XX:+TieredCompilation -jar out/host/linux-x86/framework/jack-launcher.jar -cp out/host/linux-x86/framework/jack.jar com.android.jack.server.JackSimpleServer
target R.java/Manifest.java: libchips (out/target/common/obj/JAVA_LIBRARIES/libchips_intermediates/src/R.stamp)
Building with Jack: out/target/common/obj/JAVA_LIBRARIES/colorpicker_intermediates/classes.jack
warning: string 'done' has no default translation.
Warning: AndroidManifest.xml already defines versionCode (in http://schemas.android.com/apk/res/android); using existing value in manifest.
Warning: AndroidManifest.xml already defines minSdkVersion (in http://schemas.android.com/apk/res/android); using existing value in manifest.
Warning: AndroidManifest.xml already defines targetSdkVersion (in http://schemas.android.com/apk/res/android); using existing value in manifest.
Building with Jack: out/target/common/obj/JAVA_LIBRARIES/android-opt-timezonepicker_intermediates/classes.jack
packages/apps/Calendar/res/values-ar-rXB/arrays.xml:0: warning: Resource file packages/apps/Calendar/res/values-ar-rXB/arrays.xml is skipped as pseudolocalization was done automatically.
packages/apps/Calendar/res/values-ar-rXB/strings.xml:0: warning: Resource file packages/apps/Calendar/res/values-ar-rXB/strings.xml is skipped as pseudolocalization was done automatically.
warning: string 'done' has no default translation.
Warning: AndroidManifest.xml already defines minSdkVersion (in http://schemas.android.com/apk/res/android); using existing value in manifest.
Warning: AndroidManifest.xml already defines targetSdkVersion (in http://schemas.android.com/apk/res/android); using existing value in manifest.
ERROR: /media/speedy/6952930d-d22f-4af1-af0d-650723b7deb3/devel/AOSP/frameworks/opt/timezonepicker/src/com/android/timezonepicker/TimeZoneInfo.java:205: M cannot be resolved or is not a field
make: *** [out/target/common/obj/JAVA_LIBRARIES/android-opt-timezonepicker_intermediates/classes.jack] Error 41
make: *** Waiting for unfinished jobs....

make failed to build some targets (01:39:25 (hh:mm:ss))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment