TimeUtils.java revision 901b3796fd0954cc4b01bf95dbcbd88d87414e84
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 org.apache.harmony.luni.internal.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    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