夏令时切换时段处理:适配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) } }
代码说明
- 固定英国时区:创建专属的英国时区日历,避免当前系统时区干扰夏令时判断
- 识别长短天:通过计算英国时区下当天的秒数(正常天为86400秒,切换日为82800/90000秒),精准定位切换日
- 统一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
相关产品推荐
相关产品推荐

