Home | History | Annotate | Download | only in timezonepicker
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.timezonepicker;
     18 
     19 import android.content.Context;
     20 import android.text.Spannable;
     21 import android.text.Spannable.Factory;
     22 import android.text.format.DateUtils;
     23 import android.text.format.Time;
     24 import android.text.style.ForegroundColorSpan;
     25 import android.util.Log;
     26 import android.util.SparseArray;
     27 
     28 import java.lang.reflect.Field;
     29 import java.text.DateFormat;
     30 import java.util.Arrays;
     31 import java.util.Date;
     32 import java.util.Formatter;
     33 import java.util.Locale;
     34 import java.util.TimeZone;
     35 
     36 public class TimeZoneInfo implements Comparable<TimeZoneInfo> {
     37     private static final int GMT_TEXT_COLOR = TimeZonePickerUtils.GMT_TEXT_COLOR;
     38     private static final int DST_SYMBOL_COLOR = TimeZonePickerUtils.DST_SYMBOL_COLOR;
     39     private static final char SEPARATOR = ',';
     40     private static final String TAG = null;
     41     public static int NUM_OF_TRANSITIONS = 6;
     42     public static long time = System.currentTimeMillis() / 1000;
     43     public static boolean is24HourFormat;
     44     private static final Factory mSpannableFactory = Spannable.Factory.getInstance();
     45 
     46     TimeZone mTz;
     47     public String mTzId;
     48     int mRawoffset;
     49     public String mCountry;
     50     public int groupId;
     51     public String mDisplayName;
     52     private Time recycledTime = new Time();
     53     private static StringBuilder mSB = new StringBuilder(50);
     54     private static Formatter mFormatter = new Formatter(mSB, Locale.getDefault());
     55 
     56     public TimeZoneInfo(TimeZone tz, String country) {
     57         mTz = tz;
     58         mTzId = tz.getID();
     59         mCountry = country;
     60         mRawoffset = tz.getRawOffset();
     61     }
     62 
     63     SparseArray<String> mLocalTimeCache = new SparseArray<String>();
     64     long mLocalTimeCacheReferenceTime = 0;
     65     static private long mGmtDisplayNameUpdateTime;
     66     static private SparseArray<CharSequence> mGmtDisplayNameCache =
     67             new SparseArray<CharSequence>();
     68 
     69     public String getLocalTime(long referenceTime) {
     70         recycledTime.timezone = TimeZone.getDefault().getID();
     71         recycledTime.set(referenceTime);
     72 
     73         int currYearDay = recycledTime.year * 366 + recycledTime.yearDay;
     74 
     75         recycledTime.timezone = mTzId;
     76         recycledTime.set(referenceTime);
     77 
     78         String localTimeStr = null;
     79 
     80         int hourMinute = recycledTime.hour * 60 +
     81                 recycledTime.minute;
     82 
     83         if (mLocalTimeCacheReferenceTime != referenceTime) {
     84             mLocalTimeCacheReferenceTime = referenceTime;
     85             mLocalTimeCache.clear();
     86         } else {
     87             localTimeStr = mLocalTimeCache.get(hourMinute);
     88         }
     89 
     90         if (localTimeStr == null) {
     91             String format = "%I:%M %p";
     92             if (currYearDay != (recycledTime.year * 366 + recycledTime.yearDay)) {
     93                 if (is24HourFormat) {
     94                     format = "%b %d %H:%M";
     95                 } else {
     96                     format = "%b %d %I:%M %p";
     97                 }
     98             } else if (is24HourFormat) {
     99                 format = "%H:%M";
    100             }
    101 
    102             // format = "%Y-%m-%d %H:%M";
    103             localTimeStr = recycledTime.format(format);
    104             mLocalTimeCache.put(hourMinute, localTimeStr);
    105         }
    106 
    107         return localTimeStr;
    108     }
    109 
    110     public int getLocalHr(long referenceTime) {
    111         recycledTime.timezone = mTzId;
    112         recycledTime.set(referenceTime);
    113         return recycledTime.hour;
    114     }
    115 
    116     public int getNowOffsetMillis() {
    117         return mTz.getOffset(System.currentTimeMillis());
    118     }
    119 
    120     /*
    121      * The method is synchronized because there's one mSB, which is used by
    122      * mFormatter, per instance. If there are multiple callers for
    123      * getGmtDisplayName, the output may be mangled.
    124      */
    125     public synchronized CharSequence getGmtDisplayName(Context context) {
    126         // TODO Note: The local time is shown in current time (current GMT
    127         // offset) which may be different from the time specified by
    128         // mTimeMillis
    129 
    130         final long nowMinute = System.currentTimeMillis() / DateUtils.MINUTE_IN_MILLIS;
    131         final long now = nowMinute * DateUtils.MINUTE_IN_MILLIS;
    132         final int gmtOffset = mTz.getOffset(now);
    133         int cacheKey;
    134 
    135         boolean hasFutureDST = mTz.useDaylightTime();
    136         if (hasFutureDST) {
    137             cacheKey = (int) (gmtOffset + 36 * DateUtils.HOUR_IN_MILLIS);
    138         } else {
    139             cacheKey = (int) (gmtOffset - 36 * DateUtils.HOUR_IN_MILLIS);
    140         }
    141 
    142         CharSequence displayName = null;
    143         if (mGmtDisplayNameUpdateTime != nowMinute) {
    144             mGmtDisplayNameUpdateTime = nowMinute;
    145             mGmtDisplayNameCache.clear();
    146         } else {
    147             displayName = mGmtDisplayNameCache.get(cacheKey);
    148         }
    149 
    150         if (displayName == null) {
    151             mSB.setLength(0);
    152             int flags = DateUtils.FORMAT_ABBREV_ALL;
    153             flags |= DateUtils.FORMAT_SHOW_TIME;
    154             if (TimeZoneInfo.is24HourFormat) {
    155                 flags |= DateUtils.FORMAT_24HOUR;
    156             }
    157 
    158             // mFormatter writes to mSB
    159             DateUtils.formatDateRange(context, mFormatter, now, now, flags, mTzId);
    160             mSB.append("  ");
    161             int gmtStart = mSB.length();
    162             TimeZonePickerUtils.appendGmtOffset(mSB, gmtOffset);
    163             int gmtEnd = mSB.length();
    164 
    165             int symbolStart = 0;
    166             int symbolEnd = 0;
    167             if (hasFutureDST) {
    168                 mSB.append(' ');
    169                 symbolStart = mSB.length();
    170                 mSB.append(TimeZonePickerUtils.getDstSymbol()); // Sun symbol
    171                 symbolEnd = mSB.length();
    172             }
    173 
    174             // Set the gray colors.
    175             Spannable spannableText = mSpannableFactory.newSpannable(mSB);
    176             spannableText.setSpan(new ForegroundColorSpan(GMT_TEXT_COLOR),
    177                     gmtStart, gmtEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    178 
    179             if (hasFutureDST) {
    180                 spannableText.setSpan(new ForegroundColorSpan(DST_SYMBOL_COLOR),
    181                         symbolStart, symbolEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    182             }
    183             displayName = spannableText;
    184             mGmtDisplayNameCache.put(cacheKey, displayName);
    185         }
    186         return displayName;
    187     }
    188 
    189     public boolean hasSameRules(TimeZoneInfo tzi) {
    190         return this.mTz.hasSameRules(tzi.mTz);
    191     }
    192 
    193     @Override
    194     public String toString() {
    195         StringBuilder sb = new StringBuilder();
    196 
    197         final String country = this.mCountry;
    198         final TimeZone tz = this.mTz;
    199 
    200         sb.append(mTzId);
    201         sb.append(SEPARATOR);
    202         sb.append(tz.getDisplayName(false /* daylightTime */, TimeZone.LONG));
    203         sb.append(SEPARATOR);
    204         sb.append(tz.getDisplayName(false /* daylightTime */, TimeZone.SHORT));
    205         sb.append(SEPARATOR);
    206         if (tz.useDaylightTime()) {
    207             sb.append(tz.getDisplayName(true, TimeZone.LONG));
    208             sb.append(SEPARATOR);
    209             sb.append(tz.getDisplayName(true, TimeZone.SHORT));
    210         } else {
    211             sb.append(SEPARATOR);
    212         }
    213         sb.append(SEPARATOR);
    214         sb.append(tz.getRawOffset() / 3600000f);
    215         sb.append(SEPARATOR);
    216         sb.append(tz.getDSTSavings() / 3600000f);
    217         sb.append(SEPARATOR);
    218         sb.append(country);
    219         sb.append(SEPARATOR);
    220 
    221         // 1-1-2013 noon GMT
    222         sb.append(getLocalTime(1357041600000L));
    223         sb.append(SEPARATOR);
    224 
    225         // 3-15-2013 noon GMT
    226         sb.append(getLocalTime(1363348800000L));
    227         sb.append(SEPARATOR);
    228 
    229         // 7-1-2013 noon GMT
    230         sb.append(getLocalTime(1372680000000L));
    231         sb.append(SEPARATOR);
    232 
    233         // 11-01-2013 noon GMT
    234         sb.append(getLocalTime(1383307200000L));
    235         sb.append(SEPARATOR);
    236 
    237         sb.append('\n');
    238         return sb.toString();
    239     }
    240 
    241     private static String formatTime(DateFormat df, int s) {
    242         long ms = s * 1000L;
    243         return df.format(new Date(ms));
    244     }
    245 
    246     /*
    247      * Returns a negative integer if this instance is less than the other; a
    248      * positive integer if this instance is greater than the other; 0 if this
    249      * instance has the same order as the other.
    250      */
    251     @Override
    252     public int compareTo(TimeZoneInfo other) {
    253         if (this.getNowOffsetMillis() != other.getNowOffsetMillis()) {
    254             return (other.getNowOffsetMillis() < this.getNowOffsetMillis()) ? -1 : 1;
    255         }
    256 
    257         // By country
    258         if (this.mCountry == null) {
    259             if (other.mCountry != null) {
    260                 return 1;
    261             }
    262         }
    263 
    264         if (other.mCountry == null) {
    265             return -1;
    266         } else {
    267             int diff = this.mCountry.compareTo(other.mCountry);
    268 
    269             if (diff != 0) {
    270                 return diff;
    271             }
    272         }
    273 
    274         // Finally diff by display name
    275         if (mDisplayName != null && other.mDisplayName != null)
    276             return this.mDisplayName.compareTo(other.mDisplayName);
    277 
    278         return this.mTz.getDisplayName(Locale.getDefault()).compareTo(
    279                 other.mTz.getDisplayName(Locale.getDefault()));
    280 
    281     }
    282 }
    283