Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1216 lines
36 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Encapsulates real world (wall clock) time
  4. //
  5. //=============================================================================
  6. #include "stdafx.h"
  7. #ifdef POSIX
  8. #include <sys/time.h>
  9. #else
  10. #include "winlite.h"
  11. #endif
  12. #include "rtime.h"
  13. #include <time.h>
  14. // memdbgon must be the last include file in a .cpp file!!!
  15. #include "tier0/memdbgon.h"
  16. #if defined( WIN32 ) || defined( _PS3 )
  17. // This strptime implementation is taken from the Goolge Site Map Generator project:
  18. // Copyright 2009 Google Inc.
  19. //
  20. // Licensed under the Apache License, Version 2.0 (the "License");
  21. // you may not use this file except in compliance with the License.
  22. // You may obtain a copy of the License at
  23. //
  24. // http://www.apache.org/licenses/LICENSE-2.0
  25. //
  26. // Unless required by applicable law or agreed to in writing, software
  27. // distributed under the License is distributed on an "AS IS" BASIS,
  28. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  29. // See the License for the specific language governing permissions and
  30. // limitations under the License.
  31. // Implement strptime under windows
  32. static const char* kWeekFull[] = {
  33. "Sunday", "Monday", "Tuesday", "Wednesday",
  34. "Thursday", "Friday", "Saturday"
  35. };
  36. static const char* kWeekAbbr[] = {
  37. "Sun", "Mon", "Tue", "Wed",
  38. "Thu", "Fri", "Sat"
  39. };
  40. static const char* kMonthFull[] = {
  41. "January", "February", "March", "April", "May", "June",
  42. "July", "August", "September", "October", "November", "December"
  43. };
  44. static const char* kMonthAbbr[] = {
  45. "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  46. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  47. };
  48. static const char* _parse_num(const char* s, int low, int high, int* value) {
  49. const char* p = s;
  50. for (*value = 0; *p != NULL && V_isdigit(*p); ++p) {
  51. *value = (*value) * 10 + static_cast<int>(*p) - static_cast<int>('0');
  52. }
  53. if (p == s || *value < low || *value > high) return NULL;
  54. return p;
  55. }
  56. static char* _strptime(const char *s, const char *format, struct tm *tm) {
  57. while (*format != NULL && *s != NULL) {
  58. if (*format != '%') {
  59. if (*s != *format) return NULL;
  60. ++format;
  61. ++s;
  62. continue;
  63. }
  64. ++format;
  65. int len = 0;
  66. switch (*format) {
  67. // weekday name.
  68. case 'a':
  69. case 'A':
  70. tm->tm_wday = -1;
  71. for (int i = 0; i < 7; ++i) {
  72. len = static_cast<int>(strlen(kWeekAbbr[i]));
  73. if (V_strnicmp(kWeekAbbr[i], s, len) == 0) {
  74. tm->tm_wday = i;
  75. break;
  76. }
  77. len = static_cast<int>(strlen(kWeekFull[i]));
  78. if ( V_strnicmp(kWeekFull[i], s, len) == 0) {
  79. tm->tm_wday = i;
  80. break;
  81. }
  82. }
  83. if (tm->tm_wday == -1) return NULL;
  84. s += len;
  85. break;
  86. // month name.
  87. case 'b':
  88. case 'B':
  89. case 'h':
  90. tm->tm_mon = -1;
  91. for (int i = 0; i < 12; ++i) {
  92. len = static_cast<int>(strlen(kMonthAbbr[i]));
  93. if ( V_strnicmp(kMonthAbbr[i], s, len) == 0) {
  94. tm->tm_mon = i;
  95. break;
  96. }
  97. len = static_cast<int>(strlen(kMonthFull[i]));
  98. if ( V_strnicmp(kMonthFull[i], s, len) == 0) {
  99. tm->tm_mon = i;
  100. break;
  101. }
  102. }
  103. if (tm->tm_mon == -1) return NULL;
  104. s += len;
  105. break;
  106. // month [1, 12].
  107. case 'm':
  108. s = _parse_num(s, 1, 12, &tm->tm_mon);
  109. if (s == NULL) return NULL;
  110. --tm->tm_mon;
  111. break;
  112. // day [1, 31].
  113. case 'd':
  114. case 'e':
  115. s = _parse_num(s, 1, 31, &tm->tm_mday);
  116. if (s == NULL) return NULL;
  117. break;
  118. // hour [0, 23].
  119. case 'H':
  120. s = _parse_num(s, 0, 23, &tm->tm_hour);
  121. if (s == NULL) return NULL;
  122. break;
  123. // minute [0, 59]
  124. case 'M':
  125. s = _parse_num(s, 0, 59, &tm->tm_min);
  126. if (s == NULL) return NULL;
  127. break;
  128. // seconds [0, 60]. 60 is for leap year.
  129. case 'S':
  130. s = _parse_num(s, 0, 60, &tm->tm_sec);
  131. if (s == NULL) return NULL;
  132. break;
  133. // year [1900, 9999].
  134. case 'Y':
  135. s = _parse_num(s, 1900, 9999, &tm->tm_year);
  136. if (s == NULL) return NULL;
  137. tm->tm_year -= 1900;
  138. break;
  139. // year [0, 99].
  140. case 'y':
  141. s = _parse_num(s, 0, 99, &tm->tm_year);
  142. if (s == NULL) return NULL;
  143. if (tm->tm_year <= 68) {
  144. tm->tm_year += 100;
  145. }
  146. break;
  147. // arbitray whitespace.
  148. case 't':
  149. case 'n':
  150. while (V_isspace(*s)) ++s;
  151. break;
  152. // '%'.
  153. case '%':
  154. if (*s != '%') return NULL;
  155. ++s;
  156. break;
  157. // All the other format are not supported.
  158. default:
  159. AssertMsg( false, "Invalid format string to strptime!" );
  160. return NULL;
  161. }
  162. ++format;
  163. }
  164. if (*format != NULL) {
  165. return NULL;
  166. } else {
  167. return const_cast<char*>(s);
  168. }
  169. }
  170. char* strptime(const char *buf, const char *fmt, struct tm *tm) {
  171. return _strptime(buf, fmt, tm);
  172. }
  173. #endif // WIN32
  174. // Our cached copy of the current time
  175. RTime32 CRTime::sm_nTimeLastSystemTimeUpdate = 0; // initialize to large negative value to trigger immediate FileTimeCur update
  176. char CRTime::sm_rgchLocalTimeCur[16]="";
  177. char CRTime::sm_rgchLocalDateCur[16]="";
  178. RTime32 CRTime::sm_nTimeCur = 0;
  179. //-----------------------------------------------------------------------------
  180. // Purpose: Constructor
  181. //-----------------------------------------------------------------------------
  182. CRTime::CRTime()
  183. {
  184. if ( sm_nTimeCur == 0 )
  185. {
  186. sm_nTimeCur = time(NULL);
  187. }
  188. m_nStartTime = sm_nTimeCur;
  189. m_bGMT = false;
  190. }
  191. //-----------------------------------------------------------------------------
  192. // Purpose: Returns the amount of time that's passed between our time and the
  193. // current time.
  194. // Output: Time that's passed between our time and the current time
  195. //-----------------------------------------------------------------------------
  196. int CRTime::CSecsPassed() const
  197. {
  198. return( sm_nTimeCur - m_nStartTime );
  199. }
  200. //-----------------------------------------------------------------------------
  201. // Purpose: Updates our current time value. We only
  202. // update the time once per frame-- the rest of the time, we just
  203. // access a cached copy of the time.
  204. // NOTE: This should only be called once per frame.
  205. //-----------------------------------------------------------------------------
  206. void CRTime::UpdateRealTime()
  207. {
  208. // BUGBUG Alfred: update this less often than once per frame?
  209. RTime32 nTimePrev = sm_nTimeCur;
  210. sm_nTimeCur = time(NULL);
  211. if ( sm_nTimeCur < nTimePrev )
  212. {
  213. // time can go backwards sometimes if clock sync adjusts system time; warn when this happens
  214. EmitInfo( SPEW_SYSTEM_MISC, SPEW_ALWAYS, LOG_ALWAYS, "Warning: system time went backward by %d seconds\n", ( nTimePrev - sm_nTimeCur ) );
  215. }
  216. // update our time from file time once per second
  217. if ( sm_nTimeCur - sm_nTimeLastSystemTimeUpdate >= 1 )
  218. {
  219. #ifdef _WIN32
  220. // get the local time, generate time & date strings and cache the strings, as we will need these
  221. // frequently for logs.
  222. SYSTEMTIME systemTimeLocal;
  223. GetLocalTime( &systemTimeLocal );
  224. GetTimeFormat( LOCALE_USER_DEFAULT, 0, &systemTimeLocal, "HH:mm:ss", sm_rgchLocalTimeCur, Q_ARRAYSIZE( sm_rgchLocalTimeCur ) );
  225. GetDateFormat( LOCALE_USER_DEFAULT, 0, &systemTimeLocal, "MM/dd/yy", sm_rgchLocalDateCur, Q_ARRAYSIZE( sm_rgchLocalDateCur ) );
  226. #elif defined(POSIX)
  227. time_t now;
  228. time( &now );
  229. struct tm tmStruct;
  230. struct tm *localTime = Plat_gmtime( &now, &tmStruct );
  231. strftime( sm_rgchLocalTimeCur, Q_ARRAYSIZE( sm_rgchLocalTimeCur ), "%H:%M:%S", localTime );
  232. strftime( sm_rgchLocalDateCur, Q_ARRAYSIZE( sm_rgchLocalDateCur ), "%m/%d/%y", localTime );
  233. #else
  234. #error "Implement me"
  235. #endif
  236. sm_nTimeLastSystemTimeUpdate = sm_nTimeCur;
  237. }
  238. }
  239. //-----------------------------------------------------------------------------
  240. // Purpose: Sets the system clock on this box to specified world time
  241. // Input: rTime32Current - world time to set
  242. //-----------------------------------------------------------------------------
  243. void CRTime::SetSystemClock( RTime32 rTime32Current )
  244. {
  245. #ifdef _WIN32
  246. FILETIME fileTime;
  247. SYSTEMTIME systemTime = {0};
  248. // convert from seconds since 1/1/1970 to filetime (100 nanoseconds since 1/1/1601) with this magic formula courtesy of MSDN
  249. uint64 ulTmp = ( ( (uint64) rTime32Current ) * 10 * k_nMillion ) + 116444736000000000;
  250. fileTime.dwLowDateTime = (DWORD) ulTmp;
  251. fileTime.dwHighDateTime = ulTmp >> 32;
  252. // convert from filetime to system time (note this also does time zone conversion to UTC)
  253. BOOL bRet = FileTimeToSystemTime( &fileTime, &systemTime );
  254. Assert( bRet ); // should never fail
  255. if ( !bRet ) // but if it does, don't set system clock to garbage
  256. return;
  257. // set system time in UTC
  258. bRet = SetSystemTime( &systemTime );
  259. Assert( bRet );
  260. // update our cached time
  261. sm_nTimeCur = rTime32Current;
  262. #else
  263. Assert( !"Not implemented" );
  264. #endif // _WIN32
  265. }
  266. //-----------------------------------------------------------------------------
  267. // Purpose: Renders the time
  268. // Output : ptr to time string
  269. //-----------------------------------------------------------------------------
  270. const char* CRTime::Render( char (&buf)[k_RTimeRenderBufferSize] ) const
  271. {
  272. return Render( m_nStartTime, buf );
  273. }
  274. //-----------------------------------------------------------------------------
  275. // Purpose: Renders the time - static function
  276. // Input : rTime32 - time to render
  277. // Output : ptr to time string
  278. //-----------------------------------------------------------------------------
  279. const char* CRTime::Render( const RTime32 rTime32, char (&buf)[k_RTimeRenderBufferSize] )
  280. {
  281. if ( !buf )
  282. {
  283. Assert( buf );
  284. return nullptr;
  285. }
  286. // The return value string contains exactly 26 characters and has the form: Wed Jan 02 02:03:55 1980\n\0
  287. time_t tTime = rTime32;
  288. char pchTime[32];
  289. if ( !Plat_ctime( &tTime, pchTime, Q_ARRAYSIZE( pchTime ) ) )
  290. return 0;
  291. // Remove '\n'
  292. Assert( Q_strlen( pchTime ) == 25 );
  293. pchTime[ 24 ] = '\0';
  294. if ( rTime32 == k_RTime32Infinite )
  295. Q_strncpy( buf, "Infinite time value", k_RTimeRenderBufferSize );
  296. else if ( rTime32 == k_RTime32Nil )
  297. Q_strncpy( buf, "Nil time value", k_RTimeRenderBufferSize );
  298. else if ( rTime32 < k_RTime32MinValid )
  299. Q_strncpy( buf, "Invalid time value", k_RTimeRenderBufferSize );
  300. else
  301. Q_strncpy( buf, pchTime, k_RTimeRenderBufferSize );
  302. return buf;
  303. }
  304. //-----------------------------------------------------------------------------
  305. // Purpose: Get the calendar year (absolute) for the current time
  306. //-----------------------------------------------------------------------------
  307. int CRTime::GetYear() const
  308. {
  309. time_t timeCur = m_nStartTime;
  310. struct tm tmStruct;
  311. struct tm *ptmCur = m_bGMT ? Plat_gmtime( &timeCur, &tmStruct ) : Plat_localtime( &timeCur, &tmStruct );
  312. return ptmCur->tm_year + 1900;
  313. }
  314. //-----------------------------------------------------------------------------
  315. // Purpose: Get the calendar month (0-11) for the current time
  316. //-----------------------------------------------------------------------------
  317. int CRTime::GetMonth() const
  318. {
  319. time_t timeCur = m_nStartTime;
  320. struct tm tmStruct;
  321. struct tm *ptmCur = m_bGMT ? Plat_gmtime( &timeCur, &tmStruct ) : Plat_localtime( &timeCur, &tmStruct );
  322. return ptmCur->tm_mon;
  323. }
  324. //-----------------------------------------------------------------------------
  325. // Purpose: Get the day of the calendar year (0-365) for the current time
  326. //-----------------------------------------------------------------------------
  327. int CRTime::GetDayOfYear() const
  328. {
  329. time_t timeCur = m_nStartTime;
  330. struct tm tmStruct;
  331. struct tm *ptmCur = m_bGMT ? Plat_gmtime( &timeCur, &tmStruct ) : Plat_localtime( &timeCur, &tmStruct );
  332. return ptmCur->tm_yday;
  333. }
  334. //-----------------------------------------------------------------------------
  335. // Purpose: Get the day of the month (1-31) for the current time
  336. //-----------------------------------------------------------------------------
  337. int CRTime::GetDayOfMonth() const
  338. {
  339. time_t timeCur = m_nStartTime;
  340. struct tm tmStruct;
  341. struct tm *ptmCur = m_bGMT ? Plat_gmtime( &timeCur, &tmStruct ) : Plat_localtime( &timeCur, &tmStruct );
  342. return ptmCur->tm_mday;
  343. }
  344. //-----------------------------------------------------------------------------
  345. // Purpose: Get the day of the week (0-6, 0=Sunday) for the current time
  346. //-----------------------------------------------------------------------------
  347. int CRTime::GetDayOfWeek() const
  348. {
  349. time_t timeCur = m_nStartTime;
  350. struct tm tmStruct;
  351. struct tm *ptmCur = m_bGMT ? Plat_gmtime( &timeCur, &tmStruct ) : Plat_localtime( &timeCur, &tmStruct );
  352. return ptmCur->tm_wday;
  353. }
  354. //-----------------------------------------------------------------------------
  355. // Purpose: Get the current hour (0-23)
  356. //-----------------------------------------------------------------------------
  357. int CRTime::GetHour( ) const
  358. {
  359. time_t timeCur = m_nStartTime;
  360. struct tm tmStruct;
  361. struct tm *ptmCur = m_bGMT ? Plat_gmtime( &timeCur, &tmStruct ) : Plat_localtime( &timeCur, &tmStruct );
  362. return ptmCur->tm_hour;
  363. }
  364. //-----------------------------------------------------------------------------
  365. // Purpose: Get the current minute value (0-59)
  366. //-----------------------------------------------------------------------------
  367. int CRTime::GetMinute( ) const
  368. {
  369. time_t timeCur = m_nStartTime;
  370. struct tm tmStruct;
  371. struct tm *ptmCur = m_bGMT ? Plat_gmtime( &timeCur, &tmStruct ) : Plat_localtime( &timeCur, &tmStruct );
  372. return ptmCur->tm_min;
  373. }
  374. //-----------------------------------------------------------------------------
  375. // Purpose: Get the current second value (0-59)
  376. //-----------------------------------------------------------------------------
  377. int CRTime::GetSecond() const
  378. {
  379. time_t timeCur = m_nStartTime;
  380. struct tm tmStruct;
  381. struct tm *ptmCur = m_bGMT ? Plat_gmtime( &timeCur, &tmStruct ) : Plat_localtime( &timeCur, &tmStruct );
  382. return ptmCur->tm_sec;
  383. }
  384. //-----------------------------------------------------------------------------
  385. // Purpose: Get the ISO week number
  386. //-----------------------------------------------------------------------------
  387. int CRTime::GetISOWeekOfYear() const
  388. {
  389. int nDay = GetDayOfYear() - ( 1 + GetDayOfWeek() );
  390. int nISOWeek = nDay / 7;
  391. return nISOWeek;
  392. }
  393. //-----------------------------------------------------------------------------
  394. // Purpose: let me know if this is a leap year or not
  395. //-----------------------------------------------------------------------------
  396. /* static */ bool CRTime::BIsLeapYear( int nYear )
  397. {
  398. // every for years, unless it is a century; or if it is every 4th century
  399. if ( ( nYear % 4 == 0 && nYear % 100 != 0) || nYear % 400 == 0)
  400. return true; /* leap */
  401. else
  402. return false; /* no leap */
  403. }
  404. //-----------------------------------------------------------------------------
  405. // Purpose: Calculate and return a time value corresponding to given sting
  406. // Using a format string to convert
  407. // Input: pchFmt - Format string that describes how to parse the value
  408. // YY or YYYY is year, MM month, DD day of the month,
  409. // hh mm ss is hour minute second.
  410. // Z0000 is a time-zone offset, eg -0700.
  411. // Everything except YY is optional (will be considered 0 if not given)
  412. // pchValue - String containing the value to covert
  413. // Output: RTime32
  414. //-----------------------------------------------------------------------------
  415. // STATIC
  416. RTime32 CRTime::RTime32FromFmtString( const char *pchFmt, const char* pchValue )
  417. {
  418. struct tm tm;
  419. char rgchNum[8];
  420. char rgchValue[64];
  421. Q_memset( &tm, 0x0, sizeof( tm ) );
  422. Q_strncpy( rgchValue, pchValue, sizeof( rgchValue) );
  423. int cchFmt = Q_strlen( pchFmt );
  424. int cchValue = Q_strlen( rgchValue );
  425. if ( cchFmt != cchValue || cchFmt < 4 )
  426. {
  427. Assert( false );
  428. return k_RTime32Nil;
  429. }
  430. const char *pchYYYY = Q_strstr( pchFmt, "YYYY" );
  431. const char *pchYY = Q_strstr( pchFmt, "YY" );
  432. const char *pchMM = Q_strstr( pchFmt, "MM" );
  433. const char *pchMnt = Q_strstr( pchFmt, "Mnt" );
  434. const char *pchDD = Q_strstr( pchFmt, "DD" );
  435. const char *pchThh = Q_strstr( pchFmt, "hh" );
  436. const char *pchTmm = Q_strstr( pchFmt, "mm" );
  437. const char *pchTss = Q_strstr( pchFmt, "ss" );
  438. const char *pchTzone = Q_strstr( pchFmt, "Z0000" );
  439. if ( pchYYYY )
  440. {
  441. pchYYYY = rgchValue + ( pchYYYY - pchFmt );
  442. Q_strncpy( rgchNum, pchYYYY, 5 );
  443. tm.tm_year = strtol( rgchNum, 0, 10 ) - 1900;
  444. }
  445. else if ( pchYY )
  446. {
  447. pchYY = rgchValue + ( pchYY - pchFmt );
  448. Q_strncpy( rgchNum, pchYY, 3 );
  449. tm.tm_year = strtol( rgchNum, 0, 10 ) + 100;
  450. }
  451. else
  452. return k_RTime32Nil; // must have a year
  453. if ( pchMM )
  454. {
  455. pchMM = rgchValue + ( pchMM - pchFmt );
  456. Q_strncpy( rgchNum, pchMM, 3 );
  457. tm.tm_mon = strtol( rgchNum, 0, 10 ) - 1;
  458. }
  459. if ( pchMnt )
  460. {
  461. static const char *rgszMonthNames[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
  462. pchMnt = rgchValue + ( pchMnt - pchFmt );
  463. int i;
  464. for ( i = 0; i < 12; i++ )
  465. {
  466. if ( !V_strnicmp( rgszMonthNames[i], pchMnt, 3 ) )
  467. break;
  468. }
  469. if ( i < 12 )
  470. tm.tm_mon = i;
  471. }
  472. if ( pchDD )
  473. {
  474. pchDD = rgchValue + (pchDD - pchFmt );
  475. Q_strncpy( rgchNum, pchDD, 3 );
  476. tm.tm_mday = strtol( rgchNum, 0, 10 );
  477. }
  478. if ( pchThh )
  479. {
  480. pchThh = rgchValue + ( pchThh - pchFmt );
  481. Q_strncpy( rgchNum, pchThh, 3 );
  482. tm.tm_hour = strtol( rgchNum, 0, 10 );
  483. }
  484. if ( pchTmm )
  485. {
  486. pchTmm = rgchValue + ( pchTmm - pchFmt );
  487. Q_strncpy( rgchNum, pchTmm, 3 );
  488. tm.tm_min = strtol( rgchNum, 0, 10 );
  489. }
  490. if ( pchTss )
  491. {
  492. pchTss = rgchValue + (pchTss - pchFmt );
  493. Q_strncpy( rgchNum, pchTss, 3 );
  494. tm.tm_sec = strtol( rgchNum, 0, 10 );
  495. }
  496. if ( pchTzone )
  497. {
  498. long nOffset = 0;
  499. pchTzone = rgchValue + (pchTzone - pchFmt);
  500. Q_strncpy( rgchNum, pchTzone, 6 );
  501. nOffset = strtol( rgchNum, 0, 10 );
  502. tm.tm_hour -= nOffset / 100; // to go from -0700 to UTC, need to ADD seven
  503. // is this a sub-hour timezone? eg +0545 Kathmandu
  504. int nMinutesOffset = nOffset % 100;
  505. if ( nMinutesOffset )
  506. tm.tm_min -= nMinutesOffset;
  507. // OK, so this is somewhat lame: mktime assumes our tm units are in LOCAL time.
  508. // However, we have just created a UTC time by using the supplied timezone offset.
  509. // The rational thing to do here would be to call mkgmtime() instead of mktime(),
  510. // but that function isn't available in unix-land.
  511. // SO, instead we will MANUALLY convert this tm back to local time
  512. #if ( defined( _MSC_VER ) && _MSC_VER >= 1900 )
  513. #define timezone _timezone
  514. #define daylight _daylight
  515. #endif
  516. // subtract timezone, which is in SECONDS. timezone is (UTC - local), so local = UTC - timezone
  517. tm.tm_sec -= timezone;
  518. // timezone does NOT account for DST, so if we are in DST, we need to ADD an hour.
  519. // This is because the value of timezone we subtracted was one hour TOO LARGE
  520. tm.tm_hour += daylight ? 1 : 0;
  521. }
  522. // We don't know if DST is in effect, let the CRT
  523. // figure it out
  524. tm.tm_isdst = -1;
  525. return mktime( &tm );
  526. }
  527. //-----------------------------------------------------------------------------
  528. // Purpose: Calculate and return a time value corresponding to given sting which is
  529. // expected to be in one of the common HTTP date formats.
  530. //-----------------------------------------------------------------------------
  531. // STATIC
  532. RTime32 CRTime::RTime32FromHTTPDateString( const char* pchValue )
  533. {
  534. // First format here is RFC 822/1123 format
  535. struct tm tm;
  536. if ( strptime( pchValue, "%a, %e %b %Y %H:%M:%S", &tm ) )
  537. {
  538. return Plat_timegm( &tm );
  539. }
  540. // If that failed, try RFC 850/1036 format
  541. if ( strptime( pchValue, "%a, %e-%b-%y %H:%M:%S", &tm ) )
  542. {
  543. return Plat_timegm( &tm );
  544. }
  545. // If that also failed, give up
  546. return k_RTime32Nil;
  547. }
  548. //-----------------------------------------------------------------------------
  549. // Purpose: Parse time from string RFC3339 format (assumes UTC)
  550. //-----------------------------------------------------------------------------
  551. // STATIC
  552. RTime32 CRTime::RTime32FromRFC3339UTCString( const char* pchValue )
  553. {
  554. // UTC only from RFC 3339. Should be 2005-05-15T17:11:51Z
  555. struct tm tm;
  556. if ( strptime( pchValue, "%Y-%m-%dT%H:%M:%SZ", &tm ) )
  557. {
  558. return Plat_timegm( &tm );
  559. }
  560. // If that also failed, give up
  561. return k_RTime32Nil;
  562. }
  563. //-----------------------------------------------------------------------------
  564. // Purpose: Output time in RFC3339 format (assumes UTC)
  565. //-----------------------------------------------------------------------------
  566. // STATIC
  567. const char* CRTime::RTime32ToRFC3339UTCString( const RTime32 rTime32, char (&buf)[k_RTimeRenderBufferSize] )
  568. {
  569. if ( !buf )
  570. {
  571. Assert( buf );
  572. return nullptr;
  573. }
  574. // Store the result in a temporary buffer, so that you can use several in a single printf.
  575. time_t tTime = rTime32;
  576. struct tm tmStruct;
  577. struct tm *ptm = Plat_gmtime( &tTime, &tmStruct );
  578. if ( rTime32 == k_RTime32Nil || !ptm )
  579. return "NIL";
  580. if ( rTime32 == k_RTime32Infinite )
  581. return "Infinite time value";
  582. if ( rTime32 < k_RTime32MinValid || !ptm )
  583. return "Invalid time value";
  584. Q_snprintf( buf, k_RTimeRenderBufferSize, "%04u-%02u-%02uT%02u:%02u:%02uZ", ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec );
  585. return buf;
  586. }
  587. //-----------------------------------------------------------------------------
  588. // Purpose: Calculate and return a time value corresponding to given sting
  589. // "YYYY-MM-DD hh:mm:ss" (copied from sqlhelpers.cpp)
  590. //-----------------------------------------------------------------------------
  591. // STATIC
  592. RTime32 CRTime::RTime32FromString( const char* pszValue )
  593. {
  594. struct tm tm;
  595. char num[5];
  596. char szValue[64];
  597. Q_memset( &tm, 0x0, sizeof( tm ) );
  598. Q_strncpy( szValue, pszValue, sizeof( szValue) );
  599. const char *str= szValue;
  600. num[0] =*str++; num[1] =*str++; num[2] =*str++; num[3] =*str++; num[4] = 0;
  601. tm.tm_year = strtol( num, 0, 10 ) - 1900;
  602. if (*str == '-') str++;
  603. num[0] = *str++; num[1] = *str++; num[2] = 0;
  604. tm.tm_mon = strtol( num, 0, 10 ) - 1;
  605. if (*str == '-') str++;
  606. num[0] = *str++; num[1] = *str++; num[2] = 0;
  607. tm.tm_mday = strtol( num, 0, 10 );
  608. if ( *str != 0 )
  609. {
  610. // skip an optional space or T between date and time
  611. if ( *str == ' ' || *str == 'T' )
  612. str++;
  613. // time is given too
  614. num[0] = *str++; num[1] = *str++; num[2] = 0;
  615. tm.tm_hour = strtol( num, 0, 10 );
  616. if (*str == ':') str++;
  617. num[0] = *str++; num[1] = *str++; num[2] = 0;
  618. tm.tm_min = strtol( num, 0, 10 );
  619. if (*str == ':') str++;
  620. num[0] = *str++; num[1] = *str++; num[2] = 0;
  621. tm.tm_sec = strtol( num, 0, 10 );
  622. }
  623. tm.tm_isdst = -1;
  624. return mktime( &tm );
  625. }
  626. //-----------------------------------------------------------------------------
  627. // Purpose: Returns a static string "YYYY-MM-DD hh:mm:ss" for given RTime32
  628. // Input: rTime32 -
  629. // bNoPunct - No dashes, colons or spaces will be in the output string
  630. // bOnlyDate - Only output the date
  631. // Output: const char * -- only usable till the next yield
  632. //-----------------------------------------------------------------------------
  633. // STATIC
  634. const char* CRTime::RTime32ToString( const RTime32 rTime32, char (&buf)[k_RTimeRenderBufferSize], bool bNoPunct /*=false*/, bool bOnlyDate /*= false*/ )
  635. {
  636. if ( !buf )
  637. {
  638. return nullptr;
  639. }
  640. // Store the result in a temporary buffer, so that you can use several in a single printf.
  641. time_t tTime = rTime32;
  642. struct tm tmStruct;
  643. struct tm *ptm = Plat_localtime( &tTime, &tmStruct );
  644. const char *pchOnlyDateFmt = bNoPunct ? "%04u%02u%02u" : "%04u-%02u-%02u";
  645. const char *pchDateTimeFmt = bNoPunct ? "%04u%02u%02u%02u%02u%02u" : "%04u-%02u-%02u %02u:%02u:%02u";
  646. if ( rTime32 == k_RTime32Nil || !ptm )
  647. return "NIL";
  648. if ( rTime32 == k_RTime32Infinite )
  649. return "Infinite time value";
  650. if ( rTime32 < k_RTime32MinValid || !ptm )
  651. return "Invalid time value";
  652. if ( bOnlyDate )
  653. {
  654. Q_snprintf( buf, k_RTimeRenderBufferSize, pchOnlyDateFmt,
  655. ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday );
  656. }
  657. else
  658. {
  659. Q_snprintf( buf, k_RTimeRenderBufferSize, pchDateTimeFmt,
  660. ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday,
  661. ptm->tm_hour, ptm->tm_min, ptm->tm_sec );
  662. }
  663. return buf;
  664. }
  665. //-----------------------------------------------------------------------------
  666. // Purpose: Returns a static string like "Aug 21" for given RTime32
  667. // Input: rTime32 -
  668. //
  669. // Output: const char * -- only usable till the next yield
  670. //-----------------------------------------------------------------------------
  671. // STATIC
  672. const char* CRTime::RTime32ToDayString( const RTime32 rTime32, char (&buf)[k_RTimeRenderBufferSize], bool bGMT )
  673. {
  674. if ( !buf )
  675. {
  676. return nullptr;
  677. }
  678. // Store the result in a temporary buffer, so that you can use several in a single printf.
  679. time_t tTime = rTime32;
  680. struct tm tmStruct;
  681. struct tm *ptm = bGMT ? Plat_gmtime( &tTime, &tmStruct ) : Plat_localtime( &tTime, &tmStruct );
  682. DbgVerify( strftime( buf, k_RTimeRenderBufferSize, "%b %d", ptm ) );
  683. return buf;
  684. }
  685. //-----------------------------------------------------------------------------
  686. // Purpose: Calculate and return a time value corresponding to the beginning of
  687. // the day represented by rtime32
  688. //-----------------------------------------------------------------------------
  689. // STATIC
  690. RTime32 CRTime::RTime32BeginningOfDay( const RTime32 rtime32 )
  691. {
  692. time_t timeCur = rtime32;
  693. struct tm tmStruct;
  694. struct tm *ptmCur = Plat_localtime( &timeCur, &tmStruct );
  695. if ( !ptmCur )
  696. return k_RTime32Nil;
  697. // midnight
  698. ptmCur->tm_hour = 0;
  699. ptmCur->tm_min = 0;
  700. ptmCur->tm_sec = 0;
  701. // Let it compute DST
  702. ptmCur->tm_isdst = -1;
  703. return mktime( ptmCur );
  704. }
  705. //-----------------------------------------------------------------------------
  706. // Purpose: Calculate and return a time value corresponding to the beginning of
  707. // the next day after rtime32
  708. //-----------------------------------------------------------------------------
  709. // STATIC
  710. RTime32 CRTime::RTime32BeginningOfNextDay( const RTime32 rtime32 )
  711. {
  712. time_t timeCur = rtime32;
  713. struct tm tmStruct;
  714. struct tm *ptmCur = Plat_localtime( &timeCur, &tmStruct );
  715. if ( !ptmCur )
  716. return k_RTime32Nil;
  717. // It will move to the next month etc if need be
  718. ptmCur->tm_mday++;
  719. // midnight
  720. ptmCur->tm_hour = 0;
  721. ptmCur->tm_min = 0;
  722. ptmCur->tm_sec = 0;
  723. // Let it compute DST
  724. ptmCur->tm_isdst = -1;
  725. return mktime( ptmCur );
  726. }
  727. //-----------------------------------------------------------------------------
  728. // Purpose: Calculate and return a time value corresponding to the first day of
  729. // the month indicated by rtime32
  730. //-----------------------------------------------------------------------------
  731. // STATIC
  732. RTime32 CRTime::RTime32FirstDayOfMonth( const RTime32 rtime32 )
  733. {
  734. time_t timeCur = rtime32;
  735. struct tm tmStruct;
  736. struct tm *ptmCur = Plat_localtime( &timeCur, &tmStruct );
  737. if ( !ptmCur )
  738. return k_RTime32Nil;
  739. // first day of month
  740. ptmCur->tm_mday = 1;
  741. // midnight
  742. ptmCur->tm_hour = 0;
  743. ptmCur->tm_min = 0;
  744. ptmCur->tm_sec = 0;
  745. // Let it compute DST
  746. ptmCur->tm_isdst = -1;
  747. return mktime( ptmCur );
  748. }
  749. //-----------------------------------------------------------------------------
  750. // Purpose: Calculate and return a time value corresponding to the last day of
  751. // the month indicated by rtime32
  752. //-----------------------------------------------------------------------------
  753. // STATIC
  754. RTime32 CRTime::RTime32LastDayOfMonth( const RTime32 rtime32 )
  755. {
  756. time_t timeCur = rtime32;
  757. struct tm tmStruct;
  758. struct tm *ptmCur = Plat_localtime( &timeCur, &tmStruct );
  759. if ( !ptmCur )
  760. return k_RTime32Nil;
  761. // Zeroth day of month N becomes last day of month (N-1)
  762. ptmCur->tm_mon++;
  763. ptmCur->tm_mday = 0;
  764. // midnight
  765. ptmCur->tm_hour = 0;
  766. ptmCur->tm_min = 0;
  767. ptmCur->tm_sec = 0;
  768. // Let it compute DST
  769. ptmCur->tm_isdst = -1;
  770. return mktime( ptmCur );
  771. }
  772. //-----------------------------------------------------------------------------
  773. // Purpose: Calculate and return a time value corresponding to the first day of
  774. // the month after the one indicated by rtime32
  775. //-----------------------------------------------------------------------------
  776. // STATIC
  777. RTime32 CRTime::RTime32FirstDayOfNextMonth( const RTime32 rtime32 )
  778. {
  779. time_t timeCur = rtime32;
  780. struct tm tmStruct;
  781. struct tm *ptmCur = Plat_localtime( &timeCur, &tmStruct );
  782. if ( !ptmCur )
  783. return k_RTime32Nil;
  784. ptmCur->tm_mon++;
  785. ptmCur->tm_mday = 1;
  786. // midnight
  787. ptmCur->tm_hour = 0;
  788. ptmCur->tm_min = 0;
  789. ptmCur->tm_sec = 0;
  790. // Let it compute DST
  791. ptmCur->tm_isdst = -1;
  792. return mktime( ptmCur );
  793. }
  794. //-----------------------------------------------------------------------------
  795. // Purpose: Calculate and return a time value corresponding to the last day of
  796. // the month after the one indicated by rtime32
  797. //-----------------------------------------------------------------------------
  798. // STATIC
  799. RTime32 CRTime::RTime32LastDayOfNextMonth( const RTime32 rtime32 )
  800. {
  801. time_t timeCur = rtime32;
  802. struct tm tmStruct;
  803. struct tm *ptmCur = Plat_localtime( &timeCur, &tmStruct );
  804. if ( !ptmCur )
  805. return k_RTime32Nil;
  806. // use zeroth-day trick - skip 2 months then back a day
  807. ptmCur->tm_mon += 2;
  808. ptmCur->tm_mday = 0;
  809. // midnight
  810. ptmCur->tm_hour = 0;
  811. ptmCur->tm_min = 0;
  812. ptmCur->tm_sec = 0;
  813. // Let it compute DST
  814. ptmCur->tm_isdst = -1;
  815. return mktime( ptmCur );
  816. }
  817. //-----------------------------------------------------------------------------
  818. // Purpose: Calculate and return a time value corresponding to the Nth day of
  819. // the month. If that month only has K days, K < N, it will return
  820. // the Kth day. The input should be reasonable (don't ask for the -5th
  821. // day of the month).
  822. //
  823. // Input: rtime32 - Time representing some time in the month interested in
  824. // nDay - The day of that month you want the return to be set to
  825. //
  826. // Return: Time value equal to midnight on that day.
  827. //-----------------------------------------------------------------------------
  828. // STATIC
  829. RTime32 CRTime::RTime32NthDayOfMonth( const RTime32 rtime32, int nDay )
  830. {
  831. Assert( nDay > 0 );
  832. Assert( nDay < 32 );
  833. time_t timeCur = rtime32;
  834. struct tm tmStruct;
  835. struct tm *ptmCur = Plat_localtime( &timeCur, &tmStruct );
  836. if ( !ptmCur )
  837. return k_RTime32Nil;
  838. int nCurMonth = ptmCur->tm_mon;
  839. ptmCur->tm_mday = nDay;
  840. // midnight
  841. ptmCur->tm_hour = 0;
  842. ptmCur->tm_min = 0;
  843. ptmCur->tm_sec = 0;
  844. // Let it compute DST
  845. ptmCur->tm_isdst = -1;
  846. // This call will modify ptmCur in-place
  847. time_t timeThen = mktime( ptmCur );
  848. // See if the month changed
  849. if ( ptmCur->tm_mon != nCurMonth )
  850. {
  851. // use zeroth-day trick to just get the last day of this month
  852. ptmCur->tm_mday = 0;
  853. // Let it compute DST
  854. ptmCur->tm_isdst = -1;
  855. timeThen = mktime( ptmCur );
  856. }
  857. return timeThen;
  858. }
  859. //-----------------------------------------------------------------------------
  860. // Purpose: Add X months to the current date, and return the Nth day of that
  861. // month.
  862. //
  863. // Input: nNthDayOfMonth - Day of the target month to return a date for
  864. // rtime32StartDate - Time value to add X months to
  865. // nMonthsToAdd - X
  866. //
  867. // Return: Time value equal to midnight on that day.
  868. //-----------------------------------------------------------------------------
  869. // STATIC
  870. RTime32 CRTime::RTime32MonthAddChooseDay( int nNthDayOfMonth, RTime32 rtime32StartDate, int nMonthsToAdd )
  871. {
  872. // Get the first day of start month
  873. RTime32 rtime32FirstDayOfStartMonth = CRTime( rtime32StartDate ).GetFirstDayOfMonth();
  874. // Add X months to that - guaranteed to be correct month
  875. RTime32 rtime32FirstDayOfTargetMonth = CRTime::RTime32DateAdd( rtime32FirstDayOfStartMonth, nMonthsToAdd, k_ETimeUnitMonth );
  876. // Then get the Nth day of that month
  877. RTime32 rtime32Target = CRTime::RTime32NthDayOfMonth( rtime32FirstDayOfTargetMonth, nNthDayOfMonth );
  878. return rtime32Target;
  879. }
  880. //-----------------------------------------------------------------------------
  881. // Purpose: Add or subtract N units of time from the current value.
  882. // Units may be days, weeks, seconds, etc
  883. // Input: rtime32 - Reference time
  884. // nAmount - Number of units to add (neg for subtract)
  885. // eTimeFlagAmountType - Indicates what units are on nAmount
  886. //
  887. // Return: The newly calculated offset time (the input is unmodified)
  888. //-----------------------------------------------------------------------------
  889. // STATIC
  890. RTime32 CRTime::RTime32DateAdd( const RTime32 rtime32, int nAmount, ETimeUnit eTimeAmountType )
  891. {
  892. time_t timeCur = rtime32;
  893. struct tm tmStruct;
  894. struct tm *ptmCur = Plat_localtime( &timeCur, &tmStruct );
  895. if ( !ptmCur )
  896. return k_RTime32Nil;
  897. // mktime() is smart enough to take day-of-month values that are out of range and adjust
  898. // everything to make sense. So you can go back 3 weeks by just subtracting 21 from tm_mday.
  899. switch ( eTimeAmountType )
  900. {
  901. default:
  902. AssertMsg( false, "Bad flag in RTime32DateAdd" );
  903. break;
  904. case k_ETimeUnitForever:
  905. return k_RTime32Infinite;
  906. case k_ETimeUnitYear:
  907. ptmCur->tm_year += nAmount;
  908. break;
  909. case k_ETimeUnitMonth:
  910. ptmCur->tm_mon += nAmount;
  911. break;
  912. case k_ETimeUnitWeek:
  913. ptmCur->tm_mday += 7 * nAmount;
  914. break;
  915. case k_ETimeUnitDay:
  916. ptmCur->tm_mday += nAmount;
  917. break;
  918. case k_ETimeUnitHour:
  919. ptmCur->tm_hour += nAmount;
  920. break;
  921. case k_ETimeUnitMinute:
  922. ptmCur->tm_min += nAmount;
  923. break;
  924. case k_ETimeUnitSecond:
  925. ptmCur->tm_sec += nAmount;
  926. break;
  927. }
  928. // Let it compute DST
  929. ptmCur->tm_isdst = -1;
  930. return mktime( ptmCur );
  931. }
  932. //-----------------------------------------------------------------------------
  933. // Purpose: Compare two times and evaluate what calendar boundaries have
  934. // been crossed (eg day, month, hour) between the two times.
  935. //
  936. // Note: in general, the crossing of a large boundary will be accompanied
  937. // by the crossing of all smaller boundaries. The exception is Week:
  938. // the Week boundary is from Saturday to Sunday, and it is possible to
  939. // go over a Month or Year boundary without beginning a new week.
  940. //
  941. // So, the return value is the largest time boundary that was crossed.
  942. // However, the pbWeekChanged value will be set to indicate if the week
  943. // changed in cases where the return value is Month or Year.
  944. //
  945. // Input: unTime1 - First time value
  946. // unTime2 - Second time value
  947. // pbWeekChanged - Indicates if the Week changed
  948. //
  949. // Return: Largest time boundary crossed
  950. //-----------------------------------------------------------------------------
  951. // STATIC
  952. ETimeUnit CRTime::FindTimeBoundaryCrossings( RTime32 unTime1, RTime32 unTime2, bool *pbWeekChanged )
  953. {
  954. time_t time1 = unTime1;
  955. time_t time2 = unTime2;
  956. // have to cache the first one locally, because it's a global object
  957. struct tm tmStruct1;
  958. struct tm *ptmTime1 = Plat_localtime( &time1, &tmStruct1 );
  959. if ( !ptmTime1 )
  960. return k_ETimeUnitForever;
  961. struct tm _tmTime1 = *ptmTime1;
  962. ptmTime1 = &_tmTime1;
  963. struct tm tmStruct2;
  964. struct tm *ptmTime2 = Plat_localtime( &time2, &tmStruct2 );
  965. if ( !ptmTime2 )
  966. return k_ETimeUnitForever;
  967. // Need a little extra logic to find week boundaries
  968. // Find this out first, because it may or may not be true even if a
  969. // month / year boundary was crossed.
  970. *pbWeekChanged = false;
  971. // If the difference is more than 6 days, we crossed a week boundary
  972. if ( ( ( unTime1 > unTime2 ) && ( ( unTime1 - unTime2 ) > k_cSecondsPerWeek ) )
  973. || ( ( unTime2 > unTime1 ) && ( ( unTime2 - unTime1 ) > k_cSecondsPerWeek ) ) )
  974. {
  975. *pbWeekChanged = true;
  976. }
  977. else if ( ptmTime1->tm_yday != ptmTime2->tm_yday )
  978. {
  979. // Otherwise, we have to look at wday - if the later time
  980. // has a lower or equal wday value, then we crossed a week boundary
  981. if ( unTime2 > unTime1 )
  982. {
  983. if ( ptmTime2->tm_wday <= ptmTime1->tm_wday )
  984. *pbWeekChanged = true;
  985. }
  986. else
  987. {
  988. if ( ptmTime1->tm_wday <= ptmTime2->tm_wday )
  989. *pbWeekChanged = true;
  990. }
  991. }
  992. // Evaluate larger boundaries first. As soon as we detect
  993. // that we've crossed a boundary, we consider all smaller boundaries
  994. // crossed too.
  995. // Year
  996. if ( ptmTime1->tm_year != ptmTime2->tm_year )
  997. return k_ETimeUnitYear;
  998. // Month
  999. if ( ptmTime1->tm_mon != ptmTime2->tm_mon )
  1000. return k_ETimeUnitMonth;
  1001. // If the week changed, return that now
  1002. if ( *pbWeekChanged )
  1003. return k_ETimeUnitWeek;
  1004. // Day
  1005. if ( ptmTime1->tm_yday != ptmTime2->tm_yday )
  1006. return k_ETimeUnitDay;
  1007. // Hour
  1008. if ( ptmTime1->tm_hour != ptmTime2->tm_hour )
  1009. return k_ETimeUnitHour;
  1010. // If DST changed, make sure that we know an hour boundary was crossed
  1011. // (overlap from the "fall back" case may otherwise trick us)
  1012. if ( ptmTime1->tm_isdst != ptmTime2->tm_isdst )
  1013. return k_ETimeUnitHour;
  1014. // Minute
  1015. if ( ptmTime1->tm_min != ptmTime2->tm_min )
  1016. return k_ETimeUnitMinute;
  1017. // Second
  1018. if ( ptmTime1->tm_sec != ptmTime2->tm_sec )
  1019. return k_ETimeUnitSecond;
  1020. return k_ETimeUnitNone;
  1021. }