Reputation: 61
I have a logic that schedules reminders using AlarmManager. I need to implement the following:
Logic 1: when the user changes time zone, eg he travels from UK (UTC+0) to central Europe (UTC+1), alarms should follow the time zone. Example, a reminder scheduled at 3PM UTC+0 should fire at 4PM UTC+1
Logic 2: when a time shift occurs, eg time shifts to daylight saving time in spring (from UTC+1 to UTC+2), alarms should keep the original time Example, a reminder scheduled at 3PM UTC+1 should fire at 3PM UTC+2
How can I achieve this? As of now I have no particular logic in place and all the alarms follow Logic 1. I have found no way to identify when a time shift happens.
Scheduling logic is very simple:
LocalDateTime reminderTime = LocalDateTime.of(...)
ZoneOffset currentOffsetForMyZone = ZoneId.systemDefault().getRules().getOffset(Instant.now());
reminderTime.toInstant(currentOffsetForMyZone).toEpochMilli();
alarmManager.setExact(AlarmManager.RTC_WAKEUP, reminderTime, pendingIntent);
Upvotes: 1
Views: 1192
Reputation: 61
If anyone is interested, the fix was to apply the correct offset for the date and time where the alarm is to go of, as pointed out by Ole. My silly mistake was to apply always the current timezone.
LocalDateTime alarmTime = LocalDateTime.of(...)
ZoneId zone = ZoneId.systemDefault();
ZonedDateTime zoneDateTime = ZonedDateTime.of(alarmTime , zone);
long startAtMillis = zoneDateTime.toInstant().toEpochMilli();
//Fire alarm
notificationAlarm.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, startAtMillis, pendingIntent);
Upvotes: 1
Reputation: 86140
For each alarm store the time of day and the time zone in which it was set. This suffices for firing the alarm at the right time no matter if the user is currently in a different time zone. And Java will take summer time (DST) into account.
Your example times and UTC offsets correspond to standard time in those time zones, so let’s start with an example date in standard time even though it was a couple of days ago now:
LocalTime alarmTime = LocalTime.of(15, 0);
ZoneId alarmTimeZone = ZoneId.of("Europe/London");
// Travel to Paris and see the alarm go off at 4, assuming standard time
ZoneId currentTimeZone = ZoneId.of("Europe/Paris");
Instant actualAlarmTime = LocalDate.of(2021, Month.MARCH, 18)
.atTime(alarmTime)
.atZone(alarmTimeZone)
.toInstant();
ZonedDateTime timeOnLocation = actualAlarmTime.atZone(currentTimeZone);
System.out.format("Scheduled at %s or %d millis, goes off at %s local time%n",
actualAlarmTime, actualAlarmTime.toEpochMilli(), timeOnLocation);
The code prints:
Scheduled at 2021-03-18T15:00:00Z or 1616079600000 millis, goes off at 2021-03-18T16:00+01:00[Europe/Paris] local time
Let’s also try a date in the summer time part of the year. I have changed Paris
to London
and MARCH
to APRIL
:
// Stay back home in the UK
ZoneId currentTimeZone = ZoneId.of("Europe/London");
Instant actualAlarmTime = LocalDate.of(2021, Month.APRIL, 18)
.atTime(alarmTime)
.atZone(alarmTimeZone)
.toInstant();
Scheduled at 2021-04-18T14:00:00Z or 1618754400000 millis, goes off at 2021-04-18T15:00+01:00[Europe/London] local time
The basic trick is: don’t use the current offset for the time zone where you set the alarm. Let Java automatically apply the offset for the date and time where the alarm is to go off.
Upvotes: 2