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