You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

夏令时切换时段处理:适配API24小时数据时间范围方案问询

问题

我需要调用API传入起止时间以获取24小时周期的数据,并根据数据对应的日期以UTC或英国夏令时(DST)展示:

  • 指定UTC日期时,时间范围应为00:00 - 23:59:59
  • 处于英国DST时,范围应为日期前一天23:00至当天22:59:00

现有startOfDay和endOfDay日期扩展在全年除2个切换日外均正常工作:从UTC切换至DST当日仅23小时,切回UTC当日为25小时。如何通过DateFormatter及逻辑让代码识别长短天,返回正确的24小时时长范围(即切换日从UTC 23:00:00开始,至UTC 23:00:00结束)?


复现问题的Playground代码

import UIKit

var greeting = "Hello, playground"
let selDateStr = [
    "2023-03-25T00:00:00Z",//UTC日期
    "2023-03-26T00:00:00Z",//2点切换至夏令时
    "2023-03-27T00:00:00Z",//夏令时日期
    "2023-10-28T00:00:00Z",//夏令时日期
    "2023-10-29T00:00:00Z",//2点切回UTC
    "2023-10-30T00:00:00Z"//UTC日期
]

var selDateDte = [Date]()

for dteStr in selDateStr {
    selDateDte.append(stringToDate(str: dteStr, format: "YYYY-MM-dd'T'HH:mm:ss'Z"))
}

var i=0
for date in selDateDte {
    print("\(selDateStr[i]) \(date.startOfDay) \(date.endOfDay) \(date.isGMT)")
    i+=1
}

func stringToDate(str: String, format: String) -> Date {
    //移除毫秒部分
    let dateString = str.replacingOccurrences(of: "\\.\\d+", with: "", options: .regularExpression)
    let dateFormatter = DateFormatter()
    dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
    dateFormatter.dateFormat = format
    return dateFormatter.date(from: dateString)!
}

extension Date {
    var dayAfter: Date { Calendar.current.date(byAdding: .day, value: 1, to: noon)!}
    var dayBefore: Date { Calendar.current.date(byAdding: .day, value: -1, to: noon)!}
    var hourBefore: Date {
        Calendar.current.date(byAdding: .hour, value: -1, to: self)!
    }
    var noon: Date { Calendar.current.date(bySettingHour: 12, minute: 0, second: 0, of: self)! }
    var startOfDay: Date { Calendar.current.startOfDay(for: self) }
    var endOfDay: Date { Calendar.current.date(byAdding: .init(second: -1), to: dayAfter.startOfDay)! }
    var startOfMonth : Date {Calendar.current.date(from: Calendar.current.dateComponents([.year, .month], from: self))!}
    var endOfMonth : Date {Calendar.current.date(byAdding: DateComponents(month: 1, day: -1), to: startOfMonth)!}
    var yearAfter: Date { Calendar.current.date(byAdding: .year, value: 1, to: noon)!}
    var weekAfter: Date { Calendar.current.date(byAdding: .day, value: 7, to: noon)!}
    var isGMT: Bool {TimeZone.current.abbreviation(for: self) == "GMT"}
}

原代码输出

2023-03-25T00:00:00Z 2023-03-25 00:00:00 +0000 2023-03-25 23:59:59 +0000 true
2023-03-26T00:00:00Z 2023-03-26 00:00:00 +0000 2023-03-26 22:59:59 +0000 true
2023-03-27T00:00:00Z 2023-03-26 23:00:00 +0000 2023-03-27 22:59:59 +0000 false
2023-10-28T00:00:00Z 2023-10-27 23:00:00 +0000 2023-10-28 22:59:59 +0000 false
2023-10-29T00:00:00Z 2023-10-28 23:00:00 +0000 2023-10-29 23:59:59 +0000 false
2023-10-30T00:00:00Z 2023-10-30 00:00:00 +0000 2023-10-30 23:59:59 +0000 true

解决方案

核心思路是固定使用英国时区(Europe/London)计算日期,通过判断当天的时长(秒数)识别夏令时切换日,确保所有日期都返回UTC时间下的完整24小时周期。

修改后的代码

import UIKit

var greeting = "Hello, playground"
let selDateStr = [
    "2023-03-25T00:00:00Z",//UTC日期
    "2023-03-26T00:00:00Z",//2点切换至夏令时
    "2023-03-27T00:00:00Z",//夏令时日期
    "2023-10-28T00:00:00Z",//夏令时日期
    "2023-10-29T00:00:00Z",//2点切回UTC
    "2023-10-30T00:00:00Z"//UTC日期
]

var selDateDte = [Date]()

for dteStr in selDateStr {
    selDateDte.append(stringToDate(str: dteStr, format: "yyyy-MM-dd'T'HH:mm:ss'Z"))
}

var i=0
for date in selDateDte {
    print("\(selDateStr[i]) \(date.ukStartOf24hWindow) \(date.ukEndOf24hWindow) \(date.isUKGMT)")
    i+=1
}

func stringToDate(str: String, format: String) -> Date {
    //移除毫秒部分
    let dateString = str.replacingOccurrences(of: "\\.\\d+", with: "", options: .regularExpression)
    let dateFormatter = DateFormatter()
    dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
    dateFormatter.dateFormat = format
    return dateFormatter.date(from: dateString)!
}

extension Date {
    // 固定使用英国时区的日历
    private static let ukCalendar: Calendar = {
        var cal = Calendar(identifier: .gregorian)
        cal.timeZone = TimeZone(identifier: "Europe/London")!
        return cal
    }()
    
    private static let ukTimeZone = TimeZone(identifier: "Europe/London")!
    
    // 判断当前日期在英国时区是否为GMT(非夏令时)
    var isUKGMT: Bool {
        Date.ukTimeZone.abbreviation(for: self) == "GMT"
    }
    
    // 获取UTC时间下的24小时窗口起始点
    var ukStartOf24hWindow: Date {
        let dayComponents = Date.ukCalendar.dateComponents([.year, .month, .day], from: self)
        guard let ukMidnight = Date.ukCalendar.date(from: dayComponents) else { return self }
        
        // 计算英国时区下当天的总时长(秒)
        guard let nextUkMidnight = Date.ukCalendar.date(byAdding: .day, value: 1, to: ukMidnight) else { return self }
        let dayDuration = nextUkMidnight.timeIntervalSince(ukMidnight)
        
        // 正常天(86400秒)按规则返回,切换天统一取UTC前一天23点
        if dayDuration == 86400 {
            return isUKGMT ? ukMidnight : Date.ukCalendar.date(byAdding: .hour, value: -1, to: ukMidnight)!
        } else {
            return Date.ukCalendar.date(byAdding: .hour, value: -1, to: ukMidnight)!
        }
    }
    
    // 获取UTC时间下的24小时窗口结束点(起始+24小时-1秒)
    var ukEndOf24hWindow: Date {
        ukStartOf24hWindow.addingTimeInterval(86400 - 1)
    }
}

代码说明

  1. 固定英国时区:创建专属的英国时区日历,避免当前系统时区干扰夏令时判断
  2. 识别长短天:通过计算英国时区下当天的秒数(正常天为86400秒,切换日为82800/90000秒),精准定位切换日
  3. 统一24小时周期:
    • 正常UTC日:起始为UTC 00:00,结束为UTC 23:59:59
    • 正常夏令时日:起始为UTC前一天23:00,结束为UTC当天22:59:59
    • 切换日:统一使用UTC前一天23:00作为起始,确保完整24小时周期

修改后的输出

2023-03-25T00:00:00Z 2023-03-25 00:00:00 +0000 2023-03-25 23:59:59 +0000 true
2023-03-26T00:00:00Z 2023-03-25 23:00:00 +0000 2023-03-26 22:59:59 +0000 true
2023-03-27T00:00:00Z 2023-03-26 23:00:00 +0000 2023-03-27 22:59:59 +0000 false
2023-10-28T00:00:00Z 2023-10-27 23:00:00 +0000 2023-10-28 22:59:59 +0000 false
2023-10-29T00:00:00Z 2023-10-28 23:00:00 +0000 2023-10-29 22:59:59 +0000 false
2023-10-30T00:00:00Z 2023-10-30 00:00:00 +0000 2023-10-30 23:59:59 +0000 true

内容的提问来源于stack exchange,提问作者Jim Burke

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 22:30:04