Search code examples
iosswift3ebay-apidateinterval

Convert eBay timeLeft to Swift 3.0 TimeInterval


I am trying to convert the eBay timeLeft to a TimeInterval. The timeLeft value is returned from eBay as. Apparently the value is ISO 8601 (see link and question below). I believe the timeLeft is an interval relative to when I made the API call.

http://developer.ebay.com/Devzone/finding/CallRef/findItemsByKeywords.html

From eBay reference:

searchResult.item.sellingStatus.timeLeft    

Time left before the listing ends. The duration is represented in the ISO 8601 duration format (PnYnMnDTnHnMnS). For listings that have ended, the time left is PT0S (zero seconds). See the "duration" type for information about this time format.

P10DT4H38M16S

Such that eBay Time Left Format:

P   = Pending (INDICATOR OF ISO8601 DURATION)
1Y  = 1 Year
2M  = 2 Months
2D  = Two Days
T   = Time Separator (M is ambiguous)
16H = Sixteen hours
2M  = Two minutes
40S = Forty seconds

I can format the string as follows, but this solution doesn't give me the ability process based on the attributes of the time left. For example. If the days are 0, I dont want to show "0 days". Ebay API ISO 8601 Date to timestamp

My specific question, is there a way to get this timeLeft into a swift 3.0 DateInterval so that I can use the functions native to the variable type?


Solution

  • I think eBay made a mistake in going with the duration field. It should state the start and end date of the auction to make things simpler. Date calculation is hard. I haven't gone through the whole API documentation yet, but just off the top of my mind:

    • What calendar does eBay use? Gregorian, Chinese, Persian, Buddhist? All have different concepts of day, month and year.
    • A month can last between 28 and 31 days. A year can be 365 or 366 days. How can you calculate how many seconds that a duration represents?

    Having said that, here's a possible parser for the ISO 8601 duration string:

    struct ISO8601Duration {
        var years = 0
        var months = 0
        var weeks = 0
        var days = 0
        var hours = 0
        var minutes = 0
        var seconds = 0
    
        init?(string: String) {
            guard string.hasPrefix("P") else {
                return nil
            }
    
            let regex = try! NSRegularExpression(pattern: "(\\d+)(Y|M|W|D|H|S)|(T)", options: [])
            let matches = regex.matches(in: string, options: [], range: NSMakeRange(0, string.utf16.count))
            guard matches.count > 0 else {
                return nil
            }
    
            var isTime = false
            let nsstr = string as NSString
            for m in matches {
                if nsstr.substring(with: m.range) == "T" {
                    isTime = true
                    continue
                }
    
                guard let value = Int(nsstr.substring(with: m.rangeAt(1))) else {
                    return nil
                }
                let timeUnit = nsstr.substring(with: m.rangeAt(2))
    
                switch timeUnit {
                case "Y":
                    self.years = value
                case "M":
                    if !isTime {
                        self.months = value
                    } else {
                        self.minutes = value
                    }
                case "W":
                    self.weeks = value
                case "D":
                    self.days = value
                case "H":
                    self.hours = value
                case "S":
                    self.seconds = value
                default:
                    return nil
                }
            }
        }
    
        func endDate(from date: Date = Date()) -> Date? {
            var components = DateComponents()
            components.year = self.years
            components.month = self.months
            components.day = self.weeks * 7 + self.days
            components.hour = self.hours
            components.minute = self.minutes
            components.second = self.seconds
    
            return Calendar(identifier: .gregorian).date(byAdding: components, to: date)
        }
    }
    
    let timeLeft = "P1DT13H24M17S"
    if let duration = ISO8601Duration(string: timeLeft),
        let endDate = duration.endDate()
    {
        print(endDate)
    }