Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2006 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 android.util;
     18 
     19 import android.content.res.Resources;
     20 import android.content.res.XmlResourceParser;
     21 
     22 import org.apache.harmony.luni.internal.util.ZoneInfoDB;
     23 import org.xmlpull.v1.XmlPullParser;
     24 import org.xmlpull.v1.XmlPullParserException;
     25 
     26 import java.io.IOException;
     27 import java.io.PrintWriter;
     28 import java.util.TimeZone;
     29 import java.util.Date;
     30 
     31 import com.android.internal.util.XmlUtils;
     32 
     33 /**
     34  * A class containing utility methods related to time zones.
     35  */
     36 public class TimeUtils {
     37     private static final String TAG = "TimeUtils";
     38 
     39     /**
     40      * Tries to return a time zone that would have had the specified offset
     41      * and DST value at the specified moment in the specified country.
     42      * Returns null if no suitable zone could be found.
     43      */
     44     public static TimeZone getTimeZone(int offset, boolean dst, long when, String country) {
     45         if (country == null) {
     46             return null;
     47         }
     48 
     49         TimeZone best = null;
     50 
     51         Resources r = Resources.getSystem();
     52         XmlResourceParser parser = r.getXml(com.android.internal.R.xml.time_zones_by_country);
     53         Date d = new Date(when);
     54 
     55         TimeZone current = TimeZone.getDefault();
     56         String currentName = current.getID();
     57         int currentOffset = current.getOffset(when);
     58         boolean currentDst = current.inDaylightTime(d);
     59 
     60         try {
     61             XmlUtils.beginDocument(parser, "timezones");
     62 
     63             while (true) {
     64                 XmlUtils.nextElement(parser);
     65 
     66                 String element = parser.getName();
     67                 if (element == null || !(element.equals("timezone"))) {
     68                     break;
     69                 }
     70 
     71                 String code = parser.getAttributeValue(null, "code");
     72 
     73                 if (country.equals(code)) {
     74                     if (parser.next() == XmlPullParser.TEXT) {
     75                         String maybe = parser.getText();
     76 
     77                         // If the current time zone is from the right country
     78                         // and meets the other known properties, keep it
     79                         // instead of changing to another one.
     80 
     81                         if (maybe.equals(currentName)) {
     82                             if (currentOffset == offset && currentDst == dst) {
     83                                 return current;
     84                             }
     85                         }
     86 
     87                         // Otherwise, take the first zone from the right
     88                         // country that has the correct current offset and DST.
     89                         // (Keep iterating instead of returning in case we
     90                         // haven't encountered the current time zone yet.)
     91 
     92                         if (best == null) {
     93                             TimeZone tz = TimeZone.getTimeZone(maybe);
     94 
     95                             if (tz.getOffset(when) == offset &&
     96                                 tz.inDaylightTime(d) == dst) {
     97                                 best = tz;
     98                             }
     99                         }
    100                     }
    101                 }
    102             }
    103         } catch (XmlPullParserException e) {
    104             Log.e(TAG, "Got exception while getting preferred time zone.", e);
    105         } catch (IOException e) {
    106             Log.e(TAG, "Got exception while getting preferred time zone.", e);
    107         } finally {
    108             parser.close();
    109         }
    110 
    111         return best;
    112     }
    113 
    114     /**
    115      * Returns a String indicating the version of the time zone database currently
    116      * in use.  The format of the string is dependent on the underlying time zone
    117      * database implementation, but will typically contain the year in which the database
    118      * was updated plus a letter from a to z indicating changes made within that year.
    119      *
    120      * <p>Time zone database updates should be expected to occur periodically due to
    121      * political and legal changes that cannot be anticipated in advance.  Therefore,
    122      * when computing the UTC time for a future event, applications should be aware that
    123      * the results may differ following a time zone database update.  This method allows
    124      * applications to detect that a database change has occurred, and to recalculate any
    125      * cached times accordingly.
    126      *
    127      * <p>The time zone database may be assumed to change only when the device runtime
    128      * is restarted.  Therefore, it is not necessary to re-query the database version
    129      * during the lifetime of an activity.
    130      */
    131     public static String getTimeZoneDatabaseVersion() {
    132         return ZoneInfoDB.getVersion();
    133     }
    134 
    135     /** @hide Field length that can hold 999 days of time */
    136     public static final int HUNDRED_DAY_FIELD_LEN = 19;
    137 
    138     private static final int SECONDS_PER_MINUTE = 60;
    139     private static final int SECONDS_PER_HOUR = 60 * 60;
    140     private static final int SECONDS_PER_DAY = 24 * 60 * 60;
    141 
    142     private static final Object sFormatSync = new Object();
    143     private static char[] sFormatStr = new char[HUNDRED_DAY_FIELD_LEN+5];
    144 
    145     static private int accumField(int amt, int suffix, boolean always, int zeropad) {
    146         if (amt > 99 || (always && zeropad >= 3)) {
    147             return 3+suffix;
    148         }
    149         if (amt > 9 || (always && zeropad >= 2)) {
    150             return 2+suffix;
    151         }
    152         if (always || amt > 0) {
    153             return 1+suffix;
    154         }
    155         return 0;
    156     }
    157 
    158     static private int printField(char[] formatStr, int amt, char suffix, int pos,
    159             boolean always, int zeropad) {
    160         if (always || amt > 0) {
    161             if ((always && zeropad >= 3) || amt > 99) {
    162                 int dig = amt/100;
    163                 formatStr[pos] = (char)(dig + '0');
    164                 pos++;
    165                 always = true;
    166                 amt -= (dig*100);
    167             }
    168             if ((always && zeropad >= 2) || amt > 9) {
    169                 int dig = amt/10;
    170                 formatStr[pos] = (char)(dig + '0');
    171                 pos++;
    172                 always = true;
    173                 amt -= (dig*10);
    174             }
    175             formatStr[pos] = (char)(amt + '0');
    176             pos++;
    177             formatStr[pos] = suffix;
    178             pos++;
    179         }
    180         return pos;
    181     }
    182 
    183     private static int formatDurationLocked(long duration, int fieldLen) {
    184         if (sFormatStr.length < fieldLen) {
    185             sFormatStr = new char[fieldLen];
    186         }
    187 
    188         char[] formatStr = sFormatStr;
    189 
    190         if (duration == 0) {
    191             int pos = 0;
    192             fieldLen -= 1;
    193             while (pos < fieldLen) {
    194                 formatStr[pos] = ' ';
    195             }
    196             formatStr[pos] = '0';
    197             return pos+1;
    198         }
    199 
    200         char prefix;
    201         if (duration > 0) {
    202             prefix = '+';
    203         } else {
    204             prefix = '-';
    205             duration = -duration;
    206         }
    207 
    208         int millis = (int)(duration%1000);
    209         int seconds = (int) Math.floor(duration / 1000);
    210         int days = 0, hours = 0, minutes = 0;
    211 
    212         if (seconds > SECONDS_PER_DAY) {
    213             days = seconds / SECONDS_PER_DAY;
    214             seconds -= days * SECONDS_PER_DAY;
    215         }
    216         if (seconds > SECONDS_PER_HOUR) {
    217             hours = seconds / SECONDS_PER_HOUR;
    218             seconds -= hours * SECONDS_PER_HOUR;
    219         }
    220         if (seconds > SECONDS_PER_MINUTE) {
    221             minutes = seconds / SECONDS_PER_MINUTE;
    222             seconds -= minutes * SECONDS_PER_MINUTE;
    223         }
    224 
    225         int pos = 0;
    226 
    227         if (fieldLen != 0) {
    228             int myLen = accumField(days, 1, false, 0);
    229             myLen += accumField(hours, 1, myLen > 0, 2);
    230             myLen += accumField(minutes, 1, myLen > 0, 2);
    231             myLen += accumField(seconds, 1, myLen > 0, 2);
    232             myLen += accumField(millis, 2, true, myLen > 0 ? 3 : 0) + 1;
    233             while (myLen < fieldLen) {
    234                 formatStr[pos] = ' ';
    235                 pos++;
    236                 myLen++;
    237             }
    238         }
    239 
    240         formatStr[pos] = prefix;
    241         pos++;
    242 
    243         int start = pos;
    244         boolean zeropad = fieldLen != 0;
    245         pos = printField(formatStr, days, 'd', pos, false, 0);
    246         pos = printField(formatStr, hours, 'h', pos, pos != start, zeropad ? 2 : 0);
    247         pos = printField(formatStr, minutes, 'm', pos, pos != start, zeropad ? 2 : 0);
    248         pos = printField(formatStr, seconds, 's', pos, pos != start, zeropad ? 2 : 0);
    249         pos = printField(formatStr, millis, 'm', pos, true, (zeropad && pos != start) ? 3 : 0);
    250         formatStr[pos] = 's';
    251         return pos + 1;
    252     }
    253 
    254     /** @hide Just for debugging; not internationalized. */
    255     public static void formatDuration(long duration, StringBuilder builder) {
    256         synchronized (sFormatSync) {
    257             int len = formatDurationLocked(duration, 0);
    258             builder.append(sFormatStr, 0, len);
    259         }
    260     }
    261 
    262     /** @hide Just for debugging; not internationalized. */
    263     public static void formatDuration(long duration, PrintWriter pw, int fieldLen) {
    264         synchronized (sFormatSync) {
    265             int len = formatDurationLocked(duration, fieldLen);
    266             pw.print(new String(sFormatStr, 0, len));
    267         }
    268     }
    269 
    270     /** @hide Just for debugging; not internationalized. */
    271     public static void formatDuration(long duration, PrintWriter pw) {
    272         formatDuration(duration, pw, 0);
    273     }
    274 
    275     /** @hide Just for debugging; not internationalized. */
    276     public static void formatDuration(long time, long now, PrintWriter pw) {
    277         if (time == 0) {
    278             pw.print("--");
    279             return;
    280         }
    281         formatDuration(time-now, pw, 0);
    282     }
    283 }
    284