LDAP Date to NSDate - objective-c

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

Related

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.

dateWithTimeIntervalSince1970 not returning correct date

I have the following method below that is meant to retrieve and convert a unixTimeStamp from an API call to a NSDate object that I can easily manipulate and use. For some reason, this returns wrong values. An example would be when the unixTimeStamp is 1385152832, the date SHOULD be
Fri, 22 Nov 2013 20:40:31 GMT
November 22, 2013 at 3:40:31 PM EST
but instead spits out: 45852-09-07 08:13:52 EST. Does anyone know why this would happen?
-(NSDate *)messageDate
{
NSTimeInterval unixTimeStamp = [[self messageDateString] doubleValue];
NSDate *messageDate = [NSDate dateWithTimeIntervalSince1970:unixTimeStamp];
NSAssert(messageDate, #"messageDate should not be nil");
return messageDate;
}
The messageDateString method is returning milliseconds since the epoch, not seconds since the epoch. Look at the value of unixTimeStamp in your debugger pane. It's 1384803782032. That is about 1000 times too large to be a current Unix timestamp.
An NSTimeInterval is measured in seconds, not milliseconds. Try this instead:
-(NSDate *)messageDate {
NSTimeInterval unixTimeStamp = [[self messageDateString] doubleValue] / 1000.0;
NSDate *messageDate = [NSDate dateWithTimeIntervalSince1970:unixTimeStamp];
NSAssert(messageDate, #"messageDate should not be nil");
return messageDate;
}

What would be the Objective-C equivalent of this java command:

data.date = new Date(jObjectTip.getLong("createdAt") * 1000);
That command is used to grab data from FourSquare.
Assuming that createdAt is unix timestamp, the code will be:
NSTimeInterval createdAt = ...;
NSDate *resultDate = [NSDate dateWithTimeIntervalSince1970:createdAt];
Note that NSTimeInterval is a typedef for double, and it stores the time in seconds, unlike Java, so there's no need to multiply the value by 1000;
Uh...not sure what the "createAt" * 1000 means. 1000 times the current date and time?
In Objective C you can use:
(id)initWithTimeIntervalSinceNow:(NSTimeInterval)seconds
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSDate_Class/Reference/Reference.html
to create a date time object with an offset:
// creates a date time that is 1000 seconds away from the current time
NSDate *date = [[NSDate alloc] initWithTimeIntervalSinceNow:1000];

Convert epoch time to NSDate with good timezone with Objective c

how I can convert an epoch time value to NSDate. For example I use this value : 1310412600000. and I am in the EDT time zone.
When I try this :
NSString *bar = [[NSDate dateWithTimeIntervalSince1970:epoch] description];
I got a wrong value...
What is the good way? I spend a lot of time with that....
Thanks
Epoch time (also known as "Unix" and "POSIX" time) is the number of seconds since the start of 1970. You can convert epoch time to NSDate with this algorithm:
Instantiate NSDate object and pass the epoch time as a parameter to NSDate's initWithTimeIntervalSince1970 initializer. You're done. The NSDate object is set to the epoch time. NSDate stores times internally in the UTC time zone. It's up to you how it is displayed.
[Optional] Format your NSDate to the appropriate time zone with an NSDateFormatter.
Here's the code I used:
// Sample string epochTime is number of seconds since 1970
NSString *epochTime = #"1316461149";
// Convert NSString to NSTimeInterval
NSTimeInterval seconds = [epochTime doubleValue];
// (Step 1) Create NSDate object
NSDate *epochNSDate = [[NSDate alloc] initWithTimeIntervalSince1970:seconds];
NSLog (#"Epoch time %# equates to UTC %#", epochTime, epochNSDate);
// (Step 2) Use NSDateFormatter to display epochNSDate in local time zone
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss zzz"];
NSLog (#"Epoch time %# equates to %#", epochTime, [dateFormatter stringFromDate:epochNSDate]);
// (Just for interest) Display your current time zone
NSString *currentTimeZone = [[dateFormatter timeZone] abbreviation];
NSLog (#"(Your local time zone is: %#)", currentTimeZone);
Per the documentation, dateWithTimeIntervalSince1970: runs from January 1, 1970, 00:00 GMT. So you should be getting results four hours later than those you really want.
Hence the simplest thing — if you don't mind hard coding the offset from GMT to EDT — would seem to be:
[[NSDate dateWithTimeIntervalSince1970:epoch] dateByAddingTimeInterval:-240]
// or:
[NSDate dateWithTimeIntervalSince1970:epoch - 240]
Though to eliminate arbitrary constants (and be a little cleaner), you probably want something like:
NSTimeZone *EDTTimeZone = [NSTimeZone timeZoneWithAbbreviation:#"EDT"];
NSInteger secondsDifferenceFromGMT =
[EDTTimeZone secondsFromGMTForDate:[NSDate dateWithTimeIntervalSince1970:0]];
NSDate *startOfEpoch =
[NSDate dateWithTimeIntervalSince1970:secondsDifferenceFromGMT];
...
NSDate *newDate = [startOfEpoch dateByAddingTimeInterval:firstInterval]; // etc
Are you passing the value in seconds?
The method accepts value in seconds , not milliseconds.
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSDate_Class/Reference/Reference.html

How to convert an NSTimeInterval (seconds) into minutes

I've got an amount of seconds that passed from a certain event. It's stored in a NSTimeInterval data type.
I want to convert it into minutes and seconds.
For example I have: "326.4" seconds and I want to convert it into the following string:
"5:26".
What is the best way to achieve this goal?
Thanks.
Brief Description
The answer from Brian Ramsay is more convenient if you only want to convert to minutes.
If you want Cocoa API do it for you and convert your NSTimeInterval not only to minutes but also to days, months, week, etc,... I think this is a more generic approach
Use NSCalendar method:
(NSDateComponents *)components:(NSUInteger)unitFlags fromDate:(NSDate *)startingDate toDate:(NSDate *)resultDate options:(NSUInteger)opts
"Returns, as an NSDateComponents object using specified components, the difference between two supplied dates". From the API documentation.
Create 2 NSDate whose difference is the NSTimeInterval you want to convert. (If your NSTimeInterval comes from comparing 2 NSDate you don't need to do this step, and you don't even need the NSTimeInterval).
Get your quotes from NSDateComponents
Sample Code
// The time interval
NSTimeInterval theTimeInterval = 326.4;
// Get the system calendar
NSCalendar *sysCalendar = [NSCalendar currentCalendar];
// Create the NSDates
NSDate *date1 = [[NSDate alloc] init];
NSDate *date2 = [[NSDate alloc] initWithTimeInterval:theTimeInterval sinceDate:date1];
// Get conversion to months, days, hours, minutes
unsigned int unitFlags = NSHourCalendarUnit | NSMinuteCalendarUnit | NSDayCalendarUnit | NSMonthCalendarUnit;
NSDateComponents *conversionInfo = [sysCalendar components:unitFlags fromDate:date1 toDate:date2 options:0];
NSLog(#"Conversion: %dmin %dhours %ddays %dmoths",[conversionInfo minute], [conversionInfo hour], [conversionInfo day], [conversionInfo month]);
[date1 release];
[date2 release];
Known issues
Too much for just a conversion, you are right, but that's how the API works.
My suggestion: if you get used to manage your time data using NSDate and NSCalendar, the API will do the hard work for you.
pseudo-code:
minutes = floor(326.4/60)
seconds = round(326.4 - minutes * 60)
All of these look more complicated than they need to be! Here is a short and sweet way to convert a time interval into hours, minutes and seconds:
NSTimeInterval timeInterval = 326.4;
long seconds = lroundf(timeInterval); // Since modulo operator (%) below needs int or long
int hour = seconds / 3600;
int mins = (seconds % 3600) / 60;
int secs = seconds % 60;
Note when you put a float into an int, you get floor() automatically, but you can add it to the first two if if makes you feel better :-)
Forgive me for being a Stack virgin... I'm not sure how to reply to Brian Ramsay's answer...
Using round will not work for second values between 59.5 and 59.99999. The second value will be 60 during this period. Use trunc instead...
double progress;
int minutes = floor(progress/60);
int seconds = trunc(progress - minutes * 60);
If you're targeting at or above iOS 8 or OS X 10.10, this just got a lot easier. The new NSDateComponentsFormatter class allows you to convert a given NSTimeInterval from its value in seconds to a localized string to show the user. For example:
Objective-C
NSTimeInterval interval = 326.4;
NSDateComponentsFormatter *componentFormatter = [[NSDateComponentsFormatter alloc] init];
componentFormatter.unitsStyle = NSDateComponentsFormatterUnitsStylePositional;
componentFormatter.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehaviorDropAll;
NSString *formattedString = [componentFormatter stringFromTimeInterval:interval];
NSLog(#"%#",formattedString); // 5:26
Swift
let interval = 326.4
let componentFormatter = NSDateComponentsFormatter()
componentFormatter.unitsStyle = .Positional
componentFormatter.zeroFormattingBehavior = .DropAll
if let formattedString = componentFormatter.stringFromTimeInterval(interval) {
print(formattedString) // 5:26
}
NSDateCompnentsFormatter also allows for this output to be in longer forms. More info can be found in NSHipster's NSFormatter article. And depending on what classes you're already working with (if not NSTimeInterval), it may be more convenient to pass the formatter an instance of NSDateComponents, or two NSDate objects, which can be done as well via the following methods.
Objective-C
NSString *formattedString = [componentFormatter stringFromDate:<#(NSDate *)#> toDate:<#(NSDate *)#>];
NSString *formattedString = [componentFormatter stringFromDateComponents:<#(NSDateComponents *)#>];
Swift
if let formattedString = componentFormatter.stringFromDate(<#T##startDate: NSDate##NSDate#>, toDate: <#T##NSDate#>) {
// ...
}
if let formattedString = componentFormatter.stringFromDateComponents(<#T##components: NSDateComponents##NSDateComponents#>) {
// ...
}
Brian Ramsay’s code, de-pseudofied:
- (NSString*)formattedStringForDuration:(NSTimeInterval)duration
{
NSInteger minutes = floor(duration/60);
NSInteger seconds = round(duration - minutes * 60);
return [NSString stringWithFormat:#"%d:%02d", minutes, seconds];
}
Here's a Swift version:
func durationsBySecond(seconds s: Int) -> (days:Int,hours:Int,minutes:Int,seconds:Int) {
return (s / (24 * 3600),(s % (24 * 3600)) / 3600, s % 3600 / 60, s % 60)
}
Can be used like this:
let (d,h,m,s) = durationsBySecond(seconds: duration)
println("time left: \(d) days \(h) hours \(m) minutes \(s) seconds")
NSDate *timeLater = [NSDate dateWithTimeIntervalSinceNow:60*90];
NSTimeInterval duration = [timeLater timeIntervalSinceNow];
NSInteger hours = floor(duration/(60*60));
NSInteger minutes = floor((duration/60) - hours * 60);
NSInteger seconds = floor(duration - (minutes * 60) - (hours * 60 * 60));
NSLog(#"timeLater: %#", [dateFormatter stringFromDate:timeLater]);
NSLog(#"time left: %d hours %d minutes %d seconds", hours,minutes,seconds);
Outputs:
timeLater: 22:27
timeLeft: 1 hours 29 minutes 59 seconds
Since it's essentially a double...
Divide by 60.0 and extract the integral part and the fractional part.
The integral part will be the whole number of minutes.
Multiply the fractional part by 60.0 again.
The result will be the remaining seconds.
Remember that the original question is about a string output, not pseudo-code or individual string components.
I want to convert it into the following string: "5:26"
Many answers are missing the internationalization issues, and most doing the math computations by hand. All just so 20th century...
Do not do the Math yourself (Swift 4)
let timeInterval: TimeInterval = 326.4
let dateComponentsFormatter = DateComponentsFormatter()
dateComponentsFormatter.unitsStyle = .positional
if let formatted = dateComponentsFormatter.string(from: timeInterval) {
print(formatted)
}
5:26
Leverage on libraries
If you really want individual components, and pleasantly readable code, check out SwiftDate:
import SwiftDate
...
if let minutes = Int(timeInterval).seconds.in(.minute) {
print("\(minutes)")
}
5
Credits to #mickmaccallum and #polarwar for adequate usage of DateComponentsFormatter
How I did this in Swift (including the string formatting to show it as "01:23"):
let totalSeconds: Double = someTimeInterval
let minutes = Int(floor(totalSeconds / 60))
let seconds = Int(round(totalSeconds % 60))
let timeString = String(format: "%02d:%02d", minutes, seconds)
NSLog(timeString)
Swift 2 version
extension NSTimeInterval {
func toMM_SS() -> String {
let interval = self
let componentFormatter = NSDateComponentsFormatter()
componentFormatter.unitsStyle = .Positional
componentFormatter.zeroFormattingBehavior = .Pad
componentFormatter.allowedUnits = [.Minute, .Second]
return componentFormatter.stringFromTimeInterval(interval) ?? ""
}
}
let duration = 326.4.toMM_SS()
print(duration) //"5:26"