Android AlarmManager 通过时区或夏时制调度
Posted
技术标签:
【中文标题】Android AlarmManager 通过时区或夏时制调度【英文标题】:Android AlarmManager scheduling through time zone or daylight shifts 【发布时间】:2021-06-25 18:28:31 【问题描述】:我有一个使用 AlarmManager 安排提醒的逻辑。我需要实现以下内容:
逻辑1:当用户改变时区时,例如他从英国(UTC+0)到中欧(UTC+1),警报应该跟随时区。 例如,安排在 UTC+0 下午 3 点的提醒应该在 UTC+1 下午 4 点触发
逻辑2:当时间偏移发生时,例如春季时间偏移到夏令时(从UTC+1到UTC+2),警报应该保持原来的时间 例如,安排在 UTC+1 下午 3 点的提醒应该在 UTC+2 下午 3 点触发
我怎样才能做到这一点?到目前为止,我还没有特定的逻辑,所有的警报都遵循 Logic 1。我无法确定何时发生时移。
调度逻辑很简单:
LocalDateTime reminderTime = LocalDateTime.of(...)
ZoneOffset currentOffsetForMyZone = ZoneId.systemDefault().getRules().getOffset(Instant.now());
reminderTime.toInstant(currentOffsetForMyZone).toEpochMilli();
alarmManager.setExact(AlarmManager.RTC_WAKEUP, reminderTime, pendingIntent);
【问题讨论】:
为什么不只使用 UTC 而不必担心时区? 如何仅使用 UTC 实现上述两个逻辑? UTC 时间不会改变,它没有时区,也没有夏令时。您可以使用Calendar
获取特定日期的UTC 时间戳
它不起作用。在您的场景中,所有警报都使用我的问题的逻辑 1 重新安排。当时间切换到 DST 时,所有警报也会重新安排,而我需要保持实际时间(逻辑 2)。
【参考方案1】:
为每个闹钟存储一天中的时间和设置它的时区。无论用户当前是否在不同的时区,这足以在正确的时间触发警报。 Java 将考虑夏令时 (DST)。
您的示例时间和 UTC 偏移量对应于这些时区的标准时间,所以让我们从标准时间的示例日期开始,即使它是几天前的日期:
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);
代码打印:
计划于 2021-03-18T15:00:00Z 或 1616079600000 毫秒,起飞时间为 2021-03-18T16:00+01:00[欧洲/巴黎]当地时间
让我们也尝试在一年中的夏季时间约会。我已将Paris
更改为London
和MARCH
更改为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();
计划于 2021-04-18T14:00:00Z 或 1618754400000 毫秒,起飞时间 2021-04-18T15:00+01:00[欧洲/伦敦]当地时间
基本技巧是:不要将当前偏移量用于设置闹钟的时区。让 Java 自动应用警报响起的日期和时间的偏移量。
【讨论】:
时区更改工作,谢谢。我仍然无法弄清楚如何管理 DST 时移(逻辑 2)。 AlarmManager 仅在绝对时间工作,发生时移时我没有收到任何事件。我必须设置自己的闹钟吗? 好吧,我想我明白你说的了。我的调度逻辑错了,其实解决方法很简单。【参考方案2】:如果有人感兴趣,解决方法是为警报发出的日期和时间应用正确的偏移量,正如 Ole 指出的那样。我的愚蠢错误是始终应用 current 时区。
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);
【讨论】:
以上是关于Android AlarmManager 通过时区或夏时制调度的主要内容,如果未能解决你的问题,请参考以下文章
Android中AlarmManager使用示例(持续更新)
Android开发:利用AlarmManager不间断向服务器发送请求以及notification通知
Android Doze模式下的AlarmManager策略