отримати NSDate сьогодні, вчора, цього тижня, минулого тижня, цього місяця, минулого місяця… змінні


86

Я намагаюся отримати NSDate сьогодні, вчора, цього тижня, минулого тижня, цього місяця, змінних останнього місяця, готових для порівняння заголовків, які будуть додані до заголовка UITableViewForHeaderInSection

Те, що я хочу, робиться вручну в коді нижче за дату 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, інакше у вас виникнуть дуже неприємні і важкі для налагодження проблеми, коли ваші позначки дати почнуть не узгоджуватися.
— Kendall Helmstetter Gelner

11
Це насправді не так погано, якщо ви просто хочете, щоб публікації коштували приблизно за останній тиждень або щось інше. Я вказав, що ОП слід використовувати, NSCalendarякщо потрібна точність.
— Ben S

2
Виправлені константи насправді повинні бути просто чудовими для підрахунку днів: кожен день становить 86400 секунд. Скоронні секунди просто займають удвічі більше часу - день із високосною секундою все одно має 86400 „секунд“. Що стосується місяців ... так, тримайтеся чітко :)
— Кріс,

3
@Chris, крім випадків, коли дні не складають 86400 секунд. А як щодо переходу на літній час? Це звучить незначно, але це насправді може привести вас до надзвичайно помилкових помилок, які з’являються звідки-небудь, коли потрапляє літній час, і ви не пам’ятали це врахувати.
— jpswain

1
Якщо ви просто хочете повернутися, скажімо приблизно 7 днів, можете просто відняти час, якщо ви хочете повернутися до «понеділка» цього тижня, використовуйте NSCalendar. Є варіанти використання для обох сценаріїв.
— Johnny Rockex,

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]бітами.
— Кудлата жаба

21
@ Shaggy Frog: Гадаю, він припускає, що ARC увімкнено ...;)
— orj

3
Цей код працює неправильно. Він не враховує частки секунди, які є частиною введеної дати. Тобто дата може мати 30,1234 секунди, а NSDateComponents має лише 30, оскільки значення секунд віднімає 30 секунд від NSDate, і залишається 0,1234 секунди за північ. Крім того, деякі дні мають більше (або менше) 24 годин (перехід на літній час), тому для отримання вчорашньої дати слід використовувати [компоненти setDay: -1], а не [компоненти setHour: -24].
— orj

5
@orj FYI, ARC вийшов кілька років після того, як була опублікована відповідь
— Кудлата жаба

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

Вам не потрібно включати компонент епохи?
— Chris Page

Приємно. Використовується для обчислення дати 8 років у минулому, і це працює добре. Просто потрібно виправити витік тут: [[NSDateComponents alloc] init];
— Крейг Б

@CraigB дякую. Це не має бути повним прикладом. Залишив очевидне управління пам'яттю як виклик для читача;)
— Крістіан Беер

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

Вам не потрібно включати компонент епохи?
— Chris Page

Мені довелося видалити NSDayCalendarUnit та додати NSWeekdayCalendarUnit під час обчислення DateLastWeek.
— jessecurry

Apple змінила визначення NSWeekdayCalendarUnit та NSDayCalendarUnit навколо iOS 5.0. Новим способом є використання NSWeekdayCalendarUnit. Але у старих версіях ви повинні використовувати NSDayCalendarUnit. Це безлад ...
— Дастін,

@Dustin Це, здається, не працює, навіть при оновленні до нових одиниць ... Наприклад DateThisWeek, встановлення робочого дня не впливає на NSDate ...
— AnthoPak

11

Свіфт 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 року, о 1 ранку дата буде 28 червня 2016 року, а якщо відняти 24 години, дата буде 27 червня 1 ранку. Який момент я пропустив?
— 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

Чому саме так 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

Оскільки обидва є дійсними для отримання поточної дати, обидва включають час. Тож це не правильне рішення питання!
— Крістіан Бір

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
}

А другий - лише змінити тип календаряUnit для слабких, як це

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

Я відповідав на подібне питання вже , і ось чому моя відповідь краще:

  • Свіфт 3 !
  • Використовує DateFormatter "Вчора" та "Сьогодні". Це вже перекладено компанією Apple, що економить вашу роботу!
  • Використовує вже перекладений рядок "1 тиждень" DateComponentsFormatter. (Знову менше роботи для вас, люб'язно від Apple.) Все, що вам потрібно зробити, це перекласти рядок "% @ ago". 🙂
  • Інші відповіді неправильно обчислюють час, коли день переходить із "сьогодні" на "вчора" на тощо. Фіксовані константи - це велике НІ-НІ через причини . Крім того, інші відповіді використовують поточну дату / час, коли вони повинні використовувати кінець поточного дня дати / часу .
  • Використовує autoupdatingCurrent для календаря та локалі, що гарантує, що ваш додаток відразу оновлюється відповідно до налаштувань календаря та мови користувача в Settings.app
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.