Loading core/java/android/widget/AnalogClock.java +24 −14 Original line number Original line Diff line number Diff line Loading @@ -26,12 +26,14 @@ import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable; import android.text.format.DateUtils; import android.text.format.DateUtils; import android.text.format.Time; import android.util.AttributeSet; import android.util.AttributeSet; import android.view.View; import android.view.View; import android.widget.RemoteViews.RemoteView; 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 * This widget display an analogic clock with two hands for hours and Loading @@ -45,7 +47,7 @@ import java.util.TimeZone; @RemoteView @RemoteView @Deprecated @Deprecated public class AnalogClock extends View { public class AnalogClock extends View { private Time mCalendar; private Clock mClock; @UnsupportedAppUsage @UnsupportedAppUsage private Drawable mHourHand; private Drawable mHourHand; Loading Loading @@ -99,7 +101,7 @@ public class AnalogClock extends View { mMinuteHand = context.getDrawable(com.android.internal.R.drawable.clock_hand_minute); mMinuteHand = context.getDrawable(com.android.internal.R.drawable.clock_hand_minute); } } mCalendar = new Time(); mClock = Clock.systemDefaultZone(); mDialWidth = mDial.getIntrinsicWidth(); mDialWidth = mDial.getIntrinsicWidth(); mDialHeight = mDial.getIntrinsicHeight(); mDialHeight = mDial.getIntrinsicHeight(); Loading Loading @@ -132,7 +134,7 @@ public class AnalogClock extends View { // in the main thread, therefore the receiver can't run before this method returns. // 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 // 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 // Make sure we update to the current time onTimeChanged(); onTimeChanged(); Loading Loading @@ -241,17 +243,18 @@ public class AnalogClock extends View { } } private void onTimeChanged() { private void onTimeChanged() { mCalendar.setToNow(); long nowMillis = mClock.millis(); LocalDateTime localDateTime = toLocalDateTime(nowMillis, mClock.getZone()); int hour = mCalendar.hour; int hour = localDateTime.getHour(); int minute = mCalendar.minute; int minute = localDateTime.getMinute(); int second = mCalendar.second; int second = localDateTime.getSecond(); mMinutes = minute + second / 60.0f; mMinutes = minute + second / 60.0f; mHour = hour + mMinutes / 60.0f; mHour = hour + mMinutes / 60.0f; mChanged = true; mChanged = true; updateContentDescription(mCalendar); updateContentDescription(nowMillis); } } private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { Loading @@ -259,7 +262,7 @@ public class AnalogClock extends View { public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) { if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) { String tz = intent.getStringExtra("time-zone"); String tz = intent.getStringExtra("time-zone"); mCalendar = new Time(TimeZone.getTimeZone(tz).getID()); mClock = Clock.system(ZoneId.of(tz)); } } onTimeChanged(); onTimeChanged(); Loading @@ -268,10 +271,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; final int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_24HOUR; String contentDescription = DateUtils.formatDateTime(mContext, String contentDescription = DateUtils.formatDateTime(mContext, timeMillis, flags); time.toMillis(false), flags); setContentDescription(contentDescription); 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 Original line 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.HOUR_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_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.DateUtils.YEAR_IN_MILLIS; import static android.text.format.Time.getJulianDay; import android.annotation.UnsupportedAppUsage; import android.annotation.UnsupportedAppUsage; import android.app.ActivityThread; import android.app.ActivityThread; Loading @@ -32,7 +31,6 @@ import android.content.res.Configuration; import android.content.res.TypedArray; import android.content.res.TypedArray; import android.database.ContentObserver; import android.database.ContentObserver; import android.os.Handler; import android.os.Handler; import android.text.format.Time; import android.util.AttributeSet; import android.util.AttributeSet; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo; import android.view.inspector.InspectableProperty; import android.view.inspector.InspectableProperty; Loading @@ -41,10 +39,14 @@ import android.widget.RemoteViews.RemoteView; import com.android.internal.R; import com.android.internal.R; import java.text.DateFormat; 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.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.Date; import java.util.TimeZone; // // // TODO // TODO Loading @@ -63,8 +65,9 @@ public class DateTimeView extends TextView { private static final int SHOW_TIME = 0; private static final int SHOW_TIME = 0; private static final int SHOW_MONTH_DAY_YEAR = 1; private static final int SHOW_MONTH_DAY_YEAR = 1; Date mTime; private long mTimeMillis; long mTimeMillis; // The LocalDateTime equivalent of mTimeMillis but truncated to minute, i.e. no seconds / nanos. private LocalDateTime mLocalTime; int mLastDisplay = -1; int mLastDisplay = -1; DateFormat mLastFormat; DateFormat mLastFormat; Loading Loading @@ -128,11 +131,10 @@ public class DateTimeView extends TextView { @android.view.RemotableViewMethod @android.view.RemotableViewMethod @UnsupportedAppUsage @UnsupportedAppUsage public void setTime(long time) { public void setTime(long timeMillis) { Time t = new Time(); mTimeMillis = timeMillis; t.set(time); LocalDateTime dateTime = toLocalDateTime(timeMillis, ZoneId.systemDefault()); mTimeMillis = t.toMillis(false); mLocalTime = dateTime.withSecond(0); mTime = new Date(t.year-1900, t.month, t.monthDay, t.hour, t.minute, 0); update(); update(); } } Loading Loading @@ -165,7 +167,7 @@ public class DateTimeView extends TextView { @UnsupportedAppUsage @UnsupportedAppUsage void update() { void update() { if (mTime == null || getVisibility() == GONE) { if (mLocalTime == null || getVisibility() == GONE) { return; return; } } if (mShowRelativeTime) { if (mShowRelativeTime) { Loading @@ -174,31 +176,27 @@ public class DateTimeView extends TextView { } } int display; int display; Date time = mTime; ZoneId zoneId = ZoneId.systemDefault(); Time t = new Time(); // localTime is the local time for mTimeMillis but at zero seconds past the minute. t.set(mTimeMillis); LocalDateTime localTime = mLocalTime; t.second = 0; LocalDateTime localStartOfDay = LocalDateTime.of(localTime.toLocalDate(), LocalTime.MIDNIGHT); t.hour -= 12; LocalDateTime localTomorrowStartOfDay = localStartOfDay.plusDays(1); long twelveHoursBefore = t.toMillis(false); // now is current local time but at zero seconds past the minute. t.hour += 12; LocalDateTime localNow = LocalDateTime.now(zoneId).withSecond(0); long twelveHoursAfter = t.toMillis(false); t.hour = 0; long twelveHoursBefore = toEpochMillis(localTime.minusHours(12), zoneId); t.minute = 0; long twelveHoursAfter = toEpochMillis(localTime.plusHours(12), zoneId); long midnightBefore = t.toMillis(false); long midnightBefore = toEpochMillis(localStartOfDay, zoneId); t.monthDay++; long midnightAfter = toEpochMillis(localTomorrowStartOfDay, zoneId); long midnightAfter = t.toMillis(false); long time = toEpochMillis(localTime, zoneId); long now = toEpochMillis(localNow, zoneId); long nowMillis = System.currentTimeMillis(); t.set(nowMillis); t.second = 0; nowMillis = t.normalize(false); // Choose the display mode // Choose the display mode choose_display: { choose_display: { if ((nowMillis >= midnightBefore && nowMillis < midnightAfter) if ((now >= midnightBefore && now < midnightAfter) || (nowMillis >= twelveHoursBefore && nowMillis < twelveHoursAfter)) { || (now >= twelveHoursBefore && now < twelveHoursAfter)) { display = SHOW_TIME; display = SHOW_TIME; break choose_display; break choose_display; } } Loading Loading @@ -227,7 +225,7 @@ public class DateTimeView extends TextView { } } // Set the text // Set the text String text = format.format(mTime); String text = format.format(new Date(time)); setText(text); setText(text); // Schedule the next update // Schedule the next update Loading @@ -236,7 +234,7 @@ public class DateTimeView extends TextView { mUpdateTimeMillis = twelveHoursAfter > midnightAfter ? twelveHoursAfter : midnightAfter; mUpdateTimeMillis = twelveHoursAfter > midnightAfter ? twelveHoursAfter : midnightAfter; } else { } else { // Currently showing the date // Currently showing the date if (mTimeMillis < nowMillis) { if (mTimeMillis < now) { // If the time is in the past, don't schedule an update // If the time is in the past, don't schedule an update mUpdateTimeMillis = 0; mUpdateTimeMillis = 0; } else { } else { Loading Loading @@ -277,15 +275,18 @@ public class DateTimeView extends TextView { millisIncrease = HOUR_IN_MILLIS; millisIncrease = HOUR_IN_MILLIS; } else if (duration < YEAR_IN_MILLIS) { } else if (duration < YEAR_IN_MILLIS) { // In weird cases it can become 0 because of daylight savings // In weird cases it can become 0 because of daylight savings TimeZone timeZone = TimeZone.getDefault(); LocalDateTime localDateTime = mLocalTime; count = Math.max(Math.abs(dayDistance(timeZone, mTimeMillis, now)), 1); 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 result = String.format(getContext().getResources().getQuantityString(past ? com.android.internal.R.plurals.duration_days_shortest ? com.android.internal.R.plurals.duration_days_shortest : com.android.internal.R.plurals.duration_days_shortest_future, : com.android.internal.R.plurals.duration_days_shortest_future, count), count), count); count); if (past || count != 1) { if (past || count != 1) { mUpdateTimeMillis = computeNextMidnight(timeZone); mUpdateTimeMillis = computeNextMidnight(localNow, zoneId); millisIncrease = -1; millisIncrease = -1; } else { } else { millisIncrease = DAY_IN_MILLIS; millisIncrease = DAY_IN_MILLIS; Loading @@ -311,18 +312,13 @@ public class DateTimeView extends TextView { } } /** /** * @param timeZone the timezone we are in * Returns the epoch millis for the next midnight in the specified timezone. * @return the timepoint in millis at UTC at midnight in the current timezone */ */ private long computeNextMidnight(TimeZone timeZone) { private static long computeNextMidnight(LocalDateTime time, ZoneId zoneId) { Calendar c = Calendar.getInstance(); // This ignores the chance of overflow: it should never happen. c.setTimeZone(timeZone); LocalDate tomorrow = time.toLocalDate().plusDays(1); c.add(Calendar.DAY_OF_MONTH, 1); LocalDateTime nextMidnight = LocalDateTime.of(tomorrow, LocalTime.MIDNIGHT); c.set(Calendar.HOUR_OF_DAY, 0); return toEpochMillis(nextMidnight, zoneId); c.set(Calendar.MINUTE, 0); c.set(Calendar.SECOND, 0); c.set(Calendar.MILLISECOND, 0); return c.getTimeInMillis(); } } @Override @Override Loading @@ -340,11 +336,10 @@ public class DateTimeView extends TextView { com.android.internal.R.string.now_string_shortest); com.android.internal.R.string.now_string_shortest); } } // Return the date difference for the two times in a given timezone. // Return the number of days between the two dates. private static int dayDistance(TimeZone timeZone, long startTime, private static int dayDistance(LocalDateTime start, LocalDateTime end) { long endTime) { return (int) (end.getLong(JulianFields.JULIAN_DAY) return getJulianDay(endTime, timeZone.getOffset(endTime) / 1000) - start.getLong(JulianFields.JULIAN_DAY)); - getJulianDay(startTime, timeZone.getOffset(startTime) / 1000); } } private DateFormat getTimeFormat() { private DateFormat getTimeFormat() { Loading Loading @@ -389,8 +384,11 @@ public class DateTimeView extends TextView { count); count); } else if (duration < YEAR_IN_MILLIS) { } else if (duration < YEAR_IN_MILLIS) { // In weird cases it can become 0 because of daylight savings // In weird cases it can become 0 because of daylight savings TimeZone timeZone = TimeZone.getDefault(); LocalDateTime localDateTime = mLocalTime; count = Math.max(Math.abs(dayDistance(timeZone, mTimeMillis, now)), 1); 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 result = String.format(getContext().getResources().getQuantityString(past ? com.android.internal. ? com.android.internal. R.plurals.duration_days_relative R.plurals.duration_days_relative Loading Loading @@ -526,4 +524,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(); } } } Loading
core/java/android/widget/AnalogClock.java +24 −14 Original line number Original line Diff line number Diff line Loading @@ -26,12 +26,14 @@ import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable; import android.text.format.DateUtils; import android.text.format.DateUtils; import android.text.format.Time; import android.util.AttributeSet; import android.util.AttributeSet; import android.view.View; import android.view.View; import android.widget.RemoteViews.RemoteView; 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 * This widget display an analogic clock with two hands for hours and Loading @@ -45,7 +47,7 @@ import java.util.TimeZone; @RemoteView @RemoteView @Deprecated @Deprecated public class AnalogClock extends View { public class AnalogClock extends View { private Time mCalendar; private Clock mClock; @UnsupportedAppUsage @UnsupportedAppUsage private Drawable mHourHand; private Drawable mHourHand; Loading Loading @@ -99,7 +101,7 @@ public class AnalogClock extends View { mMinuteHand = context.getDrawable(com.android.internal.R.drawable.clock_hand_minute); mMinuteHand = context.getDrawable(com.android.internal.R.drawable.clock_hand_minute); } } mCalendar = new Time(); mClock = Clock.systemDefaultZone(); mDialWidth = mDial.getIntrinsicWidth(); mDialWidth = mDial.getIntrinsicWidth(); mDialHeight = mDial.getIntrinsicHeight(); mDialHeight = mDial.getIntrinsicHeight(); Loading Loading @@ -132,7 +134,7 @@ public class AnalogClock extends View { // in the main thread, therefore the receiver can't run before this method returns. // 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 // 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 // Make sure we update to the current time onTimeChanged(); onTimeChanged(); Loading Loading @@ -241,17 +243,18 @@ public class AnalogClock extends View { } } private void onTimeChanged() { private void onTimeChanged() { mCalendar.setToNow(); long nowMillis = mClock.millis(); LocalDateTime localDateTime = toLocalDateTime(nowMillis, mClock.getZone()); int hour = mCalendar.hour; int hour = localDateTime.getHour(); int minute = mCalendar.minute; int minute = localDateTime.getMinute(); int second = mCalendar.second; int second = localDateTime.getSecond(); mMinutes = minute + second / 60.0f; mMinutes = minute + second / 60.0f; mHour = hour + mMinutes / 60.0f; mHour = hour + mMinutes / 60.0f; mChanged = true; mChanged = true; updateContentDescription(mCalendar); updateContentDescription(nowMillis); } } private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { Loading @@ -259,7 +262,7 @@ public class AnalogClock extends View { public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) { if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) { String tz = intent.getStringExtra("time-zone"); String tz = intent.getStringExtra("time-zone"); mCalendar = new Time(TimeZone.getTimeZone(tz).getID()); mClock = Clock.system(ZoneId.of(tz)); } } onTimeChanged(); onTimeChanged(); Loading @@ -268,10 +271,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; final int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_24HOUR; String contentDescription = DateUtils.formatDateTime(mContext, String contentDescription = DateUtils.formatDateTime(mContext, timeMillis, flags); time.toMillis(false), flags); setContentDescription(contentDescription); 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 Original line 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.HOUR_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_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.DateUtils.YEAR_IN_MILLIS; import static android.text.format.Time.getJulianDay; import android.annotation.UnsupportedAppUsage; import android.annotation.UnsupportedAppUsage; import android.app.ActivityThread; import android.app.ActivityThread; Loading @@ -32,7 +31,6 @@ import android.content.res.Configuration; import android.content.res.TypedArray; import android.content.res.TypedArray; import android.database.ContentObserver; import android.database.ContentObserver; import android.os.Handler; import android.os.Handler; import android.text.format.Time; import android.util.AttributeSet; import android.util.AttributeSet; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo; import android.view.inspector.InspectableProperty; import android.view.inspector.InspectableProperty; Loading @@ -41,10 +39,14 @@ import android.widget.RemoteViews.RemoteView; import com.android.internal.R; import com.android.internal.R; import java.text.DateFormat; 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.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.Date; import java.util.TimeZone; // // // TODO // TODO Loading @@ -63,8 +65,9 @@ public class DateTimeView extends TextView { private static final int SHOW_TIME = 0; private static final int SHOW_TIME = 0; private static final int SHOW_MONTH_DAY_YEAR = 1; private static final int SHOW_MONTH_DAY_YEAR = 1; Date mTime; private long mTimeMillis; long mTimeMillis; // The LocalDateTime equivalent of mTimeMillis but truncated to minute, i.e. no seconds / nanos. private LocalDateTime mLocalTime; int mLastDisplay = -1; int mLastDisplay = -1; DateFormat mLastFormat; DateFormat mLastFormat; Loading Loading @@ -128,11 +131,10 @@ public class DateTimeView extends TextView { @android.view.RemotableViewMethod @android.view.RemotableViewMethod @UnsupportedAppUsage @UnsupportedAppUsage public void setTime(long time) { public void setTime(long timeMillis) { Time t = new Time(); mTimeMillis = timeMillis; t.set(time); LocalDateTime dateTime = toLocalDateTime(timeMillis, ZoneId.systemDefault()); mTimeMillis = t.toMillis(false); mLocalTime = dateTime.withSecond(0); mTime = new Date(t.year-1900, t.month, t.monthDay, t.hour, t.minute, 0); update(); update(); } } Loading Loading @@ -165,7 +167,7 @@ public class DateTimeView extends TextView { @UnsupportedAppUsage @UnsupportedAppUsage void update() { void update() { if (mTime == null || getVisibility() == GONE) { if (mLocalTime == null || getVisibility() == GONE) { return; return; } } if (mShowRelativeTime) { if (mShowRelativeTime) { Loading @@ -174,31 +176,27 @@ public class DateTimeView extends TextView { } } int display; int display; Date time = mTime; ZoneId zoneId = ZoneId.systemDefault(); Time t = new Time(); // localTime is the local time for mTimeMillis but at zero seconds past the minute. t.set(mTimeMillis); LocalDateTime localTime = mLocalTime; t.second = 0; LocalDateTime localStartOfDay = LocalDateTime.of(localTime.toLocalDate(), LocalTime.MIDNIGHT); t.hour -= 12; LocalDateTime localTomorrowStartOfDay = localStartOfDay.plusDays(1); long twelveHoursBefore = t.toMillis(false); // now is current local time but at zero seconds past the minute. t.hour += 12; LocalDateTime localNow = LocalDateTime.now(zoneId).withSecond(0); long twelveHoursAfter = t.toMillis(false); t.hour = 0; long twelveHoursBefore = toEpochMillis(localTime.minusHours(12), zoneId); t.minute = 0; long twelveHoursAfter = toEpochMillis(localTime.plusHours(12), zoneId); long midnightBefore = t.toMillis(false); long midnightBefore = toEpochMillis(localStartOfDay, zoneId); t.monthDay++; long midnightAfter = toEpochMillis(localTomorrowStartOfDay, zoneId); long midnightAfter = t.toMillis(false); long time = toEpochMillis(localTime, zoneId); long now = toEpochMillis(localNow, zoneId); long nowMillis = System.currentTimeMillis(); t.set(nowMillis); t.second = 0; nowMillis = t.normalize(false); // Choose the display mode // Choose the display mode choose_display: { choose_display: { if ((nowMillis >= midnightBefore && nowMillis < midnightAfter) if ((now >= midnightBefore && now < midnightAfter) || (nowMillis >= twelveHoursBefore && nowMillis < twelveHoursAfter)) { || (now >= twelveHoursBefore && now < twelveHoursAfter)) { display = SHOW_TIME; display = SHOW_TIME; break choose_display; break choose_display; } } Loading Loading @@ -227,7 +225,7 @@ public class DateTimeView extends TextView { } } // Set the text // Set the text String text = format.format(mTime); String text = format.format(new Date(time)); setText(text); setText(text); // Schedule the next update // Schedule the next update Loading @@ -236,7 +234,7 @@ public class DateTimeView extends TextView { mUpdateTimeMillis = twelveHoursAfter > midnightAfter ? twelveHoursAfter : midnightAfter; mUpdateTimeMillis = twelveHoursAfter > midnightAfter ? twelveHoursAfter : midnightAfter; } else { } else { // Currently showing the date // Currently showing the date if (mTimeMillis < nowMillis) { if (mTimeMillis < now) { // If the time is in the past, don't schedule an update // If the time is in the past, don't schedule an update mUpdateTimeMillis = 0; mUpdateTimeMillis = 0; } else { } else { Loading Loading @@ -277,15 +275,18 @@ public class DateTimeView extends TextView { millisIncrease = HOUR_IN_MILLIS; millisIncrease = HOUR_IN_MILLIS; } else if (duration < YEAR_IN_MILLIS) { } else if (duration < YEAR_IN_MILLIS) { // In weird cases it can become 0 because of daylight savings // In weird cases it can become 0 because of daylight savings TimeZone timeZone = TimeZone.getDefault(); LocalDateTime localDateTime = mLocalTime; count = Math.max(Math.abs(dayDistance(timeZone, mTimeMillis, now)), 1); 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 result = String.format(getContext().getResources().getQuantityString(past ? com.android.internal.R.plurals.duration_days_shortest ? com.android.internal.R.plurals.duration_days_shortest : com.android.internal.R.plurals.duration_days_shortest_future, : com.android.internal.R.plurals.duration_days_shortest_future, count), count), count); count); if (past || count != 1) { if (past || count != 1) { mUpdateTimeMillis = computeNextMidnight(timeZone); mUpdateTimeMillis = computeNextMidnight(localNow, zoneId); millisIncrease = -1; millisIncrease = -1; } else { } else { millisIncrease = DAY_IN_MILLIS; millisIncrease = DAY_IN_MILLIS; Loading @@ -311,18 +312,13 @@ public class DateTimeView extends TextView { } } /** /** * @param timeZone the timezone we are in * Returns the epoch millis for the next midnight in the specified timezone. * @return the timepoint in millis at UTC at midnight in the current timezone */ */ private long computeNextMidnight(TimeZone timeZone) { private static long computeNextMidnight(LocalDateTime time, ZoneId zoneId) { Calendar c = Calendar.getInstance(); // This ignores the chance of overflow: it should never happen. c.setTimeZone(timeZone); LocalDate tomorrow = time.toLocalDate().plusDays(1); c.add(Calendar.DAY_OF_MONTH, 1); LocalDateTime nextMidnight = LocalDateTime.of(tomorrow, LocalTime.MIDNIGHT); c.set(Calendar.HOUR_OF_DAY, 0); return toEpochMillis(nextMidnight, zoneId); c.set(Calendar.MINUTE, 0); c.set(Calendar.SECOND, 0); c.set(Calendar.MILLISECOND, 0); return c.getTimeInMillis(); } } @Override @Override Loading @@ -340,11 +336,10 @@ public class DateTimeView extends TextView { com.android.internal.R.string.now_string_shortest); com.android.internal.R.string.now_string_shortest); } } // Return the date difference for the two times in a given timezone. // Return the number of days between the two dates. private static int dayDistance(TimeZone timeZone, long startTime, private static int dayDistance(LocalDateTime start, LocalDateTime end) { long endTime) { return (int) (end.getLong(JulianFields.JULIAN_DAY) return getJulianDay(endTime, timeZone.getOffset(endTime) / 1000) - start.getLong(JulianFields.JULIAN_DAY)); - getJulianDay(startTime, timeZone.getOffset(startTime) / 1000); } } private DateFormat getTimeFormat() { private DateFormat getTimeFormat() { Loading Loading @@ -389,8 +384,11 @@ public class DateTimeView extends TextView { count); count); } else if (duration < YEAR_IN_MILLIS) { } else if (duration < YEAR_IN_MILLIS) { // In weird cases it can become 0 because of daylight savings // In weird cases it can become 0 because of daylight savings TimeZone timeZone = TimeZone.getDefault(); LocalDateTime localDateTime = mLocalTime; count = Math.max(Math.abs(dayDistance(timeZone, mTimeMillis, now)), 1); 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 result = String.format(getContext().getResources().getQuantityString(past ? com.android.internal. ? com.android.internal. R.plurals.duration_days_relative R.plurals.duration_days_relative Loading Loading @@ -526,4 +524,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(); } } }