Sleds/libeye/eyetime.cpp

733 lines
20 KiB
C++
Raw Normal View History

2025-03-13 21:28:38 +00:00
// Copyright (c) 2013-2015 IONU Security, Inc. All rights reserved.
//
// Time utility class
//#include <cstdio>
//#include <cstring>
//#include <ctype.h>
#include <sys/stat.h>
#ifdef WIN32
#include <Windows.h>
#else
#include <sys/time.h>
#endif
#include "eyelog.h"
#include "eyetime.h"
#include "eyeutils.h"
using namespace sequencelogic;
static char timezones[] = {'Y','W','X','V','U','T','S','R','Q','P','O','N','Z','A','B','C','D','E','F','G','I','K','L','M','\0'};
static unsigned int monthlengths[] = {31,28,31,30,31,30,31,31,30,31,30,31};
static int monthnums[] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
static int monthnumsleap[] = {6, 2, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
static bool isLeapYear(unsigned int nYear)
{
return (((nYear % 4) == 0) && (((nYear % 100) != 0) || ((nYear % 400) == 0)));
}
// Constructor that parses a date/time string
EyeTime::EyeTime (const char* timestr)
{
if (!SetTime (timestr)) {
daylight = 0;
utc = 0;
year = 0;
month = 0;
day = 0;
hour = 0;
minute = 0;
second = 0;
millisec = 0;
}
}
// Days since January 1st
void EyeTime::SetYearDay()
{
yday = day - 1; // Day of this month, but yday starts from 0
for (unsigned int i = 1; i < month; ++i)
yday += monthlengths[i-1];
// And, 1 for leap year, maybe...
if (isLeapYear(year) && (month > 2))
++yday;
}
// Days since Sunday
void EyeTime::SetWeekDay()
{
unsigned int tmpDay = (year - 1900) / 4;
tmpDay += (year - 1900);
tmpDay += day;
if (isLeapYear(year))
tmpDay += monthnumsleap[month-1];
else
tmpDay += monthnums[month-1];
wday = tmpDay % 7;
}
bool EyeTime::SetTimeMs (long long tMs)
{
bool bRetVal = false;
time_t timeSecs = (time_t)(tMs / 1000);
bRetVal = SetTime(timeSecs);
millisec = (unsigned int)(tMs % 1000);
return bRetVal;
}
bool EyeTime::SetTime (time_t t)
{
struct tm tmpTime;
#ifdef WIN32
// Store in GMT...
::gmtime_s(&tmpTime, &t);
#else
gmtime_r(&t, &tmpTime);
#endif
year = tmpTime.tm_year + 1900; // tm structure has year as years since 1900
month = tmpTime.tm_mon + 1; // tm structure has Jan as month '0'
yday = tmpTime.tm_yday;
wday = tmpTime.tm_wday;
day = tmpTime.tm_mday;
hour = tmpTime.tm_hour;
minute = tmpTime.tm_min;
second = tmpTime.tm_sec;
millisec = 0;
utc = 0;
daylight = 0;
return true;
}
time_t EyeTime::GetTime()
{
struct tm tmpTime;
tmpTime.tm_year = year - 1900; // tm structure has year as years since 1900
tmpTime.tm_mon = month - 1; // tm structure has Jan as month '0'
tmpTime.tm_yday = yday;
tmpTime.tm_wday = wday;
tmpTime.tm_mday = day;
tmpTime.tm_hour = hour;
tmpTime.tm_min = minute;
tmpTime.tm_sec = second;
time_t retVal = ::mktime(&tmpTime);
return retVal;
}
#ifdef WIN32
bool EyeTime::SetTime (SYSTEMTIME sysTime)
{
year = sysTime.wYear;
month = sysTime.wMonth;
day = sysTime.wDay;
wday = sysTime.wDayOfWeek;
hour = sysTime.wHour;
minute = sysTime.wMinute;
second = sysTime.wSecond;
millisec = sysTime.wMilliseconds;
utc = 0;
SetYearDay();
return true;
}
SYSTEMTIME EyeTime::GetSysTime()
{
SYSTEMTIME retVal = {0};
retVal.wYear = static_cast<WORD>(year);
retVal.wMonth = static_cast<WORD>(month);
retVal.wDayOfWeek = static_cast<WORD>(wday);
retVal.wDay = static_cast<WORD>(day);
retVal.wHour = static_cast<WORD>(hour);
retVal.wMinute = static_cast<WORD>(minute);
retVal.wSecond = static_cast<WORD>(second);
retVal.wMilliseconds = static_cast<WORD>(millisec);
return retVal;
}
#endif
long long EyeTime::GetTimeMs()
{
long long nRetVal = 0;
#ifdef WIN32
struct tm tmpTime = {};
#else
struct tm tmpTime = {0,0,0,0,0,0,0,0,0,0,0};
#endif
tmpTime.tm_year = year - 1900; // tm structure has year as years since 1900
tmpTime.tm_mon = month - 1; // tm structure has Jan as month '0'
tmpTime.tm_wday = yday;
tmpTime.tm_mday = day;
tmpTime.tm_hour = hour;
tmpTime.tm_min = minute;
tmpTime.tm_sec = second;
#ifdef WIN32
nRetVal = ((long long)::_mkgmtime(&tmpTime) * 1000) + millisec;
#elif defined(ANDROID)
char *pTZ = getenv("TZ");
setenv ("TZ", "", 1);
tzset();
nRetVal = ((long long)mktime(&tmpTime) * 1000) + millisec;
if (pTZ != NULL)
setenv("TZ", pTZ, 1);
else
unsetenv("TZ");
tzset();
#else
nRetVal = ((long long)::timegm(&tmpTime) * 1000) + millisec;
#endif
return nRetVal;
}
bool EyeTime::SetTime (const char* timestr)
{
if (sequencelogic::StrLen (timestr, 8) == 0)
return false;
daylight = 0;
utc = 0;
year = 0;
month = 0;
day = 0;
hour = 0;
minute = 0;
second = 0;
millisec = 0;
char delim = '\0';
char tdelim = '\0';
const char* ptr = timestr;
const char* token = ptr;
bool gothour = false;
bool gotmin = false;
bool gotsec = false;
bool gotmsec = false;
bool gotzone = false;
int val = 0;
int len = 0;
while (*ptr) {
switch (*ptr) {
case '-' :
case '+' :
case ':' :
case '.' :
case ' ' :
delim = tdelim;
tdelim = *ptr++;
break;
case '0' :
case '1' :
case '2' :
case '3' :
case '4' :
case '5' :
case '6' :
case '7' :
case '8' :
case '9' :
val = *ptr++ - '0';
while (*ptr) {
if (*ptr >= '0' && *ptr <='9')
val = val * 10 + (*ptr++ - '0');
else {
delim = tdelim;
tdelim = *ptr;
break;
}
}
if (year == 0) {
year = val;
if (year > 3000 || year < 1970) {
IONUDEBUG ("EyeTime::SetTime(%s) - invalid year field", timestr);
year = 2014;
month = 1;
day = 1;
return false;
}
}
else if (month == 0) {
month = val;
if (month > 12 || month < 1) {
IONUDEBUG ("EyeTime::SetTime(%s) - invalid month field", timestr);
month = 1;
day = 1;
return false;
}
}
else if (day == 0) {
day = val;
if (day < 1 || (day > monthlengths[month-1] && month != 2 && !isLeapYear(year) && day > 29)) {
IONUDEBUG ("EyeTime::SetTime(%s) - invalid day field", timestr);
day = 1;
return false;
}
}
else if (!gothour) {
hour = val;
gothour = true;
if (hour > 23) {
IONUDEBUG ("EyeTime::SetTime(%s) - invalid hour value %d", timestr, val);
hour = 0;
return false;
}
}
else if (!gotmin) {
minute = val;
gotmin = true;
if (minute > 59) {
IONUDEBUG ("EyeTime::SetTime(%s) - invalid minute value %d", timestr, val);
minute = 0;
return false;
}
}
else if (!gotsec) {
second = val;
gotsec = true;
if (second > 60) { // Allow for leap second
IONUDEBUG ("EyeTime::SetTime(%s) - invalid second value %d", timestr, val);
second = 0;
return false;
}
}
else if (delim == '.' && !gotmsec) {
millisec = val;
gotmsec = true;
if (millisec > 999) {
IONUDEBUG ("EyeTime::SetTime(%s) - invalid millisecond value %d", timestr, val);
millisec = 0;
return false;
}
}
// time value is an offset from UTC (for example, +01:00, -07:00)
else if ((delim == '-' || delim == '+') && !gotzone) {
if (val > 12) {
IONUDEBUG ("EyeTime::SetTime(%s) - invalid timezone value %d", timestr, val);
return false;
}
if (delim == '-')
utc = -val;
else
utc = +val;
while (*ptr) ptr++; // Skip to end
gotzone = true;
ConvertToGMTime();
break;
}
else {
IONUDEBUG ("EyeTime::SetTime(%s) - invalid numeric field %s", timestr, ptr);
return false;
}
default :
while (*ptr) {
token = ptr;
if ((*ptr >= 'a' && *ptr <='z') || (*ptr >= 'A' && *ptr <='Z'))
ptr++;
else
break;
}
len = (int)(ptr - token);
if (len >= 3) {
if (sequencelogic::StrnCmp (token, "Jan", 3) == 0) month = 1;
else if (sequencelogic::StrnCmp (token, "Feb", 3) == 0) month = 2;
else if (sequencelogic::StrnCmp (token, "Mar", 3) == 0) month = 3;
else if (sequencelogic::StrnCmp (token, "Apr", 3) == 0) month = 4;
else if (sequencelogic::StrnCmp (token, "May", 3) == 0) month = 5;
else if (sequencelogic::StrnCmp (token, "Jun", 3) == 0) month = 6;
else if (sequencelogic::StrnCmp (token, "Jul", 3) == 0) month = 7;
else if (sequencelogic::StrnCmp (token, "Aug", 3) == 0) month = 8;
else if (sequencelogic::StrnCmp (token, "Sep", 3) == 0) month = 9;
else if (sequencelogic::StrnCmp (token, "Oct", 3) == 0) month = 10;
else if (sequencelogic::StrnCmp (token, "Nov", 3) == 0) month = 11;
else if (sequencelogic::StrnCmp (token, "Dec", 3) == 0) month = 12;
}
else if (len == 1) {
for (int i = 0; i < 24; ++i) {
if (timezones[i] == *token) {
utc = i - 12;
ConvertToGMTime();
break;
}
}
}
//else // Skip character and continue
// ptr++;
break;
}
}
// Set the day of week and year
SetWeekDay();
SetYearDay();
return true;
}
// Constructor that initializes with the current time
EyeTime::EyeTime () {
utc = 0;
daylight = 0;
#ifdef WIN32
SYSTEMTIME stUTC;
GetSystemTime (&stUTC);
SetTime (stUTC);
#else
timeval tv;
gettimeofday(&tv, NULL);
struct tm tm;
gmtime_r(&(tv.tv_sec), &tm);
year = tm.tm_year + 1900;
month = tm.tm_mon + 1; // Posix starts at 0
day = tm.tm_mday;
yday = tm.tm_yday;
wday = tm.tm_wday;
hour = tm.tm_hour;
minute = tm.tm_min;
second = tm.tm_sec;
millisec = (unsigned int)(tv.tv_usec / 1000);
// Set the day of week
SetWeekDay();
#endif
}
EyeTime::~EyeTime () {
}
int EyeTime::Compare (const EyeTime* other) const
{
if (year > other->year) return 1;
if (year < other->year) return -1;
if (month > other->month) return 1;
if (month < other->month) return -1;
if (day > other->day) return 1;
if (day < other->day) return -1;
if (hour > other->hour) return 1;
if (hour < other->hour) return -1;
if (minute > other->minute) return 1;
if (minute < other->minute) return -1;
// Allow for rounding to the nearest second on one of the times
if (millisec > 0 && other->millisec == 0) {
unsigned int sec = second + (millisec > 499 ? 1 : 0);
if (sec > other->second) return 1;
if (sec < other->second) return -1;
}
else if (millisec == 0 && other->millisec > 0) {
unsigned int sec = other->second + (other->millisec > 499 ? 1 : 0);
if (second > sec) return 1;
if (second < sec) return -1;
}
else {
if (second > other->second) return 1;
if (second < other->second) return -1;
if (millisec > other->millisec) return 1;
if (millisec < other->millisec) return -1;
}
return 0;
}
// Get the creation(Windows)/modified(Unix) time from a file
bool EyeTime::GetFileTime (const std::string& path)
{
if (!sequencelogic::IsValidFilename (path))
return false;
#ifdef WIN32
HANDLE hFile = CreateFile (path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return false;
else {
FILETIME ftCreate;
SYSTEMTIME stUTC;
// Retrieve the file creation time
if (!::GetFileTime (hFile, (LPFILETIME) NULL, (LPFILETIME) NULL, &ftCreate)) {
CloseHandle (hFile);
return false;
}
if (FileTimeToSystemTime (&ftCreate, &stUTC)) { // Convert the create time to UTC
year = stUTC.wYear + 1900;
month = stUTC.wMonth; // Windows starts at 1
day = stUTC.wDay;
wday = stUTC.wDayOfWeek;
hour = stUTC.wHour;
minute = stUTC.wMinute;
second = stUTC.wSecond;
millisec = stUTC.wMilliseconds;
SetYearDay();
}
else {
IONUDEBUG("EyeTime::GetFileTime(%s) - FileTimeToSystemTime failed", path.c_str());
}
CloseHandle (hFile);
}
#else
struct tm tm;
struct stat attrib;
if (stat (path.c_str(), &attrib) != 0)
return false;
gmtime_r (reinterpret_cast<const time_t *>(&(attrib.st_mtime)), &tm);
utc = 0;
year = tm.tm_year + 1900;
month = tm.tm_mon + 1; // Posix starts at 0
day = tm.tm_mday;
yday = tm.tm_yday;
wday = tm.tm_wday;
hour = tm.tm_hour;
minute = tm.tm_min;
second = tm.tm_sec;
#if defined(ANDROID)
millisec = (attrib.st_mtime_nsec / 1000000);
#elif !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)
millisec = (unsigned int)(attrib.st_mtimespec.tv_nsec / 1000000);
#else
millisec = (unsigned int)(attrib.st_mtim.tv_nsec / 1000000);
#endif
SetWeekDay();
#endif
return (true);
}
// Set the creation(Windows)/modified(Unix) time of a file
bool EyeTime::SetFileTime (const std::string& path)
{
if (!sequencelogic::IsValidFilename (path))
return false;
#ifdef WIN32
SYSTEMTIME systime = GetSysTime();
FILETIME filetime;
SystemTimeToFileTime (&systime, &filetime);
// Get a handle to eyefile with file_write_attributes access
HANDLE hFile = CreateFile (path.c_str(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
IONUDEBUG ("EyeTime::SetFileTime(%s) failed invalid file handle", path.c_str());
return false;
}
// Set the file time on the file
if (!::SetFileTime (hFile, (LPFILETIME) NULL, (LPFILETIME) NULL, &filetime)) {
CloseHandle (hFile);
IONUDEBUG ("EyeTime::SetFileTime(%s) failed ::SetFileTime", path.c_str());
return false;
}
CloseHandle (hFile);
#else
struct timeval times[2];
times[0].tv_usec = millisec * 1000; // Access time
times[1].tv_usec = millisec * 1000; // Modification time
struct tm tm;
tm.tm_year = year - 1900;
tm.tm_mon = month - 1;
tm.tm_mday = day;
tm.tm_yday = yday;
tm.tm_wday = wday;
tm.tm_hour = hour;
tm.tm_min = minute;
tm.tm_sec = second;
//time_t t = mktime (&tm) Can't use this as it's localtime() inverse, need gmtime() inverse
// Compute seconds since the epoch using the POSIX formula
time_t t = tm.tm_sec + tm.tm_min*60 + tm.tm_hour*3600 + tm.tm_yday*86400 +
(tm.tm_year-70)*31536000 + ((tm.tm_year-69)/4)*86400 -
((tm.tm_year-1)/100)*86400 + ((tm.tm_year+299)/400)*86400;
if (t > 0) {
times[0].tv_sec = t;
times[1].tv_sec = t;
if (utimes (path.c_str(), times) != 0) {
IONUDEBUG ("EyeTime::SetFileTime(%s) failed utimes()", path.c_str());
}
}
else {
IONUDEBUG ("EyeTime::SetFileTime(%s) failed invalid time", path.c_str());
}
#endif
return true;
}
// If corrupted, cap to legal values
void EyeTime::Cap()
{
if (year > 3000 || year < 1970) year = 2014;
if (month < 1 || month > 12) month = 1;
if (day < 1 || (day > monthlengths[month-1] && month != 2 && !isLeapYear(year) && day > 29)) day = 1;
if (hour > 23) hour = 1;
if (minute > 60) minute = 1;
if (second > 61) second = 1;
if (millisec > 999) millisec = 1;
if (utc < -12 || utc > 11) utc = 0;
}
// Format time as ISO 8601 formatted string (20 characters)
const char* EyeTime::GetISO8601Time ()
{
Cap();
sprintf(_isodate, "%04u-%02u-%02uT%02u:%02u:%02uZ", year, month, day, hour, minute, second);
return _isodate;
}
// Format time as ISO 8601 formatted string, with millisecs (24 characters)
const char* EyeTime::GetISO8601TimeMs ()
{
Cap();
sprintf(_isodate, "%04u-%02u-%02uT%02u:%02u:%02u.%03uZ", year, month, day, hour, minute, second, millisec);
return _isodate;
}
// Format time as ISO 8601 formatted string (20 characters)
const char* EyeTime::GetLocalISO8601Time ()
{
ConvertToLocalTime();
sprintf(_isodate, "%04u-%02u-%02uT%02u:%02u:%02u%c", year, month, day, hour, minute, second, timezones[utc+12]);
ConvertToGMTime();
return _isodate;
}
void EyeTime::ConvertToLocalTime()
{
// Convert to Local as needed
int offset = utc + daylight;
if (offset != 0) {
if ((int)hour + offset > 24) {
hour = (int)hour + offset - 24;
if (++day > monthlengths[month] && !(day == 29 && month == 2 && year % 4 == 0)) {
if (month == 12) {
month = 1;
year++;
}
else
month++;
day = 1;
}
}
else if ((int)hour + offset < 0) {
hour = (int)hour + offset + 24;
if (--day < 1) {
if (month == 1) {
month = 12;
year--;
}
else
month--;
if (month == 2 && year % 4 == 0)
day = 29;
else
day = monthlengths[month-1];
}
}
else
hour += offset;
}
}
void EyeTime::ConvertToGMTime()
{
// Convert to GMT as needed
int offset = utc - daylight;
if (offset != 0) {
if ((int)hour - offset >= 24) {
hour = (int)hour + offset - 24;
if (++day > monthlengths[month] && !(day == 29 && month == 2 && year % 4 == 0)) {
if (month == 12) {
month = 1;
year++;
}
else
month++;
day = 1;
}
}
else if ((int)hour - offset < 0) {
hour = (int)hour + offset + 24;
if (--day < 1) {
if (month == 1) {
month = 12;
year--;
}
else
month--;
if (month == 2 && year % 4 == 0)
day = 29;
else
day = monthlengths[month-1];
}
}
else
hour -= offset;
}
}
// Format time as a Unix timestamp, long, usually 10 digits
const char* EyeTime::GetUnixTime ()
{
double epoch = static_cast<double>(GetTimeMs()) / 1000.0;
sprintf (_isodate, "%0.0f", epoch);
return _isodate;
}
double EyeTime::SecondsFromNow ()
{
int tm_year = year - 1900;
#ifdef WIN32
SYSTEMTIME stUTC;
GetSystemTime (&stUTC);
int tm_yday = stUTC.wDay - 1; // Day of the month but yday starts at 0
int tm_nyear = stUTC.wYear - 1900;
for (int i = 0; i < 12; ++i) {
if (i + 1 < stUTC.wMonth) {
tm_yday += monthlengths[i];
}
}
// Account for leap years
if (stUTC.wMonth > 2 && isLeapYear (year))
tm_yday++;
double snow = stUTC.wSecond + stUTC.wMinute*60 + stUTC.wHour*3600 + tm_yday*86400 +
(tm_nyear-70)*31536000 + ((tm_nyear-69)/4)*86400 -
((tm_nyear-1)/100)*86400 + ((tm_nyear+299)/400)*86400;
#else
time_t now;
time (&now);
struct tm tm_now;
gmtime_r(&now, &tm_now);
double snow = tm_now.tm_sec + tm_now.tm_min*60 + tm_now.tm_hour*3600 + tm_now.tm_yday*86400 +
(tm_now.tm_year-70)*31536000 + ((tm_now.tm_year-69)/4)*86400 -
((tm_now.tm_year-1)/100)*86400 + ((tm_now.tm_year+299)/400)*86400;
#endif
double sthen = second + minute*60 + hour*3600 + yday*86400 +
(tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400;
return (snow - sthen);
}
void EyeTime::Dump()
{
std::cout << "year=" << year << " month=" << month << " day=" << day << " wday=" << wday << " yday=" << yday;
std::cout << " hour=" << hour << " min=" << minute << " sec=" << second << " ms=" << millisec << " tz=" << utc;
std::cout << std::endl;
}
namespace sequencelogic
{
std::ostream& operator<<(std::ostream &out, EyeTime &et)
{
out << et.GetISO8601Time();
return out;
}
}