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