Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Swift中使用Date(),在调整时区以存储和读取Firestore/Firebase时遇到问题_Swift_Google Cloud Firestore_Timezone - Fatal编程技术网

在Swift中使用Date(),在调整时区以存储和读取Firestore/Firebase时遇到问题

在Swift中使用Date(),在调整时区以存储和读取Firestore/Firebase时遇到问题,swift,google-cloud-firestore,timezone,Swift,Google Cloud Firestore,Timezone,我在Firestore中存储Swift项目的日期,没有任何问题。日期转换为UTC格式,并作为时间戳存储在Firestore中。一切都好 然后,回到客户端,我可以将它们读回并应用时区。当前的和日期/时间根据用户当前所在的时区进行相应调整 例如,最初的时间为: 墨尔本时间晚上9:00(格林尼治标准时间+10) 如果用户在纽约,则显示为上午7:00 太好了 但我有一些项目,我想调整时区(如上所述)和其他我不 假设我有两个与上面例子相同的项目,但其中一个是警报,我希望保持在最初设置的时间,而不考虑新时区

我在Firestore中存储Swift项目的日期,没有任何问题。日期转换为UTC格式,并作为时间戳存储在Firestore中。一切都好

然后,回到客户端,我可以将它们读回并应用
时区。当前的
和日期/时间根据用户当前所在的时区进行相应调整

例如,最初的时间为:

墨尔本时间晚上9:00(格林尼治标准时间+10)

如果用户在纽约,则显示为上午7:00

太好了

但我有一些项目,我想调整时区(如上所述)和其他我不

假设我有两个与上面例子相同的项目,但其中一个是警报,我希望保持在最初设置的时间,而不考虑新时区。。。所以还是保留在晚上9点

我的数据库中保存了一个
Bool
标志,上面写着
ignoreTimezone
,但当我以UTC格式从Firestore中读取时间戳并将其恢复到最初的9:00 pm时,我不知道如何以Swift执行此操作

我发现的所有问答都是关于转换时区等的,但实际上并不是在这个例子中忽略一个,并将日期和时间设置为它们最初设置的时区

提前感谢您的帮助和/或建议

根据建议更新问题 我现在已经合并了建议的代码。所以有一个日历扩展:

extension Calendar {
    func offsetFromMidnight(for date: Date) -> TimeInterval {
        return  date.timeIntervalSince(startOfDay(for: date))
    }
}
然后我执行建议的步骤

从午夜开始偏移,在本例中为当前的
Date()

let offsetSinceMidnight=UInt64(Calendar.current.offsetfrommondight(for:Date())

然后将该值存储在服务器上。 我目前在墨尔本(澳大利亚),因此用于测试的日期和时间项目是7月9日下午2:00

当在客户端的不同时区检索时,我使用的是推荐的代码:

//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:00,但由于日期不同(芝加哥仍然是7月8日),因此午夜时间间隔应用于错误的日期。所以我得到7月8日下午2点


我假设我还需要捕获原始日期组件,以便将
偏移量sincemiddnight
应用于新时区中具有匹配日期组件的日期???或者有更好的方法吗?

日期对象在世界任何地方存储时间上的瞬间。无论时区如何,它们都无法捕捉到一天中某个时间的概念

为此,我建议计算一个offsetFromMidnight值

编辑以固定返回值。 您可以在用户当前日历中调用该函数,以获取用户当前时区中自午夜起的秒数。将其保存到数据库中。(可以四舍五入到一个长整数,精度损失很小。)

我正好在纽约时报时区(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)
let baseFormatString = "YYYY-MM-dd'T'HH:mm"
let timeZoneFormatString = baseFormatString + "ZZZ"

let  noTimeZoneFormatter = DateFormatter()
noTimeZoneFormatter.dateFormat = baseFormatString

let  timeZoneFormatter = DateFormatter()
timeZoneFormatter.dateFormat = timeZoneFormatString
请注意,上面给出的小时/分/秒与从午夜开始的偏移时间相同

编辑: 如果您的目标是将警报设置为本地时区中的下一个未来时间,则需要添加逻辑来检查计算的日期/时间是否在过去,并进行调整:

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

请注意,默认情况下,日期格式化程序使用系统的时区,因此“无时区格式化程序”将假定为本地时区。如果使用它将日期字符串转换为日期,它将假定该日期位于本地时区。

日期对象在世界任何地方存储时间上的一个瞬间。无论时区如何,它们都无法捕捉到一天中某个时间的概念

为此,我建议计算一个offsetFromMidnight值

编辑以固定返回值。 您可以在用户当前日历中调用该函数,以获取用户当前时区中自午夜起的秒数。将其保存到数据库中。(可以四舍五入到一个长整数,精度损失很小。)

我正好在纽约时报时区(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)
let baseFormatString = "YYYY-MM-dd'T'HH:mm"
let timeZoneFormatString = baseFormatString + "ZZZ"

let  noTimeZoneFormatter = DateFormatter()
noTimeZoneFormatter.dateFormat = baseFormatString

let  timeZoneFormatter = DateFormatter()
timeZoneFormatter.dateFormat = timeZoneFormatString
请注意,上面给出的小时/分/秒与从午夜开始的偏移时间相同

编辑: 如果您的目标是将警报设置为本地时区中的下一个未来时间,则需要添加逻辑来检查计算的日期/时间是否在过去,并进行调整:

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

请注意,默认情况下,日期格式化程序使用系统的时区,因此“无时区格式化程序”将假定为本地时区。如果您使用它将日期字符串转换为日期,它将假定该日期位于本地时区。

感谢您的回复,并提供了如此详细的回复。在这里做新手总是让人望而生畏。我喜欢你关于如何处理这个问题的建议。对于扩展,我得到了一个错误。我必须将其更改为:
extensioncalendar{func offsetFromMidnight(for date:date)->TimeInterval{return date.timeIntervalSince(startOfDay(for:date))}
才能让它工作,还是我遗漏了什么?我确实在使用
时区(缩写:)
时遇到了一些障碍,但使用
时区(标识符:)
似乎效果更好。这正常吗?你的找零是正确的。我想在我发现问题之前,我很早就发布了我答案的这一部分,没有回去纠正它。很抱歉,很好。如果你想更新它,我会把它标记为正确的。关于我的另一点,你发现问题了吗