OSDN Git Service

Handle night display state when timezone changes
authorChristine Franks <christyfranks@google.com>
Fri, 25 Aug 2017 20:57:26 +0000 (13:57 -0700)
committerChristine Franks <christyfranks@google.com>
Wed, 20 Sep 2017 03:35:49 +0000 (03:35 +0000)
Bug: 64458884
Test: runtest -c com.android.server.NightDisplayServiceTest \
frameworks-services and manually tested Hawaii and London

Change-Id: I052034a4c64eb73c42672215e8847c11e00efeb5
(cherry picked from commit 2ce862c3ed3739c4bb794794fb57e00fe233a3d5)

config/compiled-classes-phone
config/preloaded-classes
core/java/android/provider/Settings.java
core/java/com/android/internal/app/NightDisplayController.java
services/core/java/com/android/server/display/NightDisplayService.java
services/core/java/com/android/server/twilight/TwilightState.java
services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java

index c829728..c1cbb64 100644 (file)
@@ -5243,7 +5243,6 @@ com.android.internal.app.IVoiceInteractor
 com.android.internal.app.IVoiceInteractor$Stub
 com.android.internal.app.NightDisplayController
 com.android.internal.app.NightDisplayController$Callback
-com.android.internal.app.NightDisplayController$LocalTime
 com.android.internal.app.ProcessMap
 com.android.internal.app.ResolverActivity
 com.android.internal.app.ToolbarActionBar
index cb3cbe1..2844efb 100644 (file)
@@ -2786,7 +2786,6 @@ com.android.internal.app.IVoiceInteractionManagerService$Stub
 com.android.internal.app.IVoiceInteractor
 com.android.internal.app.IVoiceInteractor$Stub
 com.android.internal.app.NightDisplayController
-com.android.internal.app.NightDisplayController$1
 com.android.internal.appwidget.IAppWidgetService
 com.android.internal.appwidget.IAppWidgetService$Stub
 com.android.internal.appwidget.IAppWidgetService$Stub$Proxy
index 40ced6c..a5c55ba 100755 (executable)
@@ -6966,8 +6966,9 @@ public final class Settings {
         public static final String NIGHT_DISPLAY_CUSTOM_END_TIME = "night_display_custom_end_time";
 
         /**
-         * Time in milliseconds (since epoch) when Night display was last activated. Use to decide
-         * whether to apply the current activated state after a reboot or user change.
+         * A String representing the LocalDateTime when Night display was last activated. Use to
+         * decide whether to apply the current activated state after a reboot or user change. In
+         * legacy cases, this is represented by the time in milliseconds (since epoch).
          * @hide
          */
         public static final String NIGHT_DISPLAY_LAST_ACTIVATED_TIME =
index 860c5c4..7a1383c 100644 (file)
@@ -32,8 +32,12 @@ import com.android.internal.R;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.Calendar;
-import java.util.Locale;
+import java.time.DateTimeException;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeParseException;
 
 /**
  * Controller for managing Night display settings.
@@ -116,8 +120,9 @@ public final class NightDisplayController {
      */
     public boolean setActivated(boolean activated) {
         if (isActivated() != activated) {
-            Secure.putLongForUser(mContext.getContentResolver(),
-                    Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, System.currentTimeMillis(),
+            Secure.putStringForUser(mContext.getContentResolver(),
+                    Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
+                    LocalDateTime.now().toString(),
                     mUserId);
         }
         return Secure.putIntForUser(mContext.getContentResolver(),
@@ -128,17 +133,22 @@ public final class NightDisplayController {
      * Returns the time when Night display's activation state last changed, or {@code null} if it
      * has never been changed.
      */
-    public Calendar getLastActivatedTime() {
+    public LocalDateTime getLastActivatedTime() {
         final ContentResolver cr = mContext.getContentResolver();
-        final long lastActivatedTimeMillis = Secure.getLongForUser(
-                cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, -1, mUserId);
-        if (lastActivatedTimeMillis < 0) {
-            return null;
+        final String lastActivatedTime = Secure.getStringForUser(
+                cr, Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, mUserId);
+        if (lastActivatedTime != null) {
+            try {
+                return LocalDateTime.parse(lastActivatedTime);
+            } catch (DateTimeParseException ignored) {}
+            // Uses the old epoch time.
+            try {
+                return LocalDateTime.ofInstant(
+                    Instant.ofEpochMilli(Long.parseLong(lastActivatedTime)),
+                    ZoneId.systemDefault());
+            } catch (DateTimeException|NumberFormatException ignored) {}
         }
-
-        final Calendar lastActivatedTime = Calendar.getInstance();
-        lastActivatedTime.setTimeInMillis(lastActivatedTimeMillis);
-        return lastActivatedTime;
+        return null;
     }
 
     /**
@@ -183,8 +193,10 @@ public final class NightDisplayController {
         }
 
         if (getAutoMode() != autoMode) {
-            Secure.putLongForUser(mContext.getContentResolver(),
-                    Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, -1L, mUserId);
+            Secure.putStringForUser(mContext.getContentResolver(),
+                    Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
+                    null,
+                    mUserId);
         }
         return Secure.putIntForUser(mContext.getContentResolver(),
                 Secure.NIGHT_DISPLAY_AUTO_MODE, autoMode, mUserId);
@@ -206,7 +218,7 @@ public final class NightDisplayController {
                     R.integer.config_defaultNightDisplayCustomStartTime);
         }
 
-        return LocalTime.valueOf(startTimeValue);
+        return LocalTime.ofSecondOfDay(startTimeValue / 1000);
     }
 
     /**
@@ -221,7 +233,7 @@ public final class NightDisplayController {
             throw new IllegalArgumentException("startTime cannot be null");
         }
         return Secure.putIntForUser(mContext.getContentResolver(),
-                Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, startTime.toMillis(), mUserId);
+                Secure.NIGHT_DISPLAY_CUSTOM_START_TIME, startTime.toSecondOfDay() * 1000, mUserId);
     }
 
     /**
@@ -240,7 +252,7 @@ public final class NightDisplayController {
                     R.integer.config_defaultNightDisplayCustomEndTime);
         }
 
-        return LocalTime.valueOf(endTimeValue);
+        return LocalTime.ofSecondOfDay(endTimeValue / 1000);
     }
 
     /**
@@ -255,7 +267,7 @@ public final class NightDisplayController {
             throw new IllegalArgumentException("endTime cannot be null");
         }
         return Secure.putIntForUser(mContext.getContentResolver(),
-                Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, endTime.toMillis(), mUserId);
+                Secure.NIGHT_DISPLAY_CUSTOM_END_TIME, endTime.toSecondOfDay() * 1000, mUserId);
     }
 
     /**
@@ -379,106 +391,6 @@ public final class NightDisplayController {
     }
 
     /**
-     * A time without a time-zone or date.
-     */
-    public static class LocalTime {
-
-        /**
-         * The hour of the day from 0 - 23.
-         */
-        public final int hourOfDay;
-        /**
-         * The minute within the hour from 0 - 59.
-         */
-        public final int minute;
-
-        public LocalTime(int hourOfDay, int minute) {
-            if (hourOfDay < 0 || hourOfDay > 23) {
-                throw new IllegalArgumentException("Invalid hourOfDay: " + hourOfDay);
-            } else if (minute < 0 || minute > 59) {
-                throw new IllegalArgumentException("Invalid minute: " + minute);
-            }
-
-            this.hourOfDay = hourOfDay;
-            this.minute = minute;
-        }
-
-        /**
-         * Returns the first date time corresponding to this local time that occurs before the
-         * provided date time.
-         *
-         * @param time the date time to compare against
-         * @return the prior date time corresponding to this local time
-         */
-        public Calendar getDateTimeBefore(Calendar time) {
-            final Calendar c = Calendar.getInstance();
-            c.set(Calendar.YEAR, time.get(Calendar.YEAR));
-            c.set(Calendar.DAY_OF_YEAR, time.get(Calendar.DAY_OF_YEAR));
-
-            c.set(Calendar.HOUR_OF_DAY, hourOfDay);
-            c.set(Calendar.MINUTE, minute);
-            c.set(Calendar.SECOND, 0);
-            c.set(Calendar.MILLISECOND, 0);
-
-            // Check if the local time has past, if so return the same time tomorrow.
-            if (c.after(time)) {
-                c.add(Calendar.DATE, -1);
-            }
-
-            return c;
-        }
-
-        /**
-         * Returns the first date time corresponding to this local time that occurs after the
-         * provided date time.
-         *
-         * @param time the date time to compare against
-         * @return the next date time corresponding to this local time
-         */
-        public Calendar getDateTimeAfter(Calendar time) {
-            final Calendar c = Calendar.getInstance();
-            c.set(Calendar.YEAR, time.get(Calendar.YEAR));
-            c.set(Calendar.DAY_OF_YEAR, time.get(Calendar.DAY_OF_YEAR));
-
-            c.set(Calendar.HOUR_OF_DAY, hourOfDay);
-            c.set(Calendar.MINUTE, minute);
-            c.set(Calendar.SECOND, 0);
-            c.set(Calendar.MILLISECOND, 0);
-
-            // Check if the local time has past, if so return the same time tomorrow.
-            if (c.before(time)) {
-                c.add(Calendar.DATE, 1);
-            }
-
-            return c;
-        }
-
-        /**
-         * Returns a local time corresponding the given number of milliseconds from midnight.
-         *
-         * @param millis the number of milliseconds from midnight
-         * @return the corresponding local time
-         */
-        private static LocalTime valueOf(int millis) {
-            final int hourOfDay = (millis / 3600000) % 24;
-            final int minutes = (millis / 60000) % 60;
-            return new LocalTime(hourOfDay, minutes);
-        }
-
-        /**
-         * Returns the local time represented as milliseconds from midnight.
-         */
-        private int toMillis() {
-            return hourOfDay * 3600000 + minute * 60000;
-        }
-
-        @Override
-        public String toString() {
-            return String.format(Locale.US, "%02d:%02d", hourOfDay, minute);
-        }
-    }
-
-    /**
      * Callback invoked whenever the Night display settings are changed.
      */
     public interface Callback {
index aafc631..9cf1367 100644 (file)
@@ -48,8 +48,10 @@ import com.android.server.twilight.TwilightListener;
 import com.android.server.twilight.TwilightManager;
 import com.android.server.twilight.TwilightState;
 
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.Calendar;
 import java.util.TimeZone;
 
 import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY;
@@ -306,7 +308,7 @@ public final class NightDisplayService extends SystemService
     }
 
     @Override
-    public void onCustomStartTimeChanged(NightDisplayController.LocalTime startTime) {
+    public void onCustomStartTimeChanged(LocalTime startTime) {
         Slog.d(TAG, "onCustomStartTimeChanged: startTime=" + startTime);
 
         if (mAutoMode != null) {
@@ -315,7 +317,7 @@ public final class NightDisplayService extends SystemService
     }
 
     @Override
-    public void onCustomEndTimeChanged(NightDisplayController.LocalTime endTime) {
+    public void onCustomEndTimeChanged(LocalTime endTime) {
         Slog.d(TAG, "onCustomEndTimeChanged: endTime=" + endTime);
 
         if (mAutoMode != null) {
@@ -414,6 +416,36 @@ public final class NightDisplayService extends SystemService
         outTemp[10] = blue;
     }
 
+    /**
+     * Returns the first date time corresponding to the local time that occurs before the
+     * provided date time.
+     *
+     * @param compareTime the LocalDateTime to compare against
+     * @return the prior LocalDateTime corresponding to this local time
+     */
+    public static LocalDateTime getDateTimeBefore(LocalTime localTime, LocalDateTime compareTime) {
+        final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
+                compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
+
+        // Check if the local time has passed, if so return the same time yesterday.
+        return ldt.isAfter(compareTime) ? ldt.minusDays(1) : ldt;
+    }
+
+    /**
+     * Returns the first date time corresponding to this local time that occurs after the
+     * provided date time.
+     *
+     * @param compareTime the LocalDateTime to compare against
+     * @return the next LocalDateTime corresponding to this local time
+     */
+    public static LocalDateTime getDateTimeAfter(LocalTime localTime, LocalDateTime compareTime) {
+        final LocalDateTime ldt = LocalDateTime.of(compareTime.getYear(), compareTime.getMonth(),
+                compareTime.getDayOfMonth(), localTime.getHour(), localTime.getMinute());
+
+        // Check if the local time has passed, if so return the same time tomorrow.
+        return ldt.isBefore(compareTime) ? ldt.plusDays(1) : ldt;
+    }
+
     private abstract class AutoMode implements NightDisplayController.Callback {
         public abstract void onStart();
 
@@ -425,10 +457,10 @@ public final class NightDisplayService extends SystemService
         private final AlarmManager mAlarmManager;
         private final BroadcastReceiver mTimeChangedReceiver;
 
-        private NightDisplayController.LocalTime mStartTime;
-        private NightDisplayController.LocalTime mEndTime;
+        private LocalTime mStartTime;
+        private LocalTime mEndTime;
 
-        private Calendar mLastActivatedTime;
+        private LocalDateTime mLastActivatedTime;
 
         CustomAutoMode() {
             mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
@@ -441,31 +473,15 @@ public final class NightDisplayService extends SystemService
         }
 
         private void updateActivated() {
-            final Calendar now = Calendar.getInstance();
-            final Calendar startTime = mStartTime.getDateTimeBefore(now);
-            final Calendar endTime = mEndTime.getDateTimeAfter(startTime);
+            final LocalDateTime now = LocalDateTime.now();
+            final LocalDateTime start = getDateTimeBefore(mStartTime, now);
+            final LocalDateTime end = getDateTimeAfter(mEndTime, start);
+            boolean activate = now.isBefore(end);
 
-            boolean activate = now.before(endTime);
             if (mLastActivatedTime != null) {
-                // Convert mLastActivatedTime to the current timezone if needed.
-                final TimeZone currentTimeZone = now.getTimeZone();
-                if (!currentTimeZone.equals(mLastActivatedTime.getTimeZone())) {
-                    final int year = mLastActivatedTime.get(Calendar.YEAR);
-                    final int dayOfYear = mLastActivatedTime.get(Calendar.DAY_OF_YEAR);
-                    final int hourOfDay = mLastActivatedTime.get(Calendar.HOUR_OF_DAY);
-                    final int minute = mLastActivatedTime.get(Calendar.MINUTE);
-
-                    mLastActivatedTime.setTimeZone(currentTimeZone);
-                    mLastActivatedTime.set(Calendar.YEAR, year);
-                    mLastActivatedTime.set(Calendar.DAY_OF_YEAR, dayOfYear);
-                    mLastActivatedTime.set(Calendar.HOUR_OF_DAY, hourOfDay);
-                    mLastActivatedTime.set(Calendar.MINUTE, minute);
-                }
-
                 // Maintain the existing activated state if within the current period.
-                if (mLastActivatedTime.before(now)
-                        && mLastActivatedTime.after(startTime)
-                        && (mLastActivatedTime.after(endTime) || now.before(endTime))) {
+                if (mLastActivatedTime.isBefore(now) && mLastActivatedTime.isAfter(start)
+                        && (mLastActivatedTime.isAfter(end) || now.isBefore(end))) {
                     activate = mController.isActivated();
                 }
             }
@@ -473,14 +489,16 @@ public final class NightDisplayService extends SystemService
             if (mIsActivated == null || mIsActivated != activate) {
                 mController.setActivated(activate);
             }
+
             updateNextAlarm(mIsActivated, now);
         }
 
-        private void updateNextAlarm(@Nullable Boolean activated, @NonNull Calendar now) {
+        private void updateNextAlarm(@Nullable Boolean activated, @NonNull LocalDateTime now) {
             if (activated != null) {
-                final Calendar next = activated ? mEndTime.getDateTimeAfter(now)
-                        : mStartTime.getDateTimeAfter(now);
-                mAlarmManager.setExact(AlarmManager.RTC, next.getTimeInMillis(), TAG, this, null);
+                final LocalDateTime next = activated ? getDateTimeAfter(mEndTime, now)
+                        : getDateTimeAfter(mStartTime, now);
+                final long millis = next.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
+                mAlarmManager.setExact(AlarmManager.RTC, millis, TAG, this, null);
             }
         }
 
@@ -510,18 +528,18 @@ public final class NightDisplayService extends SystemService
         @Override
         public void onActivated(boolean activated) {
             mLastActivatedTime = mController.getLastActivatedTime();
-            updateNextAlarm(activated, Calendar.getInstance());
+            updateNextAlarm(activated, LocalDateTime.now());
         }
 
         @Override
-        public void onCustomStartTimeChanged(NightDisplayController.LocalTime startTime) {
+        public void onCustomStartTimeChanged(LocalTime startTime) {
             mStartTime = startTime;
             mLastActivatedTime = null;
             updateActivated();
         }
 
         @Override
-        public void onCustomEndTimeChanged(NightDisplayController.LocalTime endTime) {
+        public void onCustomEndTimeChanged(LocalTime endTime) {
             mEndTime = endTime;
             mLastActivatedTime = null;
             updateActivated();
@@ -550,15 +568,14 @@ public final class NightDisplayService extends SystemService
             }
 
             boolean activate = state.isNight();
-            final Calendar lastActivatedTime = mController.getLastActivatedTime();
+            final LocalDateTime lastActivatedTime = mController.getLastActivatedTime();
             if (lastActivatedTime != null) {
-                final Calendar now = Calendar.getInstance();
-                final Calendar sunrise = state.sunrise();
-                final Calendar sunset = state.sunset();
-
+                final LocalDateTime now = LocalDateTime.now();
+                final LocalDateTime sunrise = state.sunrise();
+                final LocalDateTime sunset = state.sunset();
                 // Maintain the existing activated state if within the current period.
-                if (lastActivatedTime.before(now)
-                        && (lastActivatedTime.after(sunrise) ^ lastActivatedTime.after(sunset))) {
+                if (lastActivatedTime.isBefore(now) && (lastActivatedTime.isBefore(sunrise)
+                        ^ lastActivatedTime.isBefore(sunset))) {
                     activate = mController.isActivated();
                 }
             }
index 30a8ccc..71304a7 100644 (file)
@@ -18,7 +18,10 @@ package com.android.server.twilight;
 
 import android.text.format.DateFormat;
 
-import java.util.Calendar;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.TimeZone;
 
 /**
  * The twilight state, consisting of the sunrise and sunset times (in millis) for the current
@@ -45,12 +48,11 @@ public final class TwilightState {
     }
 
     /**
-     * Returns a new {@link Calendar} instance initialized to {@link #sunriseTimeMillis()}.
+     * Returns a new {@link LocalDateTime} instance initialized to {@link #sunriseTimeMillis()}.
      */
-    public Calendar sunrise() {
-        final Calendar sunrise = Calendar.getInstance();
-        sunrise.setTimeInMillis(mSunriseTimeMillis);
-        return sunrise;
+    public LocalDateTime sunrise() {
+        final ZoneId zoneId = TimeZone.getDefault().toZoneId();
+        return LocalDateTime.ofInstant(Instant.ofEpochMilli(mSunriseTimeMillis), zoneId);
     }
 
     /**
@@ -62,12 +64,11 @@ public final class TwilightState {
     }
 
     /**
-     * Returns a new {@link Calendar} instance initialized to {@link #sunsetTimeMillis()}.
+     * Returns a new {@link LocalDateTime} instance initialized to {@link #sunsetTimeMillis()}.
      */
-    public Calendar sunset() {
-        final Calendar sunset = Calendar.getInstance();
-        sunset.setTimeInMillis(mSunsetTimeMillis);
-        return sunset;
+    public LocalDateTime sunset() {
+        final ZoneId zoneId = TimeZone.getDefault().toZoneId();
+        return LocalDateTime.ofInstant(Instant.ofEpochMilli(mSunsetTimeMillis), zoneId);
     }
 
     /**
index 58a4456..3a92d63 100644 (file)
@@ -30,13 +30,14 @@ import android.support.test.runner.AndroidJUnit4;
 import android.test.mock.MockContentResolver;
 
 import com.android.internal.app.NightDisplayController;
-import com.android.internal.app.NightDisplayController.LocalTime;
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.server.display.DisplayTransformManager;
 import com.android.server.display.NightDisplayService;
 import com.android.server.twilight.TwilightListener;
 import com.android.server.twilight.TwilightManager;
 import com.android.server.twilight.TwilightState;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -45,6 +46,7 @@ import org.mockito.Mockito;
 
 import java.util.Calendar;
 import java.util.HashMap;
+import java.time.LocalTime;
 import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -926,11 +928,10 @@ public class NightDisplayServiceTest {
      */
     private void setActivated(boolean activated, int lastActivatedTimeOffset) {
         mNightDisplayController.setActivated(activated);
-
-        final Calendar c = Calendar.getInstance();
-        c.add(Calendar.MINUTE, lastActivatedTimeOffset);
-        Secure.putLongForUser(mContext.getContentResolver(),
-                Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME, c.getTimeInMillis(), mUserId);
+        Secure.putStringForUser(mContext.getContentResolver(),
+                Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
+                LocalDateTime.now().plusMinutes(lastActivatedTimeOffset).toString(),
+                mUserId);
     }
 
     /**
@@ -969,7 +970,7 @@ public class NightDisplayServiceTest {
     private static LocalTime getLocalTimeRelativeToNow(int offsetMinutes) {
         final Calendar c = Calendar.getInstance();
         c.add(Calendar.MINUTE, offsetMinutes);
-        return new LocalTime(c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE));
+        return LocalTime.of(c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE));
     }
 
     /**
@@ -984,13 +985,27 @@ public class NightDisplayServiceTest {
         final LocalTime sunset = getLocalTimeRelativeToNow(sunsetOffset);
         final LocalTime sunrise = getLocalTimeRelativeToNow(sunriseOffset);
 
-        final Calendar now = Calendar.getInstance();
-        long sunsetMillis = sunset.getDateTimeBefore(now).getTimeInMillis();
-        long sunriseMillis = sunrise.getDateTimeBefore(now).getTimeInMillis();
+        final LocalDateTime now = LocalDateTime.now();
+        final ZoneId zoneId = ZoneId.systemDefault();
+
+        long sunsetMillis = NightDisplayService.getDateTimeBefore(sunset, now)
+                .atZone(zoneId)
+                .toInstant()
+                .toEpochMilli();
+        long sunriseMillis = NightDisplayService.getDateTimeBefore(sunrise, now)
+                .atZone(zoneId)
+                .toInstant()
+                .toEpochMilli();
         if (sunsetMillis < sunriseMillis) {
-            sunsetMillis = sunset.getDateTimeAfter(now).getTimeInMillis();
+            sunsetMillis = NightDisplayService.getDateTimeAfter(sunset, now)
+                    .atZone(zoneId)
+                    .toInstant()
+                    .toEpochMilli();
         } else {
-            sunriseMillis = sunrise.getDateTimeAfter(now).getTimeInMillis();
+            sunriseMillis = NightDisplayService.getDateTimeAfter(sunrise, now)
+                    .atZone(zoneId)
+                    .toInstant()
+                    .toEpochMilli();
         }
 
         return new TwilightState(sunriseMillis, sunsetMillis);