views:

496

answers:

4

I'm simply trying to convert a CGEventTimestamp (which is an unsigned 64-bit integer roughly representing nanoseconds since system startup) into an NSDate. I know there's a way to convert "time since system startup" into either an NSTimeInterval or a date relative to a reference date, but I'm not finding it.

How do I convert a CGEventTimestamp into something NSDate will accept? Thanks.

(forgot to mention, needs to be 10.5 friendly and avoid Carbon if at all possible)

+2  A: 
// Determine time in NSTimeInterval seconds this event took place after system start
GCEventTimestamp nanoseconds = GetIt();
NSTimeInterval seconds = (NSTimeInterval)nanoseconds / 1000000000.0;

// GetCurrentEventTime() gives you time in seconds since system start
EventTime currentTimeInSeconds = GetCurrentEventTime();

// Given this you can figure out the date of system start, and then add
// your event time
NSDate* startTime = [NSDate dateWithTimeIntervalSinceNow:-currentTimeInSeconds];
NSDate* eventTime = [startTime dateByAddingTimeInterval:seconds];
nall
My sincere apologies for not mentioning this needs to work on 10.5-[NSDate dateByAddingTimeInterval:] is available in 10.6 only. Plus, I'm not currently linking against Carbon. I suppose I could but I'd really like to avoid it if I can.
MyztikJenz
This thread might be useful: http://www.cocoabuilder.com/archive/message/cocoa/2004/8/29/115976
nall
As for dateByAddingTimeInterval, you can just use the [[[NSDate alloc] initWithTimeInterval:-currentTimeInSeconds sinceDate:[NSDate date]] autorelease]
nall
+1  A: 

nall got me in the right direction. I slapped together a category on NSDate that takes care of the details and is both 10.5/10.6 friendly and uses no Carbon. Thanks for the help, nall!

NSDate-Additions.h

#import <Foundation/Foundation.h>
@interface NSDate (NSDate_Additions)

+(NSTimeInterval) timeIntervalSinceSystemStartup;
-(NSTimeInterval) timeIntervalSinceSystemStartup;
+(NSDate *) dateOfSystemStartup;
+(NSDate *) dateWithCGEventTimestamp:(CGEventTimestamp)cgTimestamp;

@end

NSDate-Additions.m

#import "NSDate-Additions.h"
#include <assert.h>
#include <CoreServices/CoreServices.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <unistd.h>

#if MAC_OS_X_VERSION_MAX_ALLOWED == MAC_OS_X_VERSION_10_5
@interface NSProcessInfo (SnowLeopard)
- (NSTimeInterval)systemUptime;
@end

@interface NSDate (SnowLeopard)
- (id)dateByAddingTimeInterval:(NSTimeInterval)seconds;
@end
#endif


// Boosted from Apple sample code
uint64_t UpTimeInNanoseconds(void)
{
    uint64_t        time;
    uint64_t        timeNano;
    static mach_timebase_info_data_t    sTimebaseInfo;

    time = mach_absolute_time();

    // Convert to nanoseconds.

    // If this is the first time we've run, get the timebase.
    // We can use denom == 0 to indicate that sTimebaseInfo is
    // uninitialised because it makes no sense to have a zero
    // denominator is a fraction.

    if ( sTimebaseInfo.denom == 0 ) {
     (void) mach_timebase_info(&sTimebaseInfo);
    }

    // Do the maths.  We hope that the multiplication doesn't
    // overflow; the price you pay for working in fixed point.

    timeNano = time * sTimebaseInfo.numer / sTimebaseInfo.denom;

    return timeNano;
}


@implementation NSDate (NSDate_Additions)

+(NSTimeInterval) timeIntervalSinceSystemStartup
{
    NSTimeInterval interval;
    long sysVersion;

    Gestalt( gestaltSystemVersion, &sysVersion );
    if( sysVersion >= 0x1060 )
     interval = [[NSProcessInfo processInfo] systemUptime];
    else
     interval = UpTimeInNanoseconds() / 1000000000.0;

    return( interval );
}

-(NSTimeInterval) timeIntervalSinceSystemStartup
{
    return( [self timeIntervalSinceDate:[NSDate dateOfSystemStartup]] );
}

+(NSDate *) dateOfSystemStartup
{
    return( [NSDate dateWithTimeIntervalSinceNow:-([NSDate timeIntervalSinceSystemStartup])] );
}

+(NSDate *) dateWithCGEventTimestamp:(CGEventTimestamp)cgTimestamp
{
    NSDate *ssuDate = nil;
    NSDate *cgDate = nil;
    long sysVersion;

    ssuDate = [NSDate dateOfSystemStartup];

    Gestalt( gestaltSystemVersion, &sysVersion );
    if( sysVersion >= 0x1060 )
     cgDate = [ssuDate dateByAddingTimeInterval:(cgTimestamp/1000000000.0)];
    else
     cgDate = [ssuDate addTimeInterval:(cgTimestamp/1000000000.0)];

    return( cgDate );
}

@end
MyztikJenz
Glad you got it! From my understanding GetCurrentEventTime() is just a wrapper around mach_absolute_time().
nall
A: 

This is not an answer, for some reason I cannot add a comment on your post above...

I was wondering if I could use the code you pasted in an open-source project I'm contributing to (with a link to this page).

Cheers!

Marcello Bastea-Forte
Absolutely, feel free to use it as you see fit. Consider it to have an MIT license (unless Stack Overflow says otherwise). What project are you working on, Marcello?
MyztikJenz
Thanks! I'm contributing to jpen (http://jpen.sf.net/), and trying to translate the timestamp in NSEvents to milliseconds since epoch.Unfortunately either the timestamp parameter is wrong, or the system start measurement takes too long, because the calculated time seems to end up in the future (2~3ms later than NSLog and System.currentTimeMillis, executed in java *after* the event is received), and it gets worse as events pass (it got up to 20ms before I stopped counting)... So I may end up ditching the feature altogether.
Marcello Bastea-Forte
A: 

Regarding the code NSDate-Additions.m above. The line "timeNano = time * sTimebaseInfo.numer / sTimebaseInfo.denom;" will cause memory corruption on iPhone OS 2.2.1. A fix is to use a cast like: timeNano = time * (uint64_t)sTimebaseInfo.numer / sTimebaseInfo.denom;

ThomasW