ObjFW  OFDate.m at [b97bb55c50]

File src/OFDate.m artifact 52c00039c4 part of check-in b97bb55c50


/*
 * Copyright (c) 2008 - 2010
 *   Jonathan Schleifer <js@webkeks.org>
 *
 * 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 included in
 * the packaging of this file.
 */

#include "config.h"

#include <time.h>

#include <sys/time.h>

#import "OFDate.h"
#import "OFString.h"
#import "OFExceptions.h"

#if !defined(HAVE_GMTIME_R) && defined(OF_THREADS)
# import "OFThread.h"

static OFMutex *mutex;
#endif

#ifdef HAVE_GMTIME_R
# define GMTIME_RET(field)						  \
	struct tm tm;							  \
									  \
	if (gmtime_r(&sec, &tm) == NULL)				  \
		@throw [OFOutOfRangeException newWithClass: isa];	  \
									  \
	return tm.field;
#else
# ifdef OF_THREADS
#  define GMTIME_RET(field)						  \
	struct tm *tm;							  \
									  \
	[mutex lock];							  \
									  \
	@try {								  \
		if ((tm = gmtime(&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;
# endif
#endif

@implementation OFDate
#if !defined(HAVE_GMTIME_R) && defined(OF_THREADS)
+ (void)initialize
{
	if (self == [OFDate class])
		mutex = [[OFMutex alloc] init];
}
#endif

+ date
{
	return [[[self alloc] init] autorelease];
}

+ dateWithTimeIntervalSince1970: (time_t)sec
{
	return [[[self alloc] initWithTimeIntervalSince1970: sec] autorelease];
}

+ dateWithTimeIntervalSince1970: (time_t)sec
		   microseconds: (suseconds_t)usec
{
	return [[[self alloc] initWithTimeIntervalSince1970: sec
					       microseconds: usec] autorelease];
}

- init
{
	struct timeval t;

	if (gettimeofday(&t, NULL)) {
		Class c = isa;
		[self release];
		@throw [OFInitializationFailedException newWithClass: c];
	}

	return [self initWithTimeIntervalSince1970: t.tv_sec
				      microseconds: t.tv_usec];
}

- initWithTimeIntervalSince1970: (time_t)sec_
{
	return [self initWithTimeIntervalSince1970: sec_
				      microseconds: 0];
}

- initWithTimeIntervalSince1970: (time_t)sec_
		   microseconds: (suseconds_t)usec_
{
	self = [super init];

	sec = sec_;
	usec = usec_;

	return self;
}

- (BOOL)isEqual: (id)obj
{
	if (![obj isKindOfClass: [OFDate class]])
		return NO;
	if (((OFDate*)obj)->sec != sec || ((OFDate*)obj)->usec != usec)
		return NO;

	return YES;
}

- copy
{
	return [self retain];
}

- (of_comparison_result_t)compare: (id)obj
{
	if (![obj isKindOfClass: [OFDate class]])
		@throw [OFInvalidArgumentException newWithClass: isa
						       selector: _cmd];

	if (sec < ((OFDate*)obj)->sec)
		return OF_ORDERED_ASCENDING;
	if (sec > ((OFDate*)obj)->sec)
		return OF_ORDERED_DESCENDING;

	if (usec < ((OFDate*)obj)->usec)
		return OF_ORDERED_ASCENDING;
	if (usec > ((OFDate*)obj)->usec)
		return OF_ORDERED_DESCENDING;

	return OF_ORDERED_SAME;
}

- (OFString*)description
{
	char str[20];	/* YYYY-MM-DD hh:mm:ss */

#ifdef HAVE_GMTIME_R
	struct tm tm;

	if (gmtime_r(&sec, &tm) == NULL)
		@throw [OFOutOfRangeException newWithClass: isa];

	strftime(str, 20, "%Y-%m-%dT%H:%M:%S", &tm);
#else
	struct tm *tm;

# ifdef OF_THREADS
	[mutex lock];

	@try {
# endif
		if ((tm = gmtime(&sec)) == NULL)
			@throw [OFOutOfRangeException newWithClass: isa];

		strftime(str, 20, "%Y-%m-%dT%H:%M:%S", tm);
# ifdef OF_THREADS
	} @finally {
		[mutex unlock];
	}
# endif
#endif

	if (usec == 0)
		return [OFString stringWithFormat: @"%sZ", str];

	return [OFString stringWithFormat: @"%s.%06dZ", str, usec];
}

- (int)seconds
{
	GMTIME_RET(tm_sec)
}

- (suseconds_t)microseconds
{
	return usec;
}

- (int)minutes
{
	GMTIME_RET(tm_min)
}

- (int)hours
{
	GMTIME_RET(tm_hour)
}

- (int)dayOfMonth
{
	GMTIME_RET(tm_mday)
}

- (int)monthOfYear
{
	GMTIME_RET(tm_mon + 1)
}

- (int)year
{
	GMTIME_RET(tm_year + 1900)
}

- (int)dayOfWeek
{
	GMTIME_RET(tm_wday)
}

- (int)dayOfYear
{
	GMTIME_RET(tm_yday + 1)
}
@end