@@ -1,7 +1,7 @@ /* - * Copyright (c) 2008-2022 Jonathan Schleifer + * Copyright (c) 2008-2024 Jonathan Schleifer * * All rights reserved. * * This file is part of ObjFW. It may be distributed under the terms of the * Q Public License 1.0, which can be found in the file LICENSE.QPL included in @@ -22,21 +22,23 @@ #include #include #import "OFDate.h" +#import "OFConcreteDate.h" #import "OFData.h" #import "OFDictionary.h" #import "OFMessagePackExtension.h" #ifdef OF_HAVE_THREADS # import "OFMutex.h" #endif +#import "OFStrFTime.h" #import "OFStrPTime.h" #import "OFString.h" #import "OFSystemInfo.h" +#import "OFTaggedPointerDate.h" #import "OFXMLAttribute.h" -#import "OFXMLElement.h" #import "OFInitializationFailedException.h" #import "OFInvalidArgumentException.h" #import "OFInvalidFormatException.h" #import "OFOutOfMemoryException.h" @@ -45,51 +47,40 @@ #if defined(OF_AMIGAOS_M68K) || defined(OF_MINT) /* amiga-gcc and freemint-gcc do not have trunc() */ # define trunc(x) ((int64_t)(x)) #endif -@interface OFDate () -+ (instancetype)of_alloc; +@interface OFPlaceholderDate: OFDate @end -@interface OFDateSingleton: OFDate -@end - -@interface OFDatePlaceholder: OFDateSingleton -@end - -#if defined(OF_OBJFW_RUNTIME) && UINTPTR_MAX == UINT64_MAX -@interface OFTaggedPointerDate: OFDateSingleton -@end -#endif +@interface OFConcreteDateSingleton: OFConcreteDate +@end static struct { Class isa; } placeholder; -static OFDateSingleton *zeroDate, *distantFuture, *distantPast; -#if defined(OF_OBJFW_RUNTIME) && UINTPTR_MAX == UINT64_MAX -static int dateTag; -#endif +static OFConcreteDateSingleton *zeroDate, *distantFuture, *distantPast; static void initZeroDate(void) { - zeroDate = [[OFDateSingleton alloc] initWithTimeIntervalSince1970: 0]; + zeroDate = [[OFConcreteDateSingleton alloc] + initWithTimeIntervalSince1970: 0]; } static void initDistantFuture(void) { - distantFuture = [[OFDateSingleton alloc] + distantFuture = [[OFConcreteDateSingleton alloc] initWithTimeIntervalSince1970: 64060588800.0]; } static void initDistantPast(void) { - distantPast = [[OFDateSingleton alloc] + distantPast = [[OFConcreteDateSingleton alloc] initWithTimeIntervalSince1970: -62167219200.0]; } static OFTimeInterval now(void) @@ -258,32 +249,15 @@ seconds += -(double)tz * 60; return seconds; } -@implementation OFDateSingleton -- (instancetype)autorelease -{ - return self; -} - -- (instancetype)retain -{ - return self; -} - -- (void)release -{ -} - -- (unsigned int)retainCount -{ - return OFMaxRetainCount; -} +@implementation OFConcreteDateSingleton +OF_SINGLETON_METHODS @end -@implementation OFDatePlaceholder +@implementation OFPlaceholderDate #ifdef __clang__ /* We intentionally don't call into super, so silence the warning. */ # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wunknown-pragmas" # pragma clang diagnostic ignored "-Wobjc-designated-initializers" @@ -304,38 +278,28 @@ value = OFFromBigEndian64(OFDoubleToRawUInt64(OFToBigEndianDouble( seconds))); /* Almost all dates fall into this range. */ if (value & (UINT64_C(4) << 60)) { - id ret = objc_createTaggedPointer(dateTag, - value & ~(UINT64_C(4) << 60)); + id ret = [OFTaggedPointerDate + dateWithUInt64TimeIntervalSince1970: value]; if (ret != nil) return ret; } #endif - return (id)[[OFDate of_alloc] initWithTimeIntervalSince1970: seconds]; + return (id)[[OFConcreteDate alloc] + initWithTimeIntervalSince1970: seconds]; } #ifdef __clang__ # pragma clang diagnostic pop #endif -@end - -#if defined(OF_OBJFW_RUNTIME) && UINTPTR_MAX == UINT64_MAX -@implementation OFTaggedPointerDate -- (OFTimeInterval)timeIntervalSince1970 -{ - uint64_t value = (uint64_t)object_getTaggedPointerValue(self); - - value |= UINT64_C(4) << 60; - - return OFFromBigEndianDouble(OFRawUInt64ToDouble(OFToBigEndian64( - value))); -} -@end -#endif + +OF_SINGLETON_METHODS +@end + @implementation OFDate + (void)initialize { #ifdef OF_WINDOWS @@ -343,32 +307,23 @@ #endif if (self != [OFDate class]) return; - placeholder.isa = [OFDatePlaceholder class]; + object_setClass((id)&placeholder, [OFPlaceholderDate class]); #if (!defined(HAVE_GMTIME_R) || !defined(HAVE_LOCALTIME_R)) && \ defined(OF_HAVE_THREADS) mutex = [[OFMutex alloc] init]; atexit(releaseMutex); #endif #ifdef OF_WINDOWS - if ((module = LoadLibrary("msvcrt.dll")) != NULL) + if ((module = GetModuleHandle("msvcrt.dll")) != NULL) _mktime64FuncPtr = (__time64_t (*)(struct tm *)) GetProcAddress(module, "_mktime64"); #endif - -#if defined(OF_OBJFW_RUNTIME) && UINTPTR_MAX == UINT64_MAX - dateTag = objc_registerTaggedPointerClass([OFTaggedPointerDate class]); -#endif -} - -+ (instancetype)of_alloc -{ - return [super alloc]; } + (instancetype)alloc { if (self == [OFDate class]) @@ -427,15 +382,22 @@ return [self initWithTimeIntervalSince1970: now()]; } - (instancetype)initWithTimeIntervalSince1970: (OFTimeInterval)seconds { - self = [super init]; + if ([self isMemberOfClass: [OFDate class]]) { + @try { + [self doesNotRecognizeSelector: _cmd]; + } @catch (id e) { + [self release]; + @throw e; + } - _seconds = seconds; + abort(); + } - return self; + return [super init]; } - (instancetype)initWithTimeIntervalSinceNow: (OFTimeInterval)seconds { return [self initWithTimeIntervalSince1970: now() + seconds]; @@ -494,43 +456,10 @@ objc_autoreleasePoolPop(pool); return [self initWithTimeIntervalSince1970: seconds]; } -- (instancetype)initWithSerialization: (OFXMLElement *)element -{ - OFTimeInterval seconds; - - @try { - void *pool = objc_autoreleasePoolPush(); - unsigned long long value; - - if (![element.name isEqual: @"OFDate"] || - ![element.namespace isEqual: OFSerializationNS]) - @throw [OFInvalidArgumentException exception]; - - if (![[element attributeForName: @"encoding"].stringValue - isEqual: @"hex"]) - @throw [OFInvalidFormatException exception]; - - value = [element unsignedLongLongValueWithBase: 16]; - - if (value > UINT64_MAX) - @throw [OFOutOfRangeException exception]; - - seconds = OFFromBigEndianDouble(OFRawUInt64ToDouble( - OFToBigEndian64(value))); - - objc_autoreleasePoolPop(pool); - } @catch (id e) { - [self release]; - @throw e; - } - - return [self initWithTimeIntervalSince1970: seconds]; -} - - (bool)isEqual: (id)object { OFDate *otherDate; if (object == self) @@ -582,31 +511,11 @@ return OFOrderedSame; } - (OFString *)description { - return [self dateStringWithFormat: @"%Y-%m-%dT%H:%M:%SZ"]; -} - -- (OFXMLElement *)XMLElementBySerializing -{ - void *pool = objc_autoreleasePoolPush(); - OFXMLElement *element; - - element = [OFXMLElement elementWithName: @"OFDate" - namespace: OFSerializationNS]; - - [element addAttributeWithName: @"encoding" stringValue: @"hex"]; - element.stringValue = [OFString stringWithFormat: @"%016" PRIx64, - OFFromBigEndian64(OFDoubleToRawUInt64(OFToBigEndianDouble( - self.timeIntervalSince1970)))]; - - [element retain]; - - objc_autoreleasePoolPop(pool); - - return [element autorelease]; + return [self dateStringWithFormat: @"%Y-%m-%dT%H:%M:%S%z"]; } - (OFData *)messagePackRepresentation { void *pool = objc_autoreleasePoolPush(); @@ -748,15 +657,11 @@ OFString *ret; OFTimeInterval 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(timeInterval)) @throw [OFOutOfRangeException exception]; #ifdef HAVE_GMTIME_R @@ -782,22 +687,15 @@ #endif pageSize = [OFSystemInfo pageSize]; buffer = OFAllocMemory(1, pageSize); @try { -#ifndef OF_WINDOWS - if (strftime(buffer, pageSize, format.UTF8String, &tm) == 0) + if (OFStrFTime(buffer, pageSize, format.UTF8String, &tm, + 0) == 0) @throw [OFOutOfRangeException exception]; ret = [OFString stringWithUTF8String: buffer]; -#else - if (wcsftime(buffer, pageSize / sizeof(wchar_t), - format.UTF16String, &tm) == 0) - @throw [OFOutOfRangeException exception]; - - ret = [OFString stringWithUTF16String: buffer]; -#endif } @finally { OFFreeMemory(buffer); } return ret; @@ -808,15 +706,11 @@ OFString *ret; OFTimeInterval 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(timeInterval)) @throw [OFOutOfRangeException exception]; #ifdef HAVE_LOCALTIME_R @@ -842,22 +736,15 @@ #endif pageSize = [OFSystemInfo pageSize]; buffer = OFAllocMemory(1, pageSize); @try { -#ifndef OF_WINDOWS - if (strftime(buffer, pageSize, format.UTF8String, &tm) == 0) + if (OFStrFTime(buffer, pageSize, format.UTF8String, &tm, + 0) == 0) @throw [OFOutOfRangeException exception]; ret = [OFString stringWithUTF8String: buffer]; -#else - if (wcsftime(buffer, pageSize / sizeof(wchar_t), - format.UTF16String, &tm) == 0) - @throw [OFOutOfRangeException exception]; - - ret = [OFString stringWithUTF16String: buffer]; -#endif } @finally { OFFreeMemory(buffer); } return ret; @@ -885,11 +772,11 @@ return self; } - (OFTimeInterval)timeIntervalSince1970 { - return _seconds; + OF_UNRECOGNIZED_SELECTOR } - (OFTimeInterval)timeIntervalSinceDate: (OFDate *)otherDate { return self.timeIntervalSince1970 - otherDate.timeIntervalSince1970;