

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  This file is part of CMSDK version 2.0.0.170915_Beta4 (15 September 2017)
//_/_/  
//_/_/  developed by
//_/_/  
//_/_/  Communicative Machines Ltd. 
//_/_/    http://cmlabs.com/
//_/_/  Copyright (c) 2017
//_/_/  
//_/_/  in collaboration with 
//_/_/  
//_/_/  Icelandic Institute for Intelligent Machines ses
//_/_/    Menntavegur 1
//_/_/    101 Reykjavik
//_/_/    ICELAND
//_/_/    http://www.iiim.is/
//_/_/
//_/_/  and is released under the following
//_/_/  
//_/_/  EXTENDED BSD License
//_/_/
//_/_/  Redistribution and use in source and binary forms, with or without
//_/_/  modification, is permitted provided that the following conditions 
//_/_/  are met:
//_/_/
//_/_/  - Redistributions of source code must retain the above copyright 
//_/_/    and collaboration notice, this list of conditions and the following 
//_/_/    disclaimer.
//_/_/
//_/_/  - Redistributions in binary form must reproduce the above copyright 
//_/_/    notice, this list of conditions and the following disclaimer in the 
//_/_/    documentation and/or other materials provided with the distribution.
//_/_/
//_/_/  - Neither the name of its copyright holders nor the names of its 
//_/_/    contributors may be used to endorse or promote products derived from 
//_/_/    this software without specific prior written permission.
//_/_/
//_/_/  - CADIA Clause: The license granted in and to the software under this 
//_/_/    agreement is a limited-use license. The software may not be used in 
//_/_/    furtherance of: (i) intentionally causing bodily injury or severe 
//_/_/    emotional harm to any person; (ii) invading the personal privacy or 
//_/_/    violating the human rights of any person; or (iii) committing or 
//_/_/    preparing for any act of war. 
//_/_/
//_/_/  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
//_/_/  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
//_/_/  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
//_/_/  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 
//_/_/  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
//_/_/  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
//_/_/  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
//_/_/  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
//_/_/  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
//_/_/  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
//_/_/  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//_/_/
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
#ifdef _WIN32
	// To avoid complaints about sscanf
	#define _CRT_SECURE_NO_WARNINGS
#endif

#include "PsyTime.h"
#include "Utils.h"

namespace cmlabs {

int64 GetTimeAge(uint64 t) {
	if (!t)
		return 0;
	uint64 now = GetTimeNow();
	if (now >= t)
		return (int64)(now - t);
	else
		return (int64)((t - now) * -1);
}

int32 GetTimeAgeMS(uint64 t) {
	if (!t)
		return 0;
	uint64 now = GetTimeNow();
	if (now >= t)
		return (int32)((now - t) / 1000);
	else
		return (int32)((t - now) * -1)/1000;
}

uint64 GetTimeNow() {
//	uint64 tmc = MemoryManager::GetTMC();
	//uint64 tmc = CurrentTMC;
	//uint64 lsa = NetTimeAdjust;
//	if (tmc == 0)
//		return 0;

	uint64 time = 0;
	#if defined	WINDOWS
		LARGE_INTEGER perfCount, perfFreq;
		if (QueryPerformanceCounter(&perfCount) != 0) {
			QueryPerformanceFrequency(&perfFreq);
			uint64 lastWrapuSec;
			// To avoid rounding errors...
		//	lastWrapuSec = perfCount.QuadPart * (1000000.0 / perfFreq.QuadPart);
		//	lastWrapuSec2 = (perfCount.QuadPart * 1000000) / perfFreq.QuadPart;
		//	if (lastWrapuSec != lastWrapuSec2) {
		//		lastWrapuSec = lastWrapuSec;
		//	}
			if (perfCount.QuadPart < 0xFFFFFFFFFFL)
				lastWrapuSec = (perfCount.QuadPart * 1000000) / perfFreq.QuadPart;
			else
				lastWrapuSec = (uint64)(perfCount.QuadPart * (1000000.0 / perfFreq.QuadPart));
				//lastWrapuSec = (perfCount.QuadPart / perfFreq.QuadPart) * 1000000;

			if (!CurrentTMC || ((CurrentTMCMode == TMC_MASTER) && (lastWrapuSec - LastTMCSync > TMC_SYNC_INTERVAL))) {
				CurrentTMC = SyncToHardwareClock();
				LastTMCSync = lastWrapuSec;
			}
			// Time now is usec at last wrap + usec since last wrap
			// We also need to add the Local Sync Adjustment, if syncing with other computers
			//uint64 now = tmc + lastWrapuSec + lsa;
			uint64 now = CurrentTMC + lastWrapuSec + NetTimeAdjust;

			//uint64 secs = lastWrapuSec / 1000000;
			//uint64 days = secs / (60*60*24);
			//uint64 years = days / 365;

			//uint64 nsecs = now / 1000000;
			//uint64 ndays = nsecs / (60*60*24);
			//uint64 nyears = ndays / 365;
			//int t = 0;
			return now;
		}
		else
			return 0;
		
	#elif defined LINUX
		struct timespec t;
		if (clock_gettime(CLOCK_MONOTONIC, &t) == 0) {
			uint64 lastWrapuSec = (t.tv_sec * 1000000) + (t.tv_nsec / 1000);

			if (!CurrentTMC || ((CurrentTMCMode == TMC_MASTER) && (lastWrapuSec - LastTMCSync > TMC_SYNC_INTERVAL))) {
				CurrentTMC = SyncToHardwareClock();
				LastTMCSync = lastWrapuSec;
			}

			uint64 now = CurrentTMC + lastWrapuSec + NetTimeAdjust;
			return now;
		}
		else
			return 0;
	#elif defined OSX
	#endif
	return 0;
}

bool SetCurrentNetSyncDif(int64 netTimeDif) {
	NetTimeAdjust += netTimeDif;
	return true;
}

bool SetCurrentTimeSyncData(uint64 tmc, int64 netTimeAdjust) {
	CurrentTMC = tmc;
	CurrentTMCMode = TMC_SLAVE;
	NetTimeAdjust = netTimeAdjust;
	return true;
}

bool GetCurrentTimeSyncData(uint64& tmc, int64& netTimeAdjust) {
	tmc = CurrentTMC;
	netTimeAdjust = NetTimeAdjust;
	return true;
}

// Calculate the current Time Mapping Constant
uint64 SyncToHardwareClock() {

	#if defined	WINDOWS
		// On Windows TMC contains the number of microseconds since year 0
		// when the PerfCount was last 0
		LARGE_INTEGER perfCount, perfFreq;
		if (QueryPerformanceCounter(&perfCount) != 0) {
			QueryPerformanceFrequency(&perfFreq);

			// We have performance counters here, good...
			// Now do a busy wait to find the perfCount exactly when the ftime clock ticks
			struct timeb timebuf1, timebuf2;
			ftime(&timebuf1);
			while (true) {
				ftime(&timebuf2);
				if (timebuf1.millitm != timebuf2.millitm) {
					QueryPerformanceCounter(&perfCount);
					break;
				}
			}
			// we now know the perfcount exactly when the clock in timebuf2 ticked
			uint64 lastWrapuSec;
			if (perfCount.QuadPart < 0xFFFFFFFFFFL)
				lastWrapuSec = (perfCount.QuadPart * 1000000) / perfFreq.QuadPart;
			else
				lastWrapuSec = (uint64)(perfCount.QuadPart * (1000000.0 / perfFreq.QuadPart));
				//lastWrapuSec = (perfCount.QuadPart / perfFreq.QuadPart) * 1000000;

			// usec since midnight (00:00:00), January 1, 1970, coordinated universal time (UTC)
			uint64 nowuSec = (((uint64)timebuf2.time) * 1000000) + ((uint64)timebuf2.millitm * 1000);
			// usec since 1970 - lastwrap + usec year 0-1970 = usec since year 0 at last wrap
			uint64 tmc = nowuSec - lastWrapuSec + USEC_YEAR_0_TO_1970;
			//uint64 usecs = lastWrapuSec;
			//uint64 secs = usecs / 1000000;
			//uint64 days = secs / (60*60*24);
			//uint64 years = days / 365;
			//int t = 0;
			return (CurrentTMC = tmc);
		}
		else
			return 0;
	#elif defined LINUX
		// On Linux TMC contains the number of microseconds since year 0
	// when CLOCK_MONOTONE was last 0
		struct timespec t;
		if (clock_gettime(CLOCK_MONOTONIC, &t) == 0) {
			// We have the CLOCK_MONOTONE here, good...
			// Now do a busy wait to find the perfCount exactly when the ftime clock ticks
			struct timeb timebuf1, timebuf2;
			ftime(&timebuf1);
			while (true) {
				ftime(&timebuf2);
				if (timebuf1.millitm != timebuf2.millitm) {
					clock_gettime(CLOCK_MONOTONIC, &t);
					break;
				}
			}
			// we now know the CLOCK_MONOTONE exactly when the clock in timebuf2 ticked
			uint64 lastWrapuSec = (t.tv_sec * 1000000) + (t.tv_nsec / 1000);

			// usec since midnight (00:00:00), January 1, 1970, coordinated universal time (UTC)
			uint64 nowuSec = (((uint64)timebuf2.time) * 1000000) + ((uint64)timebuf2.millitm * 1000);
			// usec since 1970 - lastwrap + usec year 0-1970 = usec since year 0 at last wrap
			uint64 tmc = nowuSec - lastWrapuSec + USEC_YEAR_0_TO_1970;
			//uint64 usecs = lastWrapuSec;
			//uint64 secs = usecs / 1000000;
			//uint64 days = secs / (60*60*24);
			//uint64 years = days / 365;
			//int t = 0;
			return (CurrentTMC = tmc);
		}
		else
			return 0;
	#elif defined OSX
		return 0;
	#endif
}

// Estimate the next TMC wrap-around
uint64 EstNextTMCWrap() {
	#if defined	WINDOWS
		LARGE_INTEGER perfCount, perfFreq;
		if (QueryPerformanceCounter(&perfCount) != 0) {
			QueryPerformanceFrequency(&perfFreq);

			uint64 nextWrapTicks = 0xFFFFFFFFFFFFFFFFL - perfCount.QuadPart;
			uint64 nextWrapuSec;

			if (nextWrapTicks < 0xFFFFFFFFFFL)
				nextWrapuSec = (nextWrapTicks  * 1000000) / perfFreq.QuadPart;
			else
				nextWrapuSec = (uint64)(nextWrapTicks * (1000000.0 / perfFreq.QuadPart));
			return nextWrapuSec;
		}
		else
			return 0;
		
	#elif defined LINUX
		return 0xFFFFFFFFFFFFFFFFL;
	#elif defined OSX
		return 0xFFFFFFFFFFFFFFFFL;
	#endif
}

struct PsyDateAndTime GetDateAndTimeUTC(uint64 t) {
	return GetDateAndTime(t, false);
}

struct PsyDateAndTime GetDateAndTime(uint64 t, bool local) {
	struct tm time;
	struct PsyDateAndTime tad;
	memset(&tad, 0, sizeof(struct PsyDateAndTime));
	if (t < USEC_YEAR_0_TO_1970)
		return tad;
	// First create the time_t from t
	uint64 usSince1970 = (t - USEC_YEAR_0_TO_1970);
	time_t timet = (time_t)(usSince1970 / 1000000);
	#if defined	WINDOWS
		if (local) {
			if (localtime_s(&time, &timet) != 0)
				return tad;
		}
		else {
			if (gmtime_s(&time, &timet) != 0)
				return tad;
		}

		_get_dstbias((long*)&tad.dstsec); // tad.dstsec = _dstbias*(-1);
		_get_timezone((long*)&tad.tzsec); // tad.tzsec = _timezone;
		size_t l;
		_get_tzname(&l, tad.zonename, 64, 1);
		// utils::strcpyavail(tad.tzname, _tzname[0], 64, true);
	#elif defined LINUX
		if (local)
			localtime_r(&timet, &time);
		else
			gmtime_r(&timet, &time);
		tad.dstsec = daylight*SECS_PER_HOUR;
		tad.tzsec = timezone;
		utils::strcpyavail(tad.zonename, tzname[0], 64, true);
	#elif defined OSX
	#endif

	tad.usec = usSince1970 % 1000000;
	tad.msec = tad.usec / 1000;
	tad.sec = time.tm_sec;
	tad.min = time.tm_min;
	tad.hour = time.tm_hour;
	tad.day = time.tm_mday;
	tad.mon = time.tm_mon + 1;
	tad.year = time.tm_year + 1900;
	tad.wday = time.tm_wday;
	tad.yday = time.tm_yday + 1;
	tad.dst = (time.tm_isdst > 0);
	return tad;
}

uint64 GetTimeFromPsyDateAndTime(struct PsyDateAndTime &tad) {
	struct tm time;
	time.tm_sec = tad.sec;
	time.tm_min = tad.min;
	time.tm_hour = tad.hour;
	time.tm_mday = tad.day;
	time.tm_mon = tad.mon - 1;
	time.tm_year = tad.year - 1900;
	time.tm_isdst = 0;

	time_t t = mktime(&time);
	if (t == (time_t)-1)
		return 0;
	
	return (((uint64)t) * 1000000) + USEC_YEAR_0_TO_1970;
}

struct PsyDateAndTime GetTimeDifference(uint64 t2, uint64 t1) {
	return GetTimeDifference(t2-t1);
}

uint32 GetTimeOffsetGMT() {
	// ##################
	return 0;
}

struct PsyDateAndTime GetTimeDifference(int64 dif) {
	struct PsyDateAndTime tad;
	memset(&tad, 0, sizeof(struct PsyDateAndTime));
	int64 t = dif;
	if (dif < 0) {
		t = -1*dif;
		tad.negative = true;
	}
	tad.usec = t % 1000000;
	tad.msec = tad.usec / 1000;
	uint64 left = t / 1000000;
	tad.yday = (uint16) (left / SECS_PER_DAY);
	tad.year = (uint16) (left / SECS_PER_YEAR);
	left = left % SECS_PER_YEAR;
	tad.mon = (uint16) (left / SECS_PER_MONTH);
	left = left % SECS_PER_MONTH;
	tad.day = (uint16) (left / SECS_PER_DAY);
	left = left % SECS_PER_DAY;
	tad.hour = (uint16) (left / SECS_PER_HOUR);
	left = left % SECS_PER_HOUR;
	tad.min = (uint16) (left / SECS_PER_MIN);
	tad.sec = left % SECS_PER_MIN;
	return tad;
}

uint64 GetTimeFromString(const char* str) {
	char dayname[64];
	char monthname[64];
	char tzone[64];
	int r;
	char* ch;
	uint32 day, hour, min, sec, year, mon = 0, usec = 0, msec = 0;
	//Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
	if ( (r = sscanf(str, "%63s %u %63s %u %u:%u:%u %63s",
			dayname, &day, monthname, &year, &hour, &min, &sec, tzone)) == 8) {
	}
	//Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
	else if ( (r = sscanf(str, "%63s %u-%63s-%u %u:%u:%u %63s",
			dayname, &day, monthname, &year, &hour, &min, &sec, tzone)) == 8) {
	}
	//Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format
	else if ( (r = sscanf(str, "%63s %63s %u %u:%u:%u %u",
			dayname, monthname, &day, &hour, &min, &sec, &year)) == 7) {
		utils::strcpyavail(tzone, "GMT", 64, true);
	}
	// Sortable format microsecs
	else if ( (r = sscanf(str, "%04u%02u%02u-%02u%02u%02u.%03u.%03u",
			&year, &mon, &day, &hour, &min, &sec, &msec, &usec)) == 8) {
		utils::strcpyavail(tzone, "GMT", 64, true);
	}
	// Sortable format millisecs
	else if ( (r = sscanf(str, "%04u%02u%02u-%02u%02u%02u.%03u",
			&year, &mon, &day, &hour, &min, &sec, &msec)) == 7) {
		utils::strcpyavail(tzone, "GMT", 64, true);
	}
	// Sortable format secs
	else if ( (r = sscanf(str, "%04u%02u%02u-%02u%02u%02u",
			&year, &mon, &day, &hour, &min, &sec)) == 6) {
		utils::strcpyavail(tzone, "GMT", 64, true);
	}
	// Sortable format
	else if ( (r = sscanf(str, "%02u%02u%02u%02u%02u%02u",
			&year, &mon, &day, &hour, &min, &sec)) == 6) {
		utils::strcpyavail(tzone, "GMT", 64, true);
	}
	else
		return 0;

	if ( (ch = strchr(dayname, ',')) != NULL)
		*ch = 0;

	if ( stricmp(tzone, "GMT") != 0 )
		return 0;

	if (year < 100)
		year += (year < 80) ? 2000 : 1900;

	struct PsyDateAndTime tad;
	memset(&tad, 0, sizeof(struct PsyDateAndTime));

	tad.usec = usec + (1000*msec);
	tad.msec = msec;
	tad.year = year;
	tad.day = day;
	tad.hour = hour;
	tad.min = min;
	tad.sec = sec;
	tad.mon = 0;

	if (mon)
		tad.mon = mon;
	else {
		for (uint16 n=0; n<12; n++) {
			if ( (stricmp(monthname, PsyMonths[n]) == 0) || (stricmp(monthname, PsyMonthsFull[n]) == 0) ) {
				tad.mon = n;
				break;
			}
		}
	}
	if (tad.mon == 0)
		return 0;

	return GetTimeFromPsyDateAndTime(tad);
}

uint32 GetHTTPTime(uint64 time, char* buffer, uint32 size) {
	if (!buffer || (size < 64))
		return 0;
	struct PsyDateAndTime ts = GetDateAndTime(time, false);
	snprintf(buffer, size, "%s, %u %s %u %02u:%02u:%02u GMT",
		PsyDays[ts.wday], ts.day, PsyMonths[ts.mon], ts.year, ts.hour, ts.min, ts.sec);
	return (uint32)strlen(buffer);
}

char* PrintTime(uint64 t, bool local, bool us, bool ms) {
	char* str = new char[1024];
	struct PsyDateAndTime time = GetDateAndTime(t, local);
	if (us)
		snprintf(str, 1024, "%02u/%02u/%04u %02u:%02u:%02u.%03u.%03u", time.day, time.mon, time.year, time.hour, time.min, time.sec, time.msec, time.usec % 1000);
	else if (ms)
		snprintf(str, 1024, "%02u/%02u/%04u %02u:%02u:%02u.%03u", time.day, time.mon, time.year, time.hour, time.min, time.sec, time.msec);
	else
		snprintf(str, 1024, "%02u/%02u/%04u %02u:%02u:%02u", time.day, time.mon, time.year, time.hour, time.min, time.sec);
	return str;
}

char* PrintTimeOnly(uint64 t, bool local, bool us, bool ms) {
	char* str = new char[1024];
	struct PsyDateAndTime time = GetDateAndTime(t, local);
	if (us)
		snprintf(str, 1024, "%02u:%02u:%02u.%03u.%03u", time.hour, time.min, time.sec, time.msec, time.usec % 1000);
	else if (ms)
		snprintf(str, 1024, "%02u:%02u:%02u.%03u", time.hour, time.min, time.sec, time.msec);
	else
		snprintf(str, 1024, "%02u:%02u:%02u", time.hour, time.min, time.sec);
	return str;
}

char* PrintDate(uint64 t, bool local) {
	char* str = new char[1024];
	struct PsyDateAndTime time = GetDateAndTime(t, local);
	snprintf(str, 1024, "%02u/%02u/%04u", time.day, time.mon, time.year);
	return str;
}

char* PrintTimeSortable(uint64 t, bool local) {
	char* str = new char[1024];
	struct PsyDateAndTime time = GetDateAndTime(t, local);
	snprintf(str, 1024, "%04u%02u%02u-%02u%02u%02u", time.year, time.mon, time.day, time.hour, time.min, time.sec);
	return str;
}

char* PrintTimeSortableMillisec(uint64 t, bool local) {
	char* str = new char[1024];
	struct PsyDateAndTime time = GetDateAndTime(t, local);
	snprintf(str, 1024, "%04u%02u%02u-%02u%02u%02u.%03u", time.year, time.mon, time.day, time.hour, time.min, time.sec, time.msec);
	return str;
}

char* PrintTimeSortableMicrosec(uint64 t, bool local) {
	char* str = new char[1024];
	struct PsyDateAndTime time = GetDateAndTime(t, local);
	snprintf(str, 1024, "%04u%02u%02u-%02u%02u%02u.%03u.%03u", time.year, time.mon, time.day, time.hour, time.min, time.sec, time.msec, time.usec%1000);
	return str;
}

char* PrintDateSortable(uint64 t, bool local) {
	char* str = new char[1024];
	struct PsyDateAndTime time = GetDateAndTime(t, local);
	snprintf(str, 1024, "%04u%02u%02u", time.year, time.mon, time.day);
	return str;
}

char* PrintDateSortableDelimiter(uint64 t, const char* del, bool local) {
	char* str = new char[1024];
	struct PsyDateAndTime time = GetDateAndTime(t, local);
	snprintf(str, 1024, "%04u%s%02u%s%02u", time.year, del, time.mon, del, time.day);
	return str;
}

char* PrintTimeDif(uint64 t, bool us, bool ms) {
	char* str = new char[1024];
	char* sign = new char[2];
	char* subsecs = NULL;
	utils::strcpyavail(sign, (t<0) ? "-" : "", 2, true);
	if (t == 0) {
		if (us)
			snprintf(str, 1024, "0us");
		else if (ms)
			snprintf(str, 1024, "0ms");
		else
			snprintf(str, 1024, "0s");
	}
	else {
		uint64 absT = abs64(t);
		if (absT < 1000) {
			if (us)
				snprintf(str, 1024, "%s%uus", sign, (uint32)absT);
			else if (ms)
				snprintf(str, 1024, "%s%ums", sign, (uint32)round((double)absT / 1000.0));
			else
				snprintf(str, 1024, "%s%us", sign, (uint32)round((double)absT / 1000000.0));
		}
		else if (absT < 1000000) {
			if (us)
				snprintf(str, 1024, "%s%.3fms", sign, absT / 1000.0);
			else if (ms)
				snprintf(str, 1024, "%s%ums", sign, (uint32)round(absT / 1000.0));
			else
				snprintf(str, 1024, "%s%us", sign, (uint32)round((double)absT/1000000.0));
		}
		else if (absT < 60000000) {
			if (us)
				snprintf(str, 1024, "%s%.6fs", sign, absT / 1000000.0);
			else if (ms)
				snprintf(str, 1024, "%s%.3fs", sign, absT / 1000000.0);
			else
				snprintf(str, 1024, "%s%us", sign, (uint32)round((double)absT / 1000000.0));
		}
		else {
			subsecs = new char[20];
			uint64 absSec = absT/1000000;
			struct PsyDateAndTime difTime = GetTimeDifference(t);

			if (us)
				snprintf(subsecs, 20, "%s%06uus", (absSec < SECS_PER_HOUR) ? " " : ".", difTime.usec);
			else if (ms)
				snprintf(subsecs, 20, "%s%03ums", (absSec < SECS_PER_HOUR) ? " " : ".", (uint32)round(difTime.usec / 1000.0));
			else
				subsecs[0] = 0;

			if (absSec < SECS_PER_HOUR)
				snprintf(str, 1024, "%s%um %us%s", sign, difTime.min, difTime.sec, subsecs);
			else if (absSec < SECS_PER_DAY)
				snprintf(str, 1024, "%s%02u:%02u:%02u%s", sign, difTime.hour, difTime.min, difTime.sec, subsecs);
			else if (difTime.yday == 1)
				snprintf(str, 1024, "%s%u day %02u:%02u:%02u%s", sign, difTime.yday, difTime.hour, difTime.min, difTime.sec, subsecs);
			else if (difTime.yday < 365)
				snprintf(str, 1024, "%s%u days %02u:%02u:%02u%s", sign, difTime.yday, difTime.hour, difTime.min, difTime.sec, subsecs);
			else
				snprintf(str, 1024, "%s%u year(s) %u month(s) %u day(s) %02u:%02u:%02u%s",
					sign, difTime.year, difTime.mon, difTime.day, difTime.hour, difTime.min, difTime.sec, subsecs);
			delete[] subsecs;
		}
	}
	delete[] sign;
	return str;
}

std::string PrintTimeNowString(bool local, bool us, bool ms) {
	return PrintTimeString(GetTimeNow(), local, us, ms);
}

std::string PrintTimeString(uint64 t, bool local, bool us, bool ms) {
	char* str = PrintTime(t, local, us, ms);
	if (!str)
		return "";
	std::string res = str;
	delete [] str;
	return res;
}

std::string PrintTimeOnlyString(uint64 t, bool local, bool us, bool ms) {
	char* str = PrintTimeOnly(t, local, us, ms);
	if (!str)
		return "";
	std::string res = str;
	delete[] str;
	return res;
}

std::string PrintDateString(uint64 t, bool local) {
	char* str = PrintDate(t, local);
	if (!str)
		return "";
	std::string res = str;
	delete [] str;
	return res;
}

std::string PrintDateStringSortable(uint64 t, bool local) {
	char* str = PrintDateSortable(t, local);
	if (!str)
		return "";
	std::string res = str;
	delete [] str;
	return res;
}

std::string PrintDateStringSortableDelimiter(uint64 t, const char* del, bool local) {
	char* str = PrintDateSortableDelimiter(t, del, local);
	if (!str)
		return "";
	std::string res = str;
	delete [] str;
	return res;
}


std::string PrintTimeDifString(uint64 t, bool us, bool ms) {
	char* str = PrintTimeDif(t, us, ms);
	if (!str)
		return "";
	std::string res = str;
	delete [] str;
	return res;
}

std::string PrintTimeSortableString(uint64 t, bool local) {
	char* str = PrintTimeSortable(t, local);
	if (!str)
		return "";
	std::string res = str;
	delete [] str;
	return res;
}

std::string PrintTimeSortableMillisecString(uint64 t, bool local) {
	char* str = PrintTimeSortableMillisec(t, local);
	if (!str)
		return "";
	std::string res = str;
	delete [] str;
	return res;
}

std::string PrintTimeSortableMicrosecString(uint64 t, bool local) {
	char* str = PrintTimeSortableMicrosec(t, local);
	if (!str)
		return "";
	std::string res = str;
	delete [] str;
	return res;
}

uint64 FTime2PsyTime(uint64 t) {
	return (t * 1000000) + USEC_YEAR_0_TO_1970;
}



bool PsyTime_UnitTest() {
	printf("\nTesting PsyTime...\n\n");

//	CurrentTMC = CalcTMCNow();
	int64 dif;

	//struct timespec t;
	//clock_gettime(CLOCK_MONOTONIC, &t);
	//uint64 lastWrapuSec = (t.tv_sec * 1000000) + (t.tv_nsec / 1000);
	//uint64 now = CurrentTMC + lastWrapuSec;
	//uint64 lastNow = now;
	//utils::Sleep(100);
	//for (int m=0; m<100; m++) {
	//	if (clock_gettime(CLOCK_MONOTONIC, &t) == 0) {
	//		uint64 lastWrapuSec = (t.tv_sec * 1000000) + (t.tv_nsec / 1000);
	//		uint64 now = CurrentTMC + lastWrapuSec;
	//		dif = now - lastNow;
	//		if ( (dif < 90000) || (dif > 110000) )
	//			printf("FAIL: %d(%u).%d(%u) -> %ld(%lu) => %ld(%lu)\n", t.tv_sec, t.tv_sec, t.tv_nsec, t.tv_nsec, lastWrapuSec, lastWrapuSec, now, now);
	//		else
	//			printf("OK:   %d(%u).%d(%u) -> %ld(%lu) => %ld(%lu)\n", t.tv_sec, t.tv_sec, t.tv_nsec, t.tv_nsec, lastWrapuSec, lastWrapuSec, now, now);
	//		lastNow = now;
	//		utils::Sleep(100);
	//	}
	//}
	//return true;

	uint64 t0 = GetTimeNow();
	utils::Sleep(10);
	uint64 t1 = GetTimeNow();

	struct PsyDateAndTime d0 = GetDateAndTime(t0);
	struct PsyDateAndTime d1 = GetDateAndTime(t1);
	struct PsyDateAndTime difTime = GetTimeDifference(t1, t0);

	printf("Time0: %02u:%02u:%02u.%06u (%d + %d)\n", d0.hour, d0.min, d0.sec, d0.usec, d0.tzsec, d0.dstsec);
	printf("Time1: %02u:%02u:%02u.%06u (%d + %d)\n", d1.hour, d1.min, d1.sec, d1.usec, d1.tzsec, d1.dstsec);
	if (difTime.negative)
		printf("Dif:   -%02u:%02u:%02u.%06u\n", difTime.hour, difTime.min, difTime.sec, difTime.usec);
	else
		printf("Dif:   +%02u:%02u:%02u.%06u\n", difTime.hour, difTime.min, difTime.sec, difTime.usec);

//	printf("\n\n");

	for (int n=0; n<100; n++) {
		t0 = GetTimeNow();
		utils::Sleep(10);
		t1 = GetTimeNow();
		dif = t1 - t0;
		if ( (dif < 8000) || (dif > 15000) ) {
			difTime = GetTimeDifference(t1, t0);
			// printf("PsyTime UnitTest Failed!\n");
			if (difTime.negative)
				printf("Dif [%d]:   -%02u:%02u:%02u.%06u (%llu - %llu = %lld)\n\n", n, difTime.hour, difTime.min, difTime.sec, difTime.usec, t1, t0, dif);
			else
				printf("Dif [%d]:   +%02u:%02u:%02u.%06u (%llu - %llu = %lld)\n\n", n, difTime.hour, difTime.min, difTime.sec, difTime.usec, t1, t0, dif);
			return false;
		}
		else {
			//printf("OK  [%d]:   -%02u:%02u:%02u.%06u (%lu - %lu = %lu)\n\n", n, difTime.hour, difTime.min, difTime.sec, difTime.usec, t1, t0, dif);
		}
	}

	printf("\n*** PsyTime test ran successfully ***\n\n");
	return true;
}


} // namespace cmlabs
