在 Swift 中使用 Date() 并在 Firestore/Firebase 中调整时区以存储和读回时遇到问题
Working with Date() in Swift and having issues adjusting timezones for storing and reading back in Firestore/Firebase
我在 Firestore 中存储 Swift 项目的日期没有任何问题。日期被转换为 UTC 格式并作为时间戳存储在 Firestore 中。一切顺利。
然后,回到客户端,我可以读回它们并应用 TimeZone.current
,date/time 会根据用户当前所在的时区进行相应调整。
因此,例如,最初的时间:
9:00 pm Melbourne time (which is GMT+10),
shows as 7:00 am if the user is in New York.
太棒了。
但我有一些项目需要根据时区进行调整(如上所述),而其他项目则不需要。
所以说我有两个项目与上面的例子相同,但一个是闹钟,我想保留它最初设置的时间,而不管新的时区......所以仍然保持在 9:00 下午.
我的数据库中保存了一个 Bool
标志,表示 ignoreTimezone
但是当从 Firestore 中读回时间戳时,我不知道如何在 Swift 中执行此操作UTC 格式并将其恢复为原始格式 9:00 pm.
我发现的所有问答都是关于转换时区等的,但在这个忽略时区并将日期和时间设置为最初设置的时区的例子中并不是真的。
提前感谢您提供任何帮助 and/or 建议。
问题已按建议更新
我现在已经合并了建议的代码。所以有一个日历扩展:
extension Calendar {
func offsetFromMidnight(for date: Date) -> TimeInterval {
return date.timeIntervalSince(startOfDay(for: date))
}
}
然后我执行推荐的步骤。
从午夜开始偏移,在本例中为当前 Date()
:
let offsetSinceMidnight = UInt64(Calendar.current.offsetFromMidnight(for: Date()))
此值随后存储在服务器上。
我目前在墨尔本(澳大利亚),所以用于测试的日期和时间项是 7 月 9 日@2:00pm。
在不同时区的客户端检索时,我使用的是推荐代码:
//Create a calendar for the target timezone
guard let chicagoTimeZone = TimeZone(identifier: "America/Chicago") else { fatalError() }
var chicagoCalendar = Calendar(identifier: .gregorian)
chicagoCalendar.timeZone = chicagoTimeZone
//Calculate midngiht in the target calendar
let chicagoMidnight = chicagoCalendar.startOfDay(for: Date())
//calculate the same time-of-day in the new timezone
let adjustedChicagoTime = Date(timeInterval: TimeInterval(offsetSinceMidnight), since: chicagoMidnight)
输出设置为芝加哥的正确时间 2:00pm,但由于日期不同(芝加哥仍然是 7 月 8 日),因此午夜时间间隔应用在错误的日期。所以我得到 7 月 8 日 @ 2:00pm.
我假设我还需要捕获原始日期组件以将 offsetSinceMidnight
应用于具有匹配日期组件的 newTimeZone 中的日期???或者有更好的方法吗?
日期对象存储世界任何地方的瞬间。无论时区如何,他们都不会捕捉一天中的时间。
为此,我建议计算一个 offsetFromMidnight 值。
编辑以修复 return 值。
extension Calendar {
func offsetFromMidnight(for date: Date) -> TimeInterval {
return date.timeIntervalSince(startOfDay(for: date))
}
}
您将在用户的当前日历中调用该函数以获取用户当前时区自午夜以来的秒数。将其保存到您的数据库中。 (您可以四舍五入到一个长整数,而精度损失很小。)
我恰好在 NYT 时区 (EDT),所以使用它作为目的地时区对我来说不起作用,因为它不会改变任何东西。相反,我将展示从我的时区转换为 GMT 的代码:
//Run on user's local machine (in EDT in my case):
let offsetSinceMidnight = UInt64(Calendar.current.offsetFromMidnight(for: Date()))
//Save offset to FireStore
然后如果你想在新时区的同一时间,你会使用这样的代码:
//Create a calendar for the target time zone (or the user's local time zone on the destination machine)
guard let gmt = TimeZone(abbreviation: "GMT") else { fatalError() }
var gmtCalendar = Calendar(identifier: .gregorian)
gmtCalendar.timeZone = gmt
//Read time offset from FireStore
let offsetFromNYC = Calendar.current.offsetFromMidnight(for: Date())
//Calculate midnight in target calendar
let gmtMidnight = gmtCalendar.startOfDay(for: Date())
//Calculate the same time-of-day in the GMT time zone
let gmtTimeToday = Date(timeInterval: TimeInterval(offsetSinceMidnight), since: gmtMidnight)
print(gmtTimeToday)
请注意,以上将为您提供与 offsetFromMidnight 时间相同的 hours/minutes/seconds。
编辑:
如果您的目标是在本地时区的下一个未来时间设置闹钟,您需要添加逻辑来检查计算的是否date/time过去并调整:
//Change adjustedChicagoTime to a var
var adjustedChicagoTime = Date(timeInterval: TimeInterval(offsetSinceMidnight), since: chicagoMidnight)
//If the alarm time is in the past, add a day to the date.
if adjustedChicagoTime < Date() {
adjustedChicagoTime = Calendar.current.date(byAdding: .day,
value: 1, to: adjustedChicagoTime, wrappingComponents: false)
}
编辑#2:
来回反复之后,听起来您有时想要保存一个独立于时区的日期和时间,例如 7 月 10 日的 9:30 上午。如果我在美国东部时间创建那个日期,而你在 Melborne 查看它,它总是 9:30 7 月 10 日上午。
其他时候,您想要上传和下载符合时区的日期和时间。
为了轻松做到这两点,我建议将 2 个不同的字符串 date/time 字段保存到 FireStore,一个有时区,一个没有。带有时区(或者更确切地说是与格林威治标准时间有偏差)的那个会捕捉到世界各地的某个时刻,并且可以转换为当地时间。
没有时区的将描述当地时间day/month/year/hours/minutes。
您可以 generate/parse Swift 中的那些字符串使用这样的日期格式化程序:
let baseFormatString = "YYYY-MM-dd'T'HH:mm"
let timeZoneFormatString = baseFormatString + "ZZZ"
let noTimeZoneFormatter = DateFormatter()
noTimeZoneFormatter.dateFormat = baseFormatString
let timeZoneFormatter = DateFormatter()
timeZoneFormatter.dateFormat = timeZoneFormatString
请注意,默认情况下,日期格式化程序使用系统时区,因此“无时区格式化程序”将采用本地时区。如果您使用它来将日期字符串转换为日期,它将假定该日期在当地时区。
我在 Firestore 中存储 Swift 项目的日期没有任何问题。日期被转换为 UTC 格式并作为时间戳存储在 Firestore 中。一切顺利。
然后,回到客户端,我可以读回它们并应用 TimeZone.current
,date/time 会根据用户当前所在的时区进行相应调整。
因此,例如,最初的时间:
9:00 pm Melbourne time (which is GMT+10),
shows as 7:00 am if the user is in New York.
太棒了。
但我有一些项目需要根据时区进行调整(如上所述),而其他项目则不需要。
所以说我有两个项目与上面的例子相同,但一个是闹钟,我想保留它最初设置的时间,而不管新的时区......所以仍然保持在 9:00 下午.
我的数据库中保存了一个 Bool
标志,表示 ignoreTimezone
但是当从 Firestore 中读回时间戳时,我不知道如何在 Swift 中执行此操作UTC 格式并将其恢复为原始格式 9:00 pm.
我发现的所有问答都是关于转换时区等的,但在这个忽略时区并将日期和时间设置为最初设置的时区的例子中并不是真的。
提前感谢您提供任何帮助 and/or 建议。
问题已按建议更新
我现在已经合并了建议的代码。所以有一个日历扩展:
extension Calendar {
func offsetFromMidnight(for date: Date) -> TimeInterval {
return date.timeIntervalSince(startOfDay(for: date))
}
}
然后我执行推荐的步骤。
从午夜开始偏移,在本例中为当前 Date()
:
let offsetSinceMidnight = UInt64(Calendar.current.offsetFromMidnight(for: Date()))
此值随后存储在服务器上。 我目前在墨尔本(澳大利亚),所以用于测试的日期和时间项是 7 月 9 日@2:00pm。
在不同时区的客户端检索时,我使用的是推荐代码:
//Create a calendar for the target timezone
guard let chicagoTimeZone = TimeZone(identifier: "America/Chicago") else { fatalError() }
var chicagoCalendar = Calendar(identifier: .gregorian)
chicagoCalendar.timeZone = chicagoTimeZone
//Calculate midngiht in the target calendar
let chicagoMidnight = chicagoCalendar.startOfDay(for: Date())
//calculate the same time-of-day in the new timezone
let adjustedChicagoTime = Date(timeInterval: TimeInterval(offsetSinceMidnight), since: chicagoMidnight)
输出设置为芝加哥的正确时间 2:00pm,但由于日期不同(芝加哥仍然是 7 月 8 日),因此午夜时间间隔应用在错误的日期。所以我得到 7 月 8 日 @ 2:00pm.
我假设我还需要捕获原始日期组件以将 offsetSinceMidnight
应用于具有匹配日期组件的 newTimeZone 中的日期???或者有更好的方法吗?
日期对象存储世界任何地方的瞬间。无论时区如何,他们都不会捕捉一天中的时间。
为此,我建议计算一个 offsetFromMidnight 值。
编辑以修复 return 值。
extension Calendar {
func offsetFromMidnight(for date: Date) -> TimeInterval {
return date.timeIntervalSince(startOfDay(for: date))
}
}
您将在用户的当前日历中调用该函数以获取用户当前时区自午夜以来的秒数。将其保存到您的数据库中。 (您可以四舍五入到一个长整数,而精度损失很小。)
我恰好在 NYT 时区 (EDT),所以使用它作为目的地时区对我来说不起作用,因为它不会改变任何东西。相反,我将展示从我的时区转换为 GMT 的代码:
//Run on user's local machine (in EDT in my case):
let offsetSinceMidnight = UInt64(Calendar.current.offsetFromMidnight(for: Date()))
//Save offset to FireStore
然后如果你想在新时区的同一时间,你会使用这样的代码:
//Create a calendar for the target time zone (or the user's local time zone on the destination machine)
guard let gmt = TimeZone(abbreviation: "GMT") else { fatalError() }
var gmtCalendar = Calendar(identifier: .gregorian)
gmtCalendar.timeZone = gmt
//Read time offset from FireStore
let offsetFromNYC = Calendar.current.offsetFromMidnight(for: Date())
//Calculate midnight in target calendar
let gmtMidnight = gmtCalendar.startOfDay(for: Date())
//Calculate the same time-of-day in the GMT time zone
let gmtTimeToday = Date(timeInterval: TimeInterval(offsetSinceMidnight), since: gmtMidnight)
print(gmtTimeToday)
请注意,以上将为您提供与 offsetFromMidnight 时间相同的 hours/minutes/seconds。
编辑:
如果您的目标是在本地时区的下一个未来时间设置闹钟,您需要添加逻辑来检查计算的是否date/time过去并调整:
//Change adjustedChicagoTime to a var
var adjustedChicagoTime = Date(timeInterval: TimeInterval(offsetSinceMidnight), since: chicagoMidnight)
//If the alarm time is in the past, add a day to the date.
if adjustedChicagoTime < Date() {
adjustedChicagoTime = Calendar.current.date(byAdding: .day,
value: 1, to: adjustedChicagoTime, wrappingComponents: false)
}
编辑#2:
来回反复之后,听起来您有时想要保存一个独立于时区的日期和时间,例如 7 月 10 日的 9:30 上午。如果我在美国东部时间创建那个日期,而你在 Melborne 查看它,它总是 9:30 7 月 10 日上午。
其他时候,您想要上传和下载符合时区的日期和时间。
为了轻松做到这两点,我建议将 2 个不同的字符串 date/time 字段保存到 FireStore,一个有时区,一个没有。带有时区(或者更确切地说是与格林威治标准时间有偏差)的那个会捕捉到世界各地的某个时刻,并且可以转换为当地时间。
没有时区的将描述当地时间day/month/year/hours/minutes。
您可以 generate/parse Swift 中的那些字符串使用这样的日期格式化程序:
let baseFormatString = "YYYY-MM-dd'T'HH:mm"
let timeZoneFormatString = baseFormatString + "ZZZ"
let noTimeZoneFormatter = DateFormatter()
noTimeZoneFormatter.dateFormat = baseFormatString
let timeZoneFormatter = DateFormatter()
timeZoneFormatter.dateFormat = timeZoneFormatString
请注意,默认情况下,日期格式化程序使用系统时区,因此“无时区格式化程序”将采用本地时区。如果您使用它来将日期字符串转换为日期,它将假定该日期在当地时区。