How to add one month to an NSDate?

前端 未结 9 2156
自闭症患者
自闭症患者 2020-12-23 11:01

How To Add Month To NSDate Object?

NSDate *someDate = [NSDate Date] + 30Days.....;
9条回答
  •  感动是毒
    2020-12-23 11:39

    Other answers work fine if your desired behaviour is adding a month and allowing for daylight savings time. This produces results such that:

    01/03/2017 00:00 + 1 month -> 31/03/2017 23:00
    01/10/2017 00:00 + 1 month -> 01/11/2017 01:00
    

    However I wanted to ignore the hour lost or gained by DST, such that:

    01/03/2017 00:00 + 1 month -> 01/04/2017 00:00
    01/10/2017 00:00 + 1 month -> 01/11/2017 00:00
    

    So I check if a DST boundary is passed, and if so either add or subtract an hour accordingly:

    func offsetDaylightSavingsTime() -> Date {
            // daylightSavingTimeOffset is either + 1hr or + 0hr. To offset DST for a given date, we need to add an hour or subtract an hour
            // +1hr -> +1hr
            // +0hr -> -1hr
            // offset = (daylightSavingTimeOffset * 2) - 1 hour
    
            let daylightSavingsTimeOffset = TimeZone.current.daylightSavingTimeOffset(for: self)
            let oneHour = TimeInterval(3600)
            let offset = (daylightSavingsTimeOffset * 2) - oneHour
            return self.addingTimeInterval(offset)
        }
    
        func isBetweeen(date date1: Date, andDate date2: Date) -> Bool {
            return date1.compare(self).rawValue * self.compare(date2).rawValue >= 0
        }
    
        func offsetDaylightSavingsTimeIfNecessary(nextDate: Date) -> Date {
            if let nextDST = TimeZone.current.nextDaylightSavingTimeTransition(after: self) {
                if nextDST.isBetweeen(date: self, andDate: nextDate){
                    let offsetDate = nextDate.offsetDaylightSavingsTime()
                    let difference = offsetDate.timeIntervalSince(nextDate)
                    return nextDate.addingTimeInterval(difference)
                }
            }
    
            return nextDate
        }
    
        func dateByAddingMonths(_ months: Int) -> Date? {
            if let dateWithMonthsAdded = Calendar.current.date(byAdding: .month, value: months, to: self) {
                return self.offsetDaylightSavingsTimeIfNecessary(nextDate: dateWithMonthsAdded)
            }
    
            return self
        }
    

    Test:

    func testDateByAddingMonths() {
    
        let date1 = "2017-01-01T00:00:00Z".asDate()
        let date2 = "2017-02-01T00:00:00Z".asDate()
        let date3 = "2017-03-01T00:00:00Z".asDate()
        let date4 = "2017-04-01T00:00:00Z".asDate()
        let date5 = "2017-05-01T00:00:00Z".asDate()
        let date6 = "2017-06-01T00:00:00Z".asDate()
        let date7 = "2017-07-01T00:00:00Z".asDate()
        let date8 = "2017-08-01T00:00:00Z".asDate()
        let date9 = "2017-09-01T00:00:00Z".asDate()
        let date10 = "2017-10-01T00:00:00Z".asDate()
        let date11 = "2017-11-01T00:00:00Z".asDate()
        let date12 = "2017-12-01T00:00:00Z".asDate()
        let date13 = "2018-01-01T00:00:00Z".asDate()
        let date14 = "2018-02-01T00:00:00Z".asDate()
    
        var testDate = "2017-01-01T00:00:00Z".asDate()
        XCTAssertEqual(testDate, date1)
    
        testDate = testDate.dateByAddingMonths(1)!
        XCTAssertEqual(testDate, date2)
    
        testDate = testDate.dateByAddingMonths(1)!
        XCTAssertEqual(testDate, date3)
    
        testDate = testDate.dateByAddingMonths(1)!
        XCTAssertEqual(testDate, date4)
    
        testDate = testDate.dateByAddingMonths(1)!
        XCTAssertEqual(testDate, date5)
    
        testDate = testDate.dateByAddingMonths(1)!
        XCTAssertEqual(testDate, date6)
    
        testDate = testDate.dateByAddingMonths(1)!
        XCTAssertEqual(testDate, date7)
    
        testDate = testDate.dateByAddingMonths(1)!
        XCTAssertEqual(testDate, date8)
    
        testDate = testDate.dateByAddingMonths(1)!
        XCTAssertEqual(testDate, date9)
    
        testDate = testDate.dateByAddingMonths(1)!
        XCTAssertEqual(testDate, date10)
    
        testDate = testDate.dateByAddingMonths(1)!
        XCTAssertEqual(testDate, date11)
    
        testDate = testDate.dateByAddingMonths(1)!
        XCTAssertEqual(testDate, date12)
    
        testDate = testDate.dateByAddingMonths(1)!
        XCTAssertEqual(testDate, date13)
    
        testDate = testDate.dateByAddingMonths(1)!
        XCTAssertEqual(testDate, date14)
    }
    

    For completeness, the .asDate() method I'm using

    extension String {
    
        static let dateFormatter = DateFormatter()
        func checkIsValidDate() -> Bool {
            return self.tryParseToDate() != nil
        }
    
        func tryParseToDate() -> Date? {
            String.dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
            return String.dateFormatter.date(from: self)
        }
    
        func asDate() -> Date {
            return tryParseToDate()!
        }
    }
    

提交回复
热议问题