19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2006 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.util;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.Resources;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.XmlResourceParser;
219630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brownimport android.os.SystemClock;
22eaaf396169abdbd213565cacb37dc0c9c391b365Jeff Sharkeyimport android.text.format.DateUtils;
23eaaf396169abdbd213565cacb37dc0c9c391b365Jeff Sharkey
24eaaf396169abdbd213565cacb37dc0c9c391b365Jeff Sharkeyimport com.android.internal.util.XmlUtils;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport org.xmlpull.v1.XmlPullParser;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport org.xmlpull.v1.XmlPullParserException;
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.IOException;
301ebccf531d1049853b3b0630035434619682c016Dianne Hackbornimport java.io.PrintWriter;
31a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Savilleimport java.util.ArrayList;
3226e1a02ba3183aba2ba262c6f60602e10dd792e3Wink Savilleimport java.util.Calendar;
33a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Savilleimport java.util.Collection;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.Date;
35eaaf396169abdbd213565cacb37dc0c9c391b365Jeff Sharkeyimport java.util.TimeZone;
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
37eaaf396169abdbd213565cacb37dc0c9c391b365Jeff Sharkeyimport libcore.util.ZoneInfoDB;
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * A class containing utility methods related to time zones.
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class TimeUtils {
43a0f8bc51aff98c2e23e73069e447f63397471a0aJesse Wilson    /** @hide */ public TimeUtils() {}
44a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville    private static final boolean DBG = false;
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String TAG = "TimeUtils";
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
47a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville    /** Cached results of getTineZones */
48a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville    private static final Object sLastLockObj = new Object();
49a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville    private static ArrayList<TimeZone> sLastZones = null;
50a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville    private static String sLastCountry = null;
51a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville
52a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville    /** Cached results of getTimeZonesWithUniqueOffsets */
53a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville    private static final Object sLastUniqueLockObj = new Object();
54a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville    private static ArrayList<TimeZone> sLastUniqueZoneOffsets = null;
55a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville    private static String sLastUniqueCountry = null;
56a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville
57a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Tries to return a time zone that would have had the specified offset
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * and DST value at the specified moment in the specified country.
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns null if no suitable zone could be found.
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static TimeZone getTimeZone(int offset, boolean dst, long when, String country) {
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        TimeZone best = null;
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Resources r = Resources.getSystem();
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        XmlResourceParser parser = r.getXml(com.android.internal.R.xml.time_zones_by_country);
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Date d = new Date(when);
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        TimeZone current = TimeZone.getDefault();
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String currentName = current.getID();
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int currentOffset = current.getOffset(when);
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean currentDst = current.inDaylightTime(d);
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
75a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville        for (TimeZone tz : getTimeZones(country)) {
76a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            // If the current time zone is from the right country
77a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            // and meets the other known properties, keep it
78a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            // instead of changing to another one.
79a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville
80a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            if (tz.getID().equals(currentName)) {
81a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                if (currentOffset == offset && currentDst == dst) {
82a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                    return current;
83a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                }
84a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            }
85a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville
86a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            // Otherwise, take the first zone from the right
87a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            // country that has the correct current offset and DST.
88a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            // (Keep iterating instead of returning in case we
89a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            // haven't encountered the current time zone yet.)
90a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville
91a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            if (best == null) {
92a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                if (tz.getOffset(when) == offset &&
93a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                    tz.inDaylightTime(d) == dst) {
94a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                    best = tz;
95a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                }
96a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            }
97a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville        }
98a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville
99a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville        return best;
100a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville    }
101a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville
102a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville    /**
103a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville     * Return list of unique time zones for the country. Do not modify
104a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville     *
105a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville     * @param country to find
106a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville     * @return list of unique time zones, maybe empty but never null. Do not modify.
107a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville     * @hide
108a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville     */
109a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville    public static ArrayList<TimeZone> getTimeZonesWithUniqueOffsets(String country) {
110a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville        synchronized(sLastUniqueLockObj) {
111a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            if ((country != null) && country.equals(sLastUniqueCountry)) {
112a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                if (DBG) {
113a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                    Log.d(TAG, "getTimeZonesWithUniqueOffsets(" +
114a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                            country + "): return cached version");
115a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                }
116a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                return sLastUniqueZoneOffsets;
117a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            }
118a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville        }
119a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville
120a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville        Collection<TimeZone> zones = getTimeZones(country);
121a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville        ArrayList<TimeZone> uniqueTimeZones = new ArrayList<TimeZone>();
122a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville        for (TimeZone zone : zones) {
123a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            // See if we already have this offset,
124a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            // Using slow but space efficient and these are small.
125a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            boolean found = false;
126a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            for (int i = 0; i < uniqueTimeZones.size(); i++) {
127a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                if (uniqueTimeZones.get(i).getRawOffset() == zone.getRawOffset()) {
128a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                    found = true;
129a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                    break;
130a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                }
131a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            }
132a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            if (found == false) {
133a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                if (DBG) {
134a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                    Log.d(TAG, "getTimeZonesWithUniqueOffsets: add unique offset=" +
135a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                            zone.getRawOffset() + " zone.getID=" + zone.getID());
136a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                }
137a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                uniqueTimeZones.add(zone);
138a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            }
139a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville        }
140a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville
141a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville        synchronized(sLastUniqueLockObj) {
142a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            // Cache the last result
143a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            sLastUniqueZoneOffsets = uniqueTimeZones;
144a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            sLastUniqueCountry = country;
145a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville
146a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            return sLastUniqueZoneOffsets;
147a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville        }
148a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville    }
149a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville
150a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville    /**
151a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville     * Returns the time zones for the country, which is the code
152a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville     * attribute of the timezone element in time_zones_by_country.xml. Do not modify.
153a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville     *
154a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville     * @param country is a two character country code.
155a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville     * @return TimeZone list, maybe empty but never null. Do not modify.
156a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville     * @hide
157a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville     */
158a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville    public static ArrayList<TimeZone> getTimeZones(String country) {
159a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville        synchronized (sLastLockObj) {
160a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            if ((country != null) && country.equals(sLastCountry)) {
161a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                if (DBG) Log.d(TAG, "getTimeZones(" + country + "): return cached version");
162a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                return sLastZones;
163a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            }
164a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville        }
165a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville
166a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville        ArrayList<TimeZone> tzs = new ArrayList<TimeZone>();
167a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville
168a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville        if (country == null) {
169a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            if (DBG) Log.d(TAG, "getTimeZones(null): return empty list");
170a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            return tzs;
171a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville        }
172a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville
173a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville        Resources r = Resources.getSystem();
174a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville        XmlResourceParser parser = r.getXml(com.android.internal.R.xml.time_zones_by_country);
175a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            XmlUtils.beginDocument(parser, "timezones");
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            while (true) {
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                XmlUtils.nextElement(parser);
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                String element = parser.getName();
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (element == null || !(element.equals("timezone"))) {
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    break;
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                String code = parser.getAttributeValue(null, "code");
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (country.equals(code)) {
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (parser.next() == XmlPullParser.TEXT) {
191a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                        String zoneIdString = parser.getText();
192a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                        TimeZone tz = TimeZone.getTimeZone(zoneIdString);
193a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                        if (tz.getID().startsWith("GMT") == false) {
194a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                            // tz.getID doesn't start not "GMT" so its valid
195a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                            tzs.add(tz);
196a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                            if (DBG) {
197a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                                Log.d(TAG, "getTimeZone('" + country + "'): found tz.getID=="
198a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville                                    + ((tz != null) ? tz.getID() : "<no tz>"));
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            }
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (XmlPullParserException e) {
205a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            Log.e(TAG, "Got xml parser exception getTimeZone('" + country + "'): e=", e);
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
207a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            Log.e(TAG, "Got IO exception getTimeZone('" + country + "'): e=", e);
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } finally {
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            parser.close();
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
212a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville        synchronized(sLastLockObj) {
213a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            // Cache the last result;
214a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            sLastZones = tzs;
215a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            sLastCountry = country;
216a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville            return sLastZones;
217a27421a306c49fbe9b3823b30f7ab1cd58b28854Wink Saville        }
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns a String indicating the version of the time zone database currently
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * in use.  The format of the string is dependent on the underlying time zone
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * database implementation, but will typically contain the year in which the database
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * was updated plus a letter from a to z indicating changes made within that year.
225a0f8bc51aff98c2e23e73069e447f63397471a0aJesse Wilson     *
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <p>Time zone database updates should be expected to occur periodically due to
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * political and legal changes that cannot be anticipated in advance.  Therefore,
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * when computing the UTC time for a future event, applications should be aware that
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the results may differ following a time zone database update.  This method allows
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * applications to detect that a database change has occurred, and to recalculate any
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * cached times accordingly.
232a0f8bc51aff98c2e23e73069e447f63397471a0aJesse Wilson     *
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <p>The time zone database may be assumed to change only when the device runtime
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * is restarted.  Therefore, it is not necessary to re-query the database version
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * during the lifetime of an activity.
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static String getTimeZoneDatabaseVersion() {
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return ZoneInfoDB.getVersion();
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2401ebccf531d1049853b3b0630035434619682c016Dianne Hackborn
241b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn    /** @hide Field length that can hold 999 days of time */
242b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn    public static final int HUNDRED_DAY_FIELD_LEN = 19;
243a0f8bc51aff98c2e23e73069e447f63397471a0aJesse Wilson
2441ebccf531d1049853b3b0630035434619682c016Dianne Hackborn    private static final int SECONDS_PER_MINUTE = 60;
2451ebccf531d1049853b3b0630035434619682c016Dianne Hackborn    private static final int SECONDS_PER_HOUR = 60 * 60;
2461ebccf531d1049853b3b0630035434619682c016Dianne Hackborn    private static final int SECONDS_PER_DAY = 24 * 60 * 60;
2471ebccf531d1049853b3b0630035434619682c016Dianne Hackborn
248b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn    private static final Object sFormatSync = new Object();
249b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn    private static char[] sFormatStr = new char[HUNDRED_DAY_FIELD_LEN+5];
250a0f8bc51aff98c2e23e73069e447f63397471a0aJesse Wilson
251eaaf396169abdbd213565cacb37dc0c9c391b365Jeff Sharkey    private static final long LARGEST_DURATION = (1000 * DateUtils.DAY_IN_MILLIS) - 1;
252eaaf396169abdbd213565cacb37dc0c9c391b365Jeff Sharkey
253b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn    static private int accumField(int amt, int suffix, boolean always, int zeropad) {
254b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        if (amt > 99 || (always && zeropad >= 3)) {
255b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            return 3+suffix;
2561ebccf531d1049853b3b0630035434619682c016Dianne Hackborn        }
257b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        if (amt > 9 || (always && zeropad >= 2)) {
258b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            return 2+suffix;
2591ebccf531d1049853b3b0630035434619682c016Dianne Hackborn        }
260b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        if (always || amt > 0) {
261b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            return 1+suffix;
2621ebccf531d1049853b3b0630035434619682c016Dianne Hackborn        }
263b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        return 0;
264b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn    }
265a0f8bc51aff98c2e23e73069e447f63397471a0aJesse Wilson
266b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn    static private int printField(char[] formatStr, int amt, char suffix, int pos,
267b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            boolean always, int zeropad) {
268b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        if (always || amt > 0) {
269901b3796fd0954cc4b01bf95dbcbd88d87414e84Bjorn Bringert            final int startPos = pos;
270b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            if ((always && zeropad >= 3) || amt > 99) {
271b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn                int dig = amt/100;
272b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn                formatStr[pos] = (char)(dig + '0');
273b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn                pos++;
274b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn                amt -= (dig*100);
275b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            }
276901b3796fd0954cc4b01bf95dbcbd88d87414e84Bjorn Bringert            if ((always && zeropad >= 2) || amt > 9 || startPos != pos) {
277b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn                int dig = amt/10;
278b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn                formatStr[pos] = (char)(dig + '0');
279b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn                pos++;
280b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn                amt -= (dig*10);
281b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            }
282b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            formatStr[pos] = (char)(amt + '0');
283b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            pos++;
284b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            formatStr[pos] = suffix;
285b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            pos++;
2861ebccf531d1049853b3b0630035434619682c016Dianne Hackborn        }
287b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        return pos;
2881ebccf531d1049853b3b0630035434619682c016Dianne Hackborn    }
289a0f8bc51aff98c2e23e73069e447f63397471a0aJesse Wilson
290b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn    private static int formatDurationLocked(long duration, int fieldLen) {
291b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        if (sFormatStr.length < fieldLen) {
292b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            sFormatStr = new char[fieldLen];
293b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        }
294a0f8bc51aff98c2e23e73069e447f63397471a0aJesse Wilson
295b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        char[] formatStr = sFormatStr;
296a0f8bc51aff98c2e23e73069e447f63397471a0aJesse Wilson
2971ebccf531d1049853b3b0630035434619682c016Dianne Hackborn        if (duration == 0) {
298b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            int pos = 0;
299b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            fieldLen -= 1;
300b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            while (pos < fieldLen) {
30147f13e7c52b260858bab42217d2101741ea8bc46Jozef BABJAK                formatStr[pos++] = ' ';
302b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            }
303b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            formatStr[pos] = '0';
304b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            return pos+1;
3051ebccf531d1049853b3b0630035434619682c016Dianne Hackborn        }
306a0f8bc51aff98c2e23e73069e447f63397471a0aJesse Wilson
307b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        char prefix;
3081ebccf531d1049853b3b0630035434619682c016Dianne Hackborn        if (duration > 0) {
309b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            prefix = '+';
3101ebccf531d1049853b3b0630035434619682c016Dianne Hackborn        } else {
311b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            prefix = '-';
3121ebccf531d1049853b3b0630035434619682c016Dianne Hackborn            duration = -duration;
3131ebccf531d1049853b3b0630035434619682c016Dianne Hackborn        }
3141ebccf531d1049853b3b0630035434619682c016Dianne Hackborn
315eaaf396169abdbd213565cacb37dc0c9c391b365Jeff Sharkey        if (duration > LARGEST_DURATION) {
316eaaf396169abdbd213565cacb37dc0c9c391b365Jeff Sharkey            duration = LARGEST_DURATION;
317eaaf396169abdbd213565cacb37dc0c9c391b365Jeff Sharkey        }
318eaaf396169abdbd213565cacb37dc0c9c391b365Jeff Sharkey
3191ebccf531d1049853b3b0630035434619682c016Dianne Hackborn        int millis = (int)(duration%1000);
3201ebccf531d1049853b3b0630035434619682c016Dianne Hackborn        int seconds = (int) Math.floor(duration / 1000);
3211ebccf531d1049853b3b0630035434619682c016Dianne Hackborn        int days = 0, hours = 0, minutes = 0;
3221ebccf531d1049853b3b0630035434619682c016Dianne Hackborn
3231ebccf531d1049853b3b0630035434619682c016Dianne Hackborn        if (seconds > SECONDS_PER_DAY) {
3241ebccf531d1049853b3b0630035434619682c016Dianne Hackborn            days = seconds / SECONDS_PER_DAY;
3251ebccf531d1049853b3b0630035434619682c016Dianne Hackborn            seconds -= days * SECONDS_PER_DAY;
3261ebccf531d1049853b3b0630035434619682c016Dianne Hackborn        }
3271ebccf531d1049853b3b0630035434619682c016Dianne Hackborn        if (seconds > SECONDS_PER_HOUR) {
3281ebccf531d1049853b3b0630035434619682c016Dianne Hackborn            hours = seconds / SECONDS_PER_HOUR;
3291ebccf531d1049853b3b0630035434619682c016Dianne Hackborn            seconds -= hours * SECONDS_PER_HOUR;
3301ebccf531d1049853b3b0630035434619682c016Dianne Hackborn        }
3311ebccf531d1049853b3b0630035434619682c016Dianne Hackborn        if (seconds > SECONDS_PER_MINUTE) {
3321ebccf531d1049853b3b0630035434619682c016Dianne Hackborn            minutes = seconds / SECONDS_PER_MINUTE;
3331ebccf531d1049853b3b0630035434619682c016Dianne Hackborn            seconds -= minutes * SECONDS_PER_MINUTE;
3341ebccf531d1049853b3b0630035434619682c016Dianne Hackborn        }
3351ebccf531d1049853b3b0630035434619682c016Dianne Hackborn
336b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        int pos = 0;
337a0f8bc51aff98c2e23e73069e447f63397471a0aJesse Wilson
338b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        if (fieldLen != 0) {
339b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            int myLen = accumField(days, 1, false, 0);
340b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            myLen += accumField(hours, 1, myLen > 0, 2);
341b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            myLen += accumField(minutes, 1, myLen > 0, 2);
342b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            myLen += accumField(seconds, 1, myLen > 0, 2);
343b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            myLen += accumField(millis, 2, true, myLen > 0 ? 3 : 0) + 1;
344b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            while (myLen < fieldLen) {
345b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn                formatStr[pos] = ' ';
346b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn                pos++;
347b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn                myLen++;
348b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            }
3491ebccf531d1049853b3b0630035434619682c016Dianne Hackborn        }
350a0f8bc51aff98c2e23e73069e447f63397471a0aJesse Wilson
351b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        formatStr[pos] = prefix;
352b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        pos++;
353a0f8bc51aff98c2e23e73069e447f63397471a0aJesse Wilson
354b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        int start = pos;
355b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        boolean zeropad = fieldLen != 0;
356b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        pos = printField(formatStr, days, 'd', pos, false, 0);
357b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        pos = printField(formatStr, hours, 'h', pos, pos != start, zeropad ? 2 : 0);
358b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        pos = printField(formatStr, minutes, 'm', pos, pos != start, zeropad ? 2 : 0);
359b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        pos = printField(formatStr, seconds, 's', pos, pos != start, zeropad ? 2 : 0);
360b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        pos = printField(formatStr, millis, 'm', pos, true, (zeropad && pos != start) ? 3 : 0);
361b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        formatStr[pos] = 's';
362b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        return pos + 1;
363b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn    }
364a0f8bc51aff98c2e23e73069e447f63397471a0aJesse Wilson
365b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn    /** @hide Just for debugging; not internationalized. */
366b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn    public static void formatDuration(long duration, StringBuilder builder) {
367b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        synchronized (sFormatSync) {
368b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            int len = formatDurationLocked(duration, 0);
369b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            builder.append(sFormatStr, 0, len);
3701ebccf531d1049853b3b0630035434619682c016Dianne Hackborn        }
3711ebccf531d1049853b3b0630035434619682c016Dianne Hackborn    }
3721ebccf531d1049853b3b0630035434619682c016Dianne Hackborn
373b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn    /** @hide Just for debugging; not internationalized. */
374b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn    public static void formatDuration(long duration, PrintWriter pw, int fieldLen) {
375b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        synchronized (sFormatSync) {
376b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            int len = formatDurationLocked(duration, fieldLen);
377b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn            pw.print(new String(sFormatStr, 0, len));
378b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        }
379b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn    }
3801ebccf531d1049853b3b0630035434619682c016Dianne Hackborn
3811ebccf531d1049853b3b0630035434619682c016Dianne Hackborn    /** @hide Just for debugging; not internationalized. */
382b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn    public static void formatDuration(long duration, PrintWriter pw) {
383b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        formatDuration(duration, pw, 0);
384b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn    }
385a0f8bc51aff98c2e23e73069e447f63397471a0aJesse Wilson
386b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn    /** @hide Just for debugging; not internationalized. */
3871ebccf531d1049853b3b0630035434619682c016Dianne Hackborn    public static void formatDuration(long time, long now, PrintWriter pw) {
3881ebccf531d1049853b3b0630035434619682c016Dianne Hackborn        if (time == 0) {
3891ebccf531d1049853b3b0630035434619682c016Dianne Hackborn            pw.print("--");
3901ebccf531d1049853b3b0630035434619682c016Dianne Hackborn            return;
3911ebccf531d1049853b3b0630035434619682c016Dianne Hackborn        }
392b5e3165129a5871cf679a67d9e9323ffad3d4902Dianne Hackborn        formatDuration(time-now, pw, 0);
3931ebccf531d1049853b3b0630035434619682c016Dianne Hackborn    }
39426e1a02ba3183aba2ba262c6f60602e10dd792e3Wink Saville
3959630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown    /** @hide Just for debugging; not internationalized. */
3969630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown    public static String formatUptime(long time) {
3979630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        final long diff = time - SystemClock.uptimeMillis();
3989630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        if (diff > 0) {
3999630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown            return time + " (in " + diff + " ms)";
4009630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        }
4019630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        if (diff < 0) {
4029630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown            return time + " (" + -diff + " ms ago)";
4039630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        }
4049630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown        return time + " (now)";
4059630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown    }
4069630704ed3b265f008a8f64ec60a33cf9dcd3345Jeff Brown
40726e1a02ba3183aba2ba262c6f60602e10dd792e3Wink Saville    /**
40826e1a02ba3183aba2ba262c6f60602e10dd792e3Wink Saville     * Convert a System.currentTimeMillis() value to a time of day value like
40926e1a02ba3183aba2ba262c6f60602e10dd792e3Wink Saville     * that printed in logs. MM-DD HH:MM:SS.MMM
41026e1a02ba3183aba2ba262c6f60602e10dd792e3Wink Saville     *
41126e1a02ba3183aba2ba262c6f60602e10dd792e3Wink Saville     * @param millis since the epoch (1/1/1970)
41226e1a02ba3183aba2ba262c6f60602e10dd792e3Wink Saville     * @return String representation of the time.
41326e1a02ba3183aba2ba262c6f60602e10dd792e3Wink Saville     * @hide
41426e1a02ba3183aba2ba262c6f60602e10dd792e3Wink Saville     */
41526e1a02ba3183aba2ba262c6f60602e10dd792e3Wink Saville    public static String logTimeOfDay(long millis) {
41626e1a02ba3183aba2ba262c6f60602e10dd792e3Wink Saville        Calendar c = Calendar.getInstance();
41726e1a02ba3183aba2ba262c6f60602e10dd792e3Wink Saville        if (millis >= 0) {
41826e1a02ba3183aba2ba262c6f60602e10dd792e3Wink Saville            c.setTimeInMillis(millis);
41926e1a02ba3183aba2ba262c6f60602e10dd792e3Wink Saville            return String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c);
42026e1a02ba3183aba2ba262c6f60602e10dd792e3Wink Saville        } else {
42126e1a02ba3183aba2ba262c6f60602e10dd792e3Wink Saville            return Long.toString(millis);
42226e1a02ba3183aba2ba262c6f60602e10dd792e3Wink Saville        }
42326e1a02ba3183aba2ba262c6f60602e10dd792e3Wink Saville    }
4249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
425