//	Zinc Application Framework - Z_UTIME.CPP
//	COPYRIGHT (C) 1990-1999.  All Rights Reserved.
//	Zinc Software Incorporated.  Pleasant Grove, Utah  USA

#include <time.h>
#include <z_ctype.hpp>
#include <z_stdlib.hpp>
#include <z_stdarg.hpp>
#include <z_string.hpp>
#include <z_utime.hpp>
#if defined(ZAF_MOTIF)
#  if defined(__linux__) && defined(__i386__)
	extern "C" int gettimeofday(struct timeval *tp, struct timezone *tzp);
#	endif
#	include <sys/time.h>
#elif defined(ZAF_INDEPENDENT) || defined(ZAF_MSWINDOWS)
#	if defined(__IBMCPP__)
#		include <sys/timeb.h>
#	elif defined(_MSC_VER)
#		include <sys/types.h>
#		include <sys/timeb.h>
#	elif defined(ZAF_VXWORKS)
#		include <sys/times.h>
#	elif defined(ZAF_PSOS)
#	else
#		include <dos.h>
#	endif
#endif

#include <z_app.hpp>
#define ZAF_UTIME_DATA_INFO
#include "lang_def.cpp"
#include "gbl_def.cpp"

// The documentation defines the basis year to start from Jan. 1, 1950.
#define ZAF_ROOT_YEAR 1950

// ----- ZafUTimeData -------------------------------------------------------

// We don't really support leap seconds.
const long uSecsPerDay = (24L * 60L * 60L * 1000L);
static int ZAF_FARDATA _daysInMonth[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static ZafIChar *_dFormat = ZAF_ITEXT("%m/%d/%Y");
static ZafIChar *_rFormat = ZAF_ITEXT("%H:%M");
static ZafIChar *_tFormat = ZAF_ITEXT("%H:%M:%S");
static struct
{
	ZafIChar *name;
	int offset;
} _timeZones[] =
{
	{ ZAF_NULLP(ZafIChar), 0 }
};

ZafUTimeData::ZafUTimeData(void) :
	ZafFormatData(), basisYear(ZAF_ROOT_YEAR), zoneOffset(0), jDay(0),
	uSec(0), recurse(false)
{
	// Check the printf binding.
	Bind();

	// Get the messages.
	dayStrings = ZafLanguageManager::Allocate(dayName, defaultDayStrings);
	monthStrings = ZafLanguageManager::Allocate(monthName, defaultMonthStrings);
	timeStrings = ZafLanguageManager::Allocate(timeName, defaultTimeStrings);

	// Clear the utime from the system.
	GetSystemTime();
//	Clear();
}

ZafUTimeData::ZafUTimeData(ZafUInt32 seconds) :
	ZafFormatData(), basisYear(ZAF_ROOT_YEAR), zoneOffset(0), jDay(0),
	uSec(0), recurse(false)
{
	// Check the printf binding.
	Bind();

	// Get the messages.
	dayStrings = ZafLanguageManager::Allocate(dayName, defaultDayStrings);
	monthStrings = ZafLanguageManager::Allocate(monthName, defaultMonthStrings);
	timeStrings = ZafLanguageManager::Allocate(timeName, defaultTimeStrings);

	// Clear the utime from the system.
//	Clear();

	// Get the utime based on separate values.
	SetUTime(ZAF_ROOT_YEAR, 1, 1, 0, 0, 0, 0);
	SetUTime(seconds);
}

ZafUTimeData::ZafUTimeData(int year, int month, int day,
	int hour, int minute, int second, int milliSecond) :
	ZafFormatData(), basisYear(ZAF_ROOT_YEAR), zoneOffset(0), jDay(0),
	uSec(0), recurse(false)
{
	// Check the printf binding.
	Bind();

	// Get the messages.
	dayStrings = ZafLanguageManager::Allocate(dayName, defaultDayStrings);
	monthStrings = ZafLanguageManager::Allocate(monthName, defaultMonthStrings);
	timeStrings = ZafLanguageManager::Allocate(timeName, defaultTimeStrings);

	// Clear the utime from the system.
//	Clear();

	// Get the utime based on separate values.
	SetUTime(year, month, day, hour, minute, second, milliSecond);
}

ZafUTimeData::ZafUTimeData(const ZafIChar *string, const ZafIChar *format) :
	ZafFormatData(), basisYear(ZAF_ROOT_YEAR), zoneOffset(0), jDay(0),
	uSec(0),	recurse(false)
{
	// Check the printf binding.
	Bind();

	// Get the messages.
	dayStrings = ZafLanguageManager::Allocate(dayName, defaultDayStrings);
	monthStrings = ZafLanguageManager::Allocate(monthName, defaultMonthStrings);
	timeStrings = ZafLanguageManager::Allocate(timeName, defaultTimeStrings);

	// Clear the utime from the system.
//	Clear();

	// Get the utime based on a string value.
	SetUTime(string, format);
}

ZafUTimeData::ZafUTimeData(const ZafUTimeData &utime) :
	ZafFormatData(utime), basisYear(utime.basisYear),
	zoneOffset(utime.zoneOffset), jDay(utime.jDay), uSec(utime.uSec),
	recurse(false)
{
	// Check the printf binding.
	Bind();

	// Get the messages.
	dayStrings = ZafLanguageManager::Allocate(dayName, defaultDayStrings);
	monthStrings = ZafLanguageManager::Allocate(monthName, defaultMonthStrings);
	timeStrings = ZafLanguageManager::Allocate(timeName, defaultTimeStrings);
}

ZafUTimeData::~ZafUTimeData(void)
{
	// Free the messages.
	ZafLanguageManager::Free(dayStrings);
	ZafLanguageManager::Free(monthStrings);
	ZafLanguageManager::Free(timeStrings);
}

void ZafUTimeData::Add(const ZafUTimeData &utime)
{
	PushLevel();
	jDay += utime.jDay;
	uSec += utime.uSec;
	MakeCanonical();
	PopLevel();
}

void ZafUTimeData::Bind(void)
{
	// Bind in the sprintf characters.
#if defined(ZAF_INDEPENDENT)
#	define bound zafUTimeData_bound
	extern bool bound;
#else
	static bool bound = false;
#endif
	if (!bound)
	{
		// Check for a valid locale.
		if (!zafLocale)
			ZafI18nData::I18nAllocate();

		// Set the lookup characters.
		bound = true;
/* START BLOCK COMMENT
**			ZafStandardArg::SetSprintf('U', ZafUTimeData::Format);
**			ZafStandardArg::SetSscanf('U', ZafUTimeData::Parse);
END BLOCK COMMENT */
		ZafFormatData::SetSprintf('U', ZafUTimeData::Format);
		ZafFormatData::SetSscanf('U', ZafUTimeData::Parse);
	}
}
void ZafUTimeData::Clear(void)
{
	PushLevel();
	SetUTime(ZAF_ROOT_YEAR, 1, 1, 0, 0, 0, 0);
	PopLevel();
}

void ZafUTimeData::GetSystemTime(void)
{
	// Reset the time.
	PushLevel();

	// Set the time values according to the system time.
	time_t t;
	time(&t);
	if (t > 0)
	{
		struct tm *localTime = localtime(&t);
		SetUTime(1900+localTime->tm_year, localTime->tm_mon+1, localTime->tm_mday,
			localTime->tm_hour, localTime->tm_min, localTime->tm_sec, 0);
	}
	else
		SetUTime(ZAF_ROOT_YEAR, 1, 1, 0, 0, 0, 0);

	SetError(ZAF_ERROR_NONE);
	PopLevel();
}

void ZafUTimeData::ConvertJDay(int *pYear, int *pMonth, int *pDay, int *pDayOfWeek) const
{
	// Start with empty dates.
	long year = 0, month = 0, day = 0;

	// JDay == 0 and uSec < 0 is a "blank".
	if (jDay != 0 || uSec >= 0)
	{
		// Compute the separate date values algorithm 5 of Practical
		// Astronomy with your Calculator.  This includes a fix for the
		// Gregorian calendar change in 1582.  Protestant countries should
		// change that value to when they adopted the Gregorian calendar.
		long C = jDay;
		if (C > (long)zafLocale->beginGregorian)
		{
			long A = (100 * C - 186721625L) / 3652425L;
			C += 1 + A - A / 4;
		}
		C += 1524;
		long D = (100 * C - 12210L) / 36525L;
		long E = (1461L * D) / 4;
		long G = ((C - E) * 10000) / 306001L;
		day = C - E - (G * 306001L) / 10000;
		month = G - ((G > 13) ? 13 : 1);
		year = D - ((month > 2) ? 4716 : 4715);
		if (year == 0)		// There is no year 0 (1 BC = -1).
			year--;
	}

	// Set the integer values.
	if (pYear)
		*pYear = (int)year;
	if (pMonth)
		*pMonth = (int)month;
 	if (pDay)
		*pDay = (int)day;
	if (pDayOfWeek)
		*pDayOfWeek = (int)(((jDay % 7) + 8) % 7);
}

void ZafUTimeData::ConvertUSec(int *hour, int *minute, int *second, int *milliSecond) const
{
	// uSec < 0 means "blank"
	long tmp = (uSec < 0 ? 0 : uSec);

	// Set the integer variables.
	if (hour)
		*hour = (int)(tmp / 3600000L);
	tmp %= 3600000L;
	if (minute)
		*minute = (int)(tmp / 60000L);
	tmp %= 60000L;
	if (second)
		*second = (int)(tmp / 1000L);
	tmp %= 1000L;
	if (milliSecond)
		*milliSecond = (int)tmp;
}

int ZafUTimeData::Day(void) const
{
	// Compute the value.
	int day;
	ConvertJDay(0, 0, &day, 0);
	return (day);
}

int ZafUTimeData::DayOfWeek(void) const
{
	// Compute the value.
	int dayOfWeek;
	ConvertJDay(0, 0, 0, &dayOfWeek);
	return (dayOfWeek);
}

int ZafUTimeData::DaysInMonth(int month) const
{
	// Compute the specified month.
	if (month == 0)
		ConvertJDay(0, &month, 0, 0);
	if (month == 2 && LeapYear())
		return (29);
	return (month ? _daysInMonth[month - 1] : 0);
}

int ZafUTimeData::DayOfYear(void) const
{
	int tDayOfYear = 0;
	for (int i = 1; i < Month(); i++)
		tDayOfYear += DaysInMonth(i);

	tDayOfYear += Day();

	return (tDayOfYear);
}
#include <stdio.h>

ZafError ZafUTimeData::Decrement(ZafUTimeData *delta)
{
	PushLevel();
	int year = Year() - delta->Year() + 1;
	int month = Month() - delta->Month() + 1;
	int day = Day() - delta->Day() + 1;
	long tUsec = uSec - delta->uSec;
	SetUTime(year, month, day, 0, 0, 0, 0);
	uSec = tUsec;
	MakeCanonical();
	PopLevel();
	return (ZAF_ERROR_NONE);
}

long ZafUTimeData::Difference(const ZafUTimeData &utime) const
{
	// First, compare jDay.
	if (jDay != utime.jDay)
		return (jDay - utime.jDay);
	return (uSec - utime.uSec);
}

ZafData *ZafUTimeData::Duplicate(void)
{
	return (new ZafUTimeData(*this));
}

bool ZafUTimeData::IsA(ZafClassID compareID) const
{
	return ((compareID == classID) ? true : ZafFormatData::IsA(compareID));
}

bool ZafUTimeData::IsA(ZafClassName compareName) const
{
	return (!strcmp(compareName, className) ? true : ZafFormatData::IsA(compareName));
}

int ZafUTimeData::FormattedText(ZafIChar *buffer, int maxLength, const ZafIChar *format) const
{
	// Format the text.
	return (Sprintf(buffer, maxLength, format ? format : zafLocale->dateTimeStringOutputFormat));
}

int ZafUTimeData::Hour(void) const
{
	// Compute the value.
	int hour;
	ConvertUSec(&hour, 0, 0, 0);
	return (hour);
}

ZafError ZafUTimeData::Increment(ZafUTimeData *delta)
{
	PushLevel();
	int year = delta->Year() + Year() - 1;
	int month = delta->Month() + Month() - 1;
	int day = delta->Day() + Day() - 1;
	long tUsec = delta->uSec + uSec;
	SetUTime(year, month, day, 0, 0, 0, 0);
	uSec = tUsec;
	MakeCanonical();
	PopLevel();
	return (ZAF_ERROR_NONE);
}

bool ZafUTimeData::LeapYear(void) const
{
	// Compute the leap year.
	int year;
	ConvertJDay(&year, 0, 0, 0);
	return ((year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0)) ? true : false);
}

void ZafUTimeData::MakeCanonical(void)
{
	// Compute the canonical date.
	if (uSec >= uSecsPerDay)
	{
		jDay += uSec / uSecsPerDay;
		uSec %= uSecsPerDay;
	}
	else
	{
		while (uSec < 0)
		{
			jDay--;
			uSec += uSecsPerDay;
		}
	}
}

int ZafUTimeData::MilliSecond(void) const
{
	// Compute the value.
	int milliSecond;
	ConvertUSec(0, 0, 0, &milliSecond);
	return (milliSecond);
}

int ZafUTimeData::Minute(void) const
{
	// Compute the value.
	int minute;
	ConvertUSec(0, &minute, 0, 0);
	return (minute);
}

int ZafUTimeData::Month(void) const
{
	// Compute the value.
	int month;
	ConvertJDay(0, &month, 0, 0);
	return (month);
}

int ZafUTimeData::Second(void) const
{
	// Compute the value.
	int second;
	ConvertUSec(0, 0, &second, 0);
	return (second);
}

void ZafUTimeData::Subtract(const ZafUTimeData &utime)
{
	PushLevel();
	jDay -= utime.jDay;
	uSec -= utime.uSec;
	MakeCanonical();
	PopLevel();
}

void ZafUTimeData::UTime(int *year, int *month, int *day,
	int *hour, int *minute, int *second,
	int *milliSecond, int *dayOfWeek)
{
	ConvertJDay(year, month, day, dayOfWeek);
	ConvertUSec(hour, minute, second, milliSecond);
}

long ZafUTimeData::Value(void) const
{
	// Return the day value.
	return (jDay ? jDay : uSec);
}

int ZafUTimeData::Year(void) const
{
	// Compute the value.
	int year;
	ConvertJDay(&year, 0, 0, 0);
	return (year);
}

ZafError ZafUTimeData::SetBasisYear(int tBasisYear)
{
	// Reset the basis year.
	// basisYear may be any year, and it is not required to be divisible by 100.
	// For example, if basisYear is 1950, a 2-digit year specified by the user
	// will fall in the range 1950..2049.
	PushLevel();
	basisYear = tBasisYear;
	PopLevel();

	// Return success.
	return (ZAF_ERROR_NONE);
}

ZafError ZafUTimeData::SetIncrementUTime(int year, int month, int day, int hour,
	int minute, int second, int millisecond)
{
	// Increments are based on the date 1/1/1 (in the canonical y/m/d format).
	// For example, an increment of one day is 1/1/2, an increment of one month
	// is 1/2/1, and an increment of one year is 2/1/1.
	return (SetUTime(year + 1, month + 1, day + 1, hour, minute, second, millisecond));
}

ZafError ZafUTimeData::SetJulianDay(long tJDay)
{
	PushLevel();
	jDay = tJDay;
	PopLevel();

	// Return success.
	return (ZAF_ERROR_NONE);
}

ZafError ZafUTimeData::SetUTime(ZafUInt32 seconds)
{
	// Change the time values.
	PushLevel();
//	SetUTime(0, 0, 0, 0, 0, (int)seconds, 0);
	uSec = (long)seconds;
	MakeCanonical();
	jDay += uSec / uSecsPerDay;
	uSec %= uSecsPerDay;
	PopLevel();

	// Return success.
	return (ZAF_ERROR_NONE);
}

ZafError ZafUTimeData::SetUTime(const ZafIChar *buffer, const ZafIChar *format)
{
	// Reset the time based on the buffer.
	PushLevel();
	if (!buffer || !buffer[0])
		Clear();
	else if (!Sscanf(buffer, (format && format[0]) ? format : zafLocale->dateTimeStringInputFormat))
		SetError(ZAF_ERROR_INVALID);
	else
		SetError(ZAF_ERROR_NONE);
	PopLevel();

	// Return the error status.
	return (Error());
}

ZafError ZafUTimeData::SetUTime(int year, int month, int day, int hour,
	int minute, int second, int millisecond)
{
	PushLevel();

	if (year == 0 && month == 0)
		jDay = day;
	else
	{
		// Compute the Julian date from three integer values using
		// algorithm 4 of Practical Astronomy with your Calculator.
		// This include a fix for the Gregorian calendar change in
		// 1582.  Protestant countries should change that value to
		// when they adopted the Gregorian calendar.
		if (month <= 2)
		{
			month += 12;
			year--;
		}
		jDay = 365L * year + (153L * (month + 1)) / 5 + day + 1720995L;
		jDay += ((year >= 0) ? year : (year-3)) / 4;
		// Gregorian date fix
		if ((jDay > (long)(zafLocale->beginGregorian + zafLocale->skipGregorian)) ||
			(jDay > 2299160L + 11))
//		if (jDay > zafLocale->beginGregorian + zafLocale->skipGregorian)
			jDay += 2 - (year / 100) + ((year / 100) / 4);
	}
	uSec = millisecond + 1000L * (second + 60L * (minute + 60L * hour));

	MakeCanonical();

	// If all zeros are sent in, mark this as "blank".
//	if (year == 0 && month == 0 && day == 0 && uSec == 0)
//		uSec = -1;

	PopLevel();

	// Return success.
	return (ZAF_ERROR_NONE);
}

ZafError ZafUTimeData::SetUTime(const ZafUTimeData &utime)
{
	// Change the time values.
	PushLevel();
	jDay = utime.jDay;
	uSec = utime.uSec;
	PopLevel();

	// Return success.
	return (ZAF_ERROR_NONE);
}

ZafError ZafUTimeData::SetZoneOffset(int tZoneOffset)
{
	// Reset the zone offset.
	PushLevel();
	zoneOffset = tZoneOffset;
	PopLevel();

	// Return success.
	return (ZAF_ERROR_NONE);
}

// ----- Operators ----------------------------------------------------------

ZafUTimeData ZafUTimeData::operator+(const ZafUTimeData &utime)
{
	// Add the specified time value.
	ZafUTimeData tmp(*this);
	tmp += utime;
	return tmp;
}

ZafUTimeData ZafUTimeData::operator-(const ZafUTimeData &utime)
{
	// Subtract the specified time value.
	ZafUTimeData tmp(*this);
	tmp -= utime;
	return tmp;
}

ZafUTimeData ZafUTimeData::operator++(void)
{
	// Reset the value.
	PushLevel();
	*this += 1L;
	PopLevel();

	// Return a reference pointer.
	return *this;
}

ZafUTimeData ZafUTimeData::operator++(int)
{
	// Reset the value.
	PushLevel();
	ZafUTimeData tmp(*this);
	*this += 1L;
	PopLevel();

	// Return a temporary reference pointer.
	return tmp;
}

ZafUTimeData ZafUTimeData::operator--(void)
{
	// Reset the value.
	PushLevel();
	*this -= 1L;
	PopLevel();

	// Return a reference pointer.
	return *this;
}

ZafUTimeData ZafUTimeData::operator--(int)
{
	// Reset the value.
	PushLevel();
	ZafUTimeData tmp(*this);
	*this -= 1L;
	PopLevel();

	// Return a temporary reference pointer.
	return tmp;
}

ZafUTimeData &ZafUTimeData::operator=(const ZafUTimeData &utime)
{
	// Reset the time values.
	SetUTime(utime);
	return (*this);
}

ZafUTimeData &ZafUTimeData::operator=(long seconds)
{
	// Reset the time values.
	SetUTime((ZafUInt32)seconds);
	return (*this);
}

ZafUTimeData &ZafUTimeData::operator+=(const ZafUTimeData &utime)
{
/* START BLOCK COMMENT
**		// Reset the time values.
**		PushLevel();
**		jDay += utime.jDay;
**		uSec += utime.uSec;
**		MakeCanonical();
**		PopLevel();
END BLOCK COMMENT */
	Add(utime);

	// Return a reference pointer.
	return (*this);
}

ZafUTimeData &ZafUTimeData::operator-=(const ZafUTimeData &utime)
{
/* START BLOCK COMMENT
**		// Reset the time values.
**		PushLevel();
**		jDay -= utime.jDay;
**		uSec -= utime.uSec;
**		MakeCanonical();
**		PopLevel();
END BLOCK COMMENT */
	Subtract(utime);

	// Return a reference pointer.
	return (*this);
}

ZafUTimeData ZafUTimeData::operator+(long seconds)
{
	return (*this + ZafUTimeData((ZafUInt32)seconds));
}

ZafUTimeData ZafUTimeData::operator-(long seconds)
{
	return (*this - ZafUTimeData((ZafUInt32)seconds));
}

// ----- sprintf/sscanf functions -------------------------------------------

void ZafUTimeData::Format(va_list *argList, ZafIChar **ptrBuffer, ZafIChar, int, int, int)
{
	ZafUTimeData *utime = va_arg(*argList, ZafUTimeData *);
	int len = utime->strftime(*ptrBuffer, 256, ZAF_NULLP(ZafIChar));
	*ptrBuffer += len;
}

int ZafUTimeData::Parse(va_list *argList, ZafIChar **ptrBuffer,
	ZafIChar , int , int , const ZafIChar **)
{
	ZafUTimeData *utime = va_arg(*argList, ZafUTimeData *);
	ZafIChar *newPtr = utime->strptime(*ptrBuffer, ZAF_NULLP(ZafIChar));
	if (newPtr != *ptrBuffer)
	{
		*ptrBuffer = newPtr;
		return 1;
	}
	return (0);
}

int ZafUTimeData::Sprintf(ZafIChar *buffer, int maxLength, const ZafIChar *format) const
{
	// Get the output format, defined as the first format in the buffer.
	ZafIChar oFormat[ZAF_FORMAT_LENGTH];
	ParseFormat(oFormat, format);
	return (strftime(buffer, maxLength, oFormat));
}

int ZafUTimeData::Sscanf(const ZafIChar *buffer, const ZafIChar *format)
{
	bool match = false;
	ZafIChar iFormat[ZAF_FORMAT_LENGTH];
	format = ParseFormat(iFormat, format);
	while (!match && *iFormat)
	{
		if (!strptime(buffer, iFormat))
			format = ParseFormat(iFormat, format);
		else
			match = true;
	}
	return (match ? 1 : 0);
}

void ZafUTimeData::ResetDate(void)
{
	PushLevel();
	long tUsec = uSec;
	SetUTime(ZAF_ROOT_YEAR, 1, 1, 0, 0, 0, 0);
	uSec = tUsec;
	PopLevel();
}

void ZafUTimeData::ResetTime(void)
{
	PushLevel();
	uSec = 0;
	PopLevel();
}

int ZafUTimeData::strftime(ZafIChar *string, int maxsize, const ZafIChar *format) const
{
	// Check for valid utime members.
	if (jDay == 0 && uSec < 0)
	{
		*string = 0;
		return (0);
	}

	// Null terminate the tring in case nothing gets "printed".
	*string = 0;

	// Check for a valid locale.
	int currentMaxsize = maxsize;

	// Get the current utime values.
	int hour, minute, second, milliSecond;
	ConvertUSec(&hour, &minute, &second, &milliSecond);
	int pYear, pMonth, pDay, pDayOfWeek;
	ConvertJDay(&pYear, &pMonth, &pDay, &pDayOfWeek);

	int pDayOfYear = 0;
	for (int j = 0; j < pMonth-1; j++)
		pDayOfYear += _daysInMonth[j];
	pDayOfYear += pDay;
	if (LeapYear() && pMonth > 2)
		pDayOfYear++;

	ZafUTimeData firstDay(pYear, 1, 1, 0, 0, 0, 0);
	int firstDayOfYear = firstDay.DayOfWeek();

	ZafIChar *ECval = ZAF_NULLP(ZafIChar), *EYval = ZAF_NULLP(ZafIChar);
	int Eyval = 0, i = 0;
	for ( ; i < zafLocale->eraTableLength; i++)
	{
		if ((long)zafLocale->eraTable[i].startDate <= jDay &&
			jDay <= (long)zafLocale->eraTable[i].endDate)
		{
			ECval = zafLocale->eraTable[i].eraName;
			EYval = zafLocale->eraTable[i].eraFormat;
			ZafUTimeData tmp; tmp.jDay = (long)zafLocale->eraTable[i].startDate;
			int baseYear;
			tmp.ConvertJDay(&baseYear, ZAF_NULLP(int), ZAF_NULLP(int), ZAF_NULLP(int));
			if (zafLocale->eraTable[i].direction)
				Eyval = zafLocale->eraTable[i].offset + pYear - baseYear;
			else
				Eyval = zafLocale->eraTable[i].offset + baseYear - pYear;
		}
	}

	// Check the format information.
	for (i = 0; format[i]; i++)
	{
		int doCopy = true;
		ZafIChar tmpStr[ZAF_FORMAT_LENGTH];
		const ZafIChar *copyStr = tmpStr;
		if (format[i] == '%')
		{
			i++;
			if (format[i] == 0)
				break;
			switch (format[i])
			{
			case 'a':
				copyStr = dayStrings->GetMessage((ZafNumberID)(ZAF_ABDAY_1+pDayOfWeek));
				break;
			case 'A':
				copyStr = dayStrings->GetMessage((ZafNumberID)(ZAF_DAY_1+pDayOfWeek));
				break;
			case 'b':
				copyStr = monthStrings->GetMessage((ZafNumberID)(ZAF_ABMON_1+pMonth-1));
				break;
			case 'B':
				copyStr = monthStrings->GetMessage((ZafNumberID)(ZAF_MON_1+pMonth-1));
				break;
			case 'c':
				maxsize -= strftime(string, maxsize, zafLocale->dateTimeStringOutputFormat);
				doCopy = false;
				break;
			case 'C':
				itoa(pYear / 100, tmpStr, 10, 2);
				break;
			case 'd':
				itoa(pDay, tmpStr, 10, 2);
				break;
			case 'D':
				maxsize -= strftime(string, maxsize, _dFormat);
				doCopy = false;
				break;
			case 'e':
				itoa(pDay, tmpStr, 10, 0);
				break;
			case 'E':	// Era conversions
				i++;
				switch (format[i])
				{
				case 'C':
					if (ECval)
						copyStr = ECval;
					else
						itoa(pYear / 100, tmpStr, 10, 2);
					break;
				case 'y':
					if (ECval)
						itoa(Eyval, tmpStr, 10, 0);
					else
						itoa(pYear % 100, tmpStr, 10, 2);
					break;
				case 'Y':
					maxsize -= strftime(string, maxsize, EYval ? EYval : ZAF_ITEXT("%Y"));
					doCopy = false;
					break;
				}
				break;
			case 'g':
				copyStr = monthStrings->GetMessage((ZafNumberID)(ZAF_SHMON_1+pMonth-1));
				break;
			case 'G':
				copyStr = dayStrings->GetMessage((ZafNumberID)(ZAF_SHDAY_1+pDayOfWeek));
				break;
			case 'h':
				itoa(hour, tmpStr, 10, 0);
				break;
			case 'H':
				itoa(hour, tmpStr, 10, 2);
				break;
			case 'i':
			{
				int tmp = hour % 12;
				itoa((tmp == 0 ? 12 : tmp), tmpStr, 10, 0);
				break;
			}
			case 'I':
			{
				int tmp = hour % 12;
				itoa((tmp == 0 ? 12 : tmp), tmpStr, 10, 2);
				break;
			}
			case 'j':
				itoa(pDayOfYear, tmpStr, 10, 3);
				break;
			case 'J':
				itoa(milliSecond / 100, tmpStr, 10, 1);
				break;
			case 'k':
				itoa(milliSecond / 10, tmpStr, 10, 2);
				break;
			case 'K':
				itoa(milliSecond, tmpStr, 10, 3);
				break;
			case 'l':
			case 'L':
				i++;
				switch (format[i])
				{
				case 'm':
					itoa(12*pYear + pMonth, tmpStr, 10, 0);
					break;
				case 'd':
					itoa(jDay, tmpStr, 10, 0);
					break;
				case 'H':
					itoa(24 * jDay + uSec / (1000*60*60), tmpStr, 10, 0);
					break;
				case 'M':
					itoa(24 * 60 * jDay + uSec / (1000*60), tmpStr, 10, 0);
					break;
				case 'S':
					itoa(24 * 60 * 60 * jDay + uSec / (1000), tmpStr, 10, 0);
					break;
				case 0:
					i--;
					break;
				}
				break;
			case 'm':
				itoa(pMonth, tmpStr, 10, 2);
				break;
			case 'M':
				itoa(minute, tmpStr, 10, 2);
				break;
			// case 'O':	alternate digits
			case 'p':
				{
				ZafNumberID tempID = (hour < 12) ? ZAF_AM : ZAF_PM;
				copyStr = timeStrings->GetMessage(tempID);
				}
				break;
			case 'r':
				maxsize -= strftime(string, maxsize, zafLocale->time12StringOutputFormat);
				doCopy = false;
				break;
			case 'R':
				maxsize -= strftime(string, maxsize, _rFormat);
				doCopy = false;
				break;
			case 's':
				itoa(second, tmpStr, 10, 0);
				break;
			case 'S':
				itoa(second, tmpStr, 10, 2);
				break;
			case 'T':
				maxsize -= strftime(string, maxsize, _tFormat);
				doCopy = false;
				break;
			case 'u':
				itoa((pDayOfWeek == 0 ? 7 : pDayOfWeek), tmpStr, 10, 0);
				break;
			case 'U':
			{
				int bias = (firstDayOfYear <= 0 ? 6 : firstDayOfYear - 1);
				itoa((pDayOfYear+bias)/7, tmpStr, 10, 2);
				break;
			}
			case 'v':
				itoa(pMonth, tmpStr, 10, 0);
				break;
			case 'V':
			{
				int bias = (firstDayOfYear <= 4 ? 5 : firstDayOfYear - 2);
				int tWeek = (pDayOfYear+bias)/7;
				itoa(tWeek ? tWeek : 53, tmpStr, 10, 2);
				break;
			}
			case 'w':
				itoa(pDayOfWeek, tmpStr, 10, 0);
				break;
			case 'W':
			{
				int bias = (firstDayOfYear <= 1 ? 5 + firstDayOfYear : firstDayOfYear - 2);
				itoa((pDayOfYear+bias)/7, tmpStr, 10, 2);
				break;
			}
			case 'x':
				maxsize -= strftime(string, maxsize, zafLocale->dateStringOutputFormat);
				doCopy = false;
				break;
			case 'X':
				maxsize -= strftime(string, maxsize, zafLocale->timeStringOutputFormat);
				doCopy = false;
				break;
			case 'y':
				itoa(pYear % 100, tmpStr, 10, 2);
				break;
			case 'Y':
				itoa(pYear, tmpStr, 10, 0);
				break;
			case 'Z':
//				copyStr = timeStrings->GetMessage(ZAF_TZ);
				doCopy = false;
				break;
			case '%':
				tmpStr[0] = '%';
				tmpStr[1] = 0;
				break;
			default:
				tmpStr[0] = 0;
				break;
			}
		}
		else
		{
			tmpStr[0] = format[i];
			tmpStr[1] = 0;
		}
		if (doCopy && copyStr)
		{
			maxsize = (int)(maxsize - strlen(copyStr));
			if (maxsize <= 0)
				return (0);
			strcpy(string, copyStr);
		}
		string += strlen(string);
	}
	*string = format[i];
	return (currentMaxsize-maxsize);
}

ZafIChar *ZafUTimeData::strptime(const ZafIChar *string, const ZafIChar *format)
{
	if (!string)
	{
		jDay = 0;
		uSec = 0;
//		uSec = -1;
		return (ZAF_NULLP(ZafIChar));
	}

	if (*string == 0)
	{
		jDay = 0;
		uSec = 0;
//		uSec = -1;
		return (ZAF_NULLP(ZafIChar));
	}
	if (!format)
		format = zafLocale->dateTimeStringInputFormat;
	if (recurse)
		jDay = uSec = 0;
	bool saveRecurse = recurse;

	// Get the default utime values.
	int hour, minute, second, milliSecond;
	ConvertUSec(&hour, &minute, &second, &milliSecond);
	int pYear, pMonth, pDay, pDayOfWeek;
	ConvertJDay(&pYear, &pMonth, &pDay, &pDayOfWeek);

	// Check the format information.
	int pDayOfYear = 0;
	int pWeek;
	int timeOffset = 0;
	milliSecond = 0;
	for (int i=0; format[i]; i++)
	{
		// White space should be ignored.
		if (IsSpace(format[i]))
		{
			while (IsSpace(format[i]))
			       i++;
		}
		while (IsSpace(*string))
	       string++;
		ZafIChar *nextChar = (ZafIChar *)string;
		if (format[i] == '%')
		{
			i++;
			if (format[i] == 0)
				return (ZAF_NULLP(ZafIChar));
			switch (format[i])
			{
			case 'a':
			case 'A':
			case 'G':
			{
				int count = 0;
				for (int j = 0; j < 7; j++)
				{
					const ZafIChar *tmp = dayStrings->GetMessage((ZafNumberID)(j+ZAF_DAY_1));
					if (strnicmp(string, tmp, (int)strlen(tmp)) == 0)
					{
						count++;
						pDayOfWeek = j;
						nextChar += strlen(tmp);
						continue;
					}
					tmp = dayStrings->GetMessage((ZafNumberID)(j+ZAF_ABDAY_1));
					if (strnicmp(string, tmp, (int)strlen(tmp)) == 0)
					{
						count++;
						pDayOfWeek = j;
						nextChar += strlen(tmp);
						continue;
					}
					tmp = dayStrings->GetMessage((ZafNumberID)(j+ZAF_SHDAY_1));
					if (strnicmp(string, tmp, (int)strlen(tmp)) == 0)
					{
						count++;
						pDayOfWeek = j;
						nextChar += strlen(tmp);
						continue;
					}
				}
				if (count != 1)
					return (ZAF_NULLP(ZafIChar));
				break;
			}

			case 'b':
			case 'g':
			case 'B':
			{
				int count = 0;
				for (int j = 0; j < 12; j++)
				{
					const ZafIChar *tmp = monthStrings->GetMessage((ZafNumberID)(j+ZAF_MON_1));
					if (strnicmp(string, tmp, (int)strlen(tmp)) == 0)
					{
						count++;
						pMonth = j + 1;
						nextChar += strlen(tmp);
						continue;
					}
					tmp = monthStrings->GetMessage((ZafNumberID)(j+ZAF_ABMON_1));
					if (strnicmp(string, tmp, (int)strlen(tmp)) == 0)
					{
						count++;
						pMonth = j + 1;
						nextChar += strlen(tmp);
						continue;
					}
					tmp = monthStrings->GetMessage((ZafNumberID)(j+ZAF_SHMON_1));
					if (strnicmp(string, tmp, (int)strlen(tmp)) == 0)
					{
						count++;
						pMonth = j + 1;
						nextChar += strlen(tmp);
						continue;
					}
				}
				if (count != 1)
					return (ZAF_NULLP(ZafIChar));
				break;
			}
			case 'c':
				recurse = true;
				nextChar = strptime(string, zafLocale->dateTimeStringInputFormat);
				break;
			case 'C': // This doesn't match the XOpen guide
				if (!IsDigit(string[0]))
					return (ZAF_NULLP(ZafIChar));
				pYear = (int)strtol(string, &nextChar, 10);
				pYear *= 100;
				break;
			case 'e':
			case 'd':
				if (!IsDigit(string[0]))
					return (ZAF_NULLP(ZafIChar));
				pDay = (int)strtol(string, &nextChar, 10);
				if (pDay < 1 || pDay > 31)
					return (ZAF_NULLP(ZafIChar));
				break;
			case 'D':
				{
				recurse = true;
				long tmpUsec = uSec;

				ZafIChar iFormat[ZAF_FORMAT_LENGTH];
				const ZafIChar *tFormat = ParseFormat(iFormat, zafLocale->dateStringInputFormat);
				while (*iFormat)
				{
					nextChar = strptime(string, iFormat);
					if (!nextChar)
						tFormat = ParseFormat(iFormat, tFormat);
					else
						break;
				}
				uSec = tmpUsec;
				if (nextChar)
					ConvertJDay(&pYear, &pMonth, &pDay, &pDayOfWeek);
				else
					return ZAF_NULLP(ZafIChar);
				}
				break;
			// case 'E':	// Era conversions
			case 'h':
			case 'H':
				if (!IsDigit(string[0]))
					return (ZAF_NULLP(ZafIChar));
				hour = (int)strtol(string, &nextChar, 10);
				if (hour < 0 || hour > 23)
					return (ZAF_NULLP(ZafIChar));
				break;
			case 'I':
				if (!IsDigit(string[0]))
					return (ZAF_NULLP(ZafIChar));
				hour = (int)strtol(string, &nextChar, 10);
				if (hour < 1 || hour > 12)
					return (ZAF_NULLP(ZafIChar));
				break;
			case 'j':
				if (!IsDigit(string[0]))
					return (ZAF_NULLP(ZafIChar));
				pDayOfYear = (int)strtol(string, &nextChar, 10);
				if (pDayOfYear < 1 || pDayOfYear > 366)
					return (ZAF_NULLP(ZafIChar));
				break;
			case 'J':
				if (!IsDigit(string[0]))
					return (ZAF_NULLP(ZafIChar));
				milliSecond = (int)strtol(string, &nextChar, 10) * 100;
				break;
			case 'k':
				if (!IsDigit(string[0]))
					return (ZAF_NULLP(ZafIChar));
				milliSecond = (int)strtol(string, &nextChar, 10) * 10;
				break;
			case 'K':
				if (!IsDigit(string[0]))
					return (ZAF_NULLP(ZafIChar));
				milliSecond = (int)strtol(string, &nextChar, 10);
				break;
			case 'v':
			case 'm':
				if (!IsDigit(string[0]))
					return (ZAF_NULLP(ZafIChar));
				pMonth = (int)strtol(string, &nextChar, 10);
				if (pMonth < 1 || pMonth > 12)
					return (ZAF_NULLP(ZafIChar));
				break;
			case 'M':
				if (!IsDigit(string[0]))
					return (ZAF_NULLP(ZafIChar));
				minute = (int)strtol(string, &nextChar, 10);
				if (minute < 0 || minute > 59)
					return (ZAF_NULLP(ZafIChar));
				break;
			case 't':
			case 'n':
				while (IsSpace(*nextChar))
				       nextChar++;
				break;
			// case 'O':	????
			case 'p':
				{
				const ZafIChar *tmp = timeStrings->GetMessage(ZAF_AM);
				if (strnicmp(string, tmp, (int)strlen(tmp)) == 0)
				{
					nextChar += strlen(tmp);
					if (hour == 12)
						timeOffset = -12;
					break;
				}
				tmp = timeStrings->GetMessage(ZAF_PM);
				if (strnicmp(string, tmp, (int)strlen(tmp)) == 0)
				{
					if (hour != 12)
						timeOffset = 12;
					nextChar += strlen(tmp);
					break;
				}
				return (ZAF_NULLP(ZafIChar));
				}
			case 'r': // This doesn't match the XOpen guide
				recurse = true;
				nextChar = strptime(string, zafLocale->timeStringInputFormat);
				break;
			case 'R':
				recurse = true;
				nextChar = strptime(string, _rFormat);
				break;
			case 's':
			case 'S':
				if (!IsDigit(string[0]))
					return (ZAF_NULLP(ZafIChar));
				second = (int)strtol(string, &nextChar, 10);
				if (second < 0 || second > 61)
					return (ZAF_NULLP(ZafIChar));
				break;
			// case 't':	see 'n'
			case 'T':
				{
				recurse = true;
				long tmpJday = jDay;

				ZafIChar iFormat[ZAF_FORMAT_LENGTH];
				const ZafIChar *tFormat = ParseFormat(iFormat, zafLocale->timeStringInputFormat);
				while (*iFormat)
				{
					nextChar = strptime(string, iFormat);
					if (!nextChar)
						tFormat = ParseFormat(iFormat, tFormat);
					else
						break;
				}
				jDay = tmpJday;
				if (nextChar)
					ConvertUSec(&hour, &minute, &second, &milliSecond);
				else
					return ZAF_NULLP(ZafIChar);
				}
				break;

			case 'U':
			case 'V':
				if (!IsDigit(string[0]))
					return (ZAF_NULLP(ZafIChar));
				pWeek = (int)strtol(string, &nextChar, 10);
				if (pWeek < 0 || pWeek > 55)
					return (ZAF_NULLP(ZafIChar));
				break;
			case 'w':
				if (!IsDigit(string[0]))
					return (ZAF_NULLP(ZafIChar));
				pDayOfWeek = (int)strtol(string, &nextChar, 10);
				if (pDayOfWeek < 0 || pDayOfWeek > 6)
					return (ZAF_NULLP(ZafIChar));
				break;
			case 'W':
				if (!IsDigit(string[0]))
					return (ZAF_NULLP(ZafIChar));
				pDayOfWeek = (int)strtol(string, &nextChar, 10);
				if (pDayOfWeek < 1 || pDayOfWeek > 7)
					return (ZAF_NULLP(ZafIChar));
				if (pDayOfWeek == 7)
					pDayOfWeek = 0;
				break;
			case 'x':
			case 'X':
				{
					recurse = true;
					bool match = false;
					ZafIChar iFormat[ZAF_FORMAT_LENGTH];
					const ZafIChar *tFormat = (format[i] == 'x') ? zafLocale->dateStringInputFormat :
						zafLocale->timeStringInputFormat;
					tFormat = ParseFormat(iFormat, tFormat);
					while (!match && *iFormat)
					{
						nextChar = strptime(string, iFormat);
						if (!nextChar)
							tFormat = ParseFormat(iFormat, tFormat);
						else
							match = true;
					}
				}
				break;
			case 'y':
				{
				if (!IsDigit(string[0]))
					return (ZAF_NULLP(ZafIChar));
				pYear = (int)strtol(string, &nextChar, 10);
				if (nextChar == string || pYear < 0 || pYear > 99)
					return (ZAF_NULLP(ZafIChar));

				// Support for basisYear not required to be divisible by 100.
				int basisYearShort = basisYear % 100;
				int basisYearCentury = basisYear - basisYearShort;
				if (pYear < basisYearShort)
					basisYearCentury += 100;

				pYear += basisYearCentury;
				break;
				}
			case 'Y':
				if (!IsDigit(string[0]))
					return (ZAF_NULLP(ZafIChar));
				pYear = (int)strtol(string, &nextChar, 10);
				if (nextChar == string)
					return (ZAF_NULLP(ZafIChar));
				break;

			case 'Z':
			{
				for (int j = 0; _timeZones[j].name; j++)
					if (strnicmp(string, _timeZones[j].name, (int)strlen(_timeZones[j].name)) == 0)
					{
						zoneOffset = _timeZones[j].offset;
						nextChar += strlen(_timeZones[j].name);
						break;
					}
				break;
			}
			case '%':
				if (*nextChar != '%')
					return (ZAF_NULLP(ZafIChar));
				nextChar++;
				break;
			default:
				break;
			}
		}
		else
		{
			if (format[i] != *nextChar)
				return (ZAF_NULLP(ZafIChar));
			nextChar++;
		}
		recurse = saveRecurse;

		// Check to see if match string has run out of match data.
		if (nextChar == ZAF_NULLP(ZafIChar))
			return (ZAF_NULLP(ZafIChar));
		string = nextChar;
		// White space should be ignored.
		if (IsSpace(format[i + 1]))
		{
			while (IsSpace(format[i + 1]))
			       i++;
		}
	}

	// Final checks for errors.
	// Leap year.
	bool leapYear = (pYear % 400 == 0) || ((pYear % 4 == 0) &&
		(pYear % 100 != 0)) ? true : false;
	if (pMonth > 0 && pMonth < 13 && pDay > _daysInMonth[pMonth - 1])
	{
		if (pMonth == 2 && leapYear && pDay == (_daysInMonth[pMonth - 1] + 1))
			; // Fall through to the other tests.
		else
			return (ZAF_NULLP(ZafIChar));
	}

	if (pDayOfYear > 0)
	{
		if (SetUTime(pYear, 1, pDayOfYear, hour + timeOffset, minute, second,
			milliSecond) == ZAF_ERROR_NONE)
		{
			return ((ZafIChar *)string);
		}

	}
	else if (SetUTime(pYear, pMonth, pDay, hour + timeOffset, minute, second,
		milliSecond) == ZAF_ERROR_NONE)
	{
		return ((ZafIChar *)string);
	}
	return (ZAF_NULLP(ZafIChar));
}

// ----- Persistent functions -----------------------------------------------
#if defined(USE_ZAF_PERSISTENCE)
ZafUTimeData::ZafUTimeData(const ZafIChar *name, ZafDataPersistence &persist) :
	ZafFormatData(name, persist.PushLevel(className, classID, ZAF_PERSIST_FILE)),
	basisYear(ZAF_ROOT_YEAR), zoneOffset(0), jDay(0), uSec(0), recurse(false)
{
	Bind();

	// Get the messages.
	dayStrings = ZafLanguageManager::Allocate(dayName, defaultDayStrings);
	monthStrings = ZafLanguageManager::Allocate(monthName, defaultMonthStrings);
	timeStrings = ZafLanguageManager::Allocate(timeName, defaultTimeStrings);

	// Read the data.
	if (persist.Error() == ZAF_ERROR_NONE)
		ZafUTimeData::ReadData(persist);

	persist.PopLevel();
	if (persist.Error() != ZAF_ERROR_NONE)
		SetError(persist.Error());
}

ZafElement *ZafUTimeData::Read(const ZafIChar *name, ZafDataPersistence &persist)
{
	return (new ZafUTimeData(name, persist));
}

void ZafUTimeData::ReadData(ZafDataPersistence &persist)
{
	// Read the data.
	ZafInt32 tJDay, tUSec;
	ZafInt16 tBasisYear, tZoneOffset;
	ZafFile *file = persist.CurrentFile();
	*file >> tJDay >> tUSec >> tBasisYear >> tZoneOffset;
	jDay = tJDay;
	uSec = tUSec;
	basisYear = tBasisYear;
	zoneOffset = tZoneOffset;
}

void ZafUTimeData::Write(ZafDataPersistence &persist)
{
	// Write the object.
	ZafFormatData::Write(persist.PushLevel(className, classID, ZAF_PERSIST_FILE));
	if (persist.Error() == ZAF_ERROR_NONE)
		ZafUTimeData::WriteData(persist);
	persist.PopLevel();
	if (persist.Error() != ZAF_ERROR_NONE)
		SetError(persist.Error());
}

void ZafUTimeData::WriteData(ZafDataPersistence &persist)
{
	// Write the data.
	ZafInt32 tJDay = jDay, tUSec = uSec;
	ZafInt16 tBasisYear = (ZafInt16)basisYear, tZoneOffset = (ZafInt16)zoneOffset;
	ZafFile *file = persist.CurrentFile();
	*file << tJDay << tUSec << tBasisYear << tZoneOffset;
}
#endif
