I want to keep date/times in a CoreData store with no seconds or milliseconds. (I'm doing some processing to round times off, and stray seconds/milliseconds become a monkey wrench.) It's easy enough to drop the seconds:
NSDate *now = [NSDate date];
NSDateComponents *time = [[NSCalendar currentCalendar]
components:NSHourCalendarUnit | NSMinuteCalendarUnit
| NSSecondCalendarUnit fromDate:now];
NSDate *nowMinus = [now addTimeInterval:-time.second];
// e.g. 29 Aug 10 4:43:05 -> 29 Aug 10 4:43:00
This works great to zero out the seconds, but I can't find an NSMillisecondCalendarUnit I could use to zero out the milliseconds, and I need to. Any ideas? Thanks.
timeIntervalSince1970 returns the number of seconds (in a double) since January the 1st, 1970. You can use this time to truncate any amount of seconds you like. To round down to the nearest minute, you could do:
NSTimeInterval timeSince1970 = [[NSDate date] timeIntervalSince1970];
timeSince1970 -= fmod(timeSince1970, 60); // subtract away any extra seconds
NSDate *nowMinus = [NSDate dateWithTimeIntervalSince1970:timeSince1970];
Floating point data types are inherently imprecise, but the above may be precise enough for your needs.
Related
I'm trying to get an NSDate object that has 21:00 as the local time - don't care about what day. I'm scratching my head at this very strange result:
NSCalendar *calendar = [[NSCalendar alloc]initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [[NSDateComponents alloc] init];
[components setHour:21];
[components setMinute:0];
[components setSecond:0];
NSDate *date = [calendar dateFromComponents:components];
NSLog(#"%#", date);
The result is 0001-01-02 04:52:58 +0000
I have no idea why. The current time is 17:34 PST, but the result doesn't change with the local time.
If I adjust the setMinute and setSecond lines to
[components setMinute:7];
[components setSecond:2];
I get 0001-01-02 05:00:00 +0000, which is correct (21:00 PST).
The problem is that railroad time wasn't implemented until November 18, 1883. You're neglecting to set a year so you're getting a date before that. Prior to the implementation of railroad time, the US time zones weren't exact hour differences from GMT. I'm not sure exactly what time zone Apple selects for you but whichever it was seems to have been adjusted by 7 minutes and 2 seconds upon the move to PST in 1883.
I have an NSDate (lets call it x), 12 September, 2012 10:18PM (GMT). I want to convert x to a minute before my current time zone's (EST) midnight. So, x represented in EST with NSDateFormatter after conversion would be 12 September, 2012 11:59PM (EST). What's the best way to do this?
Thanks
Take a look at NSDateComponents: https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSDateComponents_Class/Reference/Reference.html
I believe you'll need to convert the NSDate to NSDateComponents, set the time to 11:59PM, then convert back to NSDate.
NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [calendar components:(NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit) fromDate:date];
[components setHour:23];
[components setMinute:59];
NSDate *convertedDate = [calendar dateFromComponents:components];
NSLog(#"date=%#, convertedDate=%#", date, convertedDate);
Such manipulations can be easily done with the numeric representation of a date. In this representation, you deal with the number of seconds since a reference date.
The reference date for the purpose of the timeIntervalSinceReferenceDate is January 1st, 2001, at 00:00:00 GMT.
NSDate* date = [NSDate date];
NSInteger secondsSinceReferenceDate = [date timeIntervalSinceReferenceDate];
secondsSinceReferenceDate += 86400 - (secondsSinceReferenceDate % 86400);
secondsSinceReferenceDate -= 60;
secondsSinceReferenceDate -= [NSTimeZone.localTimeZone secondsFromGMTForDate:date];
NSDate* justBeforeToday =
[NSDate dateWithTimeIntervalSinceReferenceDate:secondsSinceReferenceDate];
NSLog(#"Date used was %#", date);
NSLog(#"Just before tomorrow is %#", justBeforeToday);
Since there are 86400 seconds in a day (24 hours times 60 minutes 60 times 60 seconds = 86400 seconds), you know that 86400 - (secondsSinceReferenceDate % 86400) is the number of seconds there are still to midnight. So if you take today's date (or any other valid date), add this number of seconds, and then subtract another 60 seconds, you'll have today's evening at 11:59 PM in the GMT timezone.
With [NSTimeZone.localTimeZone secondsFromGMTForDate:], you know how many seconds your timezone is offset from the GMT timezone. By subtracting this offset to your integer representation, you effectively get when it will be 11:59 PM in your local timezone.
Here's a sample output:
Date used was 2012-09-12 22:37:49 +0000
Just before tomorrow is 2012-09-13 03:59:00 +0000
I'm in the EDT timezone too, and this looks like the correct answer (remember Standard Time is -5 from GMT, but right now we're in daylight savings, so it's -4 from GMT, which is invariant).
double duration = 20; // duration is in seconds
NSDate* durationDate = [[NSDate alloc]initWithTimeIntervalSinceReferenceDate:duration];
NSDateFormatter *formatter = nil;
formatter = [[[NSDateFormatter alloc] init] autorelease];
[formatter setDateFormat:#"mm:ss"];
NSString *durationString=[formatter stringFromDate:durationDate];//here durationString should have 20
but i am getting 30:20? from where this 30 min is getting added. I have tried giving bigger numbers then also same result
please let me know thoughts on this and how to solve this
You're probably on India Standard Time?
The reference date is defined to be at midnight GMT. IST is GMT+5:30.
So when you add 20 seconds to the reference date you get 20 seconds past midnight in GMT. When you then create a date formatter and don't set anything else to it, it'll operate in your device's natural time zone. That means that when you ask it for the minutes and seconds at the end of the time you get 30 minutes and 20 seconds.
NSDates are an absolute time, abstract of any time zone or calendar. If five devices in five separate time zones all call [NSDate date] simultaneously to get the current time, they'd all get objects with the same value.
NSDateFormatter combines an NSDate with an NSCalendar and an NSTimeZone to determine minutes, seconds, hours, months, years, etc.
What I used to do was using the timestamp and then adding 86400 seconds ( 24 * 60 * 60 ). As I read a recent question on stackoverflow noting that that is not correct for every day, I want to change this.
So the next thing I came up with was by using NSDateComponents. I simply get the current day and add 1. Now I was wondering how "smart" that is. Like if the day is equal to 31, does it set the day to 1 and the month to whatever it is +1 ( or when it is 12 to 1 again ) ? I can do this manually but that would only work properly for the gregorian calendar so I don't really know whether that would be a good solution either..
It is smart, unless you want it to be dumb.
From the documentation for -[NSCalendar dateByAddingComponents:toDate:options]:
If you specify no options (you pass 0), overflow in a unit carries into the higher units (as in typical addition).
If you did not want units overflowing into higher units, then you would pass "NSWrapCalendarComponents" as the value of the options: parameter.
Be very careful of DST. It is different for various time zones. AND It happens at Midnight
I believe that if you call [NSCalendar date by addingComponents:toDate:options] and your date is between 11-12 pm, you risk missing or re-running a day because of DST.
To get around this, is suggest changing your date to noon on the day and then adding the day.
// increment by 1 calendar day
// and convert back to NSDate objects
NSDateComponents *comps = [[[NSDateComponents alloc] init] autorelease];
[comps setDay:1];
NSCalendar *gregorian = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
[gregorian setTimeZone:self.myTimeZone]; // time zone of location;
// Use local noontime for the date to avoid problems around
// midnight, particularly near daylightsavings time discontinuities.
NSUInteger unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
NSDateComponents* dateComponents = [gregorian components:unitFlags fromDate:date];
dateComponents.hour = 12;
NSDate* noonDate = [gregorian dateFromComponents:dateComponents];
NSDate *newDate = [gregorian dateByAddingComponents:comps toDate:noonDate options:0];
I'm not sure about the options:0 setting, but I did test this pretty thoroughly. I use it to get an array of with 10 days of dates. It gives correct wrapping around the various day/month/year discontinuities. Check out SunPose Rise Set on the app store if you want to test it. (yes, it's free)
I have an NSTimeInterval, and I'd like to know whether that timestamp falls between the start and end of a date. Basically I need to be able to do something like:
NSDate *today = [NSDate date];
NSTimeInterval myInterval = someInterval;
[date returnYesIfFallsThisDate:myInterval];
Is there a straight forward method to figure out if my NSTimeInterval falls on the same day as an NSDate object?
Thanks all.
An NSTimeInterval is just a number of seconds. To compare it to an NSDate, you need to interpret the time interval relative to some reference point, then create an NSDate with the result, then compare the two NSDates.
There are two standard reference dates: 2001-01-01 and 1970-01-01 (the latter being “UNIX time”). If neither of those is the correct reference date, use NSCalendar to create an NSDate for the correct reference date, then create your NSDate of interest relative to that.
Now for the comparison.
If all you care about is the date on the calendar, then you'll create the NSDate, then use an NSCalendar to get NSDateComponents for the date. Specifically, you'll get the year, month, and day-of-the-month, then compare those.
There's no need to worry about “start” and “end” times; in fact, if you only care about the date on the calendar, you can ignore the time-of-day entirely.
Keep in mind that NSDate doesn't represent a "day" in any form, just a point in time. You'll have to do a little work with NSDateComponents, converted using an NSCalendar (typically the user's default calendar) to figure out the start and end NSDate values, and compare use those to compare your time interval to.
I would start by taking the NSDate that falls within your day, and converting it to an NSDateComponents, but without hours, minutes or seconds. Here's a quick (untested) example to help you get started:
NSDateComponents *comps = [[NSCalendar currentCalendar] components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:[NSDate date]];
NSDateComponents *day = [[[NSDateComponents alloc] init] autorelease];
[day setDay:1];
NSDate *start = [[NSCalendar currentCalendar] dateFromComponents:comps];
NSDate *end = [[NSCalendar currentCalendar] dateByAddingComponents:day toDate:start options:0];
Then you can compare the time interval as suggested by Mr. Jalkut (or another method, NSDate can work with NSTimeIntervals in a few different ways). I would definitely spend some time looking over NSCalendar and NSDateComponents in the docs though, you might find a better method than what I'm suggesting for what you need to do.
Is it just me or does the OP seem to think that a NSTimeInterval is a representation of an interval in time from A to B? I.e. an absolute distance (in seconds) from Date A to Date B.
The original question needs to be repose to state that you need to find it based on the reference date. Something like this:
NSDate *refDate = someReferenceDate;
NSTimeInterval interval = someInterval;
NSDate *today = [NSDate date];
BOOL isInRange = false;
isInRange = [today isInterval:interval inRangeFromReference:refDate];
Then that method would use some (or parts of all) of the startegies that Damiel, Peter and Marc mention above.
Conceptually you want two dates that constitute the "start" and "end" of the target date in question. Then you can test the time interval directly against the respective time intervals of those dates.
For instance, if I live in San Francisco, then I want the "start" to be 12AM pacific time, and the end to be 11:59:59.99999 PM pacific time.
Once you've figured out what time zones/etc you want to consider as the start and end points, you can just use the time intervals to do the test:
if ((myInterval >= [earlyMorningDate timeIntervalSinceReferenceDate]) &&
(myInterval <= [lateEveningDate timeIntervalSinceReferenceDate]))
{
NSLog(#"Whoo hoo - it's the right date!");
}