86

I am trying to do is to get NSDate today, yesterday, this Week, last Week, this Month, last Month variables ready for comparison for headers to be added on UITableView's titleForHeaderInSection

What I want is done manually in the code below for date 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"];
mmmmmm
  • 32,227
  • 27
  • 88
  • 117
hasnat
  • 2,647
  • 1
  • 20
  • 23

12 Answers12

94

Adapted from the Date and Time Programming Guide:

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

If you want the correct exact number of days depending on the month, you should use an NSCalendar.

Pang
  • 9,564
  • 146
  • 81
  • 122
Ben S
  • 68,394
  • 30
  • 171
  • 212
  • sorry if you didnt understand you are giving out last 7 days on thisWeek which in a way of saying is correct but i want actual week thanks for NSCalendar suggestion – hasnat Dec 11 '09 at 18:17
  • Yes, if you want the actual week starting on Saturday, then you'll need to use `NSCalendar`. – Ben S Dec 11 '09 at 18:27
  • 77
    Fixed constants for time are BAD BAD BAD. What about leap days or leap seconds? Use NSDateComponents or you WILL suffer some very nasty and hard to debug issues when your date stamps start mismatching. – Kendall Helmstetter Gelner Dec 11 '09 at 21:22
  • 11
    It's really not that bad if you just want approximately the last week's worth of posts or whatever. I specified that the OP should use `NSCalendar` if exactness was required. – Ben S Dec 11 '09 at 22:25
  • 2
    Fixed constants should be actually just fine for counting days: every day is 86400 seconds. Leap seconds simply take twice as long - a day with a leap second still has 86400 'seconds'. As for months... yeah steer clear :) – Chris Sep 19 '11 at 12:09
  • addTimeInterval has been deprecated. This method has been replaced by dateByAddingTimeInterval: – shawnwall Feb 07 '12 at 23:58
  • @shawnwall Thanks! I updated my answer to use the new method. – Ben S Feb 08 '12 at 00:10
  • BAD BAD BAD Because of what Kendall Helmstetter said. Watch WWDC 2012 session 244 (Internationalization tips and tricks) for more info. – 3lvis Aug 23 '12 at 07:45
  • 3
    @Chris except when days aren't 86400 seconds. What about daylight savings time? It sounds insignificant, but this can actually lead you to super gnarly bugs that come out of no where when DST hits and you didn't remember to take it into account. – jpswain Mar 13 '13 at 05:33
  • 1
    If you just want to go back say 7 days approximately, you can just subtract the time, if you want to go back to 'Monday' of this week, use NSCalendar. There are use cases for both scenarios. – Johnny Rockex Sep 02 '16 at 11:24
84

Might be a better way to write this but here what i came up on Ben's NSCalendar suggestion and working from there to 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);
Axel Guilmin
  • 11,454
  • 9
  • 54
  • 64
hasnat
  • 2,647
  • 1
  • 20
  • 23
  • 13
    You're leaking memory all over with those `fromDate:[[NSDate alloc] init]` bits. – Shaggy Frog Dec 23 '09 at 06:53
  • 21
    @Shaggy Frog: I guess he is assuming ARC is enabled... ;) – orj Mar 13 '12 at 05:30
  • 3
    This code doesn't work correctly. It doesn't take into account the fractions of a second that are part of the input date. Ie, the date could have 30.1234 seconds and the NSDateComponents only has 30 as the seconds value so subtracting 30 seconds from the NSDate leaves you with 0.1234 seconds past midnight. Also, some days have more (or less) than 24 hours (daylight savings) so getting yesterday's date should really use [components setDay:-1] rather than [components setHour:-24]. – orj Mar 13 '12 at 05:33
  • 5
    @orj FYI, ARC came out ages after that answer was posted – Shaggy Frog Mar 13 '12 at 15:38
  • 4
    @orj Also, on daylight saving time changes, subtracting the hours doesn't give you midnight. -1. This code is not worth 35 upvotes. – Nikolai Ruhe May 16 '13 at 13:18
40

NSDateComponents is nice to get today:

NSCalendar *cal = [NSCalendar currentCalendar];

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

This creates a NSDate with only year, month and date:

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

To get yesterday, etc. you can calculate it using NSDateComponents:

NSDateComponents *components = [[NSDateComponents alloc] init];
[components setDay:-1];
NSDate *yesterday = [cal dateByAddingComponents:components toDate:today options:0];
Christian Beer
  • 2,027
  • 15
  • 13
  • Don't you need to include the era component? – Chris Page Oct 23 '11 at 09:48
  • Nice. Used this to calculate a date 8 years in the past and it works nicely. Just need to fix a leak here: [[NSDateComponents alloc] init]; – Craig B Nov 14 '11 at 00:45
  • @CraigB thanks. It's not meant to be a complete example. Left out the obvious memory management as a challenge for the reader ;) – Christian Beer Nov 14 '11 at 11:13
  • @ChrisPage the era component? What for? – Christian Beer Nov 14 '11 at 11:13
  • 1
    @ChristianBeer I’m pretty sure all components larger than the desired resolution must be considered. I believe `(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit)` should be `(NSEraCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit)`. – Chris Page Jan 05 '12 at 07:37
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];
}
Dustin
  • 1,030
  • 11
  • 15
  • Don't you need to include the era component? – Chris Page Oct 23 '11 at 09:46
  • I had to remove NSDayCalendarUnit and add NSWeekdayCalendarUnit when calculating DateLastWeek. – jessecurry Dec 01 '11 at 15:40
  • Apple changed the definition of NSWeekdayCalendarUnit and NSDayCalendarUnit around iOS 5.0. The new way is to use NSWeekdayCalendarUnit. But on older versions you must use NSDayCalendarUnit. It is a mess... – Dustin Jun 05 '13 at 00:22
  • @Dustin It doesn't seems to work, even when updating to new units... For example for `DateThisWeek`, setting weekday doesn't have impact on the NSDate… – AnthoPak Mar 20 '19 at 20:50
12

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)

Please note that date components are optional.

gokhanakkurt
  • 4,935
  • 4
  • 28
  • 39
  • 1
    @fengd well, Today 27 Jun 2016, at 1am date will be 28 Jun 2016, and if I subtract 24 hours, date will be 27 Jun 1 am. What is the point that i missed? – gokhanakkurt Jun 27 '16 at 10:39
  • 1
    sorry, @gokhanakkurt. I think there was some hiccups of my mind. your answer is correct – fengd Jun 28 '16 at 04:17
4

The other answers just didn't work for me (maybe because of my time zone). This is how I do it:

- (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;
}
Lucas
  • 6,675
  • 3
  • 25
  • 43
4

If you're using iOS 10+ or MacOS 10.12+, you can use these two Calendar methods to do this properly.

  • func date(byAdding component: Calendar.Component, value: Int, to date: Date, wrappingComponents: Bool = default) -> Date? (docs)
  • func dateInterval(of component: Calendar.Component, for date: Date) -> DateInterval? (docs)

Here's an example of how to use these methods in Swift 3, along with the playgrounds output in my time zone.

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

Bonus: You can use the DateIntervals to test whether a date falls in that range. Continuing from above:

thisWeekInterval!.contains(now)
// => true
lastMonthInterval!.contains(now)
// => false
Ben Packard
  • 26,102
  • 25
  • 102
  • 183
yood
  • 6,650
  • 3
  • 26
  • 24
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];
aopsfan
  • 2,451
  • 19
  • 30
hayden
  • 293
  • 1
  • 5
0

I really like the THCalendarInfo object contained in this project:

http://github.com/jaredholdcroft/kcalendar

I can't quite find the original. Using this object you can move to a previous day, the start of a week, the start of a month, get the day of a week, the day of a month... etc. etc.

Kendall Helmstetter Gelner
  • 74,769
  • 26
  • 128
  • 150
0

This is for check the date is this month or not

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
}

And second one only change type of calendarUnit for weak like this

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
}

I hope this is helpful to someone Thanks.

Ilesh P
  • 3,940
  • 1
  • 24
  • 49
0

I answered a similar question already and here's why my answer is better:

  • Swift 3!
  • Utilizes DateFormatter's "Yesterday" and "Today". This is already translated by Apple, which saves you work!
  • Uses DateComponentsFormatter's already translated "1 week" string. (Again less work for you, courtesy of Apple.) All you have to do is translate the "%@ ago" string.
  • The other answers incorrectly calculate the time when the day switches from "today" to "yesterday" to etc. Fixed constants are a big NO-NO because reasons. Also, the other answers use the current date/time when they should use the end of the current day's date/time.
  • Uses autoupdatingCurrent for Calendar & Locale which ensures your app is immediately up to date with the user's calendar and language preferences in Settings.app
Community
  • 1
  • 1
ChrisJF
  • 6,822
  • 4
  • 36
  • 41
0
NSDate *today = [[NSDate alloc] initWithTimeIntervalSinceNow:0];
Tischel
  • 25
  • 1