了解UILocalNotification timeZone

Ben*_*ynn 21 datetime ios uilocalnotification

来自文档:

fireDate中指定的日期将根据此属性的值进行解释.如果指定nil(默认值),则开火日期将被解释为绝对GMT时间,适用于倒数计时器等情况.如果为此属性分配有效的NSTimeZone对象,则开火日期将被解释为当时区发生变化时自动调整的挂钟时间; 适用于这种情况的一个例子是闹钟.

假设我在明天GMT(绝对时间)中午安排本地通知.我计划在西雅图下午1点(太平洋时间,格林威治标准时间-8),然后立即前往芝加哥,到达晚上11点(中部时间,格林威治标准时间-6).我的设备从太平洋时间调整到中部时间.请帮助我了解在以下三种情况下我的通知何时发生:

  1. UILocalNotification timeZone设置为nil.
  2. UILocalNotification timeZone设置为GMT.
  3. UILocalNotification timeZone设置为太平洋时间.

brn*_*nes 43

我刚刚在iOS 6.1.3上运行了一些测试.这是我得到的:

我在西雅图,下午1点(太平洋夏令时,格林威治标准时间-7).我创建了一个NSDate:

NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];

NSDateComponents *dateComponents = [[NSDateComponents alloc] init];
// 2013-08-31 @ 12:00:00 (noon)
dateComponents.year = 2013;
dateComponents.month = 8;
dateComponents.day = 31;
dateComponents.hour = 12;
dateComponents.minute = 0;
dateComponents.second = 0;

NSDate *fireDate = [gregorianCalendar dateFromComponents:dateComponents];
Run Code Online (Sandbox Code Playgroud)

我现在有

fireDate = 2013-08-31 19:00:00 +0000 (2013-08-31 12:00:00 -0700)
Run Code Online (Sandbox Code Playgroud)

然后我创建并安排了通知:

notification1 = [[UILocalNotification alloc] init];
notification1.fireDate = fireDate;
// notification1.timeZone is nil by default

NSLog(@"%@", notification1);

notification2 = [[UILocalNotification alloc] init];
notification2.fireDate = fireDate;
notification2.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];

NSLog(@"%@", notification2);

notification3 = [[UILocalNotification alloc] init];
notification3.fireDate = fireDate;
notification3.timeZone = [NSTimeZone defaultTimeZone];

NSLog(@"%@", notification3);
Run Code Online (Sandbox Code Playgroud)

刚刚在西雅图(太平洋夏令时,GMT-7)创建的通知的日志:

notification1:
fire date = Saturday, August 31, 2013, 12:00:00 PM Pacific Daylight Time,
time zone = (null),
next fire date = Saturday, August 31, 2013, 12:00:00 PM Pacific Daylight Time

notification2:
fire date = Saturday, August 31, 2013, 7:00:00 PM GMT,
time zone = GMT (GMT) offset 0,
next fire date = Saturday, August 31, 2013, 7:00:00 PM Pacific Daylight Time

notification3:
fire date = Saturday, August 31, 2013, 12:00:00 PM Pacific Daylight Time,
time zone = US/Pacific (PDT) offset -25200 (Daylight),
next fire date = Saturday, August 31, 2013, 12:00:00 PM Pacific Daylight Time
Run Code Online (Sandbox Code Playgroud)

我将手机的时区更改为芝加哥,现在是下午3:00(中部夏令时,GMT-5).

芝加哥的通知日志(中部夏令时,GMT-5)

notification1:
fire date = Saturday, August 31, 2013, 2:00:00 PM Central Daylight Time,
time zone = (null),
next fire date = Saturday, August 31, 2013, 2:00:00 PM Central Daylight Time

notification2:
fire date = Saturday, August 31, 2013, 7:00:00 PM GMT,
time zone = GMT (GMT) offset 0,
next fire date = Saturday, August 31, 2013, 7:00:00 PM Central Daylight Time

notification3:
fire date = Saturday, August 31, 2013, 12:00:00 PM Pacific Daylight Time,
time zone = US/Pacific (PDT) offset -25200 (Daylight),
next fire date = Saturday, August 31, 2013, 12:00:00 PM Central Daylight Time
Run Code Online (Sandbox Code Playgroud)

结论:

  1. 当UILocalNotification timeZone为零时,开火日期会及时修复.这意味着通知将在GMT-7中午12:00,格林威治标准时间-5:00 PM或格林尼治标准时间7:00触发.
  2. 当UILocalNotification timeZone设置为GMT时,将根据GMT时间计算开火日期,如果用户转到另一个时区,则会自动更新.在此示例中,时间12:00 GMT-7被转换为格林威治标准时间19:00,并且通知设置为当地时间19:00,无论我们是什么时区(格林尼治标准时间19:00,格林威治标准时间19:00 19:00或19:00 GMT-7).
  3. 当UILocalNotification timeZone设置为本地时区(太平洋夏令时,GMT-7)时,将计算当地时间的开火日期,如果用户转到另一个时区,则会自动更新.在此示例中,时间是格林威治标准时间-7:00,因此通知将在当地时间12:00开始,无论我们是什么时区(格林尼治标准时间12:00,格林威治标准时间12:00或格林威治标准时间12:00) 7).


Yan*_*ick 5

iOS 13 上带有 UNCalendarNotificationTrigger 的 Swift 5.2

我使用 @brnunes 代码作为灵感,用UNCalendarNotificationTrigger.

TimeZone.autoupdatingCurrent如果您想在当地时间发送通知,请使用。示例:您安排在下午 6 点 GMT 发送通知,然后将设备上的时区更改为 CET,通知仍将安排在下午 6 点在您的设备上。

如果您想要及时固定的开火日期,请使用任何其他时区,例如TimeZone.current或。TimeZone.init(abbreviation: "GMT")示例:您安排在东部夏令时间下午 2 点 (EDT GMT-4) 发送通知,然后将设备上的时区更改为 CEST (GMT+2),现在设备上的通知将安排在下午 8 点。这就像格林威治标准时间下午 6 点开会一样。会议时间不变,只是 EDT 为下午 2 点,CEST 为晚上 8 点。

我使用以下代码来添加通知:

func addNotification(identifier: String, timeZone: TimeZone?) {
    let content = UNMutableNotificationContent()
    content.title = "Title \(identifier)"
    content.body = "Body"

    let trigger = UNCalendarNotificationTrigger(
        dateMatching: DateComponents(
            calendar: Calendar.current,
            timeZone: timeZone,
            year: 2020,
            month: 04,
            day: 01,
            hour: 18,
            minute: 30,
            second: 0),
        repeats: false)

    let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)

    UNUserNotificationCenter
        .current()
        .add(request) { _ in
            print("Scheduled \(identifier)")
    }
}
Run Code Online (Sandbox Code Playgroud)

我添加了四个不同的通知。

addNotification(identifier: "1", timeZone: nil)
addNotification(identifier: "2", timeZone: TimeZone.init(abbreviation: "GMT")!)
addNotification(identifier: "3", timeZone: TimeZone.autoupdatingCurrent)
addNotification(identifier: "4", timeZone: TimeZone.current)
Run Code Online (Sandbox Code Playgroud)

我使用以下代码来打印预定的请求。

UNUserNotificationCenter.current().getPendingNotificationRequests(completionHandler: { notificationRequests in
    let dateFormatter = DateFormatter()
    dateFormatter.dateStyle = .medium
    dateFormatter.timeStyle = .medium
    dateFormatter.timeZone = TimeZone.current

    print(TimeZone.current)
    notificationRequests.forEach({
        let nextTriggerDate = ($0.trigger as! UNCalendarNotificationTrigger).nextTriggerDate()!
        print("\($0.identifier): \(dateFormatter.string(from: nextTriggerDate)) (\(nextTriggerDate))")
    })
})
Run Code Online (Sandbox Code Playgroud)

首先,我使用 CEST (GMT+2) 运行代码。然后,我进入 iOS 设置并将时区更改为迈阿密,EDT (GMT-4)。这是结果。

Europe/Berlin (current)
1: 1. Apr 2020 at 18:30:00 (2020-04-01 16:30:00 +0000)
2: 1. Apr 2020 at 20:30:00 (2020-04-01 18:30:00 +0000)
3: 1. Apr 2020 at 18:30:00 (2020-04-01 16:30:00 +0000)
4: 1. Apr 2020 at 18:30:00 (2020-04-01 16:30:00 +0000)

US/Eastern (current)
1: 1. Apr 2020 at 12:30:00 (2020-04-01 16:30:00 +0000)
2: 1. Apr 2020 at 14:30:00 (2020-04-01 18:30:00 +0000)
3: 1. Apr 2020 at 18:30:00 (2020-04-01 22:30:00 +0000)
4: 1. Apr 2020 at 12:30:00 (2020-04-01 16:30:00 +0000)
Run Code Online (Sandbox Code Playgroud)

正如您在 1、2 和 4 中看到的,时间日期永远不会改变。两次都是2020-04-01 16:30:00 +00002020-04-01 18:30:00 +00002020-04-01 16:30:00 +0000 。仅触发通知时的当地时间发生变化并更新。

使用 3 时,实际日期从2020-04-01 16:30:00 +0000更改为2020-04-01 22:30:00 +0000。然而,当地时间并没有改变,仍然是18:30:00

UILocalNotification正如您在这里所看到的,@brnunes 的答案中时区的工作方式与和有所不同UNCalendarNotifactionTrigger。他在第二个结论中表示,会UILocalNotifictions针对不同时区自动更新。有了UNCalendarNotificationTrigger,情况就不再是这样了。正如 @Nicolas Buquet 所指出的,TimeZone.autoupdatingCurrent相当于defaultTimeZone.