简体   繁体   English

如何在swift4中始终如一地获得两个日期之间的所有天数或列表?

[英]How can I consistently get the count of or a list of all of the days between two dates in swift4?

I keep failing to get a consistent list or count of dates between two datetimes when the date is different in the GMT and the user's timezone and when the difference in datetimes is less than 24 hours (even with separate dates).当格林威治标准时间和用户的时区中的日期不同并且日期时间的差异小于 24 小时(即使有不同的日期)时,我一直无法在两个日期时间之间获得一致的日期列表或计数。 I've googled and keep trying different approaches, but I cannot figure it out.我用谷歌搜索并不断尝试不同的方法,但我无法弄清楚。 It's even uglier when the user wants to use multiple time zones, but I think that I can solve that once I understand what I am doing wrong here.当用户想要使用多个时区时甚至更难看,但我认为一旦我明白我在这里做错了什么,我就可以解决这个问题。

In my application, I get dates from the user in several time zones, store them as strings, like: 2017-12-08 12:30:00 +0900在我的应用程序中,我从多个时区的用户那里获取日期,将它们存储为字符串,例如:2017-12-08 12:30:00 +0900

I then attempt to make an array of dates between the start and end date.然后我尝试在开始日期和结束日期之间创建一个日期数组。 However, I have not been able to get the array to include all of the actual dates.但是,我无法让数组包含所有实际日期。

First, I tried to expand an array directly between the first and last days: (This works in my project but not in a playground?? I don't know why.首先,我试图在第一天和最后一天之间直接扩展一个数组:(这在我的项目中有效,但在操场上无效?我不知道为什么。

func getMyDates(_ start:String,_ end: String) -> [Date]? {
   dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
   let fromDate = dateFormatter.date(from: start)
   let toDate = dateFormatter.date(from: end)
   var dates = [Date]()
   dates.append(fromDate!)
   dates.append(toDate!)
   print(dates)
   return dates
}
func getDays() -> [Date]? {
    guard let dates = getMyDates() else {return nil}
    print("getDays got \(dates) from getMyDates()")
    var userdates = dates
    let fromDate = dates[0]
    let toDate = dates[1]
    print("changed it to: \(fromDate ... toDate)")
    return fromDate ... toDate
}

This is working if the user's first and last dates are the same for their timezone and the GMT.如果用户的时区和 GMT 的第一个和最后一个日期相同,则此方法有效。 For example, if the string entries are: 2017-11-08 12:30:00 +0900 and 2017-11-10 23:30:15 +0900, the output includes the three expected dates.例如,如果字符串条目是:2017-11-08 12:30:00 +0900 和 2017-11-10 23:30:15 +0900,则输出包括三个预期日期。

//getDays got [2017-11-08 03:30:00 +0000, 2017-11-10 14:30:15 +0000] from getMyDates()
//changed it to: [2017-11-08 03:30:00 +0000, 2017-11-09 03:30:00 +0000, 2017-11-10 03:30:00 +0000]

However, it fails when the time of day in the second datetime is earlier than the first, like: 2017-12-08 12:30:00 +0900 and 2017-12-09 09:30:15 +0900.但是,当第二个日期时间中的时间早于第一个时,它会失败,例如:2017-12-08 12:30:00 +0900 和 2017-12-09 09:30:15 +0900。 In that case, I only get 1 day out:在这种情况下,我只有 1 天的时间:

//getDays got [2017-12-08 03:30:00 +0000, 2017-12-09 01:00:00 +0000] from getTripDates()
//changed it to: [2017-12-08 03:30:00 +0000]

Then, I tried calculating the number of days between them as suggested in this This question , but I get the same problem, as in the first one counts 2 days and the second counts 0 days.然后,我尝试按照This question 中的建议计算它们之间的天数,但我遇到了同样的问题,因为第一个计数为 2 天,第二个计数为 0 天。 My plan was to take the number of days and use this approach .我的计划是花几天时间使用这种方法

I think it is a time zone issue, but I can't figure out how to solve it.我认为这是一个时区问题,但我不知道如何解决它。 I've tried setting and not setting the time zone to .current.我试过设置而不是将时区设置为 .current。

I cannot arbitrarily add a day because that will fail when the first datetime is earlier in the day than the second datetime ( like 2017-12-08 12:30:00 +0900 and 2017-12-09 15:30:15 +0900.我不能随意添加一天,因为当第一个日期时间早于第二个日期时间时会失败(例如 2017-12-08 12:30:00 +0900 和 2017-12-09 15:30:15 +0900 .

Using @malik's suggestion seems like it will work, but somehow it does not.使用@malik 的建议似乎会起作用,但不知何故它不起作用。 We get the same behavior.我们得到相同的行为。 If the second datetime TIME is later in the day it doesn't need an additional day (and somehow the seconds remainder does not work - gives too many days.如果第二个日期时间 TIME 在当天晚些时候,则不需要额外的一天(并且以某种方式剩余的秒数不起作用 - 给出了太多天数。

//in playground swift4
import UIKit

let start = "2017-12-08 12:30:00 +0900"
let end = "2017-12-10 13:30:00 +0900"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
var fromDate = dateFormatter.date(from: start)
let toDate = dateFormatter.date(from: end)
let cal = Calendar.autoupdatingCurrent
let seconds=cal.dateComponents([.second], from: fromDate!, to: toDate!)
var days = Int(seconds.second ?? 0)/(86400)
//days prints 2
//if (Int(seconds.second ?? 0) % (86400)) > 0 {
//    days += 1
//}
var newDates = [Date]()
newDates.append(fromDate!)
for _ in 1 ... days {
    let daysBetween = cal.date(byAdding: .day, value: 1, to: fromDate!)
    fromDate = daysBetween
    newDates.append(fromDate!)
}
//print(newDates)

outputs desired:期望的输出:

// [2017-12-08 03:30:00 +0000, 2017-12-09 03:30:00 +0000, 2017-12-10 03:30:00 +0000]

However: if I change the end date to:但是:如果我将结束日期更改为:

let end = "2017-12-10 09:30:00 +0900"

the output is wrong:输出错误:

//[2017-12-08 03:30:00 +0000, 2017-12-09 03:30:00 +0000] //[2017-12-08 03:30:00 +0000, 2017-12-09 03:30:00 +0000]

Adding in the additional day for partial days helps in this case (uncommenting out the logic:)在这种情况下,为部分天添加额外的一天会有所帮助(取消注释逻辑:)

if (Int(seconds.second ?? 0) % (86400)) > 0 {
    days += 1
}

yields correctly:正确产生:

//[2017-12-08 03:30:00 +0000, 2017-12-09 03:30:00 +0000, 2017-12-10 03:30:00 +0000]

BUT: if I change end back while leaving the logic in, I get:但是:如果我在保留逻辑的同时改变结束,我会得到:

//[2017-12-08 03:30:00 +0000, 2017-12-09 03:30:00 +0000, 2017-12-10 03:30:00 +0000, 2017-12-11 03:30:00 +0000]

I thought it would be fine, but with the additional day, it messes up my later calls to see what else is going on those days because the extra day isn't in the original data.我认为它会很好,但是随着额外的一天,它会弄乱我后来的电话,看看那些日子里发生了什么,因为额外的一天不在原始数据中。

So, maybe I need to just set up some comparisons of the HH:mm strings?那么,也许我只需要设置一些 HH:mm 字符串的比较? It just doesn't make sense to me.这对我来说没有意义。 I feel like I'm missing something obvious that would allow me to expand dates in the users' locale.我觉得我错过了一些明显的东西,可以让我在用户的语言环境中扩展日期。

Using @malik's revised suggestion which lops off the hours and then calculates days works.使用@malik 修改后的建议,该建议可以减少工作时间,然后计算天数。 Then, I can loop over the number of days to create an array.然后,我可以遍历天数来创建一个数组。

It has nothing to do with time zone.它与时区无关。 You were on the right plan with the two links.您通过这两个链接制定了正确的计划。 The only problem was that the first link you provided calculated the number of days which doesn't take into account fine grain detail like hour, minutes seconds.唯一的问题是您提供的第一个链接计算的天数没有考虑到小时、分钟、秒等细粒度细节。 Since you want it fine grained, all you need to do is replace .day with .second既然你想要细粒度的,你需要做的就是用.day替换.second

extension Date {
    func secondsBetweenDateTimes(toDateTime: Date) -> Int {
        let components = Calendar.current.dateComponents([.second], from: self, to: toDateTime)
        return components.second ?? 0
    }
}

This will return you total number of seconds between your two date times.这将返回您两个日期时间之间的总秒数。 Next, you need to divide the result from this method by 86400 (number of seconds in a day) to get the number of days.接下来,您需要将此方法的结果除以86400 (一天中的秒数)以获得天数。 Then use the remainder, divide it by 24 to get the remaining hours and so on and so forth.然后使用余数,除以24得到剩余小时数,依此类推。 This way, you can get the exact difference between two DateTimes.通过这种方式,您可以获得两个 DateTime 之间的确切差异。

UPDATE更新

Your if check is a bit faulty.你的 if 检查有点错误。 I see what you are trying to do here but that is not the correct if check .我明白你在这里想做什么,但if check是不正确的。 Right now what you are telling your code to do is pretty much add an extra day if the number of seconds cannot be divided into whole days.现在,如果秒数不能分为整天,那么您告诉代码要做的几乎就是多增加一天。 As a result, you will see the behaviour that you are experiencing right now.因此,您将看到您现在正在经历的行为。 In my original answer, I said to use the remainder not the whole value to decide whether you want to add a day or not.在我的原始答案中,我说使用remainder而不是整个值来决定是否要添加一天。 Anyways, since you are not using the hours, minutes, seconds as your deciding factor, you can do it another way.无论如何,由于您没有使用小时、分钟、秒作为决定因素,您可以用另一种方式来做。 Just strip out the hour minutes and seconds before calculation and calculate the number of days as below.只需在计算前去掉小时分和秒,并计算如下天数。

let start = "2017-12-08 12:30:00 +0900"
let end = "2017-12-10 13:30:00 +0900"
let startDate = start.substring(to: start.index(start.startIndex, offsetBy: 10))
let endDate = end.substring(to: end.index(end.startIndex, offsetBy: 10))
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
var fromDate = dateFormatter.date(from: startDate)
let toDate = dateFormatter.date(from: endDate)
let cal = Calendar.autoupdatingCurrent
let days=cal.dateComponents([.day], from: fromDate!, to: toDate!)
print(days.day)    //Prints 2

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM