Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -343,10 +343,13 @@ AC_CHECK_LIB(ws2_32, main, LIBS="$LIBS -lws2_32") AC_CHECK_FUNC(gmtime_r, [ AC_DEFINE(HAVE_GMTIME_R, 1, [Whether we have gmtime_r]) ]) +AC_CHECK_FUNC(localtime_r, [ + AC_DEFINE(HAVE_LOCALTIME_R, 1, [Whether we have localtime_r]) +]) AC_CHECK_HEADER(poll.h, [ AC_DEFINE(OF_HAVE_POLL, 1, [Whether poll is supported]) ]) AC_CHECK_HEADERS(sys/select.h, [ Index: src/OFDate.h ================================================================== --- src/OFDate.h +++ src/OFDate.h @@ -67,40 +67,55 @@ * \return An initialized OFDate with the specified date and time */ - initWithTimeIntervalSince1970: (time_t)sec microseconds: (suseconds_t)usec; +/** + * \return The microsecond of the date + */ +- (suseconds_t)microsecond; + /** * \return The seconds of the date */ -- (int)seconds; - -/** - * \return The microseconds of the date - */ -- (suseconds_t)microseconds; - -/** - * \return The minutes of the date - */ -- (int)minutes; - -/** - * \return The hours of the date - */ -- (int)hours; +- (int)second; + +/** + * \return The minute of the date + */ +- (int)minute; + +/** + * \return The hour of the date + */ +- (int)hour; + +/** + * \return The hour of the date in local time + */ +- (int)localHour; /** * \return The day of the month of the date */ - (int)dayOfMonth; +/** + * \return The day of the month of the date in local time + */ +- (int)localDayOfMonth; + /** * \return The month of the year of the date */ - (int)monthOfYear; +/** + * \return The month of the year of the date in local time + */ +- (int)localMonthOfYear; + /** * \return The year of the date */ - (int)year; @@ -107,20 +122,40 @@ /** * \return The day of the week of the date */ - (int)dayOfWeek; +/** + * \return The day of the week of the date in local time + */ +- (int)localDayOfWeek; + /** * \return The day of the year of the date */ - (int)dayOfYear; +/** + * \return The day of the year of the date in local time + */ +- (int)localDayOfYear; + /** * Creates a string of the date with the specified format. * * See the manpage for strftime for information on the format. * * \param fmt The format for the date string * \return A new, autoreleased OFString */ -- (OFString*)stringWithFormat: (OFString*)fmt; +- (OFString*)dateStringWithFormat: (OFString*)fmt; + +/** + * Creates a string of the local date with the specified format. + * + * See the manpage for strftime for information on the format. + * + * \param fmt The format for the date string + * \return A new, autoreleased OFString + */ +- (OFString*)localDateStringWithFormat: (OFString*)fmt; @end Index: src/OFDate.m ================================================================== --- src/OFDate.m +++ src/OFDate.m @@ -18,11 +18,12 @@ #import "OFDate.h" #import "OFString.h" #import "OFAutoreleasePool.h" #import "OFExceptions.h" -#if !defined(HAVE_GMTIME_R) && defined(OF_THREADS) +#if (!defined(HAVE_GMTIME_R) || !defined(HAVE_LOCALTIME_R)) && \ + defined(OF_THREADS) # import "OFThread.h" static OFMutex *mutex; #endif @@ -31,10 +32,17 @@ struct tm tm; \ \ if (gmtime_r(&sec, &tm) == NULL) \ @throw [OFOutOfRangeException newWithClass: isa]; \ \ + return tm.field; +# define LOCALTIME_RET(field) \ + struct tm tm; \ + \ + if (localtime_r(&sec, &tm) == NULL) \ + @throw [OFOutOfRangeException newWithClass: isa]; \ + \ return tm.field; #else # ifdef OF_THREADS # define GMTIME_RET(field) \ struct tm *tm; \ @@ -47,23 +55,44 @@ \ return tm->field; \ } @finally { \ [mutex unlock]; \ } +# define LOCALTIME_RET(field) \ + struct tm *tm; \ + \ + [mutex lock]; \ + \ + @try { \ + if ((tm = localtime(&sec)) == NULL) \ + @throw [OFOutOfRangeException newWithClass: isa]; \ + \ + return tm->field; \ + } @finally { \ + [mutex unlock]; \ + } # else # define GMTIME_RET(field) \ struct tm *tm; \ \ if ((tm = gmtime(&sec)) == NULL) \ @throw [OFOutOfRangeException newWithClass: isa]; \ \ return tm->field; +# define LOCALTIME_RET(field) \ + struct tm *tm; \ + \ + if ((tm = localtime(&sec)) == NULL) \ + @throw [OFOutOfRangeException newWithClass: isa]; \ + \ + return tm->field; # endif #endif @implementation OFDate -#if !defined(HAVE_GMTIME_R) && defined(OF_THREADS) +#if (!defined(HAVE_GMTIME_R) || !defined(HAVE_LOCALTIME_R)) && \ + defined(OF_THREADS) + (void)initialize { if (self == [OFDate class]) mutex = [[OFMutex alloc] init]; } @@ -154,11 +183,11 @@ - (OFString*)description { OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; OFString *tmp, *ret; - tmp = [self stringWithFormat: @"%Y-%m-%dT%H:%M:%S"]; + tmp = [self dateStringWithFormat: @"%Y-%m-%dT%H:%M:%S"]; if (usec == 0) ret = [OFString stringWithFormat: @"%sZ", [tmp cString]]; else ret = [OFString stringWithFormat: @"%s.%06dZ", [tmp cString], @@ -168,39 +197,54 @@ [pool release]; return [ret autorelease]; } -- (int)seconds +- (suseconds_t)microsecond +{ + return usec; +} + +- (int)second { GMTIME_RET(tm_sec) } -- (suseconds_t)microseconds -{ - return usec; -} - -- (int)minutes +- (int)minute { GMTIME_RET(tm_min) } -- (int)hours +- (int)hour { GMTIME_RET(tm_hour) } + +- (int)localHour +{ + LOCALTIME_RET(tm_hour) +} - (int)dayOfMonth { GMTIME_RET(tm_mday) } + +- (int)localDayOfMonth +{ + LOCALTIME_RET(tm_mday) +} - (int)monthOfYear { GMTIME_RET(tm_mon + 1) } + +- (int)localMonthOfYear +{ + LOCALTIME_RET(tm_mon + 1) +} - (int)year { GMTIME_RET(tm_year + 1900) } @@ -207,17 +251,27 @@ - (int)dayOfWeek { GMTIME_RET(tm_wday) } + +- (int)localDayOfWeek +{ + LOCALTIME_RET(tm_wday) +} - (int)dayOfYear { GMTIME_RET(tm_yday + 1) } -- (OFString*)stringWithFormat: (OFString*)fmt +- (int)localDayOfYear +{ + LOCALTIME_RET(tm_yday + 1) +} + +- (OFString*)dateStringWithFormat: (OFString*)fmt { struct tm tm; char *buf; #ifdef HAVE_GMTIME_R @@ -232,10 +286,49 @@ struct tm *tmp; if ((tmp = gmtime(&sec)) == NULL) @throw [OFOutOfRangeException newWithClass: isa]; + tm = *tmp; +# ifdef OF_THREADS + } @finally { + [mutex unlock]; + } +# endif +#endif + + buf = [self allocMemoryWithSize: of_pagesize]; + + @try { + if (!strftime(buf, of_pagesize, [fmt cString], &tm)) + @throw [OFOutOfRangeException newWithClass: isa]; + + return [OFString stringWithCString: buf]; + } @finally { + [self freeMemory: buf]; + } +} + +- (OFString*)localDateStringWithFormat: (OFString*)fmt +{ + struct tm tm; + char *buf; + +#ifdef HAVE_LOCALTIME_R + if (localtime_r(&sec, &tm) == NULL) + @throw [OFOutOfRangeException newWithClass: isa]; +#else +# ifdef OF_THREADS + [mutex lock]; + + @try { +# endif + struct tm *tmp; + + if ((tmp = localtime(&sec)) == NULL) + @throw [OFOutOfRangeException newWithClass: isa]; + tm = *tmp; # ifdef OF_THREADS } @finally { [mutex unlock]; } Index: tests/OFDateTests.m ================================================================== --- tests/OFDateTests.m +++ tests/OFDateTests.m @@ -41,18 +41,18 @@ ![d1 isEqual: [OFDate dateWithTimeIntervalSince1970: 0 microseconds: 1]]) TEST(@"-[compare:]", [d1 compare: d2] == OF_ORDERED_ASCENDING) - TEST(@"-[seconds]", [d1 seconds] == 0 && [d2 seconds] == 5) - - TEST(@"-[microseconds]", - [d1 microseconds] == 0 && [d2 microseconds] == 1) - - TEST(@"-[minutes]", [d1 minutes] == 0 && [d2 minutes] == 0) - - TEST(@"-[hours]", [d1 hours] == 0 && [d2 hours] == 1) + TEST(@"-[seconds]", [d1 second] == 0 && [d2 second] == 5) + + TEST(@"-[microsecond]", + [d1 microsecond] == 0 && [d2 microsecond] == 1) + + TEST(@"-[minute]", [d1 minute] == 0 && [d2 minute] == 0) + + TEST(@"-[hour]", [d1 hour] == 0 && [d2 hour] == 1) TEST(@"-[dayOfMonth]", [d1 dayOfMonth] == 1 && [d2 dayOfMonth] == 2) TEST(@"-[monthOfYear]", [d1 monthOfYear] == 1 && [d2 monthOfYear] == 1)