| package org.robolectric.shadows; |
| |
| import static android.os.Build.VERSION_CODES.KITKAT_WATCH; |
| import static org.robolectric.util.ReflectionHelpers.ClassParameter.from; |
| |
| import android.text.format.Time; |
| import android.util.TimeFormatException; |
| import java.text.ParseException; |
| import java.text.SimpleDateFormat; |
| import java.util.Calendar; |
| import java.util.Date; |
| import java.util.Locale; |
| import java.util.TimeZone; |
| import org.robolectric.annotation.Implementation; |
| import org.robolectric.annotation.Implements; |
| import org.robolectric.annotation.RealObject; |
| import org.robolectric.util.ReflectionHelpers; |
| import org.robolectric.util.Strftime; |
| |
| @Implements(value = Time.class) |
| public class ShadowTime { |
| @RealObject |
| private Time time; |
| |
| @Implementation(maxSdk = KITKAT_WATCH) |
| protected void setToNow() { |
| time.set(ShadowSystemClock.currentTimeMillis()); |
| } |
| |
| private static final long SECOND_IN_MILLIS = 1000; |
| private static final long MINUTE_IN_MILLIS = SECOND_IN_MILLIS * 60; |
| private static final long HOUR_IN_MILLIS = MINUTE_IN_MILLIS * 60; |
| private static final long DAY_IN_MILLIS = HOUR_IN_MILLIS * 24; |
| |
| @Implementation(maxSdk = KITKAT_WATCH) |
| protected void __constructor__() { |
| __constructor__(getCurrentTimezone()); |
| } |
| |
| @Implementation(maxSdk = KITKAT_WATCH) |
| protected void __constructor__(String timezone) { |
| if (timezone == null) { |
| throw new NullPointerException("timezone is null!"); |
| } |
| time.timezone = timezone; |
| time.year = 1970; |
| time.monthDay = 1; |
| time.isDst = -1; |
| } |
| |
| @Implementation(maxSdk = KITKAT_WATCH) |
| protected void __constructor__(Time other) { |
| set(other); |
| } |
| |
| @Implementation(maxSdk = KITKAT_WATCH) |
| protected void set(Time other) { |
| time.timezone = other.timezone; |
| time.second = other.second; |
| time.minute = other.minute; |
| time.hour = other.hour; |
| time.monthDay = other.monthDay; |
| time.month = other.month; |
| time.year = other.year; |
| time.weekDay = other.weekDay; |
| time.yearDay = other.yearDay; |
| time.isDst = other.isDst; |
| time.gmtoff = other.gmtoff; |
| } |
| |
| @Implementation(maxSdk = KITKAT_WATCH) |
| protected static boolean isEpoch(Time time) { |
| long millis = time.toMillis(true); |
| return getJulianDay(millis, 0) == Time.EPOCH_JULIAN_DAY; |
| } |
| |
| @Implementation(maxSdk = KITKAT_WATCH) |
| protected static int getJulianDay(long millis, long gmtoff) { |
| long offsetMillis = gmtoff * 1000; |
| long julianDay = (millis + offsetMillis) / DAY_IN_MILLIS; |
| return (int) julianDay + Time.EPOCH_JULIAN_DAY; |
| } |
| |
| @Implementation(maxSdk = KITKAT_WATCH) |
| protected long setJulianDay(int julianDay) { |
| // Don't bother with the GMT offset since we don't know the correct |
| // value for the given Julian day. Just get close and then adjust |
| // the day. |
| //long millis = (julianDay - EPOCH_JULIAN_DAY) * DateUtils.DAY_IN_MILLIS; |
| long millis = (julianDay - Time.EPOCH_JULIAN_DAY) * DAY_IN_MILLIS; |
| set(millis); |
| |
| // Figure out how close we are to the requested Julian day. |
| // We can't be off by more than a day. |
| int approximateDay = getJulianDay(millis, time.gmtoff); |
| int diff = julianDay - approximateDay; |
| time.monthDay += diff; |
| |
| // Set the time to 12am and re-normalize. |
| time.hour = 0; |
| time.minute = 0; |
| time.second = 0; |
| millis = time.normalize(true); |
| return millis; |
| } |
| |
| @Implementation(maxSdk = KITKAT_WATCH) |
| protected void set(long millis) { |
| Calendar c = getCalendar(); |
| c.setTimeInMillis(millis); |
| set( |
| c.get(Calendar.SECOND), |
| c.get(Calendar.MINUTE), |
| c.get(Calendar.HOUR_OF_DAY), |
| c.get(Calendar.DAY_OF_MONTH), |
| c.get(Calendar.MONTH), |
| c.get(Calendar.YEAR) |
| ); |
| } |
| |
| @Implementation(maxSdk = KITKAT_WATCH) |
| protected long toMillis(boolean ignoreDst) { |
| Calendar c = getCalendar(); |
| return c.getTimeInMillis(); |
| } |
| |
| @Implementation(maxSdk = KITKAT_WATCH) |
| protected void set(int second, int minute, int hour, int monthDay, int month, int year) { |
| time.second = second; |
| time.minute = minute; |
| time.hour = hour; |
| time.monthDay = monthDay; |
| time.month = month; |
| time.year = year; |
| time.weekDay = 0; |
| time.yearDay = 0; |
| time.isDst = -1; |
| time.gmtoff = 0; |
| } |
| |
| @Implementation(maxSdk = KITKAT_WATCH) |
| protected void set(int monthDay, int month, int year) { |
| set(0, 0, 0, monthDay, month, year); |
| } |
| |
| @Implementation(maxSdk = KITKAT_WATCH) |
| protected void clear(String timezone) { |
| if (timezone == null) { |
| throw new NullPointerException("timezone is null!"); |
| } |
| time.timezone = timezone; |
| time.allDay = false; |
| time.second = 0; |
| time.minute = 0; |
| time.hour = 0; |
| time.monthDay = 0; |
| time.month = 0; |
| time.year = 0; |
| time.weekDay = 0; |
| time.yearDay = 0; |
| time.gmtoff = 0; |
| time.isDst = -1; |
| } |
| |
| @Implementation(maxSdk = KITKAT_WATCH) |
| protected static String getCurrentTimezone() { |
| return TimeZone.getDefault().getID(); |
| } |
| |
| @Implementation(maxSdk = KITKAT_WATCH) |
| protected void switchTimezone(String timezone) { |
| long date = toMillis(true); |
| long gmtoff = TimeZone.getTimeZone(timezone).getOffset(date); |
| set(date + gmtoff); |
| time.timezone = timezone; |
| time.gmtoff = (gmtoff / 1000); |
| } |
| |
| @Implementation(maxSdk = KITKAT_WATCH) |
| protected static int compare(Time a, Time b) { |
| long ams = a.toMillis(false); |
| long bms = b.toMillis(false); |
| if (ams == bms) { |
| return 0; |
| } else if (ams < bms) { |
| return -1; |
| } else { |
| return 1; |
| } |
| } |
| |
| @Implementation(maxSdk = KITKAT_WATCH) |
| protected boolean before(Time other) { |
| return Time.compare(time, other) < 0; |
| } |
| |
| @Implementation(maxSdk = KITKAT_WATCH) |
| protected boolean after(Time other) { |
| return Time.compare(time, other) > 0; |
| } |
| |
| @Implementation(maxSdk = KITKAT_WATCH) |
| protected boolean parse(String timeString) { |
| TimeZone tz; |
| if (timeString.endsWith("Z")) { |
| timeString = timeString.substring(0, timeString.length() - 1); |
| tz = TimeZone.getTimeZone("UTC"); |
| } else { |
| tz = TimeZone.getTimeZone(time.timezone); |
| } |
| SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd'T'HHmmss", Locale.ENGLISH); |
| SimpleDateFormat dfShort = new SimpleDateFormat("yyyyMMdd", Locale.ENGLISH); |
| df.setTimeZone(tz); |
| dfShort.setTimeZone(tz); |
| time.timezone = tz.getID(); |
| try { |
| set(df.parse(timeString).getTime()); |
| } catch (ParseException e) { |
| try { |
| set(dfShort.parse(timeString).getTime()); |
| } catch (ParseException e2) { |
| throwTimeFormatException(e2.getLocalizedMessage()); |
| } |
| } |
| return "UTC".equals(tz.getID()); |
| } |
| |
| @Implementation(maxSdk = KITKAT_WATCH) |
| protected String format2445() { |
| String value = format("%Y%m%dT%H%M%S"); |
| if ( "UTC".equals(time.timezone)){ |
| value += "Z"; |
| } |
| return value; |
| } |
| |
| @Implementation(maxSdk = KITKAT_WATCH) |
| protected String format3339(boolean allDay) { |
| if (allDay) { |
| return format("%Y-%m-%d"); |
| } else if ("UTC".equals(time.timezone)) { |
| return format("%Y-%m-%dT%H:%M:%S.000Z"); |
| } else { |
| String base = format("%Y-%m-%dT%H:%M:%S.000"); |
| String sign = (time.gmtoff < 0) ? "-" : "+"; |
| int offset = (int) Math.abs(time.gmtoff); |
| int minutes = (offset % 3600) / 60; |
| int hours = offset / 3600; |
| return String.format("%s%s%02d:%02d", base, sign, hours, minutes); |
| } |
| } |
| |
| @Implementation(maxSdk = KITKAT_WATCH) |
| protected boolean parse3339(String rfc3339String) { |
| SimpleDateFormat formatter = new SimpleDateFormat(); |
| // Special case Date without time first |
| if (rfc3339String.matches("\\d{4}-\\d{2}-\\d{2}")) { |
| final TimeZone tz = TimeZone.getTimeZone(time.timezone); |
| formatter.applyLocalizedPattern("yyyy-MM-dd"); |
| // Make sure we inferFromValue the date in the context of the specified time zone |
| // instead of the system default time zone. |
| formatter.setTimeZone(tz); |
| Calendar calendar = Calendar.getInstance(tz, Locale.getDefault()); |
| try { |
| calendar.setTime(formatter.parse(rfc3339String)); |
| } catch (java.text.ParseException e) { |
| throwTimeFormatException(e.getLocalizedMessage()); |
| } |
| time.second = time.minute = time.hour = 0; |
| time.monthDay = calendar.get(Calendar.DAY_OF_MONTH); |
| time.month = calendar.get(Calendar.MONTH); |
| time.year = calendar.get(Calendar.YEAR); |
| time.weekDay = calendar.get(Calendar.DAY_OF_WEEK) - Calendar.SUNDAY; |
| time.yearDay = calendar.get(Calendar.DAY_OF_YEAR); |
| time.isDst = calendar.get(Calendar.DST_OFFSET) != 0 ? 1 : 0; |
| time.allDay = true; |
| return false; |
| } |
| |
| // Store a string normalized for SimpleDateFormat; |
| String dateString = rfc3339String |
| // Look-ahead to remove the colon followed by minutes in timezone |
| .replaceFirst(":(?=\\d{2}$)", "") |
| // Look-behind to pad with minutes any timezone only defines hours |
| .replaceFirst("(?<=[+-]\\d{2})$", "00") |
| // If it ends with a Z, just replace it with no offset |
| .replaceFirst("(Z)$", "+0000"); |
| |
| formatter.setTimeZone(TimeZone.getTimeZone("UTC")); |
| formatter.applyLocalizedPattern("yyyy-MM-dd'T'HH:mm:ssZ"); |
| long millisInUtc = time.toMillis(false); |
| try { |
| millisInUtc = formatter.parse(dateString).getTime(); |
| } catch (java.text.ParseException e1) { |
| // Try again with fractional seconds. |
| formatter.applyLocalizedPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); |
| formatter.setLenient(true); |
| try { |
| millisInUtc = formatter.parse(dateString).getTime(); |
| } catch (java.text.ParseException e2) { |
| throwTimeFormatException(e2.getLocalizedMessage()); |
| } |
| } |
| // Clear to UTC, then set time; |
| clear("UTC"); |
| set(millisInUtc); |
| return true; |
| } |
| |
| private void throwTimeFormatException(String optionalMessage) { |
| throw ReflectionHelpers.callConstructor(TimeFormatException.class, from(String.class, optionalMessage == null ? "fail" : optionalMessage)); |
| } |
| |
| @Implementation(maxSdk = KITKAT_WATCH) |
| protected String format(String format) { |
| return Strftime.format(format, new Date(toMillis(false)), Locale.getDefault(), TimeZone.getTimeZone(time.timezone)); |
| } |
| |
| private Calendar getCalendar() { |
| Calendar c = Calendar.getInstance(TimeZone.getTimeZone(time.timezone)); |
| c.set(time.year, time.month, time.monthDay, time.hour, time.minute, time.second); |
| c.set(Calendar.MILLISECOND, 0); |
| return c; |
| } |
| } |