@implementation NSDate (ServerTime)
+(void)setServerTime:(NSNumber *)timestamp {
if (timestamp == nil) {
[[NSUserDefaults standardUserDefaults] removeObjectForKey:kServerTime];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:kServerTimeUptime];
} else {
[[NSUserDefaults standardUserDefaults] setObject:timestamp forKey:kServerTime];
[[NSUserDefaults standardUserDefaults] setObject:@([self uptimeSeconds]) forKey:kServerTimeUptime];
}
[[NSUserDefaults standardUserDefaults] synchronize];
}
+(NSDate *)serverTime {
NSNumber *lastServerTimestamp = [[NSUserDefaults standardUserDefaults] objectForKey:kServerTime];
NSNumber *lastUptime = [[NSUserDefaults standardUserDefaults] objectForKey:kServerTimeUptime];
if (lastServerTimestamp == nil || lastUptime == nil) {
//the user has yet to sync up with the server we have no choice but to use the system time
return [NSDate date];
}
int currentUptime = (int)[self uptimeSeconds];
if (currentUptime < [lastUptime intValue]) {
//there has been a reboot, we can't rely on the existing data, use the system time
return [NSDate date];
}
//get the difference from the store uptime to the current uptime and offset the stored server time
int delta = (currentUptime - [lastUptime intValue]);
NSTimeInterval serverTime = [lastServerTimestamp intValue] + delta;
return [NSDate dateWithTimeIntervalSince1970:serverTime];
}
+(time_t)uptimeSeconds {
struct timeval boottime;
int mib[2] = {CTL_KERN, KERN_BOOTTIME};
size_t size = sizeof(boottime);
time_t now;
time_t uptime = -1;
(void)time(&now);
if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && boottime.tv_sec != 0) {
uptime = now - boottime.tv_sec;
}
return uptime;
}
@end