I have a simple snippet which runs a loop every 1 second and displays seconds since epoch using NSDate.
while(1){
NSDate *now = [NSDate date];
NSTimeInterval nowEpochSeconds = [now timeIntervalSince1970];
NSLog(#"%f",nowEpochSeconds);
[NSThread sleepForTimeInterval:1.0f];
}
nowEpochSeconds produces a floating point value in seconds with several decimal places as such
1568646562.613972
1568646563.618479
1568646564.621624
1568646565.626183
1568646566.626722
1568646567.628425
1568646568.633329
It's very close to one second intervals. Then I try dropping the decimal place using floorf
NSLog(#"%f",floorf(nowEpochSeconds));
But the seconds just remain the same. It's not the correct number of seconds either they just stagnate at one value
1568646528.000000
1568646528.000000
1568646528.000000
1568646528.000000
1568646528.000000
1568646528.000000
I don't know why floorf is giving me strange results
NSTimeInterval is a double, so you probably want to be using floor():
int j = 0;
while(++j < 6){
NSDate *now = [NSDate date];
NSTimeInterval nowEpochSeconds = [now timeIntervalSince1970];
NSLog(#"%f",nowEpochSeconds);
NSLog(#"%f",floor(nowEpochSeconds));
NSLog(#"\n");
[NSThread sleepForTimeInterval:1.0f];
}
Output:
1568648630.428520
1568648630.000000
1568648631.429841
1568648631.000000
1568648632.431206
1568648632.000000
1568648633.432594
1568648633.000000
1568648634.433988
1568648634.000000
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).
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
I'm using the JSON library from Stig Brautaset(http://code.google.com/p/json-framework) and I need to serialize an NSDate. I was considering converting it into a string before JSONifying it, however, I ran into this weird behavior:
Why aren't these NSDates considered equal?
NSDate *d = [[NSDate alloc] init];
NSDate *dd = [NSDate dateWithString:[d description]];
NSLog(#"%#", d);
NSLog(#"%#", dd);
if( [d isEqualToDate:dd] ){
NSLog(#"Yay!");
}
When you describe the original date object you lose some sub-second precision from the original object — in other words, -description shaves off fractional seconds, and returns
A string representation of the receiver in the international format YYYY-MM-DD HH:MM:SS ±HHMM, where ±HHMM represents the time zone offset in hours and minutes from GMT
When you create a new date object based on the description, you get it in whole seconds because the string is only precise to a whole second. So -isEqualToDate: returns NO because there is a difference of a fraction of a second between your two date objects, which it's sensitive to.
This method detects sub-second differences between dates. If you want to compare dates with a less fine granularity, use timeIntervalSinceDate: to compare the two dates.
So you'd do something like this instead (NSTimeInterval measures in seconds):
if ([d timeIntervalSinceDate:dd] == 0) {
NSLog(#"Yay!");
}
isEqualToDate detects subseconds differences between dates, but the description method does not include subseconds.
Because they're not equivalent:
NSDate *d = [NSDate date];
NSDate *dd = [NSDate dateWithString:[d description]];
NSLog(#"%f", [d timeIntervalSinceReferenceDate]);
NSLog(#"%f", [dd timeIntervalSinceReferenceDate]);
Produces:
2011-04-28 11:58:11.873 EmptyFoundation[508:903] 325709891.867788
2011-04-28 11:58:11.874 EmptyFoundation[508:903] 325709891.000000
In other words, the +dateWithString: method does not maintain sub-second precision.
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"