12 #define _CRT_SECURE_NO_WARNINGS
30 return (int64)(now - t);
32 return (int64)((t - now) * -1);
40 return (int32)((now - t) / 1000);
42 return (int32)((t - now) * -1)/1000;
78 LARGE_INTEGER perfCount, perfFreq;
79 if (QueryPerformanceCounter(&perfCount) != 0) {
80 QueryPerformanceFrequency(&perfFreq);
88 if (perfCount.QuadPart < 0xFFFFFFFFFFL)
89 lastWrapuSec = (perfCount.QuadPart * 1000000) / perfFreq.QuadPart;
91 lastWrapuSec = (uint64)(perfCount.QuadPart * (1000000.0 / perfFreq.QuadPart));
116 #elif defined LINUX || defined OSX
118 if (clock_gettime(CLOCK_MONOTONIC, &t) == 0) {
119 uint64 lastWrapuSec = (uint64)(t.tv_sec) * 1000000 + (t.tv_nsec / 1000);
173 LARGE_INTEGER perfCount, perfFreq;
174 if (QueryPerformanceCounter(&perfCount) != 0) {
175 QueryPerformanceFrequency(&perfFreq);
179 struct timeb timebuf1, timebuf2;
183 if (timebuf1.millitm != timebuf2.millitm) {
184 QueryPerformanceCounter(&perfCount);
190 if (perfCount.QuadPart < 0xFFFFFFFFFFL)
191 lastWrapuSec = (perfCount.QuadPart * 1000000) / perfFreq.QuadPart;
193 lastWrapuSec = (uint64)(perfCount.QuadPart * (1000000.0 / perfFreq.QuadPart));
197 uint64 nowuSec = (((uint64)timebuf2.time) * 1000000) + ((uint64)timebuf2.millitm * 1000);
232 if (clock_gettime(CLOCK_MONOTONIC, &t) == 0) {
233 struct timeval tv1, tv2;
234 gettimeofday(&tv1, NULL);
236 const int maxWait = 1000000;
237 while (waitCount < maxWait) {
238 gettimeofday(&tv2, NULL);
239 if (tv1.tv_sec != tv2.tv_sec || tv1.tv_usec != tv2.tv_usec) {
240 clock_gettime(CLOCK_MONOTONIC, &t);
245 if (waitCount >= maxWait)
246 clock_gettime(CLOCK_MONOTONIC, &t);
247 uint64 lastWrapuSec = (uint64)(t.tv_sec) * 1000000 + (t.tv_nsec / 1000);
248 uint64 nowuSec = (uint64)tv2.tv_sec * 1000000 + (uint64)tv2.tv_usec;
258 if (clock_gettime(CLOCK_MONOTONIC, &t) == 0) {
259 struct timeval tv1, tv2;
260 gettimeofday(&tv1, NULL);
262 const int maxWait = 1000000;
263 while (waitCount < maxWait) {
264 gettimeofday(&tv2, NULL);
265 if (tv1.tv_sec != tv2.tv_sec || tv1.tv_usec != tv2.tv_usec) {
266 clock_gettime(CLOCK_MONOTONIC, &t);
271 if (waitCount >= maxWait)
272 clock_gettime(CLOCK_MONOTONIC, &t);
273 uint64 lastWrapuSec = (uint64)(t.tv_sec) * 1000000 + (t.tv_nsec / 1000);
274 uint64 nowuSec = (uint64)tv2.tv_sec * 1000000 + (uint64)tv2.tv_usec;
291 LARGE_INTEGER perfCount, perfFreq;
292 if (QueryPerformanceCounter(&perfCount) != 0) {
293 QueryPerformanceFrequency(&perfFreq);
295 uint64 nextWrapTicks = 0xFFFFFFFFFFFFFFFFL - perfCount.QuadPart;
298 if (nextWrapTicks < 0xFFFFFFFFFFL)
299 nextWrapuSec = (nextWrapTicks * 1000000) / perfFreq.QuadPart;
301 nextWrapuSec = (uint64)(nextWrapTicks * (1000000.0 / perfFreq.QuadPart));
307 #elif defined LINUX || defined OSX
308 return 0xFFFFFFFFFFFFFFFFL;
332 time_t timet = (time_t)(usSince1970 / 1000000);
335 if (localtime_s(&time, &timet) != 0)
339 if (gmtime_s(&time, &timet) != 0)
343 _get_dstbias((
long*)&tad.
dstsec);
344 _get_timezone((
long*)&tad.
tzsec);
346 _get_tzname(&l, tad.
zonename, 64, 1);
348 #elif defined LINUX || defined OSX
350 localtime_r(&timet, &time);
352 gmtime_r(&timet, &time);
354 tad.
tzsec = timezone;
358 tad.
usec = usSince1970 % 1000000;
360 tad.
sec = time.tm_sec;
361 tad.
min = time.tm_min;
362 tad.
hour = time.tm_hour;
363 tad.
day = time.tm_mday;
364 tad.
mon = time.tm_mon + 1;
365 tad.
year = time.tm_year + 1900;
366 tad.
wday = time.tm_wday;
367 tad.
yday = time.tm_yday + 1;
368 tad.
dst = (time.tm_isdst > 0);
378 time.tm_sec = tad.
sec;
379 time.tm_min = tad.
min;
380 time.tm_hour = tad.
hour;
381 time.tm_mday = tad.
day;
382 time.tm_mon = tad.
mon - 1;
383 time.tm_year = tad.
year - 1900;
386 time_t t = mktime(&time);
416 tad.
usec = t % 1000000;
418 uint64 left = t / 1000000;
444 uint32 day, hour, min, sec, year, mon = 0, usec = 0, msec = 0;
446 if ( (r = sscanf(str,
"%63s %u %63s %u %u:%u:%u %63s",
447 dayname, &day, monthname, &year, &hour, &min, &sec, tzone)) == 8) {
450 else if ( (r = sscanf(str,
"%63s %u-%63s-%u %u:%u:%u %63s",
451 dayname, &day, monthname, &year, &hour, &min, &sec, tzone)) == 8) {
454 else if ( (r = sscanf(str,
"%63s %63s %u %u:%u:%u %u",
455 dayname, monthname, &day, &hour, &min, &sec, &year)) == 7) {
459 else if ( (r = sscanf(str,
"%04u%02u%02u-%02u%02u%02u.%03u.%03u",
460 &year, &mon, &day, &hour, &min, &sec, &msec, &usec)) == 8) {
464 else if ( (r = sscanf(str,
"%04u%02u%02u-%02u%02u%02u.%03u",
465 &year, &mon, &day, &hour, &min, &sec, &msec)) == 7) {
469 else if ( (r = sscanf(str,
"%04u%02u%02u-%02u%02u%02u",
470 &year, &mon, &day, &hour, &min, &sec)) == 6) {
474 else if ( (r = sscanf(str,
"%02u%02u%02u%02u%02u%02u",
475 &year, &mon, &day, &hour, &min, &sec)) == 6) {
481 if ( (ch = strchr(dayname,
',')) != NULL)
484 if (
stricmp(tzone,
"GMT") != 0 )
488 year += (year < 80) ? 2000 : 1900;
493 tad.
usec = usec + (1000*msec);
505 for (uint16 n=0; n<12; n++) {
519 if (!buffer || (size < 64))
522 snprintf(buffer, size,
"%s, %u %s %u %02u:%02u:%02u GMT",
524 return (uint32)strlen(buffer);
527char*
PrintTime(uint64 t,
bool local,
bool us,
bool ms) {
528 char* str =
new char[1024];
531 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);
533 snprintf(str, 1024,
"%02u/%02u/%04u %02u:%02u:%02u.%03u", time.
day, time.
mon, time.
year, time.
hour, time.
min, time.
sec, time.
msec);
535 snprintf(str, 1024,
"%02u/%02u/%04u %02u:%02u:%02u", time.
day, time.
mon, time.
year, time.
hour, time.
min, time.
sec);
540 char* str =
new char[1024];
543 snprintf(str, 1024,
"%02u:%02u:%02u.%03u.%03u", time.
hour, time.
min, time.
sec, time.
msec, time.
usec % 1000);
545 snprintf(str, 1024,
"%02u:%02u:%02u.%03u", time.
hour, time.
min, time.
sec, time.
msec);
547 snprintf(str, 1024,
"%02u:%02u:%02u", time.
hour, time.
min, time.
sec);
552 char* str =
new char[1024];
554 snprintf(str, 1024,
"%02u/%02u/%04u", time.
day, time.
mon, time.
year);
559 char* str =
new char[1024];
561 snprintf(str, 1024,
"%04u%02u%02u-%02u%02u%02u", time.
year, time.
mon, time.
day, time.
hour, time.
min, time.
sec);
566 char* str =
new char[1024];
568 snprintf(str, 1024,
"%04u%02u%02u-%02u%02u%02u.%03u", time.
year, time.
mon, time.
day, time.
hour, time.
min, time.
sec, time.
msec);
573 char* str =
new char[1024];
575 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);
580 char* str =
new char[1024];
582 snprintf(str, 1024,
"%04u%02u%02u", time.
year, time.
mon, time.
day);
587 char* str =
new char[1024];
589 snprintf(str, 1024,
"%04u%s%02u%s%02u", time.
year, del, time.
mon, del, time.
day);
602 char* str =
new char[1024];
603 char* sign =
new char[2];
604 char* subsecs = NULL;
608 snprintf(str, 1024,
"0us");
610 snprintf(str, 1024,
"0ms");
612 snprintf(str, 1024,
"0s");
619 snprintf(str, 1024,
"%s%uus", sign, (uint32)absT);
621 snprintf(str, 1024,
"%s%ums", sign, (uint32)round((
double)absT / 1000.0));
623 snprintf(str, 1024,
"%s%us", sign, (uint32)round((
double)absT / 1000000.0));
625 else if (absT < 1000000) {
627 snprintf(str, 1024,
"%s%.3fms", sign, absT / 1000.0);
629 snprintf(str, 1024,
"%s%ums", sign, (uint32)round(absT / 1000.0));
631 snprintf(str, 1024,
"%s%us", sign, (uint32)round((
double)absT/1000000.0));
633 else if (absT < 60000000) {
635 snprintf(str, 1024,
"%s%.6fs", sign, absT / 1000000.0);
637 snprintf(str, 1024,
"%s%.3fs", sign, absT / 1000000.0);
639 snprintf(str, 1024,
"%s%us", sign, (uint32)round((
double)absT / 1000000.0));
642 subsecs =
new char[20];
643 uint64 absSec = absT/1000000;
647 if (us && difTime.
usec)
648 snprintf(subsecs, 20,
"%s%06uus", (absSec <
SECS_PER_HOUR) ?
" " :
".", difTime.
usec);
649 else if (ms && (roundMS = (uint32)round(difTime.
usec / 1000.0)))
650 snprintf(subsecs, 20,
"%s%03ums", (absSec <
SECS_PER_HOUR) ?
" " :
".", roundMS);
655 snprintf(str, 1024,
"%s%um %us%s", sign, difTime.
min, difTime.
sec, subsecs);
657 snprintf(str, 1024,
"%s%02u:%02u:%02u%s", sign, difTime.
hour, difTime.
min, difTime.
sec, subsecs);
658 else if (difTime.
yday == 1)
659 snprintf(str, 1024,
"%s%u day %02u:%02u:%02u%s", sign, difTime.
yday, difTime.
hour, difTime.
min, difTime.
sec, subsecs);
660 else if (difTime.
yday < 365)
661 snprintf(str, 1024,
"%s%u days %02u:%02u:%02u%s", sign, difTime.
yday, difTime.
hour, difTime.
min, difTime.
sec, subsecs);
663 snprintf(str, 1024,
"%s%u year(s) %u month(s) %u day(s) %02u:%02u:%02u%s",
680 std::string res = str;
689 std::string res = str;
698 std::string res = str;
707 std::string res = str;
716 std::string res = str;
726 std::string res = str;
735 std::string res = str;
744 std::string res = str;
753 std::string res = str;
775 if (!t0 || !t1) {
unittest::fail(
"PsyTime test: GetTimeNow returned 0\n");
return false; }
776 if (t1 <= t0) {
unittest::fail(
"PsyTime test: time did not advance across Sleep\n");
return false; }
792 int iterations = 100;
794 for (
int n=0; n<iterations; n++) {
799 if ( (dif < 8000) || (dif > 30000) ) {
805 unittest::fail(
"PsyTime test: sleep timing out of range at iteration %d (dif=%lld us)\n", n, dif);
810 double totalUs = (double)(
GetTimeNow() - loopStart);
820 "Time now, date breakdown, and sleep timing accuracy",
"core");
CMSDK time: µs-resolution 64-bit timestamps and the Time Mapping Constant (TMC).
Small, dependency-free unit test harness used by all CMSDK object tests.
Cross-platform utility toolbox for CMSDK: threading, synchronization, shared memory,...
static UnitTestRunner & instance()
Access the singleton (created on first use).
void registerTest(const char *name, UnitTestFunc func, const char *description="", const char *category="", bool inDefaultRun=true)
Register a test with the runner.
static uint64 CurrentTMC
Current Time Mapping Constant (µs offset from monotonic clock to absolute PsyTime).
std::string PrintTimeString(uint64 t, bool local=true, bool us=true, bool ms=true)
#define TMC_SYNC_INTERVAL
Re-sync interval in µs (1 minute).
#define SECS_PER_HOUR
Seconds per hour.
char * PrintDateSortable(uint64 t, bool local=true)
Date only, sortable (e.g.
std::string PrintDateStringSortableDelimiter(uint64 t, const char *del, bool local=true)
char * PrintTime(uint64 t, bool local=true, bool us=true, bool ms=true)
Full date + time-of-day.
std::string PrintTimeSortableMillisecString(uint64 t, bool local=true)
Sortable date+time with milliseconds.
char * PrintTimeSortableMicrosec(uint64 t, bool local)
Sortable date+time with microseconds.
uint64 GetTimeFromPsyDateAndTime(struct PsyDateAndTime &tad)
Convert a broken-down PsyDateAndTime back into a µs timestamp.
uint64 FTime2PsyTime(uint64 t)
Convert an ftime-style value (ms since Unix epoch) to a PsyTime µs timestamp.
#define USEC_YEAR_0_TO_1970
Microseconds between year 0000 and the Unix epoch (1970-01-01).
static uint8 CurrentTMCMode
Current TMC role: TMC_MASTER or TMC_SLAVE.
char * PrintTimeDif(uint64 t, bool us=true, bool ms=true)
Format a µs duration with scale-adaptive units: "250us", "1.500ms", "2.750s", "2m 3s",...
uint64 GetTimeNow()
Return the current absolute time (µs since year 0) according to the TMC.
#define SECS_PER_MIN
Seconds per minute.
std::string PrintDateString(uint64 t, bool local=true)
uint32 GetTimeOffsetGMT()
static char PsyMonthsFull[][10]
Full month names indexed 1..12; index 0 is "None".
char * PrintTimeSortableMillisec(uint64 t, bool local)
Sortable date+time with milliseconds.
static char PsyDays[][5]
Abbreviated weekday names indexed 1..7 (Mon..Sun); index 0 is "None".
char * PrintTimeOnly(uint64 t, bool local=true, bool us=true, bool ms=true)
Time-of-day only.
static int64 NetTimeAdjust
Signed network time adjustment (µs) applied when slaved to a remote master.
#define SECS_PER_MONTH
Average seconds per Gregorian month (365.2425/12 days).
bool SetCurrentTimeSyncData(uint64 tmc, int64 netTimeAdjust)
Install externally computed sync data (e.g.
bool PsyTime_UnitTest()
Run the PsyTime unit tests.
uint32 GetHTTPTime(uint64 time, char *buffer, uint32 size)
Format a timestamp as an HTTP-date (RFC 7231) string, e.g.
std::string PrintTimeSortableString(uint64 t, bool local=true)
Sortable date+time, second resolution.
uint64 SyncToHardwareClock()
Recompute the Time Mapping Constant by anchoring the monotonic clock to the wall clock.
std::string PrintTimeNowString(bool local=true, bool us=true, bool ms=true)
Format GetTimeNow().
std::string PrintTimeDifString(uint64 t, bool us=true, bool ms=true)
char * PrintDateSortableDelimiter(uint64 t, const char *del, bool local=true)
Sortable date with custom field delimiter.
bool SetCurrentNetSyncDif(int64 netTimeDif)
Set the network time difference for a slave node so its GetTimeNow() matches the master.
uint64 GetTimeFromString(const char *str)
Parse a textual date/time into a PsyTime timestamp.
std::string PrintTimeOnlyString(uint64 t, bool local=true, bool us=true, bool ms=true)
#define TMC_SLAVE
This node slaves its clock to a remote master.
int32 GetTimeAgeMS(uint64 t)
Age of a timestamp relative to now, in milliseconds.
#define TMC_MASTER
This node is the time master; others sync to it.
struct PsyDateAndTime GetTimeDifference(uint64 t1, uint64 t2)
Express the difference t1 - t2 as calendar fields (negative flag set when t2 > t1).
int64 GetTimeAge(uint64 t)
Age of a timestamp relative to now.
static char PsyMonths[][5]
Abbreviated month names indexed 1..12 (Jan..Dec); index 0 is "None".
struct PsyDateAndTime GetDateAndTimeUTC(uint64 t)
Break a timestamp into calendar fields in UTC.
static uint64 LastTMCSync
PsyTime timestamp of the last hardware-clock sync.
std::string PrintTimeSortableMicrosecString(uint64 t, bool local=true)
Sortable date+time with microseconds.
struct PsyDateAndTime GetDateAndTime(uint64 t, bool local=true)
Break a timestamp into calendar fields.
#define SECS_PER_DAY
Seconds per day.
#define SECS_PER_YEAR
Seconds in a (non-leap) 365-day year.
std::string PrintDateStringSortable(uint64 t, bool local=true)
char * PrintDate(uint64 t, bool local=true)
Date only, human-readable.
bool GetCurrentTimeSyncData(uint64 &tmc, int64 &netTimeAdjust)
Read the current sync data.
char * PrintTimeSortable(uint64 t, bool local)
Sortable date+time, second resolution.
uint64 EstNextTMCWrap()
Estimate when the underlying hardware counter will next wrap around.
bool Sleep(uint32 ms)
Suspend the calling thread.
uint32 strcpyavail(char *dst, const char *src, uint32 maxlen, bool copyAvailable)
Bounded strcpy that always NUL-terminates.
void fail(const char *fmt,...)
Set an explanatory reason shown on the FAIL line.
void metric(const char *name, double value, const char *unit="", bool higherIsBetter=true)
Record a performance metric.
void detail(const char *fmt,...)
Verbose-only indented diagnostic line (shown only when verbose=1).
void progress(int percent, const char *action)
Report progress with a short description of the current action.
void Register_PsyTime_Tests()
A PsyTime timestamp or time difference broken down into calendar fields.