/*==LICENSE==* CyanWorlds.com Engine - MMOG client, server and tools Copyright (C) 2011 Cyan Worlds, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Additional permissions under GNU GPL version 3 section 7 If you modify this Program, or any covered work, by linking or combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK (or a modified version of those libraries), containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the licensors of this Program grant you additional permission to convey the resulting work. Corresponding Source for a non-source form of such a combination shall include the source code for the parts of OpenSSL and IJG JPEG Library used as well as that of the covered work. You can contact Cyan Worlds, Inc. by email legal@cyan.com or by snail mail at: Cyan Worlds, Inc. 14617 N Newport Hwy Mead, WA 99021 *==LICENSE==*/ #include #include "plUnifiedTime.h" #include "hsStlUtils.h" #include "hsWindows.h" #if HS_BUILD_FOR_UNIX #include #include #endif #if HS_BUILD_FOR_WIN32 #include // for timeb #endif #include "hsUtils.h" #include #include "hsStream.h" #if HS_BUILD_FOR_WIN32 // // Converts Windows Time to Unified Time // #define MAGICWINDOWSOFFSET ((__int64)11644473600) // magic number, taken from Python Source // hsBool plUnifiedTime::SetFromWinFileTime(const FILETIME ft) { // FILETIME resolution seems to be 0.01 sec __int64 ff,ffsecs; ff = *(__int64*)(&ft); ffsecs = ff/(__int64)10000000; if (ffsecs >= MAGICWINDOWSOFFSET) // make sure we won't end up negatice { fSecs = (time_t)(ffsecs-MAGICWINDOWSOFFSET); fMicros = (UInt32)(ff % 10000000)/10; return true; } else // before the UNIX Epoch return false; } // // Sets the unified time to the current UTC time // hsBool plUnifiedTime::SetToUTC() { FILETIME ft; GetSystemTimeAsFileTime(&ft); /* 100 ns blocks since 01-Jan-1641 */ return SetFromWinFileTime(ft); } #elif HS_BUILD_FOR_UNIX // // Sets the unified time to the current UTC time // hsBool plUnifiedTime::SetToUTC() { struct timeval tv; // get the secs and micros from Jan 1, 1970 int ret = gettimeofday(&tv, nil); if (ret == 0) { fSecs = tv.tv_sec; fMicros = tv.tv_usec; return true; } else { return false; } } #else #error "Unified Time Not Implemented on this platform!" #endif struct tm * plUnifiedTime::IGetTime(const time_t * timer) const { struct tm * tm = nil; switch (fMode) { case kGmt: tm = gmtime(timer); break; default: tm = localtime(timer); } if ( tm ) tm->tm_isdst = -1; return tm; } plUnifiedTime::plUnifiedTime(plUnifiedTime_CtorNow,int mode) { SetMode((Mode)mode); ToCurrentTime(); } plUnifiedTime::plUnifiedTime(const timeval & tv) : fMode(kGmt) { *this = tv; } plUnifiedTime::plUnifiedTime(int mode, const struct tm & src) : fMode((Mode)mode) { *this = src; } plUnifiedTime::plUnifiedTime(time_t t) : fMode(kGmt) { *this = t; } plUnifiedTime::plUnifiedTime(unsigned long t) : fMode(kGmt) { *this = t; } plUnifiedTime::plUnifiedTime(int year, int month, int day, int hour, int min, int sec, unsigned long usec, int dst) : fMode(kGmt) { SetTime(year,month,day,hour,min,sec,usec,dst); } plUnifiedTime::plUnifiedTime(int mode, const char * buf, const char * fmt) : fMode((Mode)mode) { FromString(buf,fmt); } plUnifiedTime::plUnifiedTime(const plUnifiedTime & src) : fMode(src.fMode) { *this = src; } plUnifiedTime::plUnifiedTime(const plUnifiedTime * src) : fMode(src->fMode) { *this = *src; } plUnifiedTime plUnifiedTime::GetCurrentTime(Mode mode) { plUnifiedTime t; t.SetMode(mode); t.ToCurrentTime(); return t; } const plUnifiedTime & plUnifiedTime::operator=(const plUnifiedTime & src) { fSecs = src.fSecs; fMicros = src.fMicros; fMode = src.fMode; return *this; } const plUnifiedTime & plUnifiedTime::operator=(const plUnifiedTime * src) { return operator=(*src); } const plUnifiedTime & plUnifiedTime::operator=(time_t src) { fSecs = src; fMicros = 0; return *this; } const plUnifiedTime & plUnifiedTime::operator=(unsigned long src) { fSecs = (time_t)src; fMicros = 0; return *this; } const plUnifiedTime & plUnifiedTime::operator=(const struct timeval & src) { fSecs = src.tv_sec; fMicros = src.tv_usec; return *this; } const plUnifiedTime & plUnifiedTime::operator=(const struct tm & src) { struct tm atm = src; fSecs = mktime(&atm); return *this; } void plUnifiedTime::SetSecsDouble(double secs) { hsAssert(secs>=0, "plUnifiedTime::SetSecsDouble negative time"); double x,y; x = modf(secs,&y); fSecs = (time_t)y; fMicros = (UInt32)(x*1000000); } void plUnifiedTime::ToCurrentTime() { SetToUTC(); } hsBool plUnifiedTime::SetGMTime(short year, short month, short day, short hour, short minute, short second, unsigned long usec, int dst) { if( !SetTime( year, month, day, hour, minute, second, usec, dst ) ) return false; fSecs -= IGetLocalTimeZoneOffset(); fMode = kGmt; return true; } hsBool plUnifiedTime::SetTime(short year, short month, short day, short hour, short minute, short second, unsigned long usec, int dst) { struct tm atm; atm.tm_sec = second; atm.tm_min = minute; atm.tm_hour = hour; atm.tm_mday = day; atm.tm_mon = month - 1; atm.tm_year = year - 1900; atm.tm_isdst = dst; fSecs = mktime(&atm); if (fSecs == -1) return false; if (fMicros >= 1000000) return false; fMicros = usec; fMode = kLocal; return true; } hsBool plUnifiedTime::GetTime(short &year, short &month, short &day, short &hour, short &minute, short &second) const { struct tm* time = IGetTime(&fSecs); if (!time) return false; year = time->tm_year+1900; month = time->tm_mon+1; day = time->tm_mday; hour = time->tm_hour; minute = time->tm_min; second = time->tm_sec; return true; } const char* plUnifiedTime::Print() const { static std::string s; // short year, month, day, hour, minute, second; // GetTime(year, month, day, hour, minute, second); // // xtl::format(s,"yr %d mo %d day %d hour %d min %d sec %d", // year, month, day, hour, minute, second); s = Format("%c"); return s.c_str(); } const char* plUnifiedTime::PrintWMillis() const { static std::string s; xtl::format(s,"%s,s:%lu,ms:%d", Print(), (unsigned long)GetSecs(), GetMillis() ); return s.c_str(); } struct tm * plUnifiedTime::GetTm(struct tm * ptm) const { if (ptm != nil) { *ptm = *IGetTime(&fSecs); return ptm; } else return IGetTime(&fSecs); } int plUnifiedTime::GetYear() const { return GetTm() ? GetTm()->tm_year + 1900 : 0; } int plUnifiedTime::GetMonth() const { return GetTm() ? GetTm()->tm_mon + 1 : 0; } int plUnifiedTime::GetDay() const { return GetTm() ? GetTm()->tm_mday : 0; } int plUnifiedTime::GetHour() const { return GetTm() ? GetTm()->tm_hour : 0; } int plUnifiedTime::GetMinute() const { return GetTm() ? GetTm()->tm_min : 0; } int plUnifiedTime::GetSecond() const { return GetTm() ? GetTm()->tm_sec : 0; } int plUnifiedTime::GetDayOfWeek() const { return GetTm() ? GetTm()->tm_wday : 0; } int plUnifiedTime::GetMillis() const { return fMicros/1000; } #pragma optimize( "g", off ) // disable global optimizations double plUnifiedTime::GetSecsDouble() const { hsDoublePrecBegin double ret = GetSecs() + GetMicros() / 1000000.0; hsDoublePrecEnd return ret; } #pragma optimize( "", on ) // restore optimizations to their defaults void plUnifiedTime::Read(hsStream* s) { s->LogSubStreamStart("UnifiedTime"); UInt32 secs; s->LogReadSwap(&secs,"Seconds"); fSecs = (time_t)secs; s->LogReadSwap(&fMicros,"MicroSeconds"); s->LogSubStreamEnd(); // preserve fMode } void plUnifiedTime::Write(hsStream* s) const { s->WriteSwap((UInt32)fSecs); s->WriteSwap(fMicros); // preserve fMode } const plUnifiedTime & plUnifiedTime::operator-=(const plUnifiedTime & rhs) { // carry if needed if ((*this).fMicros < rhs.fMicros) { (*this).fSecs--; (*this).fMicros += 1000000; } (*this).fMicros -= rhs.fMicros; (*this).fSecs -= rhs.fSecs; return *this; } const plUnifiedTime & plUnifiedTime::operator+=(const plUnifiedTime & rhs) { (*this).fMicros += rhs.fMicros; // carry if needed if ((*this).fMicros >= 1000000) { (*this).fSecs++; (*this).fMicros -= 1000000; } (*this).fSecs += rhs.fSecs; return *this; } bool plUnifiedTime::operator==(const plUnifiedTime & rhs) const { return ((fSecs == rhs.fSecs) && (fMicros == rhs.fMicros)); } bool plUnifiedTime::operator!=(const plUnifiedTime & rhs) const { return ((fSecs != rhs.fSecs) || (fMicros != rhs.fMicros)); } bool plUnifiedTime::operator <(const plUnifiedTime & rhs) const { return ((fSecs < rhs.fSecs) || ((fSecs == rhs.fSecs) && (fMicros < rhs.fMicros))); } bool plUnifiedTime::operator >(const plUnifiedTime & rhs) const { return ((fSecs > rhs.fSecs) || ((fSecs == rhs.fSecs) && (fMicros > rhs.fMicros))); } bool plUnifiedTime::operator<=(const plUnifiedTime & rhs) const { return (*this=(const plUnifiedTime & rhs) const { return (*this>rhs) || (*this==rhs); } plUnifiedTime::operator timeval() const { #if HS_BUILD_FOR_WIN32 // tv_secs should be a time_t, but on Windows it is a long struct timeval t = {(long)fSecs, (long)fMicros}; #else struct timeval t = {fSecs, fMicros}; #endif return t; } plUnifiedTime::operator struct tm() const { return *GetTm(); } std::string plUnifiedTime::Format(const char * fmt) const { char buf[128]; struct tm * t = IGetTime(&fSecs); if (t == nil || !strftime(buf, sizeof(buf), fmt, t)) buf[0] = '\0'; return std::string(buf); } plUnifiedTime operator -(const plUnifiedTime & left, const plUnifiedTime & right) { plUnifiedTime ans = left; ans -= right; return ans; } plUnifiedTime operator +(const plUnifiedTime & left, const plUnifiedTime & right) { plUnifiedTime ans = left; ans += right; return ans; } bool operator <(const plUnifiedTime & time, int secs) { return (time.GetSecs() '9') \ return NULL; \ do { \ val *= 10; \ val += *rp++ - '0'; \ } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \ if (val < from || val > to) \ return NULL; \ } while (0) #define recursive(new_fmt) \ (*(new_fmt) != '\0' \ && (rp = strptime_internal (rp, (new_fmt), tm, mode)) != NULL) static char const weekday_name[][10] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; static char const ab_weekday_name[][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static char const month_name[][10] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; static char const ab_month_name[][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; #define HERE_D_T_FMT "%m/%d/%y %H:%M:%S" #define HERE_D_FMT "%m/%d/%y" #define HERE_AM_STR "AM" #define HERE_PM_STR "PM" #define HERE_T_FMT_AMPM "%I:%M:%S %p" #define HERE_T_FMT "%H:%M:%S" const unsigned short int __mon_yday[2][13] = { /* Normal years. */ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, /* Leap years. */ { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } }; static struct tm * time_r( const time_t *t, struct tm *tp, int mode) { struct tm *l = 0; switch (mode) { case plUnifiedTime::kGmt: l = gmtime(t); break; default: l = localtime(t); } if (! l) return 0; *tp = *l; tp->tm_isdst = -1; return tp; } /* Compute the day of the week. */ static void day_of_the_week (struct tm *tm) { /* We know that January 1st 1970 was a Thursday (= 4). Compute the the difference between this data in the one on TM and so determine the weekday. */ int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2); int wday = (-473 + (365 * (tm->tm_year - 70)) + (corr_year / 4) - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0) + (((corr_year / 4) / 25) / 4) + __mon_yday[0][tm->tm_mon] + tm->tm_mday - 1); tm->tm_wday = ((wday % 7) + 7) % 7; } /* Compute the day of the year. */ static void day_of_the_year (struct tm *tm) { tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon] + (tm->tm_mday - 1)); } static char * strptime_internal( const char * rp, const char * fmt, struct tm * tm, int mode) { const char *rp_backup; int cnt; size_t val; int have_I, is_pm; int century, want_century; int have_wday, want_xday; int have_yday; int have_mon, have_mday; have_I = is_pm = 0; century = -1; want_century = 0; have_wday = want_xday = have_yday = have_mon = have_mday = 0; while (*fmt != '\0') { /* A white space in the format string matches 0 more or white space in the input string. */ if (isspace (*fmt)) { while (isspace (*rp)) ++rp; ++fmt; continue; } /* Any character but `%' must be matched by the same character in the iput string. */ if (*fmt != '%') { match_char (*fmt++, *rp++); continue; } ++fmt; /* We need this for handling the `E' modifier. */ start_over: /* Make back up of current processing pointer. */ rp_backup = rp; switch (*fmt++) { case '%': /* Match the `%' character itself. */ match_char ('%', *rp++); break; case 'a': case 'A': /* Match day of week. */ for (cnt = 0; cnt < 7; ++cnt) { if (match_string (weekday_name[cnt], rp) || match_string (ab_weekday_name[cnt], rp)) { break; } } if (cnt == 7) /* Does not match a weekday name. */ return NULL; tm->tm_wday = cnt; have_wday = 1; break; case 'b': case 'B': case 'h': /* Match month name. */ for (cnt = 0; cnt < 12; ++cnt) { if (match_string (month_name[cnt], rp) || match_string (ab_month_name[cnt], rp)) { break; } } if (cnt == 12) /* Does not match a month name. */ return NULL; tm->tm_mon = cnt; want_xday = 1; break; case 'c': /* Match locale's date and time format. */ if (!recursive (HERE_D_T_FMT)) return NULL; want_xday = 1; break; case 'C': /* Match century number. */ get_number (0, 99, 2); century = val; want_xday = 1; break; case 'd': case 'e': /* Match day of month. */ get_number (1, 31, 2); tm->tm_mday = val; have_mday = 1; want_xday = 1; break; case 'F': if (!recursive ("%Y-%m-%d")) return NULL; want_xday = 1; break; case 'x': /* Fall through. */ case 'D': /* Match standard day format. */ if (!recursive (HERE_D_FMT)) return NULL; want_xday = 1; break; case 'k': case 'H': /* Match hour in 24-hour clock. */ get_number (0, 23, 2); tm->tm_hour = val; have_I = 0; break; case 'I': /* Match hour in 12-hour clock. */ get_number (1, 12, 2); tm->tm_hour = val % 12; have_I = 1; break; case 'j': /* Match day number of year. */ get_number (1, 366, 3); tm->tm_yday = val - 1; have_yday = 1; break; case 'm': /* Match number of month. */ get_number (1, 12, 2); tm->tm_mon = val - 1; have_mon = 1; want_xday = 1; break; case 'M': /* Match minute. */ get_number (0, 59, 2); tm->tm_min = val; break; case 'n': case 't': /* Match any white space. */ while (isspace (*rp)) ++rp; break; case 'p': /* Match locale's equivalent of AM/PM. */ if (!match_string (HERE_AM_STR, rp)) if (match_string (HERE_PM_STR, rp)) is_pm = 1; else return NULL; break; case 'r': if (!recursive (HERE_T_FMT_AMPM)) return NULL; break; case 'R': if (!recursive ("%H:%M")) return NULL; break; case 's': { /* The number of seconds may be very high so we cannot use the `get_number' macro. Instead read the number character for character and construct the result while doing this. */ time_t secs = 0; if (*rp < '0' || *rp > '9') /* We need at least one digit. */ return NULL; do { secs *= 10; secs += *rp++ - '0'; } while (*rp >= '0' && *rp <= '9'); if (time_r (&secs, tm, mode) == NULL) /* Error in function. */ return NULL; } break; case 'S': get_number (0, 61, 2); tm->tm_sec = val; break; case 'X': /* Fall through. */ case 'T': if (!recursive (HERE_T_FMT)) return NULL; break; case 'u': get_number (1, 7, 1); tm->tm_wday = val % 7; have_wday = 1; break; case 'g': get_number (0, 99, 2); /* XXX This cannot determine any field in TM. */ break; case 'G': if (*rp < '0' || *rp > '9') return NULL; /* XXX Ignore the number since we would need some more information to compute a real date. */ do ++rp; while (*rp >= '0' && *rp <= '9'); break; case 'U': case 'V': case 'W': get_number (0, 53, 2); /* XXX This cannot determine any field in TM without some information. */ break; case 'w': /* Match number of weekday. */ get_number (0, 6, 1); tm->tm_wday = val; have_wday = 1; break; case 'y': /* Match year within century. */ get_number (0, 99, 2); /* The "Year 2000: The Millennium Rollover" paper suggests that values in the range 69-99 refer to the twentieth century. */ tm->tm_year = val >= 69 ? val : val + 100; /* Indicate that we want to use the century, if specified. */ want_century = 1; want_xday = 1; break; case 'Y': /* Match year including century number. */ get_number (0, 9999, 4); tm->tm_year = val - 1900; want_century = 0; want_xday = 1; break; goto start_over; case 'O': switch (*fmt++) { case 'd': case 'e': /* Match day of month using alternate numeric symbols. */ get_number (1, 31, 2); tm->tm_mday = val; have_mday = 1; want_xday = 1; break; case 'H': /* Match hour in 24-hour clock using alternate numeric symbols. */ get_number (0, 23, 2); tm->tm_hour = val; have_I = 0; break; case 'I': /* Match hour in 12-hour clock using alternate numeric symbols. */ get_number (1, 12, 2); tm->tm_hour = val - 1; have_I = 1; break; case 'm': /* Match month using alternate numeric symbols. */ get_number (1, 12, 2); tm->tm_mon = val - 1; have_mon = 1; want_xday = 1; break; case 'M': /* Match minutes using alternate numeric symbols. */ get_number (0, 59, 2); tm->tm_min = val; break; case 'S': /* Match seconds using alternate numeric symbols. */ get_number (0, 61, 2); tm->tm_sec = val; break; case 'U': case 'V': case 'W': get_number (0, 53, 2); /* XXX This cannot determine any field in TM without further information. */ break; case 'w': /* Match number of weekday using alternate numeric symbols. */ get_number (0, 6, 1); tm->tm_wday = val; have_wday = 1; break; case 'y': /* Match year within century using alternate numeric symbols. */ get_number (0, 99, 2); tm->tm_year = val >= 69 ? val : val + 100; want_xday = 1; break; default: return NULL; } break; default: return NULL; } } if (have_I && is_pm) tm->tm_hour += 12; if (century != -1) { if (want_century) tm->tm_year = tm->tm_year % 100 + (century - 19) * 100; else /* Only the century, but not the year. Strange, but so be it. */ tm->tm_year = (century - 19) * 100; } if (want_xday && !have_wday) { if ( !(have_mon && have_mday) && have_yday) { /* We don't have tm_mon and/or tm_mday, compute them. */ int t_mon = 0; while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday) t_mon++; if (!have_mon) tm->tm_mon = t_mon - 1; if (!have_mday) tm->tm_mday = (tm->tm_yday - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1); } day_of_the_week (tm); } if (want_xday && !have_yday) day_of_the_year (tm); return (char *) rp; } } // namespace pvt_strptime #endif bool plUnifiedTime::FromString(const char * buf, const char * fmt) { struct tm tm; tm.tm_isdst = -1; #if !defined(HS_BUILD_FOR_UNIX) bool result = (pvt_strptime::strptime_internal(buf, fmt, &tm, fMode)!=nil); #else bool result = (strptime(buf, fmt, &tm)!=nil); #endif if (result) *this = tm; return result; } /// Local time zone offset stuff Int32 plUnifiedTime::fLocalTimeZoneOffset = -1; Int32 plUnifiedTime::IGetLocalTimeZoneOffset( void ) { static bool inited = false; if( !inited ) { inited = true; // Calculate the difference between local time and GMT for this system currently // Taken from devx.com from an article written by Danny Kalev // http://gethelp.devx.com/techtips/cpp_pro/10min/2001/10min1200-3.asp time_t currLocalTime = time( 0 ); // current local time struct tm local = *gmtime( &currLocalTime ); // convert curr to GMT, store as tm time_t utc = mktime( &local ); // convert GMT tm to GMT time_t double diffInSecs = difftime( utc, currLocalTime ); fLocalTimeZoneOffset = (Int32)diffInSecs; } return fLocalTimeZoneOffset; } // // static helper, return difference timeA-timeB, may be negative // double plUnifiedTime::GetTimeDifference(const plUnifiedTime& timeA, const plUnifiedTime& timeB) { bool neg = (timeB > timeA); plUnifiedTime timeDiff = neg ? (timeB - timeA) : (timeA - timeB); // always positive double t = (float)(neg ? timeDiff.GetSecsDouble() * -1. : timeDiff.GetSecsDouble()); return t; }