Jak dodać miesiąc do NSDate?


Odpowiedzi:


139

Musisz użyć NSDateComponents :

NSDateComponents *dateComponents = [[NSDateComponents alloc] init];
[dateComponents setMonth:1];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *newDate = [calendar dateByAddingComponents:dateComponents toDate:originalDate options:0];
[dateComponents release]; // If ARC is not used, release the date components

czy wiesz, jak mogę porównać teraz z datą przeznaczenia? sprawdzić, czy zdałem ten termin?
lub azran

1
Porównywanie dat odbywa się za pomocą funkcji NSDate compare, previousDate i laterDate - zobacz dokumentację
NSDate

1
@orazran możesz porównać daty z:, [date timeIntervalSinceDate:otherDate]co zwróci różnicę między nimi w sekundach (mniej niż 0 dla dat przeszłych, większe niż 0 dla dat przyszłych).
Abhi Beckert

127

W systemie iOS 8 i OS X 10.9 możesz dodać NSCalendarUnitsza pomocą NSCalendar:

Cel C

NSCalendar *cal = [NSCalendar currentCalendar];
NSDate *someDate = [cal dateByAddingUnit:NSCalendarUnitMonth value:1 toDate:[NSDate date] options:0];

Szybki 3

let date = Calendar.current.date(byAdding: .month, value: 1, to: Date())

Szybki 2

let cal = NSCalendar.currentCalendar()
let date = cal.dateByAddingUnit(.Month, value: 1, toDate: NSDate(), options: [])

Powyższe przykłady wywołują metodę, a nie uzyskują dostęp do właściwości.
Kevin

Swift 3: let date = Calendar.current.date (byAdding: .month, value: 1, to: Date ())
Maksym Bilan

To powinna być akceptowana odpowiedź. Obecnie akceptowana odpowiedź to tylko Cel-C i jest zbyt rozwlekła.
Colin Basnett,

23

Dla Swift 3.0

extension Date {
    func addMonth(n: Int) -> Date {
        let cal = NSCalendar.current
        return cal.date(byAdding: .month, value: n, to: self)!
    }
    func addDay(n: Int) -> Date {
        let cal = NSCalendar.current
        return cal.date(byAdding: .day, value: n, to: self)!
    }
    func addSec(n: Int) -> Date {
        let cal = NSCalendar.current
        return cal.date(byAdding: .second, value: n, to: self)!
    }
}

1
Co się stanie, jeśli dodam 1 miesiąc do 31 stycznia, a skończy się 2 marca lub ...?
Michał Ziobro

13

Na przykład, aby dodać 3miesiące do bieżącej daty w Swift:

let date = NSCalendar.currentCalendar().dateByAddingUnit(.MonthCalendarUnit, value: 3, toDate: NSDate(), options: nil)!

W Swift 2.0:

let date = NSCalendar.currentCalendar().dateByAddingUnit(.Month, value: 3, toDate: NSDate(), options: [])
  • Nowa OptionSetTypestruktura NSCalendarUnitumożliwia prostsze określenie.Month
  • Parametry, które przyjmują OptionSetType(takie jak options:parametr, który przyjmuje NSCalendarOptions) nie mogą być nil, więc przekaż puste zestaw ( []), aby reprezentować „brak opcji”.

.MonthCalendarUnitjest przestarzałe, użyj.CalendarUnitMonth
Arnold

3

W Swift 2.0

    let startDate = NSDate()
    let dateComponent = NSDateComponents()
    dateComponent.month = 1
    let cal = NSCalendar.currentCalendar()
    let endDate = cal.dateByAddingComponents(dateComponent, toDate: startDate, options: NSCalendarOptions(rawValue: 0))

2

DLA SWIFT 3.0

tutaj jest funkcja, możesz zredukować dni, miesiące, dni o dowolną liczbę, jak na przykład tutaj, zmniejszyłem rok bieżącej daty systemowej o 100 lat, możesz to zrobić dla dnia, miesiąca również po prostu ustaw licznik, a następnie zapisz wartości w tablicy i zrób z nią co chcesz

func currentTime(){

    let date = Date()
    let calendar = Calendar.current
    var year = calendar.component(.year, from: date)
    let month = calendar.component(.month, from: date)
    let  day = calendar.component(.day, from: date)
    let pastyear = year - 100
    var someInts = [Int]()
    printLog(msg: "\(day):\(month):\(year)" )

    for _ in pastyear...year        {
        year -= 1
                     print("\(year) ")
        someInts.append(year)
    }

    print(someInts)
}

1

Inne odpowiedzi działają dobrze, jeśli Twoim pożądanym zachowaniem jest dodanie miesiąca i uwzględnienie czasu letniego. Daje to takie wyniki, że:

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

Jednak chciałem zignorować godzinę straconą lub zyskowną przez DST, tak że:

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

Więc sprawdzam, czy granica czasu letniego została przekroczona, a jeśli tak, to odpowiednio dodaj lub odejmij godzinę:

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

Aby uzyskać kompletność, używam metody .asDate ()

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()!
    }
}

1

Czy chcesz dodać „miesiąc” czy dokładnie 30 dni lub jeden dzień lub rok w oparciu o wybór przez użytkownika automatycznego obliczania do dnia.

  NSCalendar *calendar = [NSCalendar currentCalendar];
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] 
  init];
    [dateFormatter setDateFormat:@"YYYY-MM-dd HH:mm:ss"];
    NSDateComponents *components = [calendar components:(NSCalendarUnitHour | NSCalendarUnitMinute) fromDate:[NSDate date]];

        NSDateComponents *dayComponent = [[NSDateComponents alloc] 
 init];
        int changeid = [here number of days intValue];

        dayComponent.hour = changeid;

        NSCalendar *theCalendar = [NSCalendar currentCalendar];
        NSDate *nextDate = [theCalendar 
 dateByAddingComponents:dayComponent toDate:[dateFormatter 
  dateFromString:self.fromDateTF.text] options:0];

        NSLog(@"nextDate: %@ ...", nextDate);
        [self.toDateTF setText:[dateFormatter 
            stringFromDate:nextDate]];

////miesiąc


-3

Chcesz dodać „miesiąc” czy dokładnie 30 dni? Jeśli to 30 dni, robisz to w ten sposób:

// get a date
NSDate *date = [NSDate dateWithNaturalLanguageString:@"2011-01-02"];

// add 30 days to it (in seconds)
date = [date dateByAddingTimeInterval:(30 * 24 * 60 * 60)];

NSLog(@"%@", date); // 2011-02-01

Uwaga: nie będzie to uwzględniać zmian czasu letniego ani sekund przestępnych. Użyj odpowiedzi @ TheEye, jeśli tego potrzebujesz


2
To się nie powiedzie, jeśli zostanie użyte przy zmianie czasu letniego lub sekundach przestępnych itp.
James Webster,

@JamesWebster to bardzo dobra uwaga, dodałem notatkę zawierającą ją do mojej odpowiedzi. Niski poziom dodawania / odejmowania sekund nadal należy do jednej z odpowiedzi. Nie zawsze chcesz śledzić rzeczy takie jak czas letni, to zależy od tego, do czego jest używana data.
Abhi Beckert

2
Nie, każdy miesiąc ma 30 dni
Adil Malik

To nie dodaje 30 dni. Dodaje 30 razy 86 400 sekund, co jest czymś zupełnie innym. Tego rodzaju kod to niesamowicie zła rada.
gnasher729

Prawdą jest, że daje to błędny wynik w przypadku przejścia na czas letni. Jednak sekundy przestępne nie stanowią problemu, ponieważ czas uniksowy ich nie liczy.
Martin R
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.