pobierz zmienne NSDate dziś, wczoraj, w tym tygodniu, w zeszłym tygodniu, w tym miesiącu, w ostatnim miesiącu…

86

Próbuję uzyskać NSDate dzisiaj, wczoraj, w tym tygodniu, w zeszłym tygodniu, w tym miesiącu, w zeszłym miesiącu zmienne gotowe do porównania dla nagłówków dodawanych do titleForHeaderInSection UITableView

To, co chcę, jest zrobione ręcznie w poniższym kodzie z datą 2009-12-11

 NSDate *today = [NSDate dateWithString:@"2009-12-11 00:00:00 +0000"];
 NSDate *yesterday = [NSDate dateWithString:@"2009-12-10 00:00:00 +0000"];
 NSDate *thisWeek = [NSDate dateWithString:@"2009-12-06 00:00:00 +0000"];
 NSDate *lastWeek = [NSDate dateWithString:@"2009-11-30 00:00:00 +0000"];
 NSDate *thisMonth = [NSDate dateWithString:@"2009-12-01 00:00:00 +0000"];
 NSDate *lastMonth = [NSDate dateWithString:@"2009-11-01 00:00:00 +0000"];
hasnat
źródło

Odpowiedzi:

93

Na podstawie przewodnika programowania daty i godziny :

// Right now, you can remove the seconds into the day if you want
NSDate *today = [NSDate date];

// All intervals taken from Google
NSDate *yesterday = [today dateByAddingTimeInterval: -86400.0];
NSDate *thisWeek  = [today dateByAddingTimeInterval: -604800.0];
NSDate *lastWeek  = [today dateByAddingTimeInterval: -1209600.0];

// To get the correct number of seconds in each month use NSCalendar
NSDate *thisMonth = [today dateByAddingTimeInterval: -2629743.83];
NSDate *lastMonth = [today dateByAddingTimeInterval: -5259487.66];

Jeśli chcesz uzyskać dokładną liczbę dni w zależności od miesiąca, użyj pliku NSCalendar.

Ben S.
źródło
77
Stałe stałe określające czas są ZŁE, ZŁE, ZŁE. A co z dniami przestępnymi lub sekundami przestępnymi? Użyj NSDateComponents, w przeciwnym razie będziesz miał bardzo nieprzyjemne i trudne do debugowania problemy, gdy znaczniki daty zaczną się nie zgadzać.
Kendall Helmstetter Gelner
11
Naprawdę nie jest tak źle, jeśli chcesz tylko postów z ostatniego tygodnia lub cokolwiek innego. Sprecyzowałem, że OP powinien być używany, NSCalendarjeśli wymagana jest dokładność.
Ben S,
2
Stałe stałe powinny być w rzeczywistości wystarczające do liczenia dni: każdy dzień to 86400 sekund. Sekundy przestępne po prostu trwają dwa razy dłużej - dzień z sekundą przestępną nadal ma 86400 „sekund”. A jeśli chodzi o miesiące ... tak, omijajcie :)
Chris
3
@Chris z wyjątkiem dni, które nie są 86400 sekund. A co z czasem letnim? Brzmi to nieistotnie, ale w rzeczywistości może prowadzić do bardzo groźnych błędów, które pojawiają się znikąd, gdy nadejdzie czas letni i nie pamiętasz, aby wziąć to pod uwagę.
jpswain
1
Jeśli chcesz wrócić, powiedzmy około 7 dni, możesz po prostu odjąć czas, jeśli chcesz wrócić do „poniedziałku” tego tygodnia, użyj NSCalendar. Istnieją przypadki użycia dla obu scenariuszy.
Johnny Rockex
83

Mógłby być lepszy sposób, aby to napisać, ale oto, co wpadłem na sugestię Bena NSCalendar i pracując od tego miejsca do NSDateComponents

NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components = [cal components:( NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond ) fromDate:[[NSDate alloc] init]];

[components setHour:-[components hour]];
[components setMinute:-[components minute]];
[components setSecond:-[components second]];
NSDate *today = [cal dateByAddingComponents:components toDate:[[NSDate alloc] init] options:0]; //This variable should now be pointing at a date object that is the start of today (midnight);

[components setHour:-24];
[components setMinute:0];
[components setSecond:0];
NSDate *yesterday = [cal dateByAddingComponents:components toDate: today options:0];

components = [cal components:NSWeekdayCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:[[NSDate alloc] init]];

[components setDay:([components day] - ([components weekday] - 1))]; 
NSDate *thisWeek  = [cal dateFromComponents:components];

[components setDay:([components day] - 7)];
NSDate *lastWeek  = [cal dateFromComponents:components];

[components setDay:([components day] - ([components day] -1))]; 
NSDate *thisMonth = [cal dateFromComponents:components];

[components setMonth:([components month] - 1)]; 
NSDate *lastMonth = [cal dateFromComponents:components];

NSLog(@"today=%@",today);
NSLog(@"yesterday=%@",yesterday);
NSLog(@"thisWeek=%@",thisWeek);
NSLog(@"lastWeek=%@",lastWeek);
NSLog(@"thisMonth=%@",thisMonth);
NSLog(@"lastMonth=%@",lastMonth);
hasnat
źródło
13
Z tych fromDate:[[NSDate alloc] init]kawałków wyciekasz pamięć .
Shaggy Frog
21
@Shaggy Frog: Chyba zakłada, że ​​ARC jest włączony ...;)
orj
3
Ten kod nie działa poprawnie. Nie bierze pod uwagę ułamków sekundy, które są częścią daty wejściowej. Oznacza to, że data może mieć 30,1234 sekundy, a NSDateComponents ma tylko 30 jako wartość sekund, więc odjęcie 30 sekund od wartości NSDate pozostawia 0,1234 sekundy po północy. Ponadto, niektóre dni mają więcej (lub mniej) niż 24 godziny (czas letni), więc pobranie wczorajszej daty powinno tak naprawdę używać [components setDay: -1] zamiast [components setHour: -24].
orj
5
@orj FYI, ARC wyszedł wieki po opublikowaniu tej odpowiedzi
Shaggy Frog
3
@orj Ponadto przy zmianie czasu letniego odjęcie godziny nie daje północy. -1. Ten kod nie jest wart 35 głosów za.
Nikolai Ruhe
40

NSDateComponents jest dziś miło do zdobycia:

NSCalendar *cal = [NSCalendar currentCalendar];

NSDate *date = [NSDate date];
NSDateComponents *comps = [cal components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) 
                                                          fromDate:date];
NSDate *today = [cal dateFromComponents:comps];

Spowoduje to utworzenie NSDate z tylko rokiem, miesiącem i datą:

(gdb) po today
2010-06-22 00:00:00 +0200

Aby uzyskać wczoraj itp., Możesz obliczyć to za pomocą NSDateComponents:

NSDateComponents *components = [[NSDateComponents alloc] init];
[components setDay:-1];
NSDate *yesterday = [cal dateByAddingComponents:components toDate:today options:0];
Christian Beer
źródło
Nie musisz uwzględniać komponentu era?
Chris Page
Ładny. Służy do obliczenia daty sprzed 8 lat i działa dobrze. Wystarczy naprawić wyciek tutaj: [[NSDateComponents przydziel] init];
Craig B
@CraigB thanks. Nie jest to kompletny przykład. Pominięto oczywiste zarządzanie pamięcią jako wyzwanie dla czytelnika;)
Christian Beer
@ChrisPage komponent era? Po co?
Christian Beer
1
@ChristianBeer Jestem prawie pewien, że należy wziąć pod uwagę wszystkie komponenty większe niż pożądana rozdzielczość. Myślę, że (NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit)powinno (NSEraCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit).
Chris Page
19
+ (NSDate*)dateFor:(enum DateType)dateType {

    NSCalendar *calendar = [NSCalendar currentCalendar];

    NSDateComponents *comps =
    [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit
                fromDate:[NSDate date]];

    if(dateType == DateYesterday) {

        comps.day--;
    }
    else if(dateType == DateThisWeek) {

        comps.weekday = 1;
    }
    else if(dateType == DateLastWeek) {

        comps.weekday = 1;
        comps.week--;
    }
    else if(dateType == DateThisMonth) {

        comps.day = 1;
    }
    else if(dateType == DateLastMonth) {

        comps.day = 1;
        comps.month--;
    }
    else if(dateType != DateToday)
        return nil;

    return [calendar dateFromComponents:comps];
}
Dustin
źródło
Nie musisz uwzględniać komponentu era?
Chris Page
Musiałem usunąć NSDayCalendarUnit i dodać NSWeekdayCalendarUnit podczas obliczania DateLastWeek.
jessecurry
Firma Apple zmieniła definicję NSWeekdayCalendarUnit i NSDayCalendarUnit wokół iOS 5.0. Nowym sposobem jest użycie NSWeekdayCalendarUnit. Ale w starszych wersjach musisz użyć NSDayCalendarUnit. To jest bałagan ...
Dustin,
@Dustin Wydaje się, że nie działa, nawet podczas aktualizacji do nowych jednostek ... Na przykład DateThisWeekustawienie dnia tygodnia nie ma wpływu na
NSDate
11

Swift 4.2

let today = Date()
let yesterday = today.addingTimeInterval(-86400.0)
let thisWeek = today.addingTimeInterval(-604800.0)
let lastWeek = today.addingTimeInterval(-1209600.0)

let thisMonth = today.addingTimeInterval(-2629743.83)
let lastMonth = today.addingTimeInterval(-5259487.66)

// components of the date
let calendar = Calendar(identifier: Calendar.Identifier.gregorian)
let components = calendar.dateComponents([.year, .month, .day], from: today)
let (year, month, day) = (components.year, components.month, components.day)

Należy pamiętać, że składniki daty są opcjonalne.

gokhanakkurt
źródło
1
@fengd dobrze, dzisiaj 27 czerwca 2016 r., o 1 w nocy będzie 28 czerwca 2016 r., a jeśli odejmie 24 godziny, datą będzie 27 czerwca 1 w nocy. Jaki jest punkt, który przegapiłem?
gokhanakkurt
1
przepraszam @gokhanakkurt. Wydaje mi się, że mój umysł miał pewne problemy. twoja odpowiedź jest prawidłowa
fengd
4

Inne odpowiedzi po prostu nie działały dla mnie (może z powodu mojej strefy czasowej). Oto jak to robię:

- (BOOL)isOnThisWeek:(NSDate *)dateToCompare
{
    NSCalendar * calendar = [NSCalendar currentCalendar];
    NSDate     * today    = [NSDate date];

    int todaysWeek        = [[calendar components: NSWeekCalendarUnit fromDate:today] week];
    int dateToCompareWeek = [[calendar components: NSWeekCalendarUnit fromDate:dateToCompare] week];

    int todaysYear         = [[calendar components:NSYearCalendarUnit fromDate:today] year];
    int dateToCompareYear  = [[calendar components:NSYearCalendarUnit fromDate:dateToCompare] year];

    if (todaysWeek == dateToCompareWeek && todaysYear == dateToCompareYear) {
        return YES;
    }

    return NO;
}
Lucas
źródło
4

Jeśli używasz systemu iOS 10+ lub MacOS 10.12+, możesz użyć tych dwóch Calendarmetod, aby zrobić to poprawnie.

  • func date(byAdding component: Calendar.Component, value: Int, to date: Date, wrappingComponents: Bool = default) -> Date?( dokumenty )
  • func dateInterval(of component: Calendar.Component, for date: Date) -> DateInterval?( dokumenty )

Oto przykład użycia tych metod w języku Swift 3 wraz z danymi wyjściowymi placów zabaw w mojej strefie czasowej.

let calendar = Calendar.current
let now = Date()
// => "Apr 28, 2017, 3:33 PM"

let yesterday = calendar.date(byAdding: .day, value: -1, to: now)
// => "Apr 29, 2017, 3:33 PM"
let yesterdayStartOfDay = calendar.startOfDay(for: yesterday!)
// => ""Apr 29, 2017, 12:00 AM"

let thisWeekInterval = calendar.dateInterval(of: .weekOfYear, for: now)
// => 2017-04-23 04:00:00 +0000 to 2017-04-30 04:00:00 +0000

let thisMonthInterval = calendar.dateInterval(of: .month, for: now)
// => 2017-04-01 04:00:00 +0000 to 2017-05-01 04:00:00 +0000

let aDateInLastWeek = calendar.date(byAdding: .weekOfYear, value: -1, to: now)
let lastWeekInterval = calendar.dateInterval(of: .weekOfYear, for: aDateInLastWeek!)
// => 2017-04-16 04:00:00 +0000 to 2017-04-23 04:00:00 +0000

let aDateInLastMonth = calendar.date(byAdding: .month, value: -1, to: now)
let lastMonthInterval = calendar.dateInterval(of: .weekOfYear, for: aDateInLastMonth!)
// => 2017-03-26 04:00:00 +0000 to 2017-04-02 04:00:00 +0000

Bonus: możesz użyć DateIntervals, aby sprawdzić, czy data mieści się w tym zakresie. Kontynuując z góry:

thisWeekInterval!.contains(now)
// => true
lastMonthInterval!.contains(now)
// => false
yood
źródło
Dlaczego jest tak, że yesterdaymówi się 29 kwietnia i now28 kwietnia?
Juan Boero
1
NSDate *today = [NSDate date]; // Today's date
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *weekdayComponents =[gregorian componentsNSDayCalendarUnit | NSWeekdayCalendarUnit) fromDate:today];
NSInteger day = [weekdayComponents day];
Hayden
źródło
0

Bardzo podoba mi się obiekt THCalendarInfo zawarty w tym projekcie:

http://github.com/jaredholdcroft/kcalendar

Nie mogę znaleźć oryginału. Za pomocą tego obiektu możesz przejść do poprzedniego dnia, początku tygodnia, początku miesiąca, uzyskać dzień tygodnia, dzień miesiąca ... itd. Itd.

Kendall Helmstetter Gelner
źródło
0
NSDate *today = [[NSDate alloc] initWithTimeIntervalSinceNow:0];
Tischel
źródło
14
Dlaczego nie [NSDate date]?
joshpaul,
Ponieważ oba są ważne, aby uzyskać aktualną datę, oba zawierają godzinę. Więc to nie jest właściwe rozwiązanie na to pytanie!
Christian Beer
0

To jest do sprawdzenia, czy data jest w tym miesiącu, czy nie

func isOnThisMonth(dateToCompare: NSDate) -> Bool {
    let calendar: NSCalendar = NSCalendar.currentCalendar()
    let today: NSDate = NSDate()
    let todaysWeek: Int = calendar.components(NSCalendarUnit.Month, fromDate: today).month
    let dateToCompareWeek: Int = calendar.components(.Month, fromDate: dateToCompare).month
    let todaysYear: Int = calendar.components(NSCalendarUnit.Year, fromDate: today).year
    let dateToCompareYear: Int = calendar.components(NSCalendarUnit.Year, fromDate: dateToCompare).year
    if todaysWeek == dateToCompareWeek && todaysYear == dateToCompareYear {
        return true
    }
    return false
}

A drugi tylko zmienia typ kalendarza na tak słabe

func isOnThisWeek(dateToCompare: NSDate) -> Bool {
    let calendar: NSCalendar = NSCalendar.currentCalendar()
    let today: NSDate = NSDate()
    let todaysWeek: Int = calendar.components(NSCalendarUnit.Weekday, fromDate: today).weekday
    let dateToCompareWeek: Int = calendar.components(.Weekday, fromDate: dateToCompare).weekday
    let todaysYear: Int = calendar.components(NSCalendarUnit.Year, fromDate: today).year
    let dateToCompareYear: Int = calendar.components(NSCalendarUnit.Year, fromDate: dateToCompare).year
    if todaysWeek == dateToCompareWeek && todaysYear == dateToCompareYear {
        return true
    }
    return false
}

Mam nadzieję, że to komuś pomoże. Dziękuję.

Ilesh P
źródło
0

I odpowiedział na podobne pytanie już i oto dlaczego moja odpowiedź brzmi lepiej:

  • Swift 3 !
  • Wykorzystuje „Wczoraj” i „Dzisiaj” DateFormatter. To już zostało przetłumaczone przez Apple, co oszczędza Twoją pracę!
  • Używa już przetłumaczonego ciągu DateComponentsFormatter „1 tydzień”. (Ponownie mniej pracy, dzięki uprzejmości Apple). Wszystko, co musisz zrobić, to przetłumaczyć ciąg „% @ ago”. 🙂
  • Pozostałe odpowiedzi niepoprawnie obliczają czas, w którym dzień zmienia się z „dzisiaj” na „wczoraj” na itd. Stałe stałe to duże NIE-NIE, ponieważ powody . Ponadto w przypadku innych odpowiedzi używana jest bieżąca data / godzina, kiedy powinny być używane koniec daty / godziny z bieżącego dnia .
  • Używa autoupdatingCurrent dla kalendarza i ustawień regionalnych, co zapewnia natychmiastową aktualizację Twojej aplikacji do kalendarza użytkownika i preferencji językowych w Settings.app
ChrisJF
źródło