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             final int startPos = pos;
    162             if ((always && zeropad >= 3) || amt > 99) {
    163                 int dig = amt/100;
    164                 formatStr[pos] = (char)(dig + '0');
    165                 pos++;
    166                 amt -= (dig*100);
    167             }
    168             if ((always && zeropad >= 2) || amt > 9 || startPos != pos) {
    169                 int dig = amt/10;
    170                 formatStr[pos] = (char)(dig + '0');
    171                 pos++;
    172                 amt -= (dig*10);
    173             }
    174             formatStr[pos] = (char)(amt + '0');
    175             pos++;
    176             formatStr[pos] = suffix;
    177             pos++;
    178         }
    179         return pos;
    180     }
    181 
    182     private static int formatDurationLocked(long duration, int fieldLen) {
    183         if (sFormatStr.length < fieldLen) {
    184             sFormatStr = new char[fieldLen];
    185         }
    186 
    187         char[] formatStr = sFormatStr;
    188 
    189         if (duration == 0) {
    190             int pos = 0;
    191             fieldLen -= 1;
    192             while (pos < fieldLen) {
    193                 formatStr[pos] = ' ';
    194             }
    195             formatStr[pos] = '0';
    196             return pos+1;
    197         }
    198 
    199         char prefix;
    200         if (duration > 0) {
    201             prefix = '+';
    202         } else {
    203             prefix = '-';
    204             duration = -duration;
    205         }
    206 
    207         int millis = (int)(duration%1000);
    208         int seconds = (int) Math.floor(duration / 1000);
    209         int days = 0, hours = 0, minutes = 0;
    210 
    211         if (seconds > SECONDS_PER_DAY) {
    212             days = seconds / SECONDS_PER_DAY;
    213             seconds -= days * SECONDS_PER_DAY;
    214         }
    215         if (seconds > SECONDS_PER_HOUR) {
    216             hours = seconds / SECONDS_PER_HOUR;
    217             seconds -= hours * SECONDS_PER_HOUR;
    218         }
    219         if (seconds > SECONDS_PER_MINUTE) {
    220             minutes = seconds / SECONDS_PER_MINUTE;
    221             seconds -= minutes * SECONDS_PER_MINUTE;
    222         }
    223 
    224         int pos = 0;
    225 
    226         if (fieldLen != 0) {
    227             int myLen = accumField(days, 1, false, 0);
    228             myLen += accumField(hours, 1, myLen > 0, 2);
    229             myLen += accumField(minutes, 1, myLen > 0, 2);
    230             myLen += accumField(seconds, 1, myLen > 0, 2);
    231             myLen += accumField(millis, 2, true, myLen > 0 ? 3 : 0) + 1;
    232             while (myLen < fieldLen) {
    233                 formatStr[pos] = ' ';
    234                 pos++;
    235                 myLen++;
    236             }
    237         }
    238 
    239         formatStr[pos] = prefix;
    240         pos++;
    241 
    242         int start = pos;
    243         boolean zeropad = fieldLen != 0;
    244         pos = printField(formatStr, days, 'd', pos, false, 0);
    245         pos = printField(formatStr, hours, 'h', pos, pos != start, zeropad ? 2 : 0);
    246         pos = printField(formatStr, minutes, 'm', pos, pos != start, zeropad ? 2 : 0);
    247         pos = printField(formatStr, seconds, 's', pos, pos != start, zeropad ? 2 : 0);
    248         pos = printField(formatStr, millis, 'm', pos, true, (zeropad && pos != start) ? 3 : 0);
    249         formatStr[pos] = 's';
    250         return pos + 1;
    251     }
    252 
    253     /** @hide Just for debugging; not internationalized. */
    254     public static void formatDuration(long duration, StringBuilder builder) {
    255         synchronized (sFormatSync) {
    256             int len = formatDurationLocked(duration, 0);
    257             builder.append(sFormatStr, 0, len);
    258         }
    259     }
    260 
    261     /** @hide Just for debugging; not internationalized. */
    262     public static void formatDuration(long duration, PrintWriter pw, int fieldLen) {
    263         synchronized (sFormatSync) {
    264             int len = formatDurationLocked(duration, fieldLen);
    265             pw.print(new String(sFormatStr, 0, len));
    266         }
    267     }
    268 
    269     /** @hide Just for debugging; not internationalized. */
    270     public static void formatDuration(long duration, PrintWriter pw) {
    271         formatDuration(duration, pw, 0);
    272     }
    273 
    274     /** @hide Just for debugging; not internationalized. */
    275     public static void formatDuration(long time, long now, PrintWriter pw) {
    276         if (time == 0) {
    277             pw.print("--");
    278             return;
    279         }
    280         formatDuration(time-now, pw, 0);
    281     }
    282 }
    283