Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -200,10 +200,11 @@ OFInvertedCharacterSet.m \ OFLHADecompressingStream.m \ OFMutableUTF8String.m \ OFRangeCharacterSet.m \ OFSandbox.m \ + OFStrFTime.m \ OFStrPTime.m \ OFSubarray.m \ OFSubdata.m \ OFTaggedPointerColor.m \ OFTaggedPointerDate.m \ Index: src/OFASPrintF.h ================================================================== --- src/OFASPrintF.h +++ src/OFASPrintF.h @@ -27,12 +27,13 @@ OF_ASSUME_NONNULL_BEGIN #ifdef __cplusplus extern "C" { #endif +extern int OFASPrintF(char *_Nullable *_Nonnull, const char *_Nonnull, ...); extern int OFVASPrintF( char *_Nullable *_Nonnull, const char *_Nonnull, va_list); #ifdef __cplusplus } #endif OF_ASSUME_NONNULL_END Index: src/OFASPrintF.m ================================================================== --- src/OFASPrintF.m +++ src/OFASPrintF.m @@ -34,10 +34,11 @@ #ifdef OF_HAVE_SYS_TYPES_H # include #endif +#import "OFASPrintF.h" #import "OFString.h" #import "OFLocale.h" #import "OFInitializationFailedException.h" @@ -750,10 +751,23 @@ formatFlagsState, formatFieldWidthState, formatLengthModifierState, formatConversionSpecifierState }; + +int +OFASPrintF(char **string, const char *format, ...) +{ + int ret; + va_list arguments; + + va_start(arguments, format); + ret = OFVASPrintF(string, format, arguments); + va_end(arguments); + + return ret; +} int OFVASPrintF(char **string, const char *format, va_list arguments) { struct Context ctx; Index: src/OFDate.m ================================================================== --- src/OFDate.m +++ src/OFDate.m @@ -29,10 +29,11 @@ #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" @@ -510,11 +511,11 @@ return OFOrderedSame; } - (OFString *)description { - return [self dateStringWithFormat: @"%Y-%m-%dT%H:%M:%SZ"]; + return [self dateStringWithFormat: @"%Y-%m-%dT%H:%M:%S%z"]; } - (OFData *)messagePackRepresentation { void *pool = objc_autoreleasePoolPush(); @@ -690,22 +691,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; @@ -750,22 +744,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; ADDED src/OFStrFTime.h Index: src/OFStrFTime.h ================================================================== --- src/OFStrFTime.h +++ src/OFStrFTime.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2008-2023 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 + * the packaging of this file. + * + * 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. + */ + +#ifndef __STDC_LIMIT_MACROS +# define __STDC_LIMIT_MACROS +#endif +#ifndef __STDC_CONSTANT_MACROS +# define __STDC_CONSTANT_MACROS +#endif + +#include + +#import "macros.h" + +OF_ASSUME_NONNULL_BEGIN + +#ifdef __cplusplus +extern "C" { +#endif +extern size_t OFStrFTime(char *buffer, size_t bufferLen, const char *format, + struct tm *tm, short tz); +#ifdef __cplusplus +} +#endif + +OF_ASSUME_NONNULL_END ADDED src/OFStrFTime.m Index: src/OFStrFTime.m ================================================================== --- src/OFStrFTime.m +++ src/OFStrFTime.m @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2008-2023 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 + * the packaging of this file. + * + * 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. + */ + +#include "config.h" + +#include +#include +#include + +#import "OFStrFTime.h" +#import "OFASPrintF.h" +#import "macros.h" + +static const char weekDays[7][4] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" +}; +static const char monthNames[12][4] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", + "Nov", "Dec" +}; + +size_t +OFStrFTime(char *buffer, size_t bufferLen, const char *format, struct tm *tm, + short tz) +{ + enum { + stateSearchConversionSpecifier, + stateInConversionSpecifier + } state = stateSearchConversionSpecifier; + size_t j, formatLen; + + if (bufferLen == 0) + return 0; + + formatLen = strlen(format); + + j = 0; + for (size_t i = 0; i < formatLen; i++) { + switch (state) { + case stateSearchConversionSpecifier: + if (format[i] == '%') + state = stateInConversionSpecifier; + else { + if (j >= bufferLen) + return 0; + + buffer[j++] = format[i]; + } + + break; + case stateInConversionSpecifier:; + const char *appendFormat; + unsigned int value = 0; + char *append; + int appendLen; + + switch (format[i]) { + case '%': + appendFormat = "%%"; + break; + case 'a': + if (tm->tm_wday > 6) + return 0; + + appendFormat = weekDays[tm->tm_wday]; + break; + case 'b': + if (tm->tm_mon > 11) + return 0; + + appendFormat = monthNames[tm->tm_mon]; + break; + case 'd': + appendFormat = "%02u"; + value = tm->tm_mday; + break; + case 'e': + appendFormat = "%2u"; + value = tm->tm_mday; + break; + case 'H': + appendFormat = "%02u"; + value = tm->tm_hour; + break; + case 'M': + appendFormat = "%02u"; + value = tm->tm_min; + break; + case 'm': + appendFormat = "%02u"; + value = tm->tm_mon + 1; + break; + case 'n': + appendFormat = "\n"; + break; + case 'S': + appendFormat = "%02u"; + value = tm->tm_sec; + break; + case 't': + appendFormat = "\t"; + break; + case 'Y': + appendFormat = "%4u"; + value = tm->tm_year + 1900; + break; + case 'y': + appendFormat = "%02u"; + value = tm->tm_year; + + while (value > 100) + value -= 100; + + break; + case 'z': + if (tz == 0) + appendFormat = "Z"; + else if (tz >= 0) { + appendFormat = "+%04u"; + value = tz; + } else { + appendFormat = "-%04u"; + value = -tz; + } + + value = (value / 60) * 100 + (value % 60); + break; + default: + return 0; + } + + appendLen = OFASPrintF(&append, appendFormat, value); + if (appendLen < 0) + return 0; + + if (bufferLen - j < (size_t)appendLen) { + free(append); + return 0; + } + + memcpy(buffer + j, append, appendLen); + j += appendLen; + + free(append); + state = stateSearchConversionSpecifier; + } + } + + if (j >= bufferLen) + return 0; + + buffer[j] = 0; + + return j; +} Index: src/OFStrPTime.m ================================================================== --- src/OFStrPTime.m +++ src/OFStrPTime.m @@ -15,12 +15,11 @@ #include "config.h" #include -#include - +#import "OFStrPTime.h" #import "macros.h" const char * OFStrPTime(const char *buffer, const char *format, struct tm *tm, short *tz) {