why conversion of double to string using NSDate adds extra 30 min - objective-c

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.

Related

NSDateFromString, parsing a date in the very past

I'm executing this code to parse date 01 January 1800:
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"ddMMyyyy"];
NSDate *date = [formatter dateFromString:#"01011800"];
when I print date with
NSLog(#"%#", date);
I get
1799-12-31 23:10:04 +0000
My expected result is 1799-12-31 23:00:00 +0000: I'm in central Europe and at 1st January I have a 1.00 hours offset respect to UTC, but I cannot understand where that 10 minutes and 4 seconds come from.
Have a good look on the internet about time zone changes in your area through the last few hundred years. Bets are that the result is actually correct, and that in 1800 your local time zone was off by ten minutes and four seconds.
For a date in 1800, Apple obviously doesn't use what the timezone information is today, but what it was 214 years ago.

Storing an absolute NSDate, not relative to a timezone

I am creating an iOS app to track attendance. Each attendance entry is stored in an object which has a status attribute (e.g. present, absent) and an NSDate attribute called date which denotes the day which that attendance record was taken. When I select a particular date (using a UIDatePickerView or alike) I want all the attendance records (objects) for that date to appear in a table view.
While this sounds simple in principle, I am running into an issue relating to timezones. I am aware that NSDates are stored independent of timezones (i.e. they are stored relative to UTC/GMT +0000). This means that if I am in Sydney and take attendance on, for example, Sunday 4 November 2012 because the date is stored as timezone independent, if I take my iPhone/iPad to a different time zone (such as San Francisco) all the attendance records would shift one day back, in this case to Saturday 3 November 2012, because that was the moment in time when the attendance was taken in San Francisco local time (which was actually the next day, in Sydney local time).
I don't want this to happen - I want the date to be absolute. In other words, if the attendance is taken on Sunday 4 November 2012 then it needs to stay on that date, no matter where in the world (and whichever timezone) I may go. As you can see, this is quite in contrast to, say, a calendar application where it is desirable for the timing of appointments to change depending on the timezone.
Any suggestions on a better way to approach this problem would be appreciated. Please keep in mind that I am selecting the date to display using a UIDatePickerView which returns the current NSDate in the timezone independent format, so I also need a way to do an easy comparison (preferably in an NSPredicate since the attendance objects are stored in Core Data) to get all the attendance objects for that particular day.
Have you tried converting the time to it's NSDateComponents? you can then recreate an NSDate from it regardless of the time zone.
Edited to add
// This is just so I can create a date from a string.
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"yyyy-MM-dd HH:mm:ss z"];
// Create a date as recorded in a timezone that isn't mine.
NSDate *localDate = [formatter dateFromString:#"2012-10-30 10:30:00 +0200"];
NSLog(#"Initial Date: %#", localDate);
// this logs 2012-10-30 08:30:00 +0000
// Which is what you would expect, as the original time was 2 hours ahead
NSDateComponents *components = [[NSDateComponents alloc] init];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
components = [gregorian components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit fromDate:localDate];
NSLog(#"Components: %#", components);
// Create a date from these time components in some other time zone
[gregorian setTimeZone:[NSTimeZone timeZoneWithAbbreviation:#"EST"]];
NSDate *newDate = [gregorian dateFromComponents:components];
NSLog(#"New Date: %#", newDate);
// This logs 2012-10-30 12:30:00 +0000
// Which is the local EST of 8:30 am expressed in UTC
Which demonstrates how I can turn make 8:30 am in +2 time zone look the same as for a -4 timezone.
I believe the better way for you is to use timestamp since it independ of any time zone. You can use vary methods to convert timestamps to date and back. And implement any logic you wish.
Also you can easily compare them.

Obj-C, problem formatting date, gaining a day?

I've discovered a bug in my app / function. I pass in a NSString of a date.
The function then uses the NSDateFotmatterShortStyle.
Heres a screen shot of my function in the debugger.
I'd like to end up with a date of 2011-04-18
Not sure why its added 1ppm either, I need it to be 00:00:00
Whats happening and how do I fix this ?
I use the MidnightUTC function from here ( How do I create the current date (or any date) as an NSDate without hours, minutes and seconds? ) to get rid of the hours.
I actually see no connection between the midnightUTC method and your provided code.
Anyway, the problem with your given example is that, the parsing of the string 4/18/11 will default the missing values like minutes etc AND your current time zone, but the string will be assumed as GMT time so this will result in the offset you see.
The solution is to set the time zone for the NSDateFormatter. Look at this code, I've tested it a minute ago, and the console output. aaa reveals the odd offset, bbb does look as expected.
NSDateFormatter *dt = [[[NSDateFormatter alloc] init] autorelease];
[dt setDateStyle:NSDateFormatterShortStyle];
NSDate *aaa = [dt dateFromString:#"4/18/11"];
NSLog(#"1a. %#", [dt timeZone]);
NSLog(#"1b. %#", aaa);
[dt setTimeZone:[NSTimeZone timeZoneWithAbbreviation:#"GMT"]];
NSDate *bbb = [dt dateFromString:#"4/18/11"];
NSLog(#"2a. %#", [dt timeZone]);
NSLog(#"2b. %#", bbb);
Console output
1a. Europe/Berlin (CEST) offset 7200 (Daylight)
1b. 2011-04-17 22:00:00 +0000
2a. GMT (GMT+00:00) offset 0
2b. 2011-04-18 00:00:00 +0000
the midnightUTC function you're using creates dates by setting the timeZone to GMT, which is different from UTC half the year. UTC doesn't observe any summer / daylight savings time changes, while GMT does, so [NSTimeZone timeZoneForSecondsFromGMT:0] will be an hour off UTC for roughly half the year.

How to clear NSDate milliseconds

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.

How to determine if an NSTimeInterval occurred during an arbitrary NSDate?

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!");
}