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.os.SystemClock; 20 21 import libcore.util.TimeZoneFinder; 22 import libcore.util.ZoneInfoDB; 23 24 import java.io.PrintWriter; 25 import java.text.SimpleDateFormat; 26 import java.util.Calendar; 27 import java.util.Date; 28 /** 29 * A class containing utility methods related to time zones. 30 */ 31 public class TimeUtils { TimeUtils()32 /** @hide */ public TimeUtils() {} 33 /** {@hide} */ 34 private static SimpleDateFormat sLoggingFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 35 36 /** 37 * Tries to return a time zone that would have had the specified offset 38 * and DST value at the specified moment in the specified country. 39 * Returns null if no suitable zone could be found. 40 */ getTimeZone( int offset, boolean dst, long when, String country)41 public static java.util.TimeZone getTimeZone( 42 int offset, boolean dst, long when, String country) { 43 44 android.icu.util.TimeZone icuTimeZone = getIcuTimeZone(offset, dst, when, country); 45 // We must expose a java.util.TimeZone here for API compatibility because this is a public 46 // API method. 47 return icuTimeZone != null ? java.util.TimeZone.getTimeZone(icuTimeZone.getID()) : null; 48 } 49 50 /** 51 * Tries to return a frozen ICU time zone that would have had the specified offset 52 * and DST value at the specified moment in the specified country. 53 * Returns null if no suitable zone could be found. 54 */ getIcuTimeZone( int offset, boolean dst, long when, String country)55 private static android.icu.util.TimeZone getIcuTimeZone( 56 int offset, boolean dst, long when, String country) { 57 if (country == null) { 58 return null; 59 } 60 61 android.icu.util.TimeZone bias = android.icu.util.TimeZone.getDefault(); 62 return TimeZoneFinder.getInstance() 63 .lookupTimeZoneByCountryAndOffset(country, offset, dst, when, bias); 64 } 65 66 /** 67 * Returns a String indicating the version of the time zone database currently 68 * in use. The format of the string is dependent on the underlying time zone 69 * database implementation, but will typically contain the year in which the database 70 * was updated plus a letter from a to z indicating changes made within that year. 71 * 72 * <p>Time zone database updates should be expected to occur periodically due to 73 * political and legal changes that cannot be anticipated in advance. Therefore, 74 * when computing the UTC time for a future event, applications should be aware that 75 * the results may differ following a time zone database update. This method allows 76 * applications to detect that a database change has occurred, and to recalculate any 77 * cached times accordingly. 78 * 79 * <p>The time zone database may be assumed to change only when the device runtime 80 * is restarted. Therefore, it is not necessary to re-query the database version 81 * during the lifetime of an activity. 82 */ getTimeZoneDatabaseVersion()83 public static String getTimeZoneDatabaseVersion() { 84 return ZoneInfoDB.getInstance().getVersion(); 85 } 86 87 /** @hide Field length that can hold 999 days of time */ 88 public static final int HUNDRED_DAY_FIELD_LEN = 19; 89 90 private static final int SECONDS_PER_MINUTE = 60; 91 private static final int SECONDS_PER_HOUR = 60 * 60; 92 private static final int SECONDS_PER_DAY = 24 * 60 * 60; 93 94 /** @hide */ 95 public static final long NANOS_PER_MS = 1000000; 96 97 private static final Object sFormatSync = new Object(); 98 private static char[] sFormatStr = new char[HUNDRED_DAY_FIELD_LEN+10]; 99 private static char[] sTmpFormatStr = new char[HUNDRED_DAY_FIELD_LEN+10]; 100 accumField(int amt, int suffix, boolean always, int zeropad)101 static private int accumField(int amt, int suffix, boolean always, int zeropad) { 102 if (amt > 999) { 103 int num = 0; 104 while (amt != 0) { 105 num++; 106 amt /= 10; 107 } 108 return num + suffix; 109 } else { 110 if (amt > 99 || (always && zeropad >= 3)) { 111 return 3+suffix; 112 } 113 if (amt > 9 || (always && zeropad >= 2)) { 114 return 2+suffix; 115 } 116 if (always || amt > 0) { 117 return 1+suffix; 118 } 119 } 120 return 0; 121 } 122 printFieldLocked(char[] formatStr, int amt, char suffix, int pos, boolean always, int zeropad)123 static private int printFieldLocked(char[] formatStr, int amt, char suffix, int pos, 124 boolean always, int zeropad) { 125 if (always || amt > 0) { 126 final int startPos = pos; 127 if (amt > 999) { 128 int tmp = 0; 129 while (amt != 0 && tmp < sTmpFormatStr.length) { 130 int dig = amt % 10; 131 sTmpFormatStr[tmp] = (char)(dig + '0'); 132 tmp++; 133 amt /= 10; 134 } 135 tmp--; 136 while (tmp >= 0) { 137 formatStr[pos] = sTmpFormatStr[tmp]; 138 pos++; 139 tmp--; 140 } 141 } else { 142 if ((always && zeropad >= 3) || amt > 99) { 143 int dig = amt/100; 144 formatStr[pos] = (char)(dig + '0'); 145 pos++; 146 amt -= (dig*100); 147 } 148 if ((always && zeropad >= 2) || amt > 9 || startPos != pos) { 149 int dig = amt/10; 150 formatStr[pos] = (char)(dig + '0'); 151 pos++; 152 amt -= (dig*10); 153 } 154 formatStr[pos] = (char)(amt + '0'); 155 pos++; 156 } 157 formatStr[pos] = suffix; 158 pos++; 159 } 160 return pos; 161 } 162 formatDurationLocked(long duration, int fieldLen)163 private static int formatDurationLocked(long duration, int fieldLen) { 164 if (sFormatStr.length < fieldLen) { 165 sFormatStr = new char[fieldLen]; 166 } 167 168 char[] formatStr = sFormatStr; 169 170 if (duration == 0) { 171 int pos = 0; 172 fieldLen -= 1; 173 while (pos < fieldLen) { 174 formatStr[pos++] = ' '; 175 } 176 formatStr[pos] = '0'; 177 return pos+1; 178 } 179 180 char prefix; 181 if (duration > 0) { 182 prefix = '+'; 183 } else { 184 prefix = '-'; 185 duration = -duration; 186 } 187 188 int millis = (int)(duration%1000); 189 int seconds = (int) Math.floor(duration / 1000); 190 int days = 0, hours = 0, minutes = 0; 191 192 if (seconds >= SECONDS_PER_DAY) { 193 days = seconds / SECONDS_PER_DAY; 194 seconds -= days * SECONDS_PER_DAY; 195 } 196 if (seconds >= SECONDS_PER_HOUR) { 197 hours = seconds / SECONDS_PER_HOUR; 198 seconds -= hours * SECONDS_PER_HOUR; 199 } 200 if (seconds >= SECONDS_PER_MINUTE) { 201 minutes = seconds / SECONDS_PER_MINUTE; 202 seconds -= minutes * SECONDS_PER_MINUTE; 203 } 204 205 int pos = 0; 206 207 if (fieldLen != 0) { 208 int myLen = accumField(days, 1, false, 0); 209 myLen += accumField(hours, 1, myLen > 0, 2); 210 myLen += accumField(minutes, 1, myLen > 0, 2); 211 myLen += accumField(seconds, 1, myLen > 0, 2); 212 myLen += accumField(millis, 2, true, myLen > 0 ? 3 : 0) + 1; 213 while (myLen < fieldLen) { 214 formatStr[pos] = ' '; 215 pos++; 216 myLen++; 217 } 218 } 219 220 formatStr[pos] = prefix; 221 pos++; 222 223 int start = pos; 224 boolean zeropad = fieldLen != 0; 225 pos = printFieldLocked(formatStr, days, 'd', pos, false, 0); 226 pos = printFieldLocked(formatStr, hours, 'h', pos, pos != start, zeropad ? 2 : 0); 227 pos = printFieldLocked(formatStr, minutes, 'm', pos, pos != start, zeropad ? 2 : 0); 228 pos = printFieldLocked(formatStr, seconds, 's', pos, pos != start, zeropad ? 2 : 0); 229 pos = printFieldLocked(formatStr, millis, 'm', pos, true, (zeropad && pos != start) ? 3 : 0); 230 formatStr[pos] = 's'; 231 return pos + 1; 232 } 233 234 /** @hide Just for debugging; not internationalized. */ formatDuration(long duration, StringBuilder builder)235 public static void formatDuration(long duration, StringBuilder builder) { 236 synchronized (sFormatSync) { 237 int len = formatDurationLocked(duration, 0); 238 builder.append(sFormatStr, 0, len); 239 } 240 } 241 242 /** @hide Just for debugging; not internationalized. */ formatDuration(long duration, StringBuilder builder, int fieldLen)243 public static void formatDuration(long duration, StringBuilder builder, int fieldLen) { 244 synchronized (sFormatSync) { 245 int len = formatDurationLocked(duration, fieldLen); 246 builder.append(sFormatStr, 0, len); 247 } 248 } 249 250 /** @hide Just for debugging; not internationalized. */ formatDuration(long duration, PrintWriter pw, int fieldLen)251 public static void formatDuration(long duration, PrintWriter pw, int fieldLen) { 252 synchronized (sFormatSync) { 253 int len = formatDurationLocked(duration, fieldLen); 254 pw.print(new String(sFormatStr, 0, len)); 255 } 256 } 257 258 /** @hide Just for debugging; not internationalized. */ formatDuration(long duration)259 public static String formatDuration(long duration) { 260 synchronized (sFormatSync) { 261 int len = formatDurationLocked(duration, 0); 262 return new String(sFormatStr, 0, len); 263 } 264 } 265 266 /** @hide Just for debugging; not internationalized. */ formatDuration(long duration, PrintWriter pw)267 public static void formatDuration(long duration, PrintWriter pw) { 268 formatDuration(duration, pw, 0); 269 } 270 271 /** @hide Just for debugging; not internationalized. */ formatDuration(long time, long now, PrintWriter pw)272 public static void formatDuration(long time, long now, PrintWriter pw) { 273 if (time == 0) { 274 pw.print("--"); 275 return; 276 } 277 formatDuration(time-now, pw, 0); 278 } 279 280 /** @hide Just for debugging; not internationalized. */ formatUptime(long time)281 public static String formatUptime(long time) { 282 final long diff = time - SystemClock.uptimeMillis(); 283 if (diff > 0) { 284 return time + " (in " + diff + " ms)"; 285 } 286 if (diff < 0) { 287 return time + " (" + -diff + " ms ago)"; 288 } 289 return time + " (now)"; 290 } 291 292 /** 293 * Convert a System.currentTimeMillis() value to a time of day value like 294 * that printed in logs. MM-DD HH:MM:SS.MMM 295 * 296 * @param millis since the epoch (1/1/1970) 297 * @return String representation of the time. 298 * @hide 299 */ logTimeOfDay(long millis)300 public static String logTimeOfDay(long millis) { 301 Calendar c = Calendar.getInstance(); 302 if (millis >= 0) { 303 c.setTimeInMillis(millis); 304 return String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c); 305 } else { 306 return Long.toString(millis); 307 } 308 } 309 310 /** {@hide} */ formatForLogging(long millis)311 public static String formatForLogging(long millis) { 312 if (millis <= 0) { 313 return "unknown"; 314 } else { 315 return sLoggingFormat.format(new Date(millis)); 316 } 317 } 318 } 319