【问题标题】:Understanding UILocalNotification timeZone了解 UILocalNotification 时区
【发布时间】:2013-08-27 18:55:10
【问题描述】:

来自文档:

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

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

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

【问题讨论】:

    标签: ios datetime uilocalnotification


    【解决方案1】:

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

    我在西雅图,下午 1:00(太平洋夏令时间,GMT-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];
    

    现在我有

    fireDate = 2013-08-31 19:00:00 +0000 (2013-08-31 12:00:00 -0700)
    

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

    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);
    

    刚刚在西雅图(太平洋夏令时间,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
    

    我将手机的时区更改为芝加哥,现在是下午 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
    

    结论:

    1. 当 UILocalNotification timeZone 为 nil 时,触发日期及时固定。这意味着通知将在格林威治标准时间 7 点中午 12:00、格林威治标准时间 5 点下午 2:00 或格林威治标准时间 7:00 触发。
    2. 当 UILocalNotification timeZone 设置为 GMT 时,触发日期以 GMT 时间计算,如果用户转到另一个时区,将自动更新。在此示例中,时间 12:00 GMT-7 转换为 19:00 GMT,并且通知设置为本地时间 19:00,无论我们在哪个时区(格林威治标准时间 19:00、格林威治标准时间 19:00 或19:00 GMT-7)。
    3. 当 UILocalNotification timeZone 设置为本地时区(太平洋夏令时间,GMT-7)时,触发日期以本地时间计算,如果用户转到另一个时区,将自动更新。在此示例中,时间为 12:00 GMT-7,因此无论我们在哪个时区(格林威治标准时间 12:00、格林威治标准时间 12:00 或格林威治标准时间 12:00),通知都将在当地时间 12:00 触发7).

    【讨论】:

    • 太棒了。从技术上讲,您将其安排在格林威治标准时间晚上 7 点而不是中午,但它也能很好地回答问题。
    • 是否有人对 iOS 10 中的 UNNotification 触发日期进行过类似调查?
    • @Jonny 我也想知道。设置时区似乎没有什么区别。
    【解决方案2】:

    在 Swift 3 中,使用 TimeZone.autoupdatingCurrent 的行为与 Objective-C 中的 [NSTimeZone defaultTimeZone] 相同。

    【讨论】:

    • 这不回答 OP。你有任何进一步的信息吗?
    【解决方案3】:

    iOS 13 上带有 UNCalendarNotificationTrigger 的 Swift 5.2

    我以@brnunes 代码为灵感,对UNCalendarNotificationTrigger 做同样的事情。

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

    如果您想要一个及时确定的开火日期,请使用任何其他时区,例如 TimeZone.currentTimeZone.init(abbreviation: "GMT")。示例:您在东部夏令时间 (EDT GMT-4) 下午 2 点安排通知,然后将您设备上的时区更改为 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)")
        }
    }
    

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

    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)
    

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

    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))")
        })
    })
    

    首先,我使用 CEST (GMT+2) 运行代码。然后,我转到我的 iOS 设置并将我的时区更改为美国东部夏令时间 (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)
    

    正如您在 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

    正如您在此处看到的,@brnunes 对UILocalNotificationUNCalendarNotifactionTrigger 的回答中时区的工作方式有所不同。在他的第二个结论中,他说UILocalNotifictions 会针对不同的时区自动更新。对于UNCalendarNotificationTrigger,情况不再如此。正如@Nicolas Buquet 所指出的,TimeZone.autoupdatingCurrent 等同于defaultTimeZone

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多