Как определить, есть ли 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: я не согласен. Ваш код по-прежнему должен обрабатывать временные интервалы, но Apple предоставляет методы, которые уже учитывают это - см .: stackoverflow.com/a/17642033/106435
vikingosegundo

1
Метод NSDate «description» отображает дату в формате UTC, то есть среднее время по Гринвичу, без корректировки на летнее время. Если вы опаздываете вечером в США или рано утром в Индии, он не будет отображать то, что «сегодня» согласно вашему календарю. Вам нужен объект NSCalendar, чтобы преобразовать NSDate в день и преобразовать текущую дату в день, а затем сравнить их.
gnasher729

Ответы:


308

В macOS 10.9+ и iOS 8+ в NSCalendar / Calendar есть метод, который делает именно это!

- (BOOL)isDateInToday:(NSDate *)date 

Так что вы бы просто сделать

Objective-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 *) date 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
}

Редактировать:

Мне больше нравится метод Стефана, я думаю, что он делает более понятным и понятным утверждение if:

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
}

Крис, я включил ваше предложение. Мне пришлось посмотреть, какая была эпоха, поэтому для тех, кто не знает, она различает BC и AD. Это, вероятно, не нужно для большинства людей, но это легко проверить и добавить определенности, поэтому я включил его. Если вы идете на скорости, это, вероятно, не очень хороший метод в любом случае.


ПРИМЕЧАНИЕ, как и во многих ответах по SO, через 7 лет это полностью устарело. В Swift сейчас просто используют.isDateInToday


1
Спасибо. Я думаю, что это более аккуратное и безопасное для будущего решение моего вопроса.
Seunghoon

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

1
Так как «otherDay» и «today» в любом случае заполнены только этими четырьмя полями (это все, что вы просили), вы можете просто использовать [today equalTo: otherDay], не перепрыгивая через обручи во втором.
Гленн Мейнард

@Glenn Вы предлагаете мне сравнить NSDateComponents в первом? Я действительно не думал об этом, но это также кажется хорошей идеей.
Дэвид Канарек

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

27

Это ответ на ваш вопрос, но если вы хотите напечатать NSDate с «Today» или «Yesterday», используйте функцию

- (void)setDoesRelativeDateFormatting:(BOOL)b

для NSDateFormatter


3
Умная идея Но я предполагаю, что поиск строки «Сегодня» прервется, если вы локализуетесь.
Базилик Бурк

16

Я попытался бы нормализовать сегодняшнюю дату до полуночи, а вторую дату - до полуночи, а затем сравнить, если это та же самая дата NSD.

Из примера 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

Расширение Working 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.
Андреас Лей

5

Вы также можете проверить временной интервал между вашей датой и текущей датой:

[myDate timeIntervalSinceNow]

Это даст вам временной интервал в секундах между myDate и текущей датой / временем.

Link .

Изменить: Примечание для всех: я хорошо знаю, что [myDate timeIntervalSinceNow] не однозначно определяет, является ли myDate сегодня.

Я оставляю этот ответ как есть, так что если кто-то ищет что-то подобное и [myDate timeIntervalSinceNow] полезен, он может найти его здесь.


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

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

@ Дэвид: правда. Не подумал об этом, когда я отвечал на вопрос 3 другими вещами, и ваш ответ не был опубликован, когда я загрузил страницу.
alesplin

1
Это не отвечает на вопрос вообще. Вы должны удалить его.
Викингосегунд

1
Этот ответ не что иное, как шум. Он не пытается ответить на вопрос, и нет необходимости ссылаться на него, timeIntervalSinceNowкак это описано во многих других сообщениях. Также обработка сравнения дней с проверкой секунд поощряет подверженное ошибкам деление на 86400.
vikingosegundo

4

Быстрое расширение на основе лучших ответов:

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

Спасибо за это. Принятый способ был слишком ресурсоемким для нашего приложения, поэтому нам нужно было оптимизировать это.
Accatyc

1
Этот код не будет работать в летнее время. Каждый, кто использует код с печально известным номером 86 400, заслуживает головной боли, которую он может вызвать.
Викингосегундо

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 из-за перехода на летнее время?
Викингосегунд

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 часов.
Викингосегунд

Хм, это правда. Однако среднее значение за год составляет ровно 24 часа, поэтому, на мой взгляд, в худшем случае он будет отключен только на один час. Возможно, это можно было бы проверить явно.
devios1

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

«secondsFromGMT» резко меняется два раза в год в большинстве мест в мире. Намного безопаснее использовать методы 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.