Bagaimana cara menentukan apakah NSDate hari ini?

156

Cara memeriksa apakah suatu NSDate milik hari ini?

Saya biasa memeriksanya menggunakan 10 karakter pertama dari [aDate description]. [[aDate description] substringToIndex:10]mengembalikan string seperti "YYYY-MM-DD"jadi saya membandingkan string dengan string yang dikembalikan oleh [[[NSDate date] description] substringToIndex:10].

Apakah ada cara yang lebih cepat dan / atau rapi untuk memeriksa?

Terima kasih.

Seunghoon
sumber
Saya percaya metode saya adalah satu-satunya (pada tulisan saya ini) yang sebenarnya merujuk pada dokumentasi Apple dan melakukan apa yang mereka sarankan di dalamnya untuk menentukan apakah dua hari sama. Meskipun metode lain mungkin berhasil, saya percaya metode saya adalah bukti paling depan. stackoverflow.com/a/17641925/901641
ArtOfWarfare
@ ArtOfWarfare: Saya tidak setuju. Kode Anda masih perlu berurusan dengan interval waktu, tetapi apple menyediakan metode yang sudah memperhitungkan faktor ini - lihat: stackoverflow.com/a/17642033/106435
vikingosegundo
1
Metode "deskripsi" NSDate menampilkan tanggal dalam format UTC, yaitu Greenwich Mean Time, tanpa koreksi untuk waktu musim panas. Jika Anda larut malam di AS, atau pagi di India, itu tidak akan menampilkan apa yang "hari ini" menurut kalender Anda. Anda memerlukan objek NSCalendar untuk menerjemahkan NSDate menjadi satu hari, dan mengubah tanggal saat ini menjadi satu hari, lalu membandingkannya.
gnasher729

Jawaban:

308

Di macOS 10.9+ & iOS 8+, ada metode di NSCalendar / Kalender yang melakukan ini!

- (BOOL)isDateInToday:(NSDate *)date 

Jadi, Anda cukup melakukannya

Tujuan-C:

BOOL today = [[NSCalendar currentCalendar] isDateInToday:date];

Swift 3:

let today = Calendar.current.isDateInToday(date)
Catfish_Man
sumber
1
Periksa menggunakan iOS 8 SDK
Catfish_Man
1
Saya bingung dengan komentar di atas, ini jelas iOS 8: - (BOOL) isDateInToday: (NSDate *) date NS_AVAILABLE (10_9, 8_0);
powerj1984
Ya, itulah yang saya katakan. Komentator sebelumnya bingung dengan melihat iOS 7 SDK, mungkin. Itu deklarasi yang berbeda.
Catfish_Man
untuk 'hari ini' ini adalah jawaban yang sempurna :)
Daij-Djan
Jawaban yang sangat bagus!
Supertecnoboff
192

Anda dapat membandingkan komponen tanggal:

NSDateComponents *otherDay = [[NSCalendar currentCalendar] components:NSCalendarUnitEra | NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:aDate];
NSDateComponents *today = [[NSCalendar currentCalendar] components:NSCalendarUnitEra | NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:[NSDate date]];
if([today day] == [otherDay day] &&
   [today month] == [otherDay month] &&
   [today year] == [otherDay year] &&
   [today era] == [otherDay era]) {
    //do stuff
}

Edit:

Saya lebih suka metode Stefan lebih, saya pikir itu membuat pernyataan yang lebih bersih dan lebih dimengerti:

NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components = [cal components:(NSCalendarUnitEra | NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:[NSDate date]];
NSDate *today = [cal dateFromComponents:components];
components = [cal components:(NSCalendarUnitEra | NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:aDate];
NSDate *otherDate = [cal dateFromComponents:components];

if([today isEqualToDate:otherDate]) {
    //do stuff
}

Chris, saya sudah memasukkan saran Anda. Saya harus melihat era apa itu, jadi bagi siapa pun yang tidak tahu, itu membedakan antara SM dan AD. Ini mungkin tidak perlu bagi kebanyakan orang, tetapi mudah untuk memeriksa dan menambahkan kepastian, jadi saya sudah memasukkannya. Jika Anda ingin kecepatan, ini mungkin bukan metode yang baik.


Perhatikan seperti banyak jawaban pada SO, setelah 7 tahun ini benar-benar ketinggalan zaman. Di Swift sekarang gunakan saja.isDateInToday

David Kanarek
sumber
1
Terima kasih. Saya pikir ini adalah solusi yang lebih rapi dan aman di masa depan untuk pertanyaan saya.
Seunghoon
1
Saya pikir Anda perlu memasukkan komponen era juga. Saya pikir ini adalah lemparan koin antara dua pendekatan; isEqualToDate tampaknya lebih bagus, tetapi tampaknya sedikit boros dan membutuhkan jumlah baris yang sama untuk merekonstruksi tanggal dari komponen.
Chris Halaman
1
Karena "otherDay" dan "today" hanya mengisi keempat bidang itu saja (itu saja yang Anda minta), Anda bisa menggunakan [today equalTo: otherDay], tanpa melompati lingkaran di yang kedua.
Glenn Maynard
@ Glenn Apakah Anda menyarankan saya membandingkan NSDateComponents di yang pertama? Saya belum benar-benar memikirkan hal itu, tetapi juga sepertinya ide yang bagus.
David Kanarek
1
@ Davidvidan Menggali posting lama ... Saya tidak akan merekomendasikan membandingkan komponen tanggal secara langsung, jika Anda masuk ke situasi di mana Anda perlu mengimbangi salah satu komponen, Anda mungkin mendapatkan sesuatu seperti bulan == 3, hari == 34. Mengubah ke tanggal akan dengan benar menafsirkan ini sebagai 3 April, membandingkan komponen tanggal tidak akan melihat ini sama dengan bulan == 4, hari == 3.
Sean Kladek
27

Ini merupakan cabang dari pertanyaan Anda, tetapi jika Anda ingin mencetak NSDate dengan "Today" atau "Yesterday", gunakan fungsi ini

- (void)setDoesRelativeDateFormatting:(BOOL)b

untuk NSDateFormatter

Jon
sumber
3
Ide pintar Tapi saya berasumsi bahwa mencari string "Hari ini" akan rusak jika Anda melokalkan.
Basil Bourque
16

Saya akan mencoba untuk mendapatkan tanggal hari ini dinormalisasi ke tengah malam dan tanggal kedua, menormalkan ke tengah malam kemudian membandingkan jika itu adalah NSDate yang sama.

Dari contoh Apple, inilah cara Anda menormalkan hingga tanggal tengah malam hari ini, lakukan hal yang sama untuk kencan kedua dan bandingkan:

NSCalendar * gregorian = [[NSCalendar alloc]
                               initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents * components =
    [gregorian components:
                 (NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit)
                 fromDate:[NSDate date]];
NSDate * today = [gregorian dateFromComponents:components];
Stefan
sumber
Saya pikir Anda perlu memasukkan jamannya juga.
Chris Halaman
Bukankah seharusnya sebuah tanggal dinormalisasi dengan menetapkan waktu ke 12, yaitu tengah hari, bukan tengah malam? Karena tanggal dalam GMT, mengaturnya ke tengah hari memastikan bahwa variasi zona waktu kedua arah (dan mereka naik ke 12h hanya dua arah) tidak 'melompat' ke hari sebelum atau sesudah.
artooras
13

Bekerja Swift 3 & 4 ekstensi saran oleh Catfish_Man:

extension Date {

    func isToday() -> Bool {
        return Calendar.current.isDateInToday(self)
    }

}
Benno Kress
sumber
12

Tidak perlu menyulap dengan komponen, era dan hal-hal.

NSCalendar menyediakan metode untuk memulai unit waktu tertentu untuk tanggal yang ada.

Kode ini akan memulai hari ini dan tanggal lainnya dan membandingkannya. Jika dievaluasi NSOrderedSame, kedua tanggal adalah pada hari yang sama - jadi hari ini.

NSDate *today = nil;
NSDate *beginningOfOtherDate = nil;

NSDate *now = [NSDate date];
[[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit startDate:&today interval:NULL forDate:now];
[[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit startDate:&beginningOfOtherDate interval:NULL forDate:beginningOfOtherDate];

if([today compare:beginningOfOtherDate] == NSOrderedSame) {
    //otherDate is a date in the current day
}
vikingosegundo
sumber
9
extension NSDate {
  func isToday() -> Bool {
    let cal = NSCalendar.currentCalendar()
    var components = cal.components([.Era, .Year, .Month, .Day], fromDate:NSDate())
    let today = cal.dateFromComponents(components)!

    components = cal.components([.Era, .Year, .Month, .Day], fromDate:self)
    let otherDate = cal.dateFromComponents(components)!

    return today.isEqualToDate(otherDate)
}

Bekerja untuk saya di Swift 2.0

Omar Albeik
sumber
6

Versi cepat jawaban terbaik:

let cal = NSCalendar.currentCalendar()
var components = cal.components([.Era, .Year, .Month, .Day], fromDate:NSDate())
let today = cal.dateFromComponents(components)!

components = cal.components([.Era, .Year, .Month, .Day], fromDate:aDate);
let otherDate = cal.dateFromComponents(components)!

if(today.isEqualToDate(otherDate)) {
    //do stuff
}
Vojtech Vrbka
sumber
5

Lihat entri dokumentasi Apple yang berjudul "Melakukan Penghitungan Kalender" [tautan] .

Listing 13 pada halaman itu menyarankan bahwa untuk menentukan jumlah tengah malam antar hari, Anda menggunakan:

- (NSInteger)midnightsFromDate:(NSDate *)startDate toDate:(NSDate *)endDate
{
    NSCalendar *calendar = [NSCalendar autoupdatingCurrentCalendar];
    NSInteger startDay = [calendar ordinalityOfUnit:NSDayCalendarUnit
                                             inUnit:NSEraCalendarUnit
                                            forDate:startDate];
    NSInteger endDay = [calendar ordinalityOfUnit:NSDayCalendarUnit
                                           inUnit:NSEraCalendarUnit
                                          forDate:endDate];
    return endDay - startDay;
}

Anda kemudian dapat menentukan apakah dua hari sama dengan menggunakan metode itu dan melihat apakah itu mengembalikan 0 atau tidak.

ArtOfWarfare
sumber
2
Ingatlah bahwa Era dimulai pada pukul 00:00 GMT! Metode ini mungkin tidak berfungsi dengan benar jika salah satu tanggal (atau keduanya) tidak berada dalam zona waktu GMT.
Andreas Ley
5

Anda juga dapat memeriksa interval waktu antara tanggal yang Anda miliki, dan tanggal saat ini:

[myDate timeIntervalSinceNow]

Ini akan memberi Anda interval waktu, dalam detik, antara myDate dan tanggal / waktu saat ini.

Link .

Sunting: Catatan untuk semua orang: Saya sangat menyadari bahwa [myDate timeIntervalSinceNow] tidak menentukan apakah myDate hari ini atau tidak.

Saya meninggalkan jawaban ini sebagaimana adanya sehingga jika seseorang mencari sesuatu yang serupa dan [myDate timeIntervalSinceNow] berguna, mereka mungkin menemukannya di sini.

alesplin
sumber
1
Apa yang Anda katakan itu benar, tetapi intervalnya tidak terlalu membantu untuk memeriksa apakah tanggalnya hari ini.
Jaanus
3
Hanya perlu diingat bahwa ini memberitahu Anda jika Anda berada dalam 24 jam sejak sekarang, tetapi tidak jika tanggalnya sama. Perbedaan satu detik dapat berupa tanggal yang berbeda dan perbedaan 86399 detik bisa menjadi tanggal yang sama.
David Kanarek
@ David: Benar. Tidak memikirkan hal itu karena saya melakukan 3 hal lain saat menjawab pertanyaan, dan jawaban Anda tidak diposting ketika saya memuat halaman.
alesplin
1
Ini sama sekali tidak menjawab pertanyaan. Anda harus menghapusnya.
vikingosegundo
1
Jawaban ini hanyalah kebisingan. Itu tidak berusaha untuk menjawab pertanyaan dan juga tidak perlu merujuk timeIntervalSinceNowseperti yang dibahas dalam banyak posting lainnya. Juga menangani perbandingan hari dengan memeriksa detik mendorong divisi rawan kesalahan oleh 86400.
vikingosegundo
4

Ekstensi Swift berdasarkan jawaban terbaik:

extension NSDate {
    func isToday() -> Bool {
        let cal = NSCalendar.currentCalendar()
        if cal.respondsToSelector("isDateInToday:") {
            return cal.isDateInToday(self)
        }
        var components = cal.components((.CalendarUnitEra | .CalendarUnitYear | .CalendarUnitMonth | .CalendarUnitDay), fromDate:NSDate())
        let today = cal.dateFromComponents(components)!

        components = cal.components((.CalendarUnitEra | .CalendarUnitYear | .CalendarUnitMonth | .CalendarUnitDay), fromDate:self);
        let otherDate = cal.dateFromComponents(components)!
        return today.isEqualToDate(otherDate)
    }
}
ZiggyST
sumber
2

Jika Anda memiliki banyak perbandingan tanggal ini, maka panggilan ke calendar:components:fromDate mulai memakan banyak waktu. Menurut beberapa profil yang telah saya lakukan, harganya agak mahal.

Katakanlah Anda mencoba menentukan yang dari beberapa tanggal, katakanlah NSArray *datesToCompare, adalah hari yang sama dengan hari tertentu, katakanlah NSDate *baseDate, maka Anda dapat menggunakan sesuatu seperti yang berikut (sebagian diadaptasi dari jawaban di atas):

NSDate *baseDate = [NSDate date];

NSArray *datesToCompare = [NSArray arrayWithObjects:[NSDate date], 
                           [NSDate dateWithTimeIntervalSinceNow:100],
                           [NSDate dateWithTimeIntervalSinceNow:1000],
                           [NSDate dateWithTimeIntervalSinceNow:-10000],
                           [NSDate dateWithTimeIntervalSinceNow:100000],
                           [NSDate dateWithTimeIntervalSinceNow:1000000],
                           [NSDate dateWithTimeIntervalSinceNow:50],
                           nil];

// determine the NSDate for midnight of the base date:
NSCalendar* calendar = [NSCalendar currentCalendar];
NSDateComponents* comps = [calendar components:(NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit) 
                                       fromDate:baseDate];
NSDate* theMidnightHour = [calendar dateFromComponents:comps];

// set up a localized date formatter so we can see the answers are right!
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
[dateFormatter setTimeStyle:NSDateFormatterMediumStyle];

// determine which dates in an array are on the same day as the base date:
for (NSDate *date in datesToCompare) {
    NSTimeInterval interval = [date timeIntervalSinceDate:theMidnightHour];
    if (interval >= 0 && interval < 60*60*24) {
        NSLog(@"%@ is on the same day as %@", [dateFormatter stringFromDate:date], [dateFormatter stringFromDate:baseDate]);
    }
    else {
        NSLog(@"%@ is NOT on the same day as %@", [dateFormatter stringFromDate:date], [dateFormatter stringFromDate:baseDate]);
    }
}

Keluaran:

Nov 23, 2011 1:32:00 PM is on the same day as Nov 23, 2011 1:32:00 PM
Nov 23, 2011 1:33:40 PM is on the same day as Nov 23, 2011 1:32:00 PM
Nov 23, 2011 1:48:40 PM is on the same day as Nov 23, 2011 1:32:00 PM
Nov 23, 2011 10:45:20 AM is on the same day as Nov 23, 2011 1:32:00 PM
Nov 24, 2011 5:18:40 PM is NOT on the same day as Nov 23, 2011 1:32:00 PM
Dec 5, 2011 3:18:40 AM is NOT on the same day as Nov 23, 2011 1:32:00 PM
Nov 23, 2011 1:32:50 PM is on the same day as Nov 23, 2011 1:32:00 PM
Julian Richardson
sumber
Terima kasih untuk ini. Cara yang diterima terlalu intensif CPU untuk aplikasi kami sehingga kami perlu mengoptimalkan ini.
Accatyyc
1
Kode ini akan gagal untuk waktu musim panas. Semua orang yang menggunakan kode dengan angka 86.400 yang terkenal pantas sakit kepala yang ditimbulkannya.
vikingosegundo
2

Ada cara yang lebih mudah daripada banyak jawaban di atas!

NSDate *date = ... // The date you wish to test
NSCalendar *calendar = [NSCalendar currentCalendar];

if([calendar isDateInToday:date]) {
    //do stuff
}
aryland
sumber
1
Sama seperti jawaban Catfish_Man!
Thanh Pham
0

Ini mungkin dapat dikerjakan ulang sebagai kategori NSDate, tetapi saya menggunakan:

// Seconds per day (24h * 60m * 60s)
#define kSecondsPerDay 86400.0f

+ (BOOL) dateIsToday:(NSDate*)dateToCheck
{
    // Split today into components
    NSCalendar* gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
    NSDateComponents* comps = [gregorian components:(NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit|NSHourCalendarUnit|NSMinuteCalendarUnit|NSSecondCalendarUnit) 
                                        fromDate:[NSDate date]];

    // Set to this morning 00:00:00
    [comps setHour:0];
    [comps setMinute:0];
    [comps setSecond:0];
    NSDate* theMidnightHour = [gregorian dateFromComponents:comps];
    [gregorian release];

    // Get time difference (in seconds) between date and then
    NSTimeInterval diff = [dateToCheck timeIntervalSinceDate:theMidnightHour];
    return ( diff>=0.0f && diff<kSecondsPerDay );
}

(Namun, membandingkan dua string tanggal seperti dalam pertanyaan asli hampir terasa 'bersih' ..)

LeoN
sumber
Mengapa menyertakan jam, menit, dan detik saat membuat compssaat Anda langsung mengaturnya ke nol? Juga, saya pikir Anda perlu memasukkan era.
Chris Halaman
Harap dicatat bahwa sementara ini akan bekerja di hampir setiap kasus, ada hal-hal seperti detik kabisat. Saya tidak tahu apakah atau bagaimana kelas NSDate Apple menangani mereka, tetapi ada kemungkinan ini tidak akan berhasil. Ini lebih merupakan "tahukah Anda" daripada kritik terhadap metode Anda karena mereka sangat jarang (24 dalam sejarah).
David Kanarek
bagaimana dengan tanggal yang tidak 24 jam, tetapi 23 atau 25 karena Daylight Saving Times?
vikingosegundo
0

untuk iOS7 dan sebelumnya:

//this is now => need that for the current date
NSDate * now = [NSDate date];

NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
[calendar setTimeZone:[NSTimeZone systemTimeZone]];

NSDateComponents * components = [calendar components:( NSYearCalendarUnit|    NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit) fromDate: now];

[components setMinute:0];
[components setHour:0];
[components setSecond:0];

//this is Today's Midnight
NSDate *todaysMidnight = [calendar dateFromComponents: components];



//now timeIntervals since Midnight => in seconds
NSTimeInterval todayTimeInterval = [now timeIntervalSinceDate: todaysMidnight];

//now timeIntervals since OtherDate => in seconds
NSTimeInterval otherDateTimeInterval = [now timeIntervalSinceDate: otherDate];

if(otherDateTimeInterval > todayTimeInterval) //otherDate is not in today
{
    if((otherDateTimeInterval - todayTimeInterval) <= 86400) //86400 == a day total seconds
    {
        @"yesterday";
    }
    else
    {
        @"earlier";
    }
}
else
{
    @"today";
}


now = nil;
calendar = nil;
components = nil;
todaysMidnight = nil;

NSLog("Thank you :-)");
pengguna3744424
sumber
0

Solusi yang benar dan aman tanpa membuka paksa, bekerja pada Swift 2.2 dan sebelum iOS 8:

func isToday() -> Bool {
    let calendar = NSCalendar.currentCalendar()
    if #available(iOS 8.0, *) {
        return calendar.isDateInToday(self)
    }

    let todayComponents = calendar.components([.Era, .Year, .Month, .Day], fromDate:NSDate())
    let dayComponents = calendar.components([.Era, .Year, .Month, .Day], fromDate:self)

    guard let today = calendar.dateFromComponents(todayComponents),
        day = calendar.dateFromComponents(dayComponents) else {
        return false
    }

    return today.compare(day) == .OrderedSame
}
fpg1503
sumber
-1

Inilah jawaban 2 sen saya untuk membangun jawaban yang diterima tetapi juga mendukung API yang lebih baru. Catatan: Saya menggunakan kalender Gregorian karena sebagian besar perangko waktu adalah GMT tetapi ubah milik Anda sesuai keinginan Anda

func isDateToday(date: NSDate) -> Bool {
    let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
    if calendar.respondsToSelector("isDateInToday:") {
        return calendar.isDateInToday(date)
    }
    let dateComponents = NSCalendarUnit.CalendarUnitYear | NSCalendarUnit.CalendarUnitMonth | NSCalendarUnit.CalendarUnitDay
    let today = calendar.dateFromComponents(calendar.components(dateComponents, fromDate: NSDate()))!
    let dateToCompare = calendar.dateFromComponents(calendar.components(dateComponents, fromDate: date))!

    return dateToCompare == today
}
Daniel Galasko
sumber
-3

Solusi saya adalah menghitung berapa hari berlalu sejak 1970 oleh divisi dan membandingkan bagian integer

#define kOneDay (60*60*24)
- (BOOL)isToday {
  NSInteger offset = [[NSTimeZone defaultTimeZone] secondsFromGMT];

  NSInteger days =[self timeIntervalSince1970] + offset;
  NSInteger currentDays = [[NSDate date] timeIntervalSince1970] + offset;
  return (days / kOneDay == currentDays / kOneDay);
}
stcui
sumber
Itu sebenarnya bukan solusi yang buruk. Tidak yakin mengapa itu begitu diputuskan. Menggunakan divisi integer akan memotong sisanya dan hanya membandingkan nilai "hari" dari unix lama, setelah mengoreksi GMT. Saya pikir ini adalah solusi terbersih dari semuanya agar cukup jujur.
devios1
1
@chaiguy: ini adalah solusi buruk karena diasumsikan, bahwa setiap hari adalah 24 jam - apa yang tidak terjadi. Karena Daylight Saving Times, waktu hari bisa 23, 24 atau 25 jam.
vikingosegundo
Hmm itu benar. Namun rata-rata per tahun tepatnya 24 jam, jadi itu hanya akan berpotensi turun dengan kasus terburuk satu jam, seperti yang saya lihat. Mungkin itu bisa diperiksa secara eksplisit.
devios1
2
@chaiguy: tidak. tidak pernah menggunakan kode yang mengandung 60 * 60 * 24. atau hidup dengan sakit kepala Anda.
vikingosegundo
"secondsFromGMT" berubah tiba-tiba dua kali setahun di sebagian besar tempat di dunia. Jauh lebih aman untuk menggunakan metode NSCalendar.
gnasher729
-4
NSDate *dateOne = yourDate;
NSDate *dateTwo = [NSDate date];  

switch ([dateOne compare:dateTwo])
{  
    case NSOrderedAscending:  
        NSLog(@”NSOrderedAscending”);  
        break;  

    case NSOrderedSame: 
        NSLog(@”NSOrderedSame”);  
        break;

    case NSOrderedDescending:  
        NSLog(@”NSOrderedDescending”);  
        break;  
}  
rajesh
sumber
4
Ini tidak memberi tahu Anda jika kedua NSDates adalah tanggal yang sama, hanya mana yang lebih awal, bahkan jika mereka pada hari yang sama.
David Kanarek