Converting NSDate - objective-c

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).

Related

Objective-C: Get the number of calendar days between two dates

I just don't get the correct result...so depending on the timezone I want the calendar day difference between two dates. So if one starts at 23:00 on day 1 and ends on 14:00 on day 2 it should return 1. Now my method returns 0, why? Because it's less than 24 hours? Example:
MY Nslog:
CheckForPictures departure date: Tue Jan 28 23:10:00 2020 destinationDate: Wed Jan 29 09:30:00 2020 in timeZone:Europe/Zurich and get a day difference: 0
(Computer has also timezone Zurich, so it is local time)
My Method:
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar setTimeZone:timeZone];
NSDateComponents *components = [calendar components:NSCalendarUnitDay
fromDate:self.departureTime
toDate:self.destinationTime
options:0];
NSLog(#"CheckForPictures departure date: %# destinationDate: %# in timeZone:%# and get a day difference: %ld", self.departureTime, self.destinationTime, timeZone.name, components.day);
return components.day;
And this code returns 0 and logs above log...
I believe you're asking NSCalendar the wrong question. You want to know if the arrival date is different than the departure date, but you're asking for the number of days between arrival and departure. A date change could happen even if the difference in time is only a few minutes. "A few minutes" rounds to "0 days" if you ask how many days have passed.
You actually want to know if the date has changed. I think I would be doing something like getting the day of the month for each date and comparing that. Since no months have 1 day, I would think that would work.
With Craigs input I came up with this solution:
- (NSInteger)calendarDaysBetweenDepartureAndArrivalTimeForTimeZone:(NSTimeZone *)timeZone
{
NSCalendar *calendar = [NSCalendar currentCalendar];
[calendar setTimeZone:timeZone];
NSDateComponents *departureComponents = [calendar components:(NSCalendarUnitDay) fromDate:self.departureTime];
NSDateComponents *destinationComponents = [calendar components:(NSCalendarUnitDay) fromDate:self.destinationTime];
NSInteger difference = destinationComponents.day - departureComponents.day;
if(difference < 0){
//Month overlapping
NSRange range = [calendar rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:self.departureTime];
difference = range.length - departureComponents.day + departureComponents.day;
}
NSLog(#"CheckForPictures departure date: %# destinationDate: %# in timeZone:%# and get a day difference: %ld", self.departureTime, self.destinationTime, timeZone.name, difference);
return difference;
}

NSDateComponents - Combining a date and time loses 1 hour?

I am trying to combine a NSDate (date) with a NSDate (time) into a new NSDate object, for whatever reason I seem to be losing 1 hour.
I have an array of formatted time objects that are 15 minutes between each one.
I have a selectable date item, which referes to a NSDate object.
A user selects a formatted time object (I have a reference to the NSDate in a dictionary) and then when it comes to save; I do this;
if (self.selectedDate && self.selectedTimeObject)
{
// Combine selected date and selected time into a new NSDate object
NSCalendar *cal = [NSCalendar currentCalendar];
unsigned unitFlagsDate = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
unsigned unitFlagsTime = NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
NSDateComponents *dateComponents = [cal components:unitFlagsDate fromDate:self.selectedDate];
NSDateComponents *timeComponents = [cal components:unitFlagsTime fromDate:self.selectedTimeObject];
[dateComponents setSecond:[timeComponents second]];
[dateComponents setHour:[timeComponents hour]];
[dateComponents setMinute:[timeComponents minute]];
NSDate *combinedDate = [cal dateFromComponents:dateComponents];
NSLog(#"targetDate = %#", combinedDate);
}
In my example I choose 01:00; but for some reason I lose 1 hour depending on the date.
Logs;
targetDate = 2015-03-31 00:00:00 +0000
targetDate = 2015-03-30 00:00:00 +0000
targetDate = 2015-03-29 01:00:00 +0000
targetDate = 2015-03-28 01:00:00 +0000
targetDate = 2015-03-27 01:00:00 +0000
targetDate = 2015-03-31 00:00:00 +0000
targetDate = 2015-04-01 00:00:00 +0000
targetDate = 2015-04-02 00:00:00 +0000
Now in my logs, I noticed that anything before 28 is fine, but anything after that date loses 1 hour without explanation.
I have checked my array, I have checked my indexes, I have checked that the objects are valid time objects; however I am stumped -- why does it lose 1 hour?
The issue is most likely that you use NSLog(#"%#", date); to log your date. This will log times in UTC, i.e. +0000. If you are not living in a country that uses UTC (e.g. UK, Iceland, Ghana, Portugal, ...) these dates appear different from what you would expect. (See also: https://stackoverflow.com/a/4976950/457406)
If you create a date with NSDateComponents it uses your local timezone. Depending on your local timezone the same local time (e.g. 01:00), has different representations in UTC throughout the year. Also know as Daylight Savings Time.
In your experiments you saw that change at the end of March. Which is the time of the year when Daylight Savings Time starts in many countries. For example in Germany it starts on Sunday, March 29 at 2:00 in the morning. Which would explain the change in your logs on March 30 1:00.
To make a long story short, use something else to log your dates. For example:
NSLog(#"%#", [NSDateFormatter localizedStringFromDate: combinedDate dateStyle:NSDateFormatterShortStyle timeStyle:NSDateFormatterShortStyle]);

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.

NSDateComponents issue - incorrect day

I have a NSDateComponents problem. I have two NSDates that I am trying to compare by checking if their year, month and day match. This I am doing by converting the NSDate values to these integer components as follows:
//NSDate *cgiDate is previously set to 2011-08-04 00:00:00 +0000
//NSDate *orderDate is previously set to 2011-08-04 14:49:02 +0000
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *cgiDateComponents = [calendar components:( NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit ) fromDate:cgiDate];
NSCalendar *orderCalendar = [NSCalendar currentCalendar];
NSDateComponents *orderDateComponents = [orderCalendar components:( NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit ) fromDate:orderDate];
if (([cgiDateComponents day] == [orderDateComponents day]) &&
([cgiDateComponents month] == [orderDateComponents month]) &&
([cgiDateComponents year] == [orderDateComponents year])) {
GHTestLog(#"MATCHED");
} else {
GHTestLog(#"Not matched");
GHTestLog(#"Day: %d vs. %d", [cgiDateComponents day], [orderDateComponents day]);
}
My result is Not Matched, Day: 3 vs. 4. Why would this be?
I have read with great interest the following questions:
NSDateComponents - day method returning wrong day and
https://stackoverflow.com/questions/3920445/nsdatecomponents-incorrectly-reporting-day however neither answer my question of why this is not working.
Any advice?
I think the issue is the following:
Your dates are set in GMT time zone (+0000)
If you are in the US, for example at GTM-6, then by the -currentCalendar the first date will be 6pm on Aug 3rd while second date will be 8:49am on Aug 4th.
You should force your calendar to have the UTC (GMT) timezone, or put the dates in your time zone, depending what is correct for your application.
It looks like you have a time zone issue. Though your dates are set at time zone +0000, your [NSCalendar calendar] call likely returns you a calendar for your local time zone. Given the adjustment to local time, the orderDate is likely in the next day. Manually set your calendars to time zone +0000.
NSDate has an isEqualToDate: function. so what you are trying to do can be done by:
[datea isEqualToDate:dateb];
there is also a Compare: function available, that returns the ordering of the dates.
edit: I'm now aware this doesn't answer your question about why the days return different values when they are set the same. Sorry! this still may help make your code a bit cleaner.

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.