converting an float to NSDate - objective-c

I want to convert a float to a NSDate
I converted a NSDate into a float using this:
// Turn the date into Integers
NSCalendar *calendar= [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSCalendarUnit unitFlags = NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
NSDateComponents *dateComponents = [calendar components:unitFlags fromDate:nsdate_wakeTime];
NSInteger hour = [dateComponents hour];
NSInteger min = [dateComponents minute];
//Convert the time in 24:60 to x.x format.
float myTime = hour + min/60;
after some math stuff I do on the mytime variable i get a bunch of other times in the same float format.
How do I turn a float into a NSDate?
Thanks!

If you convert the time you've computed to seconds (so, mytime * 60), then you can use dateWithTimeIntervalSinceReferenceDate: to get back to an NSDate. From the math you are doing, it looks like the referenced date here would be 00:00 for the day in question. As Jason mentioned though, there's probably a better way to do what you are trying to accomplish.
Also, you need to change your "myTime" computation to dividing by 60.0 if you actually want the minutes; your sample code is dividing an integer value less than 60 by the integer value 60, which will always be 0.

Related

Calculate time in seconds between 2 NSDates

I cant calculate output from "old" and "now" NSDate. Here is the code:
NSLog(#"past is %#", past);
NSDate *now = [NSDate date];
NSLog(#"Now time is is %#", now);
NSTimeInterval distanceBetweenDates = [now timeIntervalSinceDate:past];
double secondsInAnHour = 3600;
NSInteger hoursBetweenDates = distanceBetweenDates / secondsInAnHour;
NSLog(#" Time between is %i", hoursBetweenDates);
Here is console output:
2015-11-11 18:52:35.608 TaskTimer[2578:130664] past is 2015-11-11 15:52:02 +0000
2015-11-11 18:52:35.608 TaskTimer[2578:130664] now is 2015-11-11 15:52:35 +0000
2015-11-11 18:52:35.609 TaskTimer[2578:130664] Time between is 0
I want to add, that last value is 0 even when time between two values more then couple of minutes. Why is it 0?
Because you're converting to an integer and it rounds down. Anything < a particular integer value will round down. And a few minutes is < 1 so you'll get 0 hours.
If you want to round to nearest then use round(distanceBetweenDates / secondsInAnHour), or to round up you would use ceil(distanceBetweenDates / secondsInAnHour) (though it will also round 2.1 up to 3)
There are 33 minutes between your 2 dates, which is a fraction of an hour.
NSInteger hoursBetweenDates = distanceBetweenDates / secondsInAnHour;
That statement is losing the fractional precision, and rounding down to 0, which is what you are displaying.
An alternative is to display in hh:mm:ss format using NSDateComponentsFormatter
NSDateComponentsFormatter *formatter = [[NSDateComponentsFormatter alloc] init];
formatter.allowedUnits = NSCalendarUnitHour | NSCalendarUnitMinute |
NSCalendarUnitSecond;
NSLog(#"Time between is %#", [formatter stringFromTimeInterval:distanceBetweenDates]);

Why is Excel serial date-time to NSDate conversion two days, one hour and 40 minutes ahead?

I'm converting dates from an Excel spreadsheet to NSDate's, but for some reason they always come out two days ahead: Sundays come out as Tuesdays, etc.
My conversion method is based on the following info from cpearson.com:
Excel stores dates and times as a number representing the number of
days since 1900-Jan-0, plus a fractional portion of a 24 hour day:
ddddd.tttttt . This is called a serial date, or serial date-time.
(...) The integer portion of the number, ddddd, represents the number
of days since 1900-Jan-0. (...) The fractional portion of the number,
ttttt, represents the fractional portion of a 24 hour day. For
example, 6:00 AM is stored as 0.25, or 25% of a 24 hour day.
Similarly, 6PM is stored at 0.75, or 75% percent of a 24 hour day.
- (NSDate *)dateFromExcelSerialDate:(double)serialdate
{
if (serialdate == 0)
return nil;
NSTimeInterval theTimeInterval;
NSInteger numberOfSecondsInOneDay = 86400;
double integral;
double fractional = modf(serialdate, &integral);
NSLog(#"%# %# \r serialdate = %f, integral = %f, fractional = %f",
[self class], NSStringFromSelector(_cmd),
serialdate, integral, fractional);
theTimeInterval = integral * numberOfSecondsInOneDay; //number of days
if (fractional > 0) {
theTimeInterval += numberOfSecondsInOneDay / fractional; //portion of one day
}
NSCalendar *nl_gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
NSTimeZone *nl_timezone = [[NSTimeZone alloc] initWithName:#"Europe/Amsterdam"];
[nl_gregorianCalendar setTimeZone:nl_timezone];
NSDateComponents *excelBaseDateComps = [[NSDateComponents alloc] init];
[excelBaseDateComps setMonth:1];
[excelBaseDateComps setDay:1];
[excelBaseDateComps setHour:00];
[excelBaseDateComps setMinute:00];
[excelBaseDateComps setTimeZone:nl_timezone];
[excelBaseDateComps setYear:1900];
NSDate *excelBaseDate = [nl_gregorianCalendar dateFromComponents:excelBaseDateComps];
NSDate *inputDate = [NSDate dateWithTimeInterval:theTimeInterval sinceDate:excelBaseDate];
NSLog(#"%# %# \r serialdate %f, theTimeInterval = %f \r inputDate = %#",
[self class], NSStringFromSelector(_cmd),
serialdate, theTimeInterval,
[self.nl_dateFormatter stringFromDate:inputDate]);
return inputDate;
}
The spreadsheet was produced in the Netherlands, presumably on a Dutch version of Microsoft Excel.
Spreadsheet date Sunday July 6, 2014 00:00 yields the following results:
dateFromExcelSerialDate:
serialdate = 41826.000000, integral = 41826.000000, fractional =
0.000000 theTimeInterval = 3613766400.000000 inputDate = 08 jul. 2014 01:40
Similarly, Sunday July 13, 2014 00:00 yields:
serialdate = 41833.000000, integral = 41833.000000, fractional =
0.000000 theTimeInterval = 3614371200.000000 inputDate = 15 jul. 2014 01:40
I can correct the output by subtracting 2 days, one hour and 40 minutes:
theTimeInterval -= ((60 * 60 * 24 * 2) + (60*60) + (60*40));
but I have no idea how robust that is.
That difference of two days made me think it had something to do with leap year corrections, so I tried to let the calendar do the calculations by adding the NSTimeInterval seconds to the excelBaseDate, like so:
NSDateComponents *comps = [[NSDateComponents alloc] init];
[comps setSecond:theInterval];
NSDate *inputDate = [nl_gregorianCalendar dateByAddingComponents:comps
toDate:excelBaseDate
options:0];
Strangely enough, that gave me dates somewhere in the 1870's. Who knows what is going on?
there are two things here:
your start date is 1900-Jan-1 but your referred description clearly says: the reference is 1900-Jan-0 – you may add an extra day here;
year 1900 was not a leap-year – you may add an extra day here;
I guess, this is pretty much the reason why you get two extra days every occasion.
Microsoft knows about that, see more about the topic here.

Error with how NSCalendar dateByAddingComponents handles Daylight Savings

I'm having a hard time understanding an exception to how dateByAddingComponents handles Daylight Savings. I've read through the Apple Date and Time Programming Guide and expected dateByAddingComponents to take into account DST changes. However, on the date of the DST change, its not working for me.
Here's the code:
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
[gregorian setTimeZone:[NSTimeZone localTimeZone]];
NSDateComponents *midnight = [gregorian components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit) fromDate:self.currentDate];
midnight.hour = 0;
midnight.minute = 0;
midnight.second = 0;
NSDate *startDate = [gregorian dateFromComponents:midnight];
NSDateComponents *offSetComponents = [[NSDateComponents alloc] init];
[offSetComponents setDay:1];
NSDate *endDate = [gregorian dateByAddingComponents:offSetComponents toDate:startDate options:0];
//Calculate start time from config (hours/min from seconds)
int startTimeInMinutes = self.club.clubConfiguration.startTime.integerValue;
int startTimeHours = startTimeInMinutes / 60;
int startTimeMins = startTimeInMinutes % 60;
NSLog(#"---- startTimeHours %i", startTimeHours);
NSLog(#"---- startTImeMins %i", startTimeMins);
NSDateComponents *offSetComponents2 = [[NSDateComponents alloc] init];
[offSetComponents2 setHour:startTimeHours];
[offSetComponents2 setMinute:startTimeMins];
NSDate *firstTeeTime = [gregorian dateByAddingComponents:offSetComponents2 toDate:startDate options:0];
Explanation:
I'm getting a startTimeInMinutes from the server that I use to calculate the firstTeeTime. For example, I'm expecting to add 6 hours to the startDate (12am in my use case) and get 6am (localTimeZone).
Using dateByAddingComponents works both before and after the DST change however, on the day of DST change Sunday Nov 3, I'm getting 5am.
Theory: Since there are actually 2 2am's on Sunday Nov 3rd, I may have to account for that? If thats the case, I'd have to write some logic to account for the actual day of DST change and add an offset if appropriate using daylightSavingTimeOffsetForDate.
What am I missing???
EDIT: Ok, I decided to work around the issue by determining if today was the DST change and add/remove an hour offset. Feels kinda like I'm missing something here about NSDate however, it works. Hope this helps someone else out there scratching their heads all morning.
Work Around Code:
////// Work around for DST
NSTimeZone *currentZone = [gregorian timeZone];
NSDate *dstTransitionDate = [currentZone nextDaylightSavingTimeTransitionAfterDate:startDate];
NSTimeInterval dstOffset = [currentZone daylightSavingTimeOffsetForDate:endDate];
NSDateComponents *startDateComponents = [gregorian components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:startDate];
NSDateComponents *dstTransitionDateComponents = [gregorian components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit) fromDate:dstTransitionDate];
int offset = 0;
if ( [startDateComponents year] == [dstTransitionDateComponents year] &&
[startDateComponents month] == [dstTransitionDateComponents month] &&
[startDateComponents day] == [dstTransitionDateComponents day])
{
if (dstOffset > 0){
offset = -1;
} else {
offset = 1;
}
}
//////
I totally sympathize with you, as I have fallen into the same trap. First, I scratched my head at the results and docs, then attempted a fool's errand by attempting to roll out a custom "adding date components" logic.
Then, came across your answer, which worked beautifully, and my tests finally pass:
// assert!
XCTAssertEqual(dateFormatter.stringFromDate(dstSwitch.date), "11/1/15, 12:00 AM")
// Offseting the date should just work
do {
let dstOffset = dstSwitch + 3.hours
XCTAssertEqual(dateFormatter.stringFromDate(dstOffset.date), "11/1/15, 3:00 AM")
}
... But hang on! Words of wisdom from #RobNapier set me on the right path ... We are adding 6 hours, which means a person at the event will only spend six hours of their life. If the start date is midnight, they will adjust their watches at 2 AM back to 1 AM, and therefor spend the lost hour in oblivion according to the calendar.
So... If the event truly starts at midnight, and ends at 6 AM, heed Rob's words and don't use the duration. Use the exact date components. Because that duration is actually 7 hours.
For fun, I came to that realization when my tests bothered me. Why should I only add the offset on the same day? Why does changing days "just works"? ... Liiiight bulb.
And, btw, my test are for a library I am rolling out that should end the short comings of all date calculation and end the confusion .. With really concise syntax for swift. Will be available as Datez, part of Kitz.

NSDatePicker with date in yy to yyyy format

I have as NSDatePicker, where I enter 0023 and I expect it to change it to 2023. My logic is to convert the yy to yyyy based on +-50 years.
But the default behavior of NSDatePicker changes it to 0023 etc.
What I need to do to show in yyyy format with nearest 50 years range.
Is there any way to do it through Interface Builder or through codes.
Your help will be highly appreciable.
It does not "change" 0023 to 0023, it leaves it at 0023, which is correct the correct behaviour. You'd need to manually check and fix this yourself. Maybe like (untested):
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar
components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit
fromDate:myPossiblyIncorrectDate
];
NSDate *correctedDate;
if ([components year] < 100) {
NSUInteger currentYearLastDigits = 13; // Insert logic to get current year here.
NSUInteger yearLastDigits = [components year];
if (yearLastDigits > currentYearLastDigits) {
[components setYear:1900 + yearLastDigits];
} else {
[components setYear:2000 + yearLastDigits];
}
correctedDate = [calendar dateFromComponents:components];
} else {
correctedDate = myPossiblyIncorrectDate;
}
Depending on how exact you need this to be, you might want to get/set more components. See the NSCalendar constants.
But it would be better to catch the wrong year number before the number is even interpreted as year number since the date might be invalid otherwise.

LDAP Date to NSDate

I am trying to parse the pwdLastSet value from NSTask response when I do an ldapsearch. I've successfully extracted the value (129875475241190194) and I am trying to convert it to an NSDate Object.
Reference: http://www.chrisnowell.com/information_security_tools/date_converter/Windows_active_directory_date_converter.asp
I tried to extract the Javascript code from the page above and convert it but I am getting a different date.
int iYearsFrom1601to1970 = 1970 - 1601;
int iDaysFrom1601to1970 = iYearsFrom1601to1970 * 365;
iDaysFrom1601to1970 += (int)(iYearsFrom1601to1970 / 4); // leap years
iDaysFrom1601to1970 -= 3; // non-leap centuries (1700,1800,1900). 2000 is a leap century
float iSecondsFrom1601to1970 = iDaysFrom1601to1970 * 24 * 60 * 60;
int iTotalSecondsSince1601 = (int)(129875475241190194 / 10000000);
float iTotalSecondsSince1970 = iTotalSecondsSince1601 - iSecondsFrom1601to1970;
NSDate *date = [NSDate dateWithTimeIntervalSince1970:iTotalSecondsSince1970];
Any help would be appreciated.
Thanks!
Here's how I would do it:
NSDateComponents *base = [[NSDateComponents alloc] init];
[base setDay:1];
[base setMonth:1];
[base setYear:1601];
[base setEra:1]; // AD
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *baseDate = [gregorian dateFromComponents:base];
[base release];
[gregorian release];
NSTimeInterval timestamp = 129875475241190194.0 / 10000000.0;
NSDate *finalDate = [baseDate dateByAddingTimeInterval:timestamp];
This gives me a finalDate of 2012-07-24 03:58:22 +0000.
Since the timestamp is a time interval since Jan 1, 1601 at 00:00 UTC, you can use the -dateByAddingTimeInterval: method on NSDate to add the timestamp to the base date to get the final NSDate.
Once you've done that, you can run it through an NSDateFormatter to format it for display.
Assuming the, well, daring conversion between the basetimes is correct: actually looking at the warnings, instead of casting them away, might actually help:
int main(void)
{
int iTotalSecondsSince1601 = (129875475241190194 / 10000000);
return 0;
}
stieber#gatekeeper:~$ clang++ Test.cpp
Test.cpp:4:8: warning: implicit conversion from 'long' to 'int' changes value from 12987547524 to 102645636
....
That should account for a good deal of the difference...
Try this
NSTimeInterval value = 129875475241190194;
// instead of trying to compute seconds between 1601 and 1970
const NSTimeInterval EPOCH = 11644473600;
const NSTimeInterval NANO = 10000000;
NSTimeInterval seconds = value / NANO - EPOCH;
NSDate *answer = [NSDate dateWithTimeIntervalSince1970:seconds];
Also this is reason you don't want to calculate seconds since 1601: ...in the last millennium, 1600 and 2000 were leap years, but 1700, 1800 and 1900 were not. Excerpt from Wikipedia on Gregorian calendar.
The value for EPOCH is explained on Convert Active Directory "LastLogon:" time to (UNIX) readable time
.
Note: The information about accountExpires which starts from 12-31-1601 (11644473600). The values lastLogon and lastLogonTimeStamp however use 01-01-1601 as the date to calculate this value (11676009600).