Index: src/OFDate.h ================================================================== --- src/OFDate.h +++ src/OFDate.h @@ -27,11 +27,13 @@ /*! * @class OFDate OFDate.h ObjFW/OFDate.h * * @brief A class for storing, accessing and comparing dates. */ +#ifndef OF_DATE_M OF_SUBCLASSING_RESTRICTED +#endif @interface OFDate: OFObject { of_time_interval_t _seconds; } Index: src/OFDate.m ================================================================== --- src/OFDate.m +++ src/OFDate.m @@ -12,10 +12,12 @@ * Alternatively, it may be distributed under the terms of the GNU General * Public License, either version 2 or 3, which can be found in the file * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this * file. */ + +#define OF_DATE_M #include "config.h" #include #include @@ -43,10 +45,40 @@ #ifdef OF_AMIGAOS_M68K /* amiga-gcc does not have trunc() */ # define trunc(x) ((int64_t)(x)) #endif + +@interface OFDate () ++ (instancetype)of_alloc; +@end + +@interface OFDatePlaceholder: OFDate +@end + +@interface OFDateSingleton: OFDate +@end + +static struct { + Class isa; +} placeholder; + +static OFDateSingleton *distantFuture, *distantPast; + +static void +initDistantFuture(void) +{ + distantFuture = [[OFDateSingleton alloc] + initWithTimeIntervalSince1970: 64060588800.0]; +} + +static void +initDistantPast(void) +{ + distantPast = [[OFDateSingleton alloc] + initWithTimeIntervalSince1970: -62167219200.0]; +} #if (!defined(HAVE_GMTIME_R) || !defined(HAVE_LOCALTIME_R)) && \ defined(OF_HAVE_THREADS) static OFMutex *mutex; #endif @@ -55,38 +87,41 @@ static __time64_t (*func__mktime64)(struct tm *); #endif #ifdef HAVE_GMTIME_R # define GMTIME_RET(field) \ - time_t seconds = (time_t)_seconds; \ + of_time_interval_t timeInterval = self.timeIntervalSince1970; \ + time_t seconds = (time_t)timeInterval; \ struct tm tm; \ \ - if (seconds != trunc(_seconds)) \ + if (seconds != trunc(timeInterval)) \ @throw [OFOutOfRangeException exception]; \ \ if (gmtime_r(&seconds, &tm) == NULL) \ @throw [OFOutOfRangeException exception]; \ \ return tm.field; # define LOCALTIME_RET(field) \ - time_t seconds = (time_t)_seconds; \ + of_time_interval_t timeInterval = self.timeIntervalSince1970; \ + time_t seconds = (time_t)timeInterval; \ struct tm tm; \ \ - if (seconds != trunc(_seconds)) \ + if (seconds != trunc(timeInterval)) \ @throw [OFOutOfRangeException exception]; \ \ if (localtime_r(&seconds, &tm) == NULL) \ @throw [OFOutOfRangeException exception]; \ \ return tm.field; #else # ifdef OF_HAVE_THREADS # define GMTIME_RET(field) \ - time_t seconds = (time_t)_seconds; \ + of_time_interval_t timeInterval = self.timeIntervalSince1970; \ + time_t seconds = (time_t)timeInterval; \ struct tm *tm; \ \ - if (seconds != trunc(_seconds)) \ + if (seconds != trunc(timeInterval)) \ @throw [OFOutOfRangeException exception]; \ \ [mutex lock]; \ \ @try { \ @@ -96,14 +131,15 @@ return tm->field; \ } @finally { \ [mutex unlock]; \ } # define LOCALTIME_RET(field) \ - time_t seconds = (time_t)_seconds; \ + of_time_interval_t timeInterval = self.timeIntervalSince1970; \ + time_t seconds = (time_t)timeInterval; \ struct tm *tm; \ \ - if (seconds != trunc(_seconds)) \ + if (seconds != trunc(timeInterval)) \ @throw [OFOutOfRangeException exception]; \ \ [mutex lock]; \ \ @try { \ @@ -114,25 +150,27 @@ } @finally { \ [mutex unlock]; \ } # else # define GMTIME_RET(field) \ - time_t seconds = (time_t)_seconds; \ + of_time_interval_t timeInterval = self.timeIntervalSince1970; \ + time_t seconds = (time_t)timeInterval; \ struct tm *tm; \ \ - if (seconds != trunc(_seconds)) \ + if (seconds != trunc(timeInterval)) \ @throw [OFOutOfRangeException exception]; \ \ if ((tm = gmtime(&seconds)) == NULL) \ @throw [OFOutOfRangeException exception]; \ \ return tm->field; # define LOCALTIME_RET(field) \ - time_t seconds = (time_t)_seconds; \ + of_time_interval_t timeInterval = self.timeIntervalSince1970; \ + time_t seconds = (time_t)timeInterval; \ struct tm *tm; \ \ - if (seconds != trunc(_seconds)) \ + if (seconds != trunc(timeInterval)) \ @throw [OFOutOfRangeException exception]; \ \ if ((tm = localtime(&seconds)) == NULL) \ @throw [OFOutOfRangeException exception]; \ \ @@ -186,10 +224,67 @@ /* Time zone */ seconds += -(double)*tz * 60; return seconds; } + +@implementation OFDatePlaceholder +- (instancetype)init +{ + return (id)[[OFDate of_alloc] init]; +} + +- (instancetype)initWithTimeIntervalSince1970: (of_time_interval_t)seconds +{ + return (id)[[OFDate of_alloc] initWithTimeIntervalSince1970: seconds]; +} + +- (instancetype)initWithTimeIntervalSinceNow: (of_time_interval_t)seconds +{ + return (id)[[OFDate of_alloc] initWithTimeIntervalSinceNow: seconds]; +} + +- (instancetype)initWithDateString: (OFString *)string + format: (OFString *)format +{ + return (id)[[OFDate of_alloc] initWithDateString: string + format: format]; +} + +- (instancetype)initWithLocalDateString: (OFString *)string + format: (OFString *)format +{ + return (id)[[OFDate of_alloc] initWithLocalDateString: string + format: format]; +} + +- (instancetype)initWithSerialization: (OFXMLElement *)element +{ + return (id)[[OFDate of_alloc] initWithSerialization: element]; +} +@end + +@implementation OFDateSingleton +- (instancetype)autorelease +{ + return self; +} + +- (instancetype)retain +{ + return self; +} + +- (void)release +{ +} + +- (unsigned int)retainCount +{ + return OF_RETAIN_COUNT_MAX; +} +@end @implementation OFDate + (void)initialize { #ifdef OF_WINDOWS @@ -196,10 +291,12 @@ HMODULE module; #endif if (self != [OFDate class]) return; + + placeholder.isa = [OFDatePlaceholder class]; #if (!defined(HAVE_GMTIME_R) || !defined(HAVE_LOCALTIME_R)) && \ defined(OF_HAVE_THREADS) mutex = [[OFMutex alloc] init]; #endif @@ -208,10 +305,23 @@ if ((module = LoadLibrary("msvcrt.dll")) != NULL) func__mktime64 = (__time64_t (*)(struct tm *)) GetProcAddress(module, "_mktime64"); #endif } + ++ (instancetype)of_alloc +{ + return [super alloc]; +} + ++ (instancetype)alloc +{ + if (self == [OFDate class]) + return (id)&placeholder; + + return [super alloc]; +} + (instancetype)date { return [[[self alloc] init] autorelease]; } @@ -242,18 +352,20 @@ format: format] autorelease]; } + (instancetype)distantFuture { - return [[[self alloc] - initWithTimeIntervalSince1970: 64060588800.0] autorelease]; + static of_once_t once = OF_ONCE_INIT; + of_once(&once, initDistantFuture); + return distantFuture; } + (instancetype)distantPast { - return [[[self alloc] - initWithTimeIntervalSince1970: -62167219200.0] autorelease]; + static of_once_t once = OF_ONCE_INIT; + of_once(&once, initDistantPast); + return distantPast; } - (instancetype)init { struct timeval t; @@ -389,11 +501,11 @@ if (![object isKindOfClass: [OFDate class]]) return false; otherDate = object; - if (otherDate->_seconds != _seconds) + if (otherDate.timeIntervalSince1970 != self.timeIntervalSince1970) return false; return true; } @@ -402,11 +514,11 @@ uint32_t hash; double tmp; OF_HASH_INIT(hash); - tmp = OF_BSWAP_DOUBLE_IF_BE(_seconds); + tmp = OF_BSWAP_DOUBLE_IF_BE(self.timeIntervalSince1970); for (size_t i = 0; i < sizeof(double); i++) OF_HASH_ADD(hash, ((char *)&tmp)[i]); OF_HASH_FINALIZE(hash); @@ -426,13 +538,13 @@ if (![(id)object isKindOfClass: [OFDate class]]) @throw [OFInvalidArgumentException exception]; otherDate = (OFDate *)object; - if (_seconds < otherDate->_seconds) + if (self.timeIntervalSince1970 < otherDate.timeIntervalSince1970) return OF_ORDERED_ASCENDING; - if (_seconds > otherDate->_seconds) + if (self.timeIntervalSince1970 > otherDate.timeIntervalSince1970) return OF_ORDERED_DESCENDING; return OF_ORDERED_SAME; } @@ -449,11 +561,11 @@ element = [OFXMLElement elementWithName: self.className namespace: OF_SERIALIZATION_NS]; element.stringValue = [OFString stringWithFormat: @"%016" PRIx64, OF_BSWAP64_IF_LE(OF_DOUBLE_TO_INT_RAW(OF_BSWAP_DOUBLE_IF_LE( - _seconds)))]; + self.timeIntervalSince1970)))]; [element retain]; objc_autoreleasePoolPop(pool); @@ -461,12 +573,14 @@ } - (OFData *)messagePackRepresentation { void *pool = objc_autoreleasePoolPush(); - int64_t seconds = (int64_t)_seconds; - uint32_t nanoseconds = (_seconds - trunc(_seconds)) * 1000000000; + of_time_interval_t timeInterval = self.timeIntervalSince1970; + int64_t seconds = (int64_t)timeInterval; + uint32_t nanoseconds = + (timeInterval - trunc(timeInterval)) * 1000000000; OFData *ret; if (seconds >= 0 && seconds < 0x400000000) { if (seconds <= UINT32_MAX && nanoseconds == 0) { uint32_t seconds32 = (uint32_t)seconds; @@ -515,11 +629,13 @@ return [ret autorelease]; } - (uint32_t)microsecond { - return (uint32_t)((_seconds - trunc(_seconds)) * 1000000); + of_time_interval_t timeInterval = self.timeIntervalSince1970; + + return (uint32_t)((timeInterval - trunc(timeInterval)) * 1000000); } - (uint8_t)second { GMTIME_RET(tm_sec) @@ -596,20 +712,21 @@ } - (OFString *)dateStringWithFormat: (OFConstantString *)format { OFString *ret; - time_t seconds = (time_t)_seconds; + of_time_interval_t timeInterval = self.timeIntervalSince1970; + time_t seconds = (time_t)timeInterval; struct tm tm; size_t pageSize; #ifndef OF_WINDOWS char *buffer; #else wchar_t *buffer; #endif - if (seconds != trunc(_seconds)) + if (seconds != trunc(timeInterval)) @throw [OFOutOfRangeException exception]; #ifdef HAVE_GMTIME_R if (gmtime_r(&seconds, &tm) == NULL) @throw [OFOutOfRangeException exception]; @@ -656,20 +773,21 @@ } - (OFString *)localDateStringWithFormat: (OFConstantString *)format { OFString *ret; - time_t seconds = (time_t)_seconds; + of_time_interval_t timeInterval = self.timeIntervalSince1970; + time_t seconds = (time_t)timeInterval; struct tm tm; size_t pageSize; #ifndef OF_WINDOWS char *buffer; #else wchar_t *buffer; #endif - if (seconds != trunc(_seconds)) + if (seconds != trunc(timeInterval)) @throw [OFOutOfRangeException exception]; #ifdef HAVE_LOCALTIME_R if (localtime_r(&seconds, &tm) == NULL) @throw [OFOutOfRangeException exception]; @@ -742,11 +860,11 @@ return _seconds; } - (of_time_interval_t)timeIntervalSinceDate: (OFDate *)otherDate { - return _seconds - otherDate->_seconds; + return self.timeIntervalSince1970 - otherDate.timeIntervalSince1970; } - (of_time_interval_t)timeIntervalSinceNow { struct timeval t; @@ -755,13 +873,14 @@ OF_ENSURE(gettimeofday(&t, NULL) == 0); seconds = t.tv_sec; seconds += (of_time_interval_t)t.tv_usec / 1000000; - return _seconds - seconds; + return self.timeIntervalSince1970 - seconds; } - (OFDate *)dateByAddingTimeInterval: (of_time_interval_t)seconds { - return [OFDate dateWithTimeIntervalSince1970: _seconds + seconds]; + return [OFDate dateWithTimeIntervalSince1970: + self.timeIntervalSince1970 + seconds]; } @end