TimeUtils.java revision 08065b9f09ead8895d97b2971622af8c179e1768
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
17package android.util;
18
19import android.content.res.Resources;
20import android.content.res.XmlResourceParser;
21
22import libcore.util.ZoneInfoDB;
23import org.xmlpull.v1.XmlPullParser;
24import org.xmlpull.v1.XmlPullParserException;
25
26import java.io.IOException;
27import java.io.PrintWriter;
28import java.util.TimeZone;
29import java.util.Date;
30
31import com.android.internal.util.XmlUtils;
32
33/**
34 * A class containing utility methods related to time zones.
35 */
36public class TimeUtils {
37    /** @hide */ public TimeUtils() {}
38    private static final String TAG = "TimeUtils";
39
40    /**
41     * Tries to return a time zone that would have had the specified offset
42     * and DST value at the specified moment in the specified country.
43     * Returns null if no suitable zone could be found.
44     */
45    public static TimeZone getTimeZone(int offset, boolean dst, long when, String country) {
46        if (country == null) {
47            return null;
48        }
49
50        TimeZone best = null;
51
52        Resources r = Resources.getSystem();
53        XmlResourceParser parser = r.getXml(com.android.internal.R.xml.time_zones_by_country);
54        Date d = new Date(when);
55
56        TimeZone current = TimeZone.getDefault();
57        String currentName = current.getID();
58        int currentOffset = current.getOffset(when);
59        boolean currentDst = current.inDaylightTime(d);
60
61        try {
62            XmlUtils.beginDocument(parser, "timezones");
63
64            while (true) {
65                XmlUtils.nextElement(parser);
66
67                String element = parser.getName();
68                if (element == null || !(element.equals("timezone"))) {
69                    break;
70                }
71
72                String code = parser.getAttributeValue(null, "code");
73
74                if (country.equals(code)) {
75                    if (parser.next() == XmlPullParser.TEXT) {
76                        String maybe = parser.getText();
77
78                        // If the current time zone is from the right country
79                        // and meets the other known properties, keep it
80                        // instead of changing to another one.
81
82                        if (maybe.equals(currentName)) {
83                            if (currentOffset == offset && currentDst == dst) {
84                                return current;
85                            }
86                        }
87
88                        // Otherwise, take the first zone from the right
89                        // country that has the correct current offset and DST.
90                        // (Keep iterating instead of returning in case we
91                        // haven't encountered the current time zone yet.)
92
93                        if (best == null) {
94                            TimeZone tz = TimeZone.getTimeZone(maybe);
95
96                            if (tz.getOffset(when) == offset &&
97                                tz.inDaylightTime(d) == dst) {
98                                best = tz;
99                            }
100                        }
101                    }
102                }
103            }
104        } catch (XmlPullParserException e) {
105            Log.e(TAG, "Got exception while getting preferred time zone.", e);
106        } catch (IOException e) {
107            Log.e(TAG, "Got exception while getting preferred time zone.", e);
108        } finally {
109            parser.close();
110        }
111
112        return best;
113    }
114
115    /**
116     * Returns a String indicating the version of the time zone database currently
117     * in use.  The format of the string is dependent on the underlying time zone
118     * database implementation, but will typically contain the year in which the database
119     * was updated plus a letter from a to z indicating changes made within that year.
120     *
121     * <p>Time zone database updates should be expected to occur periodically due to
122     * political and legal changes that cannot be anticipated in advance.  Therefore,
123     * when computing the UTC time for a future event, applications should be aware that
124     * the results may differ following a time zone database update.  This method allows
125     * applications to detect that a database change has occurred, and to recalculate any
126     * cached times accordingly.
127     *
128     * <p>The time zone database may be assumed to change only when the device runtime
129     * is restarted.  Therefore, it is not necessary to re-query the database version
130     * during the lifetime of an activity.
131     */
132    public static String getTimeZoneDatabaseVersion() {
133        return ZoneInfoDB.getVersion();
134    }
135
136    /** @hide Field length that can hold 999 days of time */
137    public static final int HUNDRED_DAY_FIELD_LEN = 19;
138
139    private static final int SECONDS_PER_MINUTE = 60;
140    private static final int SECONDS_PER_HOUR = 60 * 60;
141    private static final int SECONDS_PER_DAY = 24 * 60 * 60;
142
143    private static final Object sFormatSync = new Object();
144    private static char[] sFormatStr = new char[HUNDRED_DAY_FIELD_LEN+5];
145
146    static private int accumField(int amt, int suffix, boolean always, int zeropad) {
147        if (amt > 99 || (always && zeropad >= 3)) {
148            return 3+suffix;
149        }
150        if (amt > 9 || (always && zeropad >= 2)) {
151            return 2+suffix;
152        }
153        if (always || amt > 0) {
154            return 1+suffix;
155        }
156        return 0;
157    }
158
159    static private int printField(char[] formatStr, int amt, char suffix, int pos,
160            boolean always, int zeropad) {
161        if (always || amt > 0) {
162            final int startPos = pos;
163            if ((always && zeropad >= 3) || amt > 99) {
164                int dig = amt/100;
165                formatStr[pos] = (char)(dig + '0');
166                pos++;
167                amt -= (dig*100);
168            }
169            if ((always && zeropad >= 2) || amt > 9 || startPos != pos) {
170                int dig = amt/10;
171                formatStr[pos] = (char)(dig + '0');
172                pos++;
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