Loading core/java/android/widget/AnalogClock.java +24 −14 Original line number Diff line number Diff line Loading @@ -25,12 +25,14 @@ import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.text.format.DateUtils; import android.text.format.Time; import android.util.AttributeSet; import android.view.View; import android.widget.RemoteViews.RemoteView; import java.util.TimeZone; import java.time.Clock; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; /** * This widget display an analogic clock with two hands for hours and Loading @@ -44,7 +46,7 @@ import java.util.TimeZone; @RemoteView @Deprecated public class AnalogClock extends View { private Time mCalendar; private Clock mClock; private Drawable mHourHand; private Drawable mMinuteHand; Loading Loading @@ -93,7 +95,7 @@ public class AnalogClock extends View { mMinuteHand = context.getDrawable(com.android.internal.R.drawable.clock_hand_minute); } mCalendar = new Time(); mClock = Clock.systemDefaultZone(); mDialWidth = mDial.getIntrinsicWidth(); mDialHeight = mDial.getIntrinsicHeight(); Loading Loading @@ -126,7 +128,7 @@ public class AnalogClock extends View { // in the main thread, therefore the receiver can't run before this method returns. // The time zone may have changed while the receiver wasn't registered, so update the Time mCalendar = new Time(); mClock = Clock.systemDefaultZone(); // Make sure we update to the current time onTimeChanged(); Loading Loading @@ -235,17 +237,18 @@ public class AnalogClock extends View { } private void onTimeChanged() { mCalendar.setToNow(); long nowMillis = mClock.millis(); LocalDateTime localDateTime = toLocalDateTime(nowMillis, mClock.getZone()); int hour = mCalendar.hour; int minute = mCalendar.minute; int second = mCalendar.second; int hour = localDateTime.getHour(); int minute = localDateTime.getMinute(); int second = localDateTime.getSecond(); mMinutes = minute + second / 60.0f; mHour = hour + mMinutes / 60.0f; mChanged = true; updateContentDescription(mCalendar); updateContentDescription(nowMillis); } private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { Loading @@ -253,7 +256,7 @@ public class AnalogClock extends View { public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) { String tz = intent.getStringExtra("time-zone"); mCalendar = new Time(TimeZone.getTimeZone(tz).getID()); mClock = Clock.system(ZoneId.of(tz)); } onTimeChanged(); Loading @@ -262,10 +265,17 @@ public class AnalogClock extends View { } }; private void updateContentDescription(Time time) { private void updateContentDescription(long timeMillis) { final int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_24HOUR; String contentDescription = DateUtils.formatDateTime(mContext, time.toMillis(false), flags); String contentDescription = DateUtils.formatDateTime(mContext, timeMillis, flags); setContentDescription(contentDescription); } private static LocalDateTime toLocalDateTime(long timeMillis, ZoneId zoneId) { // java.time types like LocalDateTime / Instant can support the full range of "long millis" // with room to spare so we do not need to worry about overflow / underflow and the // resulting exceptions while the input to this class is a long. Instant instant = Instant.ofEpochMilli(timeMillis); return LocalDateTime.ofInstant(instant, zoneId); } } core/java/android/widget/DateTimeView.java +68 −57 Original line number Diff line number Diff line Loading @@ -20,7 +20,6 @@ import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static android.text.format.DateUtils.YEAR_IN_MILLIS; import static android.text.format.Time.getJulianDay; import android.app.ActivityThread; import android.content.BroadcastReceiver; Loading @@ -30,9 +29,7 @@ import android.content.IntentFilter; import android.content.res.Configuration; import android.content.res.TypedArray; import android.database.ContentObserver; import android.icu.util.Calendar; import android.os.Handler; import android.text.format.Time; import android.util.AttributeSet; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.RemoteViews.RemoteView; Loading @@ -40,9 +37,14 @@ import android.widget.RemoteViews.RemoteView; import com.android.internal.R; import java.text.DateFormat; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.ZoneId; import java.time.temporal.JulianFields; import java.util.ArrayList; import java.util.Date; import java.util.TimeZone; // // TODO Loading @@ -61,8 +63,9 @@ public class DateTimeView extends TextView { private static final int SHOW_TIME = 0; private static final int SHOW_MONTH_DAY_YEAR = 1; Date mTime; long mTimeMillis; private long mTimeMillis; // The LocalDateTime equivalent of mTimeMillis but truncated to minute, i.e. no seconds / nanos. private LocalDateTime mLocalTime; int mLastDisplay = -1; DateFormat mLastFormat; Loading Loading @@ -124,11 +127,10 @@ public class DateTimeView extends TextView { } @android.view.RemotableViewMethod public void setTime(long time) { Time t = new Time(); t.set(time); mTimeMillis = t.toMillis(false); mTime = new Date(t.year-1900, t.month, t.monthDay, t.hour, t.minute, 0); public void setTime(long timeMillis) { mTimeMillis = timeMillis; LocalDateTime dateTime = toLocalDateTime(timeMillis, ZoneId.systemDefault()); mLocalTime = dateTime.withSecond(0); update(); } Loading @@ -150,7 +152,7 @@ public class DateTimeView extends TextView { } void update() { if (mTime == null || getVisibility() == GONE) { if (mLocalTime == null || getVisibility() == GONE) { return; } if (mShowRelativeTime) { Loading @@ -159,31 +161,27 @@ public class DateTimeView extends TextView { } int display; Date time = mTime; Time t = new Time(); t.set(mTimeMillis); t.second = 0; t.hour -= 12; long twelveHoursBefore = t.toMillis(false); t.hour += 12; long twelveHoursAfter = t.toMillis(false); t.hour = 0; t.minute = 0; long midnightBefore = t.toMillis(false); t.monthDay++; long midnightAfter = t.toMillis(false); long nowMillis = System.currentTimeMillis(); t.set(nowMillis); t.second = 0; nowMillis = t.normalize(false); ZoneId zoneId = ZoneId.systemDefault(); // localTime is the local time for mTimeMillis but at zero seconds past the minute. LocalDateTime localTime = mLocalTime; LocalDateTime localStartOfDay = LocalDateTime.of(localTime.toLocalDate(), LocalTime.MIDNIGHT); LocalDateTime localTomorrowStartOfDay = localStartOfDay.plusDays(1); // now is current local time but at zero seconds past the minute. LocalDateTime localNow = LocalDateTime.now(zoneId).withSecond(0); long twelveHoursBefore = toEpochMillis(localTime.minusHours(12), zoneId); long twelveHoursAfter = toEpochMillis(localTime.plusHours(12), zoneId); long midnightBefore = toEpochMillis(localStartOfDay, zoneId); long midnightAfter = toEpochMillis(localTomorrowStartOfDay, zoneId); long time = toEpochMillis(localTime, zoneId); long now = toEpochMillis(localNow, zoneId); // Choose the display mode choose_display: { if ((nowMillis >= midnightBefore && nowMillis < midnightAfter) || (nowMillis >= twelveHoursBefore && nowMillis < twelveHoursAfter)) { if ((now >= midnightBefore && now < midnightAfter) || (now >= twelveHoursBefore && now < twelveHoursAfter)) { display = SHOW_TIME; break choose_display; } Loading Loading @@ -212,7 +210,7 @@ public class DateTimeView extends TextView { } // Set the text String text = format.format(mTime); String text = format.format(new Date(time)); setText(text); // Schedule the next update Loading @@ -221,7 +219,7 @@ public class DateTimeView extends TextView { mUpdateTimeMillis = twelveHoursAfter > midnightAfter ? twelveHoursAfter : midnightAfter; } else { // Currently showing the date if (mTimeMillis < nowMillis) { if (mTimeMillis < now) { // If the time is in the past, don't schedule an update mUpdateTimeMillis = 0; } else { Loading Loading @@ -262,15 +260,18 @@ public class DateTimeView extends TextView { millisIncrease = HOUR_IN_MILLIS; } else if (duration < YEAR_IN_MILLIS) { // In weird cases it can become 0 because of daylight savings TimeZone timeZone = TimeZone.getDefault(); count = Math.max(Math.abs(dayDistance(timeZone, mTimeMillis, now)), 1); LocalDateTime localDateTime = mLocalTime; ZoneId zoneId = ZoneId.systemDefault(); LocalDateTime localNow = toLocalDateTime(now, zoneId); count = Math.max(Math.abs(dayDistance(localDateTime, localNow)), 1); result = String.format(getContext().getResources().getQuantityString(past ? com.android.internal.R.plurals.duration_days_shortest : com.android.internal.R.plurals.duration_days_shortest_future, count), count); if (past || count != 1) { mUpdateTimeMillis = computeNextMidnight(timeZone); mUpdateTimeMillis = computeNextMidnight(localNow, zoneId); millisIncrease = -1; } else { millisIncrease = DAY_IN_MILLIS; Loading @@ -296,18 +297,13 @@ public class DateTimeView extends TextView { } /** * @param timeZone the timezone we are in * @return the timepoint in millis at UTC at midnight in the current timezone * Returns the epoch millis for the next midnight in the specified timezone. */ private long computeNextMidnight(TimeZone timeZone) { Calendar c = Calendar.getInstance(); c.setTimeZone(libcore.icu.DateUtilsBridge.icuTimeZone(timeZone)); c.add(Calendar.DAY_OF_MONTH, 1); c.set(Calendar.HOUR_OF_DAY, 0); c.set(Calendar.MINUTE, 0); c.set(Calendar.SECOND, 0); c.set(Calendar.MILLISECOND, 0); return c.getTimeInMillis(); private static long computeNextMidnight(LocalDateTime time, ZoneId zoneId) { // This ignores the chance of overflow: it should never happen. LocalDate tomorrow = time.toLocalDate().plusDays(1); LocalDateTime nextMidnight = LocalDateTime.of(tomorrow, LocalTime.MIDNIGHT); return toEpochMillis(nextMidnight, zoneId); } @Override Loading @@ -325,11 +321,10 @@ public class DateTimeView extends TextView { com.android.internal.R.string.now_string_shortest); } // Return the date difference for the two times in a given timezone. private static int dayDistance(TimeZone timeZone, long startTime, long endTime) { return getJulianDay(endTime, timeZone.getOffset(endTime) / 1000) - getJulianDay(startTime, timeZone.getOffset(startTime) / 1000); // Return the number of days between the two dates. private static int dayDistance(LocalDateTime start, LocalDateTime end) { return (int) (end.getLong(JulianFields.JULIAN_DAY) - start.getLong(JulianFields.JULIAN_DAY)); } private DateFormat getTimeFormat() { Loading Loading @@ -374,8 +369,11 @@ public class DateTimeView extends TextView { count); } else if (duration < YEAR_IN_MILLIS) { // In weird cases it can become 0 because of daylight savings TimeZone timeZone = TimeZone.getDefault(); count = Math.max(Math.abs(dayDistance(timeZone, mTimeMillis, now)), 1); LocalDateTime localDateTime = mLocalTime; ZoneId zoneId = ZoneId.systemDefault(); LocalDateTime localNow = toLocalDateTime(now, zoneId); count = Math.max(Math.abs(dayDistance(localDateTime, localNow)), 1); result = String.format(getContext().getResources().getQuantityString(past ? com.android.internal. R.plurals.duration_days_relative Loading Loading @@ -511,4 +509,17 @@ public class DateTimeView extends TextView { } } } private static LocalDateTime toLocalDateTime(long timeMillis, ZoneId zoneId) { // java.time types like LocalDateTime / Instant can support the full range of "long millis" // with room to spare so we do not need to worry about overflow / underflow and the rsulting // exceptions while the input to this class is a long. Instant instant = Instant.ofEpochMilli(timeMillis); return LocalDateTime.ofInstant(instant, zoneId); } private static long toEpochMillis(LocalDateTime time, ZoneId zoneId) { Instant instant = time.toInstant(zoneId.getRules().getOffset(time)); return instant.toEpochMilli(); } } core/java/com/android/internal/http/HttpDateTime.javadeleted 100644 → 0 +0 −225 Original line number Diff line number Diff line /* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.http; import android.text.format.Time; import java.util.Calendar; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Helper for parsing an HTTP date. */ public final class HttpDateTime { /* * Regular expression for parsing HTTP-date. * * Wdy, DD Mon YYYY HH:MM:SS GMT * RFC 822, updated by RFC 1123 * * Weekday, DD-Mon-YY HH:MM:SS GMT * RFC 850, obsoleted by RFC 1036 * * Wdy Mon DD HH:MM:SS YYYY * ANSI C's asctime() format * * with following variations * * Wdy, DD-Mon-YYYY HH:MM:SS GMT * Wdy, (SP)D Mon YYYY HH:MM:SS GMT * Wdy,DD Mon YYYY HH:MM:SS GMT * Wdy, DD-Mon-YY HH:MM:SS GMT * Wdy, DD Mon YYYY HH:MM:SS -HHMM * Wdy, DD Mon YYYY HH:MM:SS * Wdy Mon (SP)D HH:MM:SS YYYY * Wdy Mon DD HH:MM:SS YYYY GMT * * HH can be H if the first digit is zero. * * Mon can be the full name of the month. */ private static final String HTTP_DATE_RFC_REGEXP = "([0-9]{1,2})[- ]([A-Za-z]{3,9})[- ]([0-9]{2,4})[ ]" + "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])"; private static final String HTTP_DATE_ANSIC_REGEXP = "[ ]([A-Za-z]{3,9})[ ]+([0-9]{1,2})[ ]" + "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])[ ]([0-9]{2,4})"; /** * The compiled version of the HTTP-date regular expressions. */ private static final Pattern HTTP_DATE_RFC_PATTERN = Pattern.compile(HTTP_DATE_RFC_REGEXP); private static final Pattern HTTP_DATE_ANSIC_PATTERN = Pattern.compile(HTTP_DATE_ANSIC_REGEXP); private static class TimeOfDay { TimeOfDay(int h, int m, int s) { this.hour = h; this.minute = m; this.second = s; } int hour; int minute; int second; } public static long parse(String timeString) throws IllegalArgumentException { int date = 1; int month = Calendar.JANUARY; int year = 1970; TimeOfDay timeOfDay; Matcher rfcMatcher = HTTP_DATE_RFC_PATTERN.matcher(timeString); if (rfcMatcher.find()) { date = getDate(rfcMatcher.group(1)); month = getMonth(rfcMatcher.group(2)); year = getYear(rfcMatcher.group(3)); timeOfDay = getTime(rfcMatcher.group(4)); } else { Matcher ansicMatcher = HTTP_DATE_ANSIC_PATTERN.matcher(timeString); if (ansicMatcher.find()) { month = getMonth(ansicMatcher.group(1)); date = getDate(ansicMatcher.group(2)); timeOfDay = getTime(ansicMatcher.group(3)); year = getYear(ansicMatcher.group(4)); } else { throw new IllegalArgumentException(); } } // FIXME: Y2038 BUG! if (year >= 2038) { year = 2038; month = Calendar.JANUARY; date = 1; } Time time = new Time(Time.TIMEZONE_UTC); time.set(timeOfDay.second, timeOfDay.minute, timeOfDay.hour, date, month, year); return time.toMillis(false /* use isDst */); } private static int getDate(String dateString) { if (dateString.length() == 2) { return (dateString.charAt(0) - '0') * 10 + (dateString.charAt(1) - '0'); } else { return (dateString.charAt(0) - '0'); } } /* * jan = 9 + 0 + 13 = 22 * feb = 5 + 4 + 1 = 10 * mar = 12 + 0 + 17 = 29 * apr = 0 + 15 + 17 = 32 * may = 12 + 0 + 24 = 36 * jun = 9 + 20 + 13 = 42 * jul = 9 + 20 + 11 = 40 * aug = 0 + 20 + 6 = 26 * sep = 18 + 4 + 15 = 37 * oct = 14 + 2 + 19 = 35 * nov = 13 + 14 + 21 = 48 * dec = 3 + 4 + 2 = 9 */ private static int getMonth(String monthString) { int hash = Character.toLowerCase(monthString.charAt(0)) + Character.toLowerCase(monthString.charAt(1)) + Character.toLowerCase(monthString.charAt(2)) - 3 * 'a'; switch (hash) { case 22: return Calendar.JANUARY; case 10: return Calendar.FEBRUARY; case 29: return Calendar.MARCH; case 32: return Calendar.APRIL; case 36: return Calendar.MAY; case 42: return Calendar.JUNE; case 40: return Calendar.JULY; case 26: return Calendar.AUGUST; case 37: return Calendar.SEPTEMBER; case 35: return Calendar.OCTOBER; case 48: return Calendar.NOVEMBER; case 9: return Calendar.DECEMBER; default: throw new IllegalArgumentException(); } } private static int getYear(String yearString) { if (yearString.length() == 2) { int year = (yearString.charAt(0) - '0') * 10 + (yearString.charAt(1) - '0'); if (year >= 70) { return year + 1900; } else { return year + 2000; } } else if (yearString.length() == 3) { // According to RFC 2822, three digit years should be added to 1900. int year = (yearString.charAt(0) - '0') * 100 + (yearString.charAt(1) - '0') * 10 + (yearString.charAt(2) - '0'); return year + 1900; } else if (yearString.length() == 4) { return (yearString.charAt(0) - '0') * 1000 + (yearString.charAt(1) - '0') * 100 + (yearString.charAt(2) - '0') * 10 + (yearString.charAt(3) - '0'); } else { return 1970; } } private static TimeOfDay getTime(String timeString) { // HH might be H int i = 0; int hour = timeString.charAt(i++) - '0'; if (timeString.charAt(i) != ':') hour = hour * 10 + (timeString.charAt(i++) - '0'); // Skip ':' i++; int minute = (timeString.charAt(i++) - '0') * 10 + (timeString.charAt(i++) - '0'); // Skip ':' i++; int second = (timeString.charAt(i++) - '0') * 10 + (timeString.charAt(i++) - '0'); return new TimeOfDay(hour, minute, second); } } core/tests/benchmarks/src/android/text/format/AndroidTimeVsOthersBenchmark.java 0 → 100644 +85 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.text.format; import com.google.caliper.Benchmark; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZoneOffset; public class AndroidTimeVsOthersBenchmark { private static final String[] TIMEZONE_IDS = { "Europe/London", "America/Los_Angeles", "Asia/Shanghai", }; @Benchmark public void toMillis_androidTime(int reps) { long answer = 0; for (int i = 0; i < reps; i++) { String timezoneId = TIMEZONE_IDS[i % TIMEZONE_IDS.length]; Time time = new Time(timezoneId); time.set(1, 2, 3, 4, 5, 2010); answer = time.toMillis(false); } // System.out.println(answer); } @Benchmark public void toMillis_javaTime(int reps) { long answer = 0; for (int i = 0; i < reps; i++) { String timezoneId = TIMEZONE_IDS[i % TIMEZONE_IDS.length]; LocalDateTime time = LocalDateTime.of(2010, 5 + 1, 4, 3, 2, 1); ZoneOffset offset = ZoneId.of(timezoneId).getRules().getOffset(time); answer = time.toInstant(offset).toEpochMilli(); } // System.out.println(answer); } @Benchmark public void toMillis_javaUtil(int reps) { long answer = 0; for (int i = 0; i < reps; i++) { String timezoneId = TIMEZONE_IDS[i % TIMEZONE_IDS.length]; java.util.TimeZone timeZone = java.util.TimeZone.getTimeZone(timezoneId); java.util.Calendar calendar = new java.util.GregorianCalendar(timeZone); calendar.set(2010, 5, 4, 3, 2, 1); calendar.set(java.util.Calendar.MILLISECOND, 0); answer = calendar.getTimeInMillis(); } // System.out.println(answer); } @Benchmark public void toMillis_androidIucUtil(int reps) { long answer = 0; for (int i = 0; i < reps; i++) { String timezoneId = TIMEZONE_IDS[i % TIMEZONE_IDS.length]; android.icu.util.TimeZone timeZone = android.icu.util.TimeZone.getTimeZone(timezoneId); android.icu.util.Calendar calendar = new android.icu.util.GregorianCalendar(timeZone); calendar.set(2010, 5, 4, 3, 2, 1); calendar.set(android.icu.util.Calendar.MILLISECOND, 0); answer = calendar.getTimeInMillis(); } // System.out.println(answer); } } services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +3 −3 Original line number Diff line number Diff line Loading @@ -41,7 +41,6 @@ import static android.telephony.CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_ import static android.telephony.CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT; import static android.telephony.SubscriptionPlan.BYTES_UNLIMITED; import static android.telephony.SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED; import static android.text.format.Time.TIMEZONE_UTC; import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS; import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_MULTIPATH; Loading Loading @@ -122,7 +121,6 @@ import android.telephony.SubscriptionPlan; import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.MediumTest; import android.text.TextUtils; import android.text.format.Time; import android.util.DataUnit; import android.util.Log; import android.util.Range; Loading Loading @@ -174,6 +172,7 @@ import java.util.Calendar; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.TimeZone; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; Loading Loading @@ -212,6 +211,7 @@ public class NetworkPolicyManagerServiceTest { * Path on assets where files used by {@link NetPolicyXml} are located. */ private static final String NETPOLICY_DIR = "NetworkPolicyManagerServiceTest/netpolicy"; private static final String TIMEZONE_UTC = "UTC"; private BroadcastInterceptingContext mServiceContext; private File mPolicyDir; Loading Loading @@ -1690,7 +1690,7 @@ public class NetworkPolicyManagerServiceTest { private static NetworkPolicy buildFakeMobilePolicy(int cycleDay, long warningBytes, long limitBytes, boolean inferred){ final NetworkTemplate template = buildTemplateMobileAll(FAKE_SUBSCRIBER_ID); return new NetworkPolicy(template, cycleDay, new Time().timezone, warningBytes, return new NetworkPolicy(template, cycleDay, TimeZone.getDefault().getID(), warningBytes, limitBytes, SNOOZE_NEVER, SNOOZE_NEVER, true, inferred); } Loading Loading
core/java/android/widget/AnalogClock.java +24 −14 Original line number Diff line number Diff line Loading @@ -25,12 +25,14 @@ import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.text.format.DateUtils; import android.text.format.Time; import android.util.AttributeSet; import android.view.View; import android.widget.RemoteViews.RemoteView; import java.util.TimeZone; import java.time.Clock; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; /** * This widget display an analogic clock with two hands for hours and Loading @@ -44,7 +46,7 @@ import java.util.TimeZone; @RemoteView @Deprecated public class AnalogClock extends View { private Time mCalendar; private Clock mClock; private Drawable mHourHand; private Drawable mMinuteHand; Loading Loading @@ -93,7 +95,7 @@ public class AnalogClock extends View { mMinuteHand = context.getDrawable(com.android.internal.R.drawable.clock_hand_minute); } mCalendar = new Time(); mClock = Clock.systemDefaultZone(); mDialWidth = mDial.getIntrinsicWidth(); mDialHeight = mDial.getIntrinsicHeight(); Loading Loading @@ -126,7 +128,7 @@ public class AnalogClock extends View { // in the main thread, therefore the receiver can't run before this method returns. // The time zone may have changed while the receiver wasn't registered, so update the Time mCalendar = new Time(); mClock = Clock.systemDefaultZone(); // Make sure we update to the current time onTimeChanged(); Loading Loading @@ -235,17 +237,18 @@ public class AnalogClock extends View { } private void onTimeChanged() { mCalendar.setToNow(); long nowMillis = mClock.millis(); LocalDateTime localDateTime = toLocalDateTime(nowMillis, mClock.getZone()); int hour = mCalendar.hour; int minute = mCalendar.minute; int second = mCalendar.second; int hour = localDateTime.getHour(); int minute = localDateTime.getMinute(); int second = localDateTime.getSecond(); mMinutes = minute + second / 60.0f; mHour = hour + mMinutes / 60.0f; mChanged = true; updateContentDescription(mCalendar); updateContentDescription(nowMillis); } private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { Loading @@ -253,7 +256,7 @@ public class AnalogClock extends View { public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) { String tz = intent.getStringExtra("time-zone"); mCalendar = new Time(TimeZone.getTimeZone(tz).getID()); mClock = Clock.system(ZoneId.of(tz)); } onTimeChanged(); Loading @@ -262,10 +265,17 @@ public class AnalogClock extends View { } }; private void updateContentDescription(Time time) { private void updateContentDescription(long timeMillis) { final int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_24HOUR; String contentDescription = DateUtils.formatDateTime(mContext, time.toMillis(false), flags); String contentDescription = DateUtils.formatDateTime(mContext, timeMillis, flags); setContentDescription(contentDescription); } private static LocalDateTime toLocalDateTime(long timeMillis, ZoneId zoneId) { // java.time types like LocalDateTime / Instant can support the full range of "long millis" // with room to spare so we do not need to worry about overflow / underflow and the // resulting exceptions while the input to this class is a long. Instant instant = Instant.ofEpochMilli(timeMillis); return LocalDateTime.ofInstant(instant, zoneId); } }
core/java/android/widget/DateTimeView.java +68 −57 Original line number Diff line number Diff line Loading @@ -20,7 +20,6 @@ import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static android.text.format.DateUtils.YEAR_IN_MILLIS; import static android.text.format.Time.getJulianDay; import android.app.ActivityThread; import android.content.BroadcastReceiver; Loading @@ -30,9 +29,7 @@ import android.content.IntentFilter; import android.content.res.Configuration; import android.content.res.TypedArray; import android.database.ContentObserver; import android.icu.util.Calendar; import android.os.Handler; import android.text.format.Time; import android.util.AttributeSet; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.RemoteViews.RemoteView; Loading @@ -40,9 +37,14 @@ import android.widget.RemoteViews.RemoteView; import com.android.internal.R; import java.text.DateFormat; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.ZoneId; import java.time.temporal.JulianFields; import java.util.ArrayList; import java.util.Date; import java.util.TimeZone; // // TODO Loading @@ -61,8 +63,9 @@ public class DateTimeView extends TextView { private static final int SHOW_TIME = 0; private static final int SHOW_MONTH_DAY_YEAR = 1; Date mTime; long mTimeMillis; private long mTimeMillis; // The LocalDateTime equivalent of mTimeMillis but truncated to minute, i.e. no seconds / nanos. private LocalDateTime mLocalTime; int mLastDisplay = -1; DateFormat mLastFormat; Loading Loading @@ -124,11 +127,10 @@ public class DateTimeView extends TextView { } @android.view.RemotableViewMethod public void setTime(long time) { Time t = new Time(); t.set(time); mTimeMillis = t.toMillis(false); mTime = new Date(t.year-1900, t.month, t.monthDay, t.hour, t.minute, 0); public void setTime(long timeMillis) { mTimeMillis = timeMillis; LocalDateTime dateTime = toLocalDateTime(timeMillis, ZoneId.systemDefault()); mLocalTime = dateTime.withSecond(0); update(); } Loading @@ -150,7 +152,7 @@ public class DateTimeView extends TextView { } void update() { if (mTime == null || getVisibility() == GONE) { if (mLocalTime == null || getVisibility() == GONE) { return; } if (mShowRelativeTime) { Loading @@ -159,31 +161,27 @@ public class DateTimeView extends TextView { } int display; Date time = mTime; Time t = new Time(); t.set(mTimeMillis); t.second = 0; t.hour -= 12; long twelveHoursBefore = t.toMillis(false); t.hour += 12; long twelveHoursAfter = t.toMillis(false); t.hour = 0; t.minute = 0; long midnightBefore = t.toMillis(false); t.monthDay++; long midnightAfter = t.toMillis(false); long nowMillis = System.currentTimeMillis(); t.set(nowMillis); t.second = 0; nowMillis = t.normalize(false); ZoneId zoneId = ZoneId.systemDefault(); // localTime is the local time for mTimeMillis but at zero seconds past the minute. LocalDateTime localTime = mLocalTime; LocalDateTime localStartOfDay = LocalDateTime.of(localTime.toLocalDate(), LocalTime.MIDNIGHT); LocalDateTime localTomorrowStartOfDay = localStartOfDay.plusDays(1); // now is current local time but at zero seconds past the minute. LocalDateTime localNow = LocalDateTime.now(zoneId).withSecond(0); long twelveHoursBefore = toEpochMillis(localTime.minusHours(12), zoneId); long twelveHoursAfter = toEpochMillis(localTime.plusHours(12), zoneId); long midnightBefore = toEpochMillis(localStartOfDay, zoneId); long midnightAfter = toEpochMillis(localTomorrowStartOfDay, zoneId); long time = toEpochMillis(localTime, zoneId); long now = toEpochMillis(localNow, zoneId); // Choose the display mode choose_display: { if ((nowMillis >= midnightBefore && nowMillis < midnightAfter) || (nowMillis >= twelveHoursBefore && nowMillis < twelveHoursAfter)) { if ((now >= midnightBefore && now < midnightAfter) || (now >= twelveHoursBefore && now < twelveHoursAfter)) { display = SHOW_TIME; break choose_display; } Loading Loading @@ -212,7 +210,7 @@ public class DateTimeView extends TextView { } // Set the text String text = format.format(mTime); String text = format.format(new Date(time)); setText(text); // Schedule the next update Loading @@ -221,7 +219,7 @@ public class DateTimeView extends TextView { mUpdateTimeMillis = twelveHoursAfter > midnightAfter ? twelveHoursAfter : midnightAfter; } else { // Currently showing the date if (mTimeMillis < nowMillis) { if (mTimeMillis < now) { // If the time is in the past, don't schedule an update mUpdateTimeMillis = 0; } else { Loading Loading @@ -262,15 +260,18 @@ public class DateTimeView extends TextView { millisIncrease = HOUR_IN_MILLIS; } else if (duration < YEAR_IN_MILLIS) { // In weird cases it can become 0 because of daylight savings TimeZone timeZone = TimeZone.getDefault(); count = Math.max(Math.abs(dayDistance(timeZone, mTimeMillis, now)), 1); LocalDateTime localDateTime = mLocalTime; ZoneId zoneId = ZoneId.systemDefault(); LocalDateTime localNow = toLocalDateTime(now, zoneId); count = Math.max(Math.abs(dayDistance(localDateTime, localNow)), 1); result = String.format(getContext().getResources().getQuantityString(past ? com.android.internal.R.plurals.duration_days_shortest : com.android.internal.R.plurals.duration_days_shortest_future, count), count); if (past || count != 1) { mUpdateTimeMillis = computeNextMidnight(timeZone); mUpdateTimeMillis = computeNextMidnight(localNow, zoneId); millisIncrease = -1; } else { millisIncrease = DAY_IN_MILLIS; Loading @@ -296,18 +297,13 @@ public class DateTimeView extends TextView { } /** * @param timeZone the timezone we are in * @return the timepoint in millis at UTC at midnight in the current timezone * Returns the epoch millis for the next midnight in the specified timezone. */ private long computeNextMidnight(TimeZone timeZone) { Calendar c = Calendar.getInstance(); c.setTimeZone(libcore.icu.DateUtilsBridge.icuTimeZone(timeZone)); c.add(Calendar.DAY_OF_MONTH, 1); c.set(Calendar.HOUR_OF_DAY, 0); c.set(Calendar.MINUTE, 0); c.set(Calendar.SECOND, 0); c.set(Calendar.MILLISECOND, 0); return c.getTimeInMillis(); private static long computeNextMidnight(LocalDateTime time, ZoneId zoneId) { // This ignores the chance of overflow: it should never happen. LocalDate tomorrow = time.toLocalDate().plusDays(1); LocalDateTime nextMidnight = LocalDateTime.of(tomorrow, LocalTime.MIDNIGHT); return toEpochMillis(nextMidnight, zoneId); } @Override Loading @@ -325,11 +321,10 @@ public class DateTimeView extends TextView { com.android.internal.R.string.now_string_shortest); } // Return the date difference for the two times in a given timezone. private static int dayDistance(TimeZone timeZone, long startTime, long endTime) { return getJulianDay(endTime, timeZone.getOffset(endTime) / 1000) - getJulianDay(startTime, timeZone.getOffset(startTime) / 1000); // Return the number of days between the two dates. private static int dayDistance(LocalDateTime start, LocalDateTime end) { return (int) (end.getLong(JulianFields.JULIAN_DAY) - start.getLong(JulianFields.JULIAN_DAY)); } private DateFormat getTimeFormat() { Loading Loading @@ -374,8 +369,11 @@ public class DateTimeView extends TextView { count); } else if (duration < YEAR_IN_MILLIS) { // In weird cases it can become 0 because of daylight savings TimeZone timeZone = TimeZone.getDefault(); count = Math.max(Math.abs(dayDistance(timeZone, mTimeMillis, now)), 1); LocalDateTime localDateTime = mLocalTime; ZoneId zoneId = ZoneId.systemDefault(); LocalDateTime localNow = toLocalDateTime(now, zoneId); count = Math.max(Math.abs(dayDistance(localDateTime, localNow)), 1); result = String.format(getContext().getResources().getQuantityString(past ? com.android.internal. R.plurals.duration_days_relative Loading Loading @@ -511,4 +509,17 @@ public class DateTimeView extends TextView { } } } private static LocalDateTime toLocalDateTime(long timeMillis, ZoneId zoneId) { // java.time types like LocalDateTime / Instant can support the full range of "long millis" // with room to spare so we do not need to worry about overflow / underflow and the rsulting // exceptions while the input to this class is a long. Instant instant = Instant.ofEpochMilli(timeMillis); return LocalDateTime.ofInstant(instant, zoneId); } private static long toEpochMillis(LocalDateTime time, ZoneId zoneId) { Instant instant = time.toInstant(zoneId.getRules().getOffset(time)); return instant.toEpochMilli(); } }
core/java/com/android/internal/http/HttpDateTime.javadeleted 100644 → 0 +0 −225 Original line number Diff line number Diff line /* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.http; import android.text.format.Time; import java.util.Calendar; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Helper for parsing an HTTP date. */ public final class HttpDateTime { /* * Regular expression for parsing HTTP-date. * * Wdy, DD Mon YYYY HH:MM:SS GMT * RFC 822, updated by RFC 1123 * * Weekday, DD-Mon-YY HH:MM:SS GMT * RFC 850, obsoleted by RFC 1036 * * Wdy Mon DD HH:MM:SS YYYY * ANSI C's asctime() format * * with following variations * * Wdy, DD-Mon-YYYY HH:MM:SS GMT * Wdy, (SP)D Mon YYYY HH:MM:SS GMT * Wdy,DD Mon YYYY HH:MM:SS GMT * Wdy, DD-Mon-YY HH:MM:SS GMT * Wdy, DD Mon YYYY HH:MM:SS -HHMM * Wdy, DD Mon YYYY HH:MM:SS * Wdy Mon (SP)D HH:MM:SS YYYY * Wdy Mon DD HH:MM:SS YYYY GMT * * HH can be H if the first digit is zero. * * Mon can be the full name of the month. */ private static final String HTTP_DATE_RFC_REGEXP = "([0-9]{1,2})[- ]([A-Za-z]{3,9})[- ]([0-9]{2,4})[ ]" + "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])"; private static final String HTTP_DATE_ANSIC_REGEXP = "[ ]([A-Za-z]{3,9})[ ]+([0-9]{1,2})[ ]" + "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])[ ]([0-9]{2,4})"; /** * The compiled version of the HTTP-date regular expressions. */ private static final Pattern HTTP_DATE_RFC_PATTERN = Pattern.compile(HTTP_DATE_RFC_REGEXP); private static final Pattern HTTP_DATE_ANSIC_PATTERN = Pattern.compile(HTTP_DATE_ANSIC_REGEXP); private static class TimeOfDay { TimeOfDay(int h, int m, int s) { this.hour = h; this.minute = m; this.second = s; } int hour; int minute; int second; } public static long parse(String timeString) throws IllegalArgumentException { int date = 1; int month = Calendar.JANUARY; int year = 1970; TimeOfDay timeOfDay; Matcher rfcMatcher = HTTP_DATE_RFC_PATTERN.matcher(timeString); if (rfcMatcher.find()) { date = getDate(rfcMatcher.group(1)); month = getMonth(rfcMatcher.group(2)); year = getYear(rfcMatcher.group(3)); timeOfDay = getTime(rfcMatcher.group(4)); } else { Matcher ansicMatcher = HTTP_DATE_ANSIC_PATTERN.matcher(timeString); if (ansicMatcher.find()) { month = getMonth(ansicMatcher.group(1)); date = getDate(ansicMatcher.group(2)); timeOfDay = getTime(ansicMatcher.group(3)); year = getYear(ansicMatcher.group(4)); } else { throw new IllegalArgumentException(); } } // FIXME: Y2038 BUG! if (year >= 2038) { year = 2038; month = Calendar.JANUARY; date = 1; } Time time = new Time(Time.TIMEZONE_UTC); time.set(timeOfDay.second, timeOfDay.minute, timeOfDay.hour, date, month, year); return time.toMillis(false /* use isDst */); } private static int getDate(String dateString) { if (dateString.length() == 2) { return (dateString.charAt(0) - '0') * 10 + (dateString.charAt(1) - '0'); } else { return (dateString.charAt(0) - '0'); } } /* * jan = 9 + 0 + 13 = 22 * feb = 5 + 4 + 1 = 10 * mar = 12 + 0 + 17 = 29 * apr = 0 + 15 + 17 = 32 * may = 12 + 0 + 24 = 36 * jun = 9 + 20 + 13 = 42 * jul = 9 + 20 + 11 = 40 * aug = 0 + 20 + 6 = 26 * sep = 18 + 4 + 15 = 37 * oct = 14 + 2 + 19 = 35 * nov = 13 + 14 + 21 = 48 * dec = 3 + 4 + 2 = 9 */ private static int getMonth(String monthString) { int hash = Character.toLowerCase(monthString.charAt(0)) + Character.toLowerCase(monthString.charAt(1)) + Character.toLowerCase(monthString.charAt(2)) - 3 * 'a'; switch (hash) { case 22: return Calendar.JANUARY; case 10: return Calendar.FEBRUARY; case 29: return Calendar.MARCH; case 32: return Calendar.APRIL; case 36: return Calendar.MAY; case 42: return Calendar.JUNE; case 40: return Calendar.JULY; case 26: return Calendar.AUGUST; case 37: return Calendar.SEPTEMBER; case 35: return Calendar.OCTOBER; case 48: return Calendar.NOVEMBER; case 9: return Calendar.DECEMBER; default: throw new IllegalArgumentException(); } } private static int getYear(String yearString) { if (yearString.length() == 2) { int year = (yearString.charAt(0) - '0') * 10 + (yearString.charAt(1) - '0'); if (year >= 70) { return year + 1900; } else { return year + 2000; } } else if (yearString.length() == 3) { // According to RFC 2822, three digit years should be added to 1900. int year = (yearString.charAt(0) - '0') * 100 + (yearString.charAt(1) - '0') * 10 + (yearString.charAt(2) - '0'); return year + 1900; } else if (yearString.length() == 4) { return (yearString.charAt(0) - '0') * 1000 + (yearString.charAt(1) - '0') * 100 + (yearString.charAt(2) - '0') * 10 + (yearString.charAt(3) - '0'); } else { return 1970; } } private static TimeOfDay getTime(String timeString) { // HH might be H int i = 0; int hour = timeString.charAt(i++) - '0'; if (timeString.charAt(i) != ':') hour = hour * 10 + (timeString.charAt(i++) - '0'); // Skip ':' i++; int minute = (timeString.charAt(i++) - '0') * 10 + (timeString.charAt(i++) - '0'); // Skip ':' i++; int second = (timeString.charAt(i++) - '0') * 10 + (timeString.charAt(i++) - '0'); return new TimeOfDay(hour, minute, second); } }
core/tests/benchmarks/src/android/text/format/AndroidTimeVsOthersBenchmark.java 0 → 100644 +85 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.text.format; import com.google.caliper.Benchmark; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZoneOffset; public class AndroidTimeVsOthersBenchmark { private static final String[] TIMEZONE_IDS = { "Europe/London", "America/Los_Angeles", "Asia/Shanghai", }; @Benchmark public void toMillis_androidTime(int reps) { long answer = 0; for (int i = 0; i < reps; i++) { String timezoneId = TIMEZONE_IDS[i % TIMEZONE_IDS.length]; Time time = new Time(timezoneId); time.set(1, 2, 3, 4, 5, 2010); answer = time.toMillis(false); } // System.out.println(answer); } @Benchmark public void toMillis_javaTime(int reps) { long answer = 0; for (int i = 0; i < reps; i++) { String timezoneId = TIMEZONE_IDS[i % TIMEZONE_IDS.length]; LocalDateTime time = LocalDateTime.of(2010, 5 + 1, 4, 3, 2, 1); ZoneOffset offset = ZoneId.of(timezoneId).getRules().getOffset(time); answer = time.toInstant(offset).toEpochMilli(); } // System.out.println(answer); } @Benchmark public void toMillis_javaUtil(int reps) { long answer = 0; for (int i = 0; i < reps; i++) { String timezoneId = TIMEZONE_IDS[i % TIMEZONE_IDS.length]; java.util.TimeZone timeZone = java.util.TimeZone.getTimeZone(timezoneId); java.util.Calendar calendar = new java.util.GregorianCalendar(timeZone); calendar.set(2010, 5, 4, 3, 2, 1); calendar.set(java.util.Calendar.MILLISECOND, 0); answer = calendar.getTimeInMillis(); } // System.out.println(answer); } @Benchmark public void toMillis_androidIucUtil(int reps) { long answer = 0; for (int i = 0; i < reps; i++) { String timezoneId = TIMEZONE_IDS[i % TIMEZONE_IDS.length]; android.icu.util.TimeZone timeZone = android.icu.util.TimeZone.getTimeZone(timezoneId); android.icu.util.Calendar calendar = new android.icu.util.GregorianCalendar(timeZone); calendar.set(2010, 5, 4, 3, 2, 1); calendar.set(android.icu.util.Calendar.MILLISECOND, 0); answer = calendar.getTimeInMillis(); } // System.out.println(answer); } }
services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +3 −3 Original line number Diff line number Diff line Loading @@ -41,7 +41,6 @@ import static android.telephony.CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_ import static android.telephony.CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT; import static android.telephony.SubscriptionPlan.BYTES_UNLIMITED; import static android.telephony.SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED; import static android.text.format.Time.TIMEZONE_UTC; import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS; import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_MULTIPATH; Loading Loading @@ -122,7 +121,6 @@ import android.telephony.SubscriptionPlan; import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.MediumTest; import android.text.TextUtils; import android.text.format.Time; import android.util.DataUnit; import android.util.Log; import android.util.Range; Loading Loading @@ -174,6 +172,7 @@ import java.util.Calendar; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.TimeZone; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; Loading Loading @@ -212,6 +211,7 @@ public class NetworkPolicyManagerServiceTest { * Path on assets where files used by {@link NetPolicyXml} are located. */ private static final String NETPOLICY_DIR = "NetworkPolicyManagerServiceTest/netpolicy"; private static final String TIMEZONE_UTC = "UTC"; private BroadcastInterceptingContext mServiceContext; private File mPolicyDir; Loading Loading @@ -1690,7 +1690,7 @@ public class NetworkPolicyManagerServiceTest { private static NetworkPolicy buildFakeMobilePolicy(int cycleDay, long warningBytes, long limitBytes, boolean inferred){ final NetworkTemplate template = buildTemplateMobileAll(FAKE_SUBSCRIBER_ID); return new NetworkPolicy(template, cycleDay, new Time().timezone, warningBytes, return new NetworkPolicy(template, cycleDay, TimeZone.getDefault().getID(), warningBytes, limitBytes, SNOOZE_NEVER, SNOOZE_NEVER, true, inferred); } Loading