2

I have a timestamp that represents the the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight, January 1, 0001 (according to http://msdn.microsoft.com/en-us/library/system.datetime.ticks.aspx). This value is generated by a server written in C#, but I need to convert this to a date in Objective-C on iOS.

For example, the timestamp 634794644225861250 is supposed to give a date of August 2, 2012.

Dan Watkins
  • 918
  • 9
  • 25
  • Well I don't know ObjectiveC, but what you should be doing is creating a date 1/1/1 and then adding the value to it. If you can't add ticks then you do a bit of math to get seconds or some such. – Tony Hopkinson Aug 09 '12 at 16:50
  • Try looking up the actual values for minutes in a day and days in a year. They aren't exactly the nice round values that we all use. Also, the rules for leap years are a little more complicated: there's a leap year every four years, unless the year is divisible by 100 then there is not, but if the year is divisible by 400 then there is. There are also leap seconds to consider. I think there are also some dates in some Medieval year that don't exist due to when people switched to the Gregorian calendar. Over two thousand years, all of this can add up to make a pretty significant difference. – mrranstrom Aug 09 '12 at 16:53
  • For those who are looking for Swift methods to do this, see here: http://stackoverflow.com/a/41625877/253938 – RenniePet Jan 13 '17 at 01:16

4 Answers4

5

This C# code might help you:

// The Unix epoch is 1970-01-01 00:00:00.000
DateTime   UNIX_EPOCH = new DateTime( 1970 , 1 , 1 ) ;

// The Unix epoch represented in CLR ticks.
// This is also available as UNIX_EPOCH.Ticks
const long UNIX_EPOCH_IN_CLR_TICKS = 621355968000000000 ;

// A CLR tick is 1/10000000 second (100ns).
// Available as Timespan.TicksPerSecond
const long CLR_TICKS_PER_SECOND = 10000000 ;

DateTime now       = DateTime.Now                        ; // current moment in time
long     ticks_now = now.Ticks                           ; // get its number of tics
long     ticks     = ticks_now - UNIX_EPOCH_IN_CLR_TICKS ; // compute the current moment in time as the number of ticks since the Unix epoch began.
long     time_t    = ticks / CLR_TICKS_PER_SECOND        ; // convert that to a time_t, the number of seconds since the Unix Epoch
DateTime computed  = EPOCH.AddSeconds( time_t )          ; // and convert back to a date time value

// 'computed' is the the current time with 1-second precision.

Once you have your time_t value, the number of seconds since the Unix epoch began, you should be able to get an NSDATE in Objective-C thusly:

NSDate* myNSDate = [NSDate dateWithTimeIntervalSince1970:<my_time_t_value_here> ] ;

which see: https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSDate_Class/Reference/Reference.html

Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135
1

On iOS you cannot use dateWithString but you can still do it easily. This solution should work on both iOS and Mac. (note: I'm typing it here, not tested)

@interface NSDate (CLRTicks)

+(NSDate*)dateWithCLRTicks:(long)ticks;

@end



@implementation NSDate (CLRTicks)

+(NSDate*)dateWithCLRTicks:(long)ticks
{
    return [NSDate dateWithTimeIntervalSince1970: (ticks-621355968000000000L)/10000000.0]
}

@end

It is basically the same solution Nicholas posted, except in a better form. You should probably make it even better by defining the constants symbolically.

Analog File
  • 5,280
  • 20
  • 23
1

Add a category for NSDate:

@implementation NSDate (CLRTicks)

+ (NSDate *)dateWithCLRTicks:(int64_t)ticks {
    return [NSDate dateWithCLRTicks:ticks withTimeIntervalAddition:0.0];
}

+ (NSDate *)dateWithCLRTicks:(int64_t)ticks withTimeIntervalAddition:(NSTimeInterval)timeIntervalAddition {
    const double GMTOffset = [[NSTimeZone defaultTimeZone] secondsFromGMT];
    const int64_t CLROffset = 621355968000000000;
    double timeStamp = ((double)(ticks - CLROffset) / 10000000.0) - GMTOffset + timeIntervalAddition;

    return [NSDate dateWithTimeIntervalSince1970:timeStamp];
}

@end
Nikita
  • 1,811
  • 1
  • 20
  • 41
0

Did not do all the computations, but your leap year computation is incomplete.

You get a leap year every 4 years. But you skip one every 100 years. And you do not skip it every 400, which is why 2000 was a leap year but 1900 was not.

For example:

2012 is a leap year (divisible by 4 but not 100) 2100 is not a leap year (divisible by 100 but not 400) 2400 is a leap year (divisible 400)

In cocoa you can use NSDate.

NSDate* reference = [NSDate dateWithString:@"0001-01-01 00:00:00 +0000"];
NSDate* myDate = [NSDate dateWithTimeInterval: (ticks/10000000.0)
                                    sinceDate: reference];
Analog File
  • 5,280
  • 20
  • 23
  • Your solution seems legitimate, but on iOS cocoa is not readily available, meaning dateWithString is not available for NSDate. There is dateFromString for NSDateFormatter, but I could never get it to produce a valid NSDate. – Dan Watkins Aug 09 '12 at 18:57
  • oh, I had not noticed that you were talking of iOS. But I do not see the problem. I'll post another solution even if you already accepted Nicholas one. – Analog File Aug 09 '12 at 19:57