отримати 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.