Як визначити, чи є сьогодні NSDate?


156

Як перевірити, чи є NSDate належить він сьогодні?

Я перевіряв це, використовуючи перші 10 символів від [aDate description]. [[aDate description] substringToIndex:10]повертає рядок "YYYY-MM-DD"так, як я порівнював рядок із рядком, повернутим [[[NSDate date] description] substringToIndex:10].

Чи є більш швидкий і / або акуратний спосіб перевірити?

Дякую.


Я вважаю, що мій метод є єдиним (на момент мого написання цього), який насправді посилається на документацію Apple і робить те, що вони пропонують в ній, щоб визначити, чи два дні однакові. Хоча інші методи можуть працювати, я вважаю, що цей метод є найбільш доказовим доказом. stackoverflow.com/a/17641925/901641
ArtOfWarfare

@ArtOfWarfare: Я не згоден. Ваш код все ще доводиться мати справу з тимчасовими інтервалами, але яблуко надає методи , які вже ФАКТОР це далеко - бачать: stackoverflow.com/a/17642033/106435
vikingosegundo

1
Метод "опису" NSDate відображає дату у форматі UTC, тобто середній час Грінвіч, без корекції літнього часу. Якщо ви пізно ввечері в США або рано вранці в Індії, він не відображатиме, що є "сьогодні" відповідно до вашого календаря. Вам потрібен об’єкт NSCalendar, щоб перевести NSDate в день і перетворити поточну дату в день, а потім порівняти їх.
gnasher729

Відповіді:


308

У macOS 10.9+ та iOS 8+ існує метод NSCalendar / Календар, який робить саме це!

- (BOOL)isDateInToday:(NSDate *)date 

Так ви просто зробите

Завдання-C:

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

Швидкий 3:

let today = Calendar.current.isDateInToday(date)

1
Перевірте використання iOS 8 SDK
Catfish_Man

1
Мене бентежить вищезазначений коментар, це безумовно iOS 8: - (BOOL) isDateInToday: (NSDate *) дата NS_AVAILABLE (10_9, 8_0);
powerj1984

Так, це я сказав. Попередній коментатор розгубився, дивлячись на iOS 7 SDK, імовірно. Вона мала іншу декларацію.
Catfish_Man

для "сьогодні" це ідеальна відповідь :)
Дайдж-Джан

Відмінна відповідь!
Supertecnoboff

192

Ви можете порівняти компоненти дати:

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
}

Редагувати:

Мені більше подобається метод stefan, я думаю, що він робить більш чистим і зрозумілим, якщо заява:

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
}

Кріс, я включив твою пропозицію. Мені довелося шукати, що це за епоха, тому для всіх, хто не знає, вона розрізняє до н.е. та н.е. Для більшості людей це, мабуть, непотрібно, але це легко перевірити і додає певної визначеності, тому я включив його. Якщо ви рухаєтесь за швидкістю, це, мабуть, не дуже вдалий метод.


ПРИМІТКА, як і у багатьох відповідях на ТА, після 7 років це абсолютно застаріло. У Swift зараз просто користуйся.isDateInToday


1
Дякую. Я думаю, що це більш акуратне та безпечне для мого питання рішення.
Seunghoon

1
Я думаю, вам потрібно включити і епоху. Я думаю, що це метання монети між двома підходами; isEqualToDate здається приємнішим, але він здається трохи марнотратним і потребує приблизно стільки ж рядків, щоб відновити дату з компонентів.
Кріс Пейдж

1
Оскільки і "otherDay", і "сьогодні" заповнюють лише ці чотири поля (це все, що ви просили), ви можете просто скористатися [сьогодні рівнимTo: otherDay], не перестрибуючи обручі у другому.
Гленн Мейнард

@Glenn Ви пропонуєте мені порівняти NSDateComponents в першому? Я не дуже про це думав, але це також здається гарною ідеєю.
Девід Канарек

1
@DavidKanarek Перекопуючи старі повідомлення ... Я б не рекомендував порівнювати компоненти дат безпосередньо, якщо ви потрапите в ситуацію, коли вам потрібно зрушити один із компонентів, ви можете отримати щось на зразок місяць == 3, день == 34. Перетворення на дати буде правильно трактувати це як 3 квітня, порівнюючи компоненти дати, це не буде таким, як місяць == 4, день == 3.
Шон Кладек

27

Це відповідь на ваше запитання, але якщо ви хочете надрукувати NSDate за допомогою "Сьогодні" або "Вчора", скористайтеся функцією

- (void)setDoesRelativeDateFormatting:(BOOL)b

для NSDateFormatter


3
Розумна ідея. Але я припускаю, що пошук рядка "Сьогодні" порушиться, якщо локалізувати.
Василь Бурк

16

Я б спробував нормалізувати сьогоднішню дату до півночі, а другу дату, нормалізувати до півночі, а потім порівняти, якщо це той самий NSDate.

З прикладу Apple, ось як ви нормалізуєте сьогоднішню дату опівночі, зробіть те ж саме для другого побачення та порівняйте:

NSCalendar * gregorian = [[NSCalendar alloc]
                               initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents * components =
    [gregorian components:
                 (NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit)
                 fromDate:[NSDate date]];
NSDate * today = [gregorian dateFromComponents:components];

Я думаю, вам потрібно включити і епоху.
Кріс Пейдж

Чи не слід нормалізувати побачення, встановивши час на 12, тобто полуденок, а не півночі? Оскільки дата перебуває в GMT, встановлення її до полудня гарантує, що коливання часового поясу в обидва напрямки (і вони тривають до 12 год лише обома способами) не «перескочить» у день до або після.
artooras

13

Робоче розширення пропозиції Swift 3 і 4 від Catfish_Man:

extension Date {

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

}

12

Не потрібно жонглювати компонентами, епохами та іншим предметом.

NSCalendar надає метод отримати початок певної одиниці часу для існуючої дати.

Цей код отримає початок сьогоднішнього дня та іншу дату та порівняємо його. Якщо він оцінює NSOrderedSame, обидві дати проходять в один і той же день - так сьогодні.

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
}

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

Працював для мене на Swift 2.0


6

Швидка версія найкращої відповіді:

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
}

5

Зверніться до документації компанії Apple під назвою "Виконання розрахунків календаря" [посилання] .

Лістинг 13 на цій сторінці говорить про те, що для визначення кількості опівночі між днями ви використовуєте:

- (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;
}

Потім ви можете визначити, чи два дні однакові, скориставшись цим методом і побачивши, повертає він 0 чи ні.


2
Майте на увазі, що ера починається о 00:00 GMT! Цей метод може не працювати належним чином, якщо одна з дат (або обидві) не знаходяться у часовому поясі GMT.
Андреас Лей

5

Ви також можете перевірити проміжок часу між вашою датою та поточною датою:

[myDate timeIntervalSinceNow]

Це дасть вам часовий інтервал у секундах між myDate та поточною датою / часом.

Посилання .

Редагувати: Примітка для всіх: Я добре знаю, що [myDate timeIntervalSinceNow] не однозначно визначає, чи є myDate сьогодні.

Я залишаю цю відповідь так, що якщо хтось шукає щось подібне і [myDate timeIntervalSinceNow] є корисним, він може його знайти тут.


1
Те, що ви говорите, є правильним, але інтервал насправді не допомагає перевірити, чи є дата сьогодні.
Яанус

3
Тільки майте на увазі, що саме це говорить вам, якщо ви знаходитесь протягом 24 годин з моменту, але не, якщо дати однакові. Різниця на одну секунду може бути різною датою, а 86399 секунда різниця може бути однаковою датою.
Девід Канарек

@David: Правда. Я не думав про це, як я робив 3 інші речі, відповідаючи на запитання, і ваша відповідь не була розміщена, коли я завантажував сторінку.
alesplin

1
Це зовсім не відповідає на питання. Ви повинні її видалити.
vikingosegundo

1
Ця відповідь - це не що інше, як шум. Він не намагається відповісти на питання, і не слід посилатися на timeIntervalSinceNowнього, як це висвітлено у багатьох інших публікаціях. Також обробка дня порівняння з перевіркою секунд заохочує поділ, схильний до помилок на 86400.
vikingosegundo

4

Розширення Swift на основі найкращих відповідей:

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

2

Якщо у вас багато порівнянь з датою, то заклики calendar:components:fromDateпочати займуть багато часу. За деякими профілями, які я зробив, вони здаються досить дорогими.

Скажімо, ви намагаєтеся визначити, з якого масиву дат, скажімо NSArray *datesToCompare, такий самий день, як у певного дня, скажімо NSDate *baseDate, тоді ви можете використовувати щось на кшталт наступного (частково адаптованого з відповіді вище):

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

Вихід:

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

Дякую за це Прийнятий спосіб був занадто інтенсивним для процесора для нашого додатка, тому нам потрібно було оптимізувати це.
Accatyyc

1
Цей код не вдасться в літній час. Усі, хто використовує код із сумнозвісним номером 86,400, заслуговують на головний біль.
vikingosegundo

2

Існує простіший спосіб, ніж багато з наведених вище відповідей!

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

if([calendar isDateInToday:date]) {
    //do stuff
}

1
Те саме, що відповідь Catfish_Man!
Тонг Фам

0

Це, ймовірно, може бути перероблено як категорію NSDate, але я використовував:

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

(Однак, порівнюючи два рядки дати, як і в оригінальному запитанні, майже відчувається "чистішим".)


Навіщо включати години, хвилини та секунди під час створення, compsколи ви відразу встановлюєте їх на нуль? Також, я думаю, вам потрібно включити епоху.
Кріс Пейдж

Зверніть увагу, що, хоча це буде працювати майже у кожному випадку, є такі речі, як стрибні секунди. Я не знаю, чи обробляє їх клас NSDate від Apple, але є певна ймовірність, що це не спрацює. Це скоріше "ви знали", а не критику вашого методу, оскільки вони надзвичайно рідкісні (24 в історії).
Девід Канарек

як щодо дат, що тривають не 24 години, але 23 або 25 через літній час?
vikingosegundo

0

для iOS7 та новіших версій:

//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 :-)");


0

Правильне та безпечне рішення без розгортання сили, працюючи на Swift 2.2 та до 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
}

-1

Ось мій відповідь на 2 центри на прийнятій відповіді, але також підтримує новіший API. Примітка. Я використовую григоріанський календар, оскільки більшість відміток часу є GMT, але змінюйте свій, як вважаєте за потрібне

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
}

-3

Моє рішення - обчислити, скільки днів пройшло з 1970 року за діленням і порівняти цілу частину

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

Це насправді не погане рішення. Не впевнений, чому це було так прихильно. Використання цілого поділу дозволить відрізати залишок та порівняти лише значення "днів" для Unix довгий час після виправлення для GMT. Я думаю, що це найчистіше рішення їх усіх бути відвертим.
devios1

1
@chaiguy: це неправильне рішення, як передбачається, що кожен день триває 24 години - що не так. Через літній час дні можуть тривати 23, 24 або 25 годин.
vikingosegundo

Хм, це правда. Однак середній показник на рік становить рівно 24 години, тому він, як я бачу, потенційно вимкнеться лише на одну годину. Можливо, це можна було б чітко перевірити.
devios1

2
@chaiguy: ні. просто ніколи не використовуйте код, який містить 60 * 60 * 24. або жити з головним болем.
vikingosegundo

"секунди від ГМТ" різко змінюється двічі на рік у більшості місць світу. Набагато безпечніше використовувати методи 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;  
}  

4
Це не говорить вам про те, чи є обидва NSDates одна і та ж дата, яка саме є раніше, навіть якщо вони в один і той же день.
Девід Канарек
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.