简体   繁体   English

从当前日期到特定日期的倒数

[英]countdown from current date to specific date swift

Users can sell items and add an expiration date that varies from 1-3 weeks out from current date. 用户可以出售商品,并添加一个有效期,该有效期从当前日期起1-3周。

This is how I store the expiration date as a Double . 这就是我将到期日期存储为Double Do I need to be explicit about other date components like year, month, etc? 我是否需要明确说明其他日期成分,例如年,月等?

enum Weeks: Int  {
    case one
    case two
    case three
}

extension Weeks {
    var timeInterval: Double? {
        let currentDate = Date()
        let calendar = Calendar.current
        let calendarComponents: Set<Calendar.Component> = Set(arrayLiteral: Calendar.Component.year, Calendar.Component.month, Calendar.Component.day, Calendar.Component.hour, Calendar.Component.minute, Calendar.Component.second)

        var components = calendar.dateComponents(calendarComponents, from: currentDate)

        switch self {
        case .one: components.weekOfMonth = 1
        case .two: components.weekOfMonth = 2
        case .three: components.weekOfMonth = 3
        }

        if let expirationDate = calendar.date(from: components) {
            return expirationDate.timeIntervalSince1970 as Double
        } else {
            return nil
        }
    }
}

This is how I calculate the countdown in Date extension: 这是我如何计算Date扩展中的倒计时:

func countdown(to date: Date) -> CountdownResponse {
    let difference = Calendar.current.dateComponents([.day, .hour, .minute, .second], from: self, to: date)

    guard let day = difference.day, let hour = difference.hour, let minute = difference.minute, let second = difference.second else {
        return .error(message: "One or more date components are nil.")
    }

    if day <= 0 && hour <= 0 && minute <= 0 && second <= 0 {
        return .isFinished
    }

    let days = displayableText(from: difference.day)
    let hours = displayableText(from: difference.hour)
    let minutes = displayableText(from: difference.minute)
    let seconds = displayableText(from: difference.second)

    let timeRemaining = "\(days) : \(hours) : \(minutes) : \(seconds)"

    return .result(time: timeRemaining)
}

This is how I'd like to get the countdown, and then I check the response enum for error, or result. 这就是我想要倒计时的方式,然后我检查响应枚举是否有错误或结果。 But this gives me incorrect time. 但这给了我错误的时间。

let expirationDate = Date(timeIntervalSince1970: self.item.expirationDate)

let response = currentDate.countdown(to: expirationDate)

When I create date manually and test like below, it works as expected. 当我手动创建日期并进行如下测试时,它可以正常工作。

var comp = calendar.dateComponents(calendarComponents, from: Date())

comp.year = 2017
comp.month = 8
comp.day = 24
comp.minute = 50
comp.second = 30

What am I doing wrong? 我究竟做错了什么? Am I persisting date as time interval incorrectly, or creating date from timeInterval incorrectly? 我是错误地将日期持久化为时间间隔,还是错误地从timeInterval创建日期?

As I look over the code I realize that I may be calculating the week wrong. 当我查看代码时,我意识到我可能在计算星期错误。 If it's the second week of the month and a user chooses for item to expire in 3 weeks, week of month shouldn't be 3, it needs to be 3 weeks from current week of month. 如果是当月的第二个星期,并且用户选择3个月后到期的商品,那么当月的星期不应该是3,而应该是当月的当前星期3。 Any guidance on how to fix this logic is appreciated. 任何有关如何解决此逻辑的指导表示赞赏。

Why did you make your code so complicated? 您为什么使代码如此复杂? The Foundation framework provide everything you need, from calendrical calculation (add and subtract dates) to output formatting (x days, y hours, z minutes, etc.). Foundation框架提供了您所需的一切,从日历计算(添加和减去日期)到输出格式(x天,y小时,z分钟等)。

Here's an example of how you can shorten it: 这是一个如何缩短它的示例:

enum Week: Int {
    case one = 7, two = 14, three = 21
}

enum CountdownResponse {
    case isFinished
    case result(time: String)
}

struct ProductListing {
    // Customize this if you want to change timeRemaining's format
    // It automatically take care of singular vs. plural, i.e. 1 hr and 2 hrs
    private static let dateComponentFormatter: DateComponentsFormatter = {
        var formatter = DateComponentsFormatter()
        formatter.allowedUnits = [.day, .hour, .minute, .second]
        formatter.unitsStyle = .short
        return formatter
    }()

    var listingDate: Date
    var duration: Week
    var expirationDate: Date {
        return Calendar.current.date(byAdding: .day, value: duration.rawValue, to: listingDate)!
    }

    var timeRemaining: CountdownResponse {
        let now = Date()

        if expirationDate <= now {
            return .isFinished
        } else {
            let timeRemaining = ProductListing.dateComponentFormatter.string(from: now, to: expirationDate)!
            return .result(time: timeRemaining)
        }
    }
}

// Usage
let august01 = DateComponents(calendar: .current, year: 2017, month: 8, day: 1).date!
let august19 = DateComponents(calendar: .current, year: 2017, month: 8, day: 19).date!

let listing1 = ProductListing(listingDate: august01, duration: .three)
let listing2 = ProductListing(listingDate: august19, duration: .one)

print(listing1.timeRemaining) // .isFinished
print(listing2.timeRemaining) // .result("2 days, 4 hrs, 9 min, 23 secs")

Note though, the calculation becomes real hairy when it crosses over the clock change days as the time shifts back and forth. 但是请注意,当时间来回移动时,当它越过时钟变化天数时,该计算将变得非常繁琐。 I haven't test these edge cases with the code above. 我没有使用上面的代码测试这些极端情况。

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

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