получить NSDate сегодня, вчера, на этой неделе, на прошлой неделе, в этом месяце, в прошлом месяце ... переменные

86

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

То, что я хочу, сделано вручную в приведенном ниже коде на дату 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"];
Хаснат
источник

Ответы:

93

Адаптировано из Руководства по программированию даты и времени :

// 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];

Если вам нужно точное количество дней в зависимости от месяца, вам следует использовать файл NSCalendar.

Бен С
источник
77
Постоянные постоянные времени - ПЛОХО ПЛОХО ПЛОХО. А как насчет високосных дней или високосных секунд? Используйте NSDateComponents, иначе вы БУДЕТЕ столкнуться с очень неприятными и трудными для отладки проблемами, когда ваши отметки даты начнут не совпадать.
Кендалл Хельмштеттер Гельнер,
11
Это действительно не так уж и плохо, если вы просто хотите примерно постов за последнюю неделю или что-то еще. Я указал, что OP следует использовать, NSCalendarесли требуется точность.
Ben S
2
Фиксированные константы действительно подходят для подсчета дней: каждый день составляет 86400 секунд. Просто дополнительные секунды занимают вдвое больше - день с дополнительной секундой по-прежнему имеет 86400 «секунд». Что касается месяцев ... да, держитесь подальше :)
Крис
3
@Chris, кроме случаев, когда дни не 86400 секунд. А как насчет перехода на летнее время? Это звучит незначительно, но на самом деле это может привести вас к очень грубым ошибкам, которые возникают из ниоткуда, когда наступает переход на летнее время, и вы не забыли это принять во внимание.
jpswain
1
Если вы просто хотите вернуться, скажем, примерно на 7 дней, вы можете просто вычесть время, если вы хотите вернуться к «понедельнику» этой недели, используйте NSCalendar. Есть варианты использования для обоих сценариев.
Johnny Rockex 02
83

Возможно, это лучший способ написать это, но вот что я придумал по предложению Бена NSCalendar и работал оттуда с 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);
Хаснат
источник
13
Из-за этих fromDate:[[NSDate alloc] init]битов вы полностью теряете память .
Shaggy Frog
21
@Shaggy Frog: Я думаю, он предполагает, что ARC включен ...;)
orj
3
Этот код работает некорректно. Он не учитывает доли секунды, которые являются частью даты ввода. То есть дата может иметь значение 30,1234 секунды, а NSDateComponents имеет только 30 секунд, поэтому вычитание 30 секунд из NSDate оставляет вам 0,1234 секунды после полуночи. Кроме того, в некоторые дни больше (или меньше) 24 часов (летнее время), поэтому для получения вчерашней даты действительно следует использовать [components setDay: -1], а не [components setHour: -24].
orj
5
@orj К вашему сведению, ARC вышел спустя много лет после того, как был опубликован этот ответ
Shaggy Frog
3
@orj Кроме того, при переходе на летнее время вычитание часов не дает полуночи. -1. Этот код не стоит 35 голосов.
Николай Рухе
40

NSDateComponents приятно получить сегодня:

NSCalendar *cal = [NSCalendar currentCalendar];

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

Это создает NSDate только с годом, месяцем и датой:

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

Чтобы получить вчерашний день и т.д., вы можете рассчитать его с помощью NSDateComponents:

NSDateComponents *components = [[NSDateComponents alloc] init];
[components setDay:-1];
NSDate *yesterday = [cal dateByAddingComponents:components toDate:today options:0];
Христианское пиво
источник
Разве вам не нужно включать компонент эпохи?
Крис Пейдж
Ницца. Использовал это для вычисления даты на 8 лет назад, и он отлично работает. Просто нужно исправить утечку здесь: [[NSDateComponents alloc] init];
Craig B
@CraigB, спасибо. Это не полный пример. Оставил в стороне очевидное управление памятью как проблему для читателя;)
Christian Beer
@ChrisPage компонент эпохи? Зачем?
Christian Beer
1
@ChristianBeer Я уверен, что нужно учитывать все компоненты, разрешение которых превышает желаемое. Я считаю, что (NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit)должно быть (NSEraCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit).
Крис Пейдж
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];
}
Дастин
источник
Разве вам не нужно включать компонент эпохи?
Крис Пейдж
Мне пришлось удалить NSDayCalendarUnit и добавить NSWeekdayCalendarUnit при вычислении DateLastWeek.
jessecurry 01
Apple изменила определение NSWeekdayCalendarUnit и NSDayCalendarUnit около iOS 5.0. Новый способ - использовать NSWeekdayCalendarUnit. Но в более старых версиях вы должны использовать NSDayCalendarUnit. Это беспорядок ...
Дастин
@Dustin Кажется, что не работает, даже при обновлении до новых единиц ... Например DateThisWeek, установка дня недели не влияет на NSDate ...
AnthoPak
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)

Обратите внимание, что компоненты даты не являются обязательными.

гоханаккурт
источник
1
@fengd хорошо, сегодня, 27 июня 2016 г., в час ночи дата будет 28 июня 2016 года, и если я вычту 24 часа, дата будет 27 июня 01:00. Что я упустил?
gokhanakkurt
1
прости, @gokhanakkurt. Думаю, у меня была какая-то икота. Ваш ответ правильный
fengd
4

Другие ответы просто не сработали для меня (возможно, из-за моего часового пояса). Вот как я это делаю:

- (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;
}
Лукас
источник
4

Если вы используете iOS 10+ или MacOS 10.12+, вы можете использовать эти два Calendarметода, чтобы сделать это правильно.

  • func date(byAdding component: Calendar.Component, value: Int, to date: Date, wrappingComponents: Bool = default) -> Date?( документы )
  • func dateInterval(of component: Calendar.Component, for date: Date) -> DateInterval?( документы )

Вот пример того, как использовать эти методы в Swift 3 вместе с выводом игровых площадок в моем часовом поясе.

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

Бонус: вы можете использовать DateIntervals, чтобы проверить, попадает ли дата в этот диапазон. Продолжая сверху:

thisWeekInterval!.contains(now)
// => true
lastMonthInterval!.contains(now)
// => false
yood
источник
Почему yesterdayнаписано 29 апреля, а nowсказано 28 апреля?
Хуан Боэро,
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];
Хайден
источник
0

Мне очень нравится объект THCalendarInfo, содержащийся в этом проекте:

http://github.com/jaredholdcroft/kcalendar

Я не могу найти оригинал. Используя этот объект, вы можете перейти к предыдущему дню, началу недели, началу месяца, получить день недели, день месяца ... и т. Д. И т. Д.

Кендалл Хельмштеттер Гельнер
источник
0
NSDate *today = [[NSDate alloc] initWithTimeIntervalSinceNow:0];
Тишель
источник
14
А почему бы и нет [NSDate date]?
joshpaul 06
Поскольку оба действительны для получения текущей даты, оба включают время. Так что это неправильное решение вопроса!
Christian Beer
0

Это для проверки, дата в этом месяце или нет

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
}

А второй - только изменить тип CalendarUnit для слабых вроде этого

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
}

Надеюсь, это кому-то поможет. Спасибо.

Илеш П
источник
0

Я отвечал на подобный вопрос уже , и вот почему мой ответ лучше:

  • Swift 3 !
  • Использует DateFormatter «Вчера» и «Сегодня». Это уже переведено Apple, что экономит вам работу!
  • Использует уже переведенную строку DateComponentsFormatter "1 неделя". (Снова меньше работы, любезно предоставлено Apple.) Все, что вам нужно сделать, это перевести строку "% @ ago". 🙂
  • В других ответах неправильно вычисляется время, когда день переключается с «сегодня» на «вчера» и т. Д. Фиксированные константы - это большое НЕТ-НЕТ по причинам . Кроме того, другие ответы используют текущую дату / время, когда они должны использовать дату / время конца текущего дня .
  • Использует autoupdatingCurrent для календаря и локали, что обеспечивает немедленное обновление вашего приложения с учетом календаря пользователя и языковых настроек в Settings.app
ChrisJF
источник