EADateTime.cpp 77 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403
  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (c) Electronic Arts Inc. All rights reserved.
  3. ///////////////////////////////////////////////////////////////////////////////
  4. ///////////////////////////////////////////////////////////////////////////////
  5. // Implements a suite of functionality to manage dates, times, and calendars.
  6. ///////////////////////////////////////////////////////////////////////////////
  7. #include <EAStdC/internal/Config.h>
  8. #include <EAStdC/EADateTime.h>
  9. #include <EAStdC/EAString.h>
  10. #include <EAStdC/EASprintf.h>
  11. #include <EAStdC/EACType.h>
  12. #include <EAStdC/EAStopwatch.h>
  13. #include <EAStdC/EAMemory.h>
  14. #include <float.h>
  15. #include <math.h>
  16. #include <string.h>
  17. #include <EAAssert/eaassert.h>
  18. #if EASTDC_TIME_H_AVAILABLE
  19. #include <time.h>
  20. #else
  21. static tm sTm;
  22. time_t time(time_t*)
  23. {
  24. return 0;
  25. }
  26. tm* gmtime(const time_t*)
  27. {
  28. return &sTm;
  29. }
  30. tm* localtime(const time_t*)
  31. {
  32. return &sTm;
  33. }
  34. #endif
  35. #if EASTDC_LOCALE_H_AVAILABLE
  36. #include <locale.h>
  37. #endif
  38. #if defined(EA_PLATFORM_APPLE)
  39. #include <mach/mach.h>
  40. #include <mach/mach_time.h>
  41. #endif
  42. #if defined(EA_PLATFORM_MICROSOFT)
  43. #if defined(_MSC_VER)
  44. #pragma warning(push, 0)
  45. #endif
  46. #if (defined(EA_PLATFORM_WINDOWS) && !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP))
  47. #include <Windows.h>
  48. #else
  49. #include <WinSock2.h>
  50. #endif
  51. #if defined(_MSC_VER)
  52. #pragma warning(pop)
  53. #endif
  54. // Microsoft has a GetLocalTime function, but it isn't available for all platform build targets.
  55. // The following is the most Microsoft-portable workaround version we can come up with.
  56. void GetLocalTimeAlternative(SYSTEMTIME* pSystemTime)
  57. {
  58. #if defined(EA_PLATFORM_WINDOWS) || defined(EA_PLATFORM_WINDOWS_PHONE)
  59. GetLocalTime(pSystemTime);
  60. #else
  61. // Get current date/time
  62. FILETIME time, localTime;
  63. ::GetSystemTimeAsFileTime(&time);
  64. ::FileTimeToLocalFileTime(&time, &localTime);
  65. ::FileTimeToSystemTime(&localTime, pSystemTime);
  66. #endif
  67. }
  68. #if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
  69. bool EAStdCGetDateFormat(DWORD dwFlags, const SYSTEMTIME* lpDate, char* lpDateStr, size_t cchDate)
  70. {
  71. // size_t cchDate is unsigned and 32 or 64 bit so needs to be cast to pass to GetDateFormat
  72. return (GetDateFormatA(LOCALE_USER_DEFAULT, dwFlags, lpDate, NULL, lpDateStr, static_cast<int>(cchDate)) != 0);
  73. }
  74. bool EAStdCGetTimeFormat(DWORD dwFlags, const SYSTEMTIME* lpTime, char* timeStr, size_t cchTime)
  75. {
  76. // size_t cchTime is unsigned and 32 or 64 bit so needs to be cast to pass to GetTimeFormat
  77. return (GetTimeFormatA(LOCALE_USER_DEFAULT, dwFlags, lpTime, NULL, timeStr, static_cast<int>(cchTime)) != 0);
  78. }
  79. int EAStdCGetLocaleInfo(LCTYPE lcType, char* lcData, size_t cchData)
  80. {
  81. // size_t cchData is unsigned and 32 or 64 bit so needs to be cast to pass to GetLocaleInfo
  82. return GetLocaleInfoA(LOCALE_USER_DEFAULT, lcType, lcData, static_cast<int>(cchData));
  83. }
  84. #else
  85. bool EAStdCGetDateFormat(DWORD dwFlags, const SYSTEMTIME* lpDate, char* dateStr, size_t cchDate)
  86. {
  87. wchar_t* temp = static_cast<wchar_t*>(EAAlloca(cchDate * sizeof(wchar_t)));
  88. const bool res = GetDateFormatEx(LOCALE_NAME_USER_DEFAULT, dwFlags, lpDate, NULL, temp, static_cast<int>(cchDate), NULL) != 0;
  89. EA::StdC::Strlcpy(dateStr, temp, cchDate);
  90. return res;
  91. }
  92. bool EAStdCGetTimeFormat(DWORD dwFlags, const SYSTEMTIME* lpTime, char* timeStr, size_t cchTime)
  93. {
  94. wchar_t* temp = static_cast<wchar_t*>(EAAlloca(cchTime * sizeof(wchar_t)));
  95. const bool res = GetTimeFormatEx(LOCALE_NAME_USER_DEFAULT, dwFlags, lpTime, NULL, temp, static_cast<int>(cchTime)) != 0;
  96. EA::StdC::Strlcpy(timeStr, temp, cchTime);
  97. return res;
  98. }
  99. int EAStdCGetLocaleInfo(LCTYPE lcType, char* lcData, size_t cchData)
  100. {
  101. wchar_t* temp = lcData ? static_cast<wchar_t*>(EAAlloca(cchData * sizeof(wchar_t))) : nullptr;
  102. const int res = GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, lcType, temp, static_cast<int>(cchData));
  103. if (lcData)
  104. EA::StdC::Strlcpy(lcData, temp, cchData);
  105. return res;
  106. }
  107. #endif
  108. #else
  109. typedef struct _FILETIME
  110. {
  111. uint32_t dwLowDateTime;
  112. uint32_t dwHighDateTime;
  113. } FILETIME;
  114. typedef struct _SYSTEMTIME
  115. {
  116. uint16_t wYear;
  117. uint16_t wMonth;
  118. uint16_t wDayOfWeek;
  119. uint16_t wDay;
  120. uint16_t wHour;
  121. uint16_t wMinute;
  122. uint16_t wSecond;
  123. uint16_t wMilliseconds;
  124. } SYSTEMTIME;
  125. #endif
  126. #ifdef _MSC_VER
  127. #pragma warning(push)
  128. #pragma warning(disable: 4365) // 'argument' : conversion from 'int' to 'uint32_t', signed/unsigned mismatch
  129. #endif
  130. namespace EA
  131. {
  132. namespace StdC
  133. {
  134. // static table containing number of days for each month
  135. static const uint32_t kDaysInMonth[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  136. // static table containing total number of days within a year up to a given month
  137. static const uint32_t kDaysInYear[26] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365, // for regular years
  138. 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }; // for leap years
  139. // macro for computing number of leap years that occured up to the specified year
  140. #define EADT_COUNT_LEAP_YEARS(Y) (((Y - 1) / 4) - ((Y - 1) / 100) + ((Y - 1) / 400))
  141. #ifndef EASTDC_ABS
  142. #define EASTDC_ABS(x) (x >= 0 ? x : -x)
  143. #endif
  144. ///////////////////////////////////////////////////////////////////////
  145. // GetTime
  146. //
  147. // Returns nanoseconds since January 1, 1970.
  148. // There are 584 years worth of nanoseconds that can be stored in a uint64_t.
  149. // See the declaration for a more precise specification.
  150. //
  151. EASTDC_API uint64_t GetTime()
  152. {
  153. // This code is thread-unsafe for the case that the first ever call to GetTime
  154. // occurs by two threads at the same time, and/or there is a memory view latency
  155. // between two CPUs that call this. The fix is probably to use a formal atomic in
  156. // initializing the stopwatch and sSystemTimeDiffAtCapture variables.
  157. static EA::StdC::Stopwatch sStopwatch(Stopwatch::kUnitsNanoseconds, true);
  158. static uint64_t sInitialTime = 0;
  159. uint64_t t = sStopwatch.GetElapsedTime();
  160. if(sInitialTime == 0) // If this is the first time this function is called...
  161. {
  162. timeval tv;
  163. GetTimeOfDay(&tv, NULL, true); // Returns time in the form of seconds/useconds since 1970.
  164. sInitialTime = (uint64_t)((tv.tv_sec * UINT64_C(1000000000)) + (tv.tv_usec * UINT64_C(1000)));
  165. }
  166. return sInitialTime + t;
  167. }
  168. #if 0 // Alternative variation, which on the surface seems more efficient.
  169. /*
  170. EASTDC_API uint64_t GetTime()
  171. {
  172. static struct TimeWatch
  173. {
  174. EA::StdC::Stopwatch stopwatch;
  175. uint64_t initialTime;
  176. TimeWatch() :
  177. stopwatch(Stopwatch::kUnitsNanoseconds, true)
  178. {
  179. timeval tv;
  180. GetTimeOfDay(&tv, NULL, true);
  181. initialTime = (uint64_t)((tv.tv_sec * UINT64_C(1000000000)) + (tv.tv_usec * UINT64_C(1000)));
  182. }
  183. } sTimeWatch;
  184. return sTimeWatch.initialTime + sTimeWatch.stopwatch.GetElapsedTime();
  185. }
  186. */
  187. #endif
  188. ///////////////////////////////////////////////////////////////////////
  189. // GetTimePrecision
  190. //
  191. // Returns the precision of the GetTime and GetTimeOfDay functions.
  192. //
  193. EASTDC_API uint64_t GetTimePrecision()
  194. {
  195. #if defined(EA_PLATFORM_MICROSOFT)
  196. return 100; // 100-nanosecond precision.
  197. #elif defined(EA_PLATFORM_APPLE) || defined(__APPLE__) || defined(EA_PLATFORM_UNIX) || defined(EA_PLATFORM_IPHONE)
  198. return 1000; // Microsecond precision.
  199. #else
  200. return UINT64_C(1000000000); // Second-level precision.
  201. #endif
  202. }
  203. ///////////////////////////////////////////////////////////////////////////
  204. // DateTimeParameters
  205. ///////////////////////////////////////////////////////////////////////////
  206. DateTimeParameters::DateTimeParameters()
  207. {
  208. EA_COMPILETIME_ASSERT(kDateTimeIgnored == 0xffffffff);
  209. memset(this, 0xff, sizeof(DateTimeParameters));
  210. }
  211. ///////////////////////////////////////////////////////////////////////////
  212. // DateTime
  213. ///////////////////////////////////////////////////////////////////////////
  214. ///////////////////////////////////////////////////////////////////////
  215. // GetParameter
  216. //
  217. // Gets the given parameter. If you want to get the year,
  218. // you would call GetParameter(kParameterYear).
  219. //
  220. uint32_t DateTime::GetParameter(Parameter parameter) const
  221. {
  222. uint32_t result = 0;
  223. switch (parameter)
  224. {
  225. // Refers to full year value, such as 1994, 2006, etc. but not
  226. // a two digit value such as 94 or 04. The valid range is 0 - INT_MAX.
  227. case kParameterYear:
  228. {
  229. // compute total number of days
  230. const int64_t nDays = (mnSeconds / kSecondsPerDay);
  231. // compute the year
  232. const int64_t leapYearCount = EADT_COUNT_LEAP_YEARS(nDays / 365);
  233. result = 1 + (uint32_t)((nDays - 1 - leapYearCount) / 365);
  234. break;
  235. }
  236. // Refers to month of year, starting with 1 for January. The valid range is 1 - 12.
  237. case kParameterMonth:
  238. {
  239. // get all the data we need
  240. const uint32_t nYear = GetParameter(kParameterYear);
  241. const uint32_t nDayOfYear = GetParameter(kParameterDayOfYear);
  242. // check if the year is a leap year
  243. const bool bLeap = IsLeapYear(nYear);
  244. // compute the month
  245. for(uint32_t nMonth = kMonthJanuary; nMonth <= kMonthDecember; nMonth++)
  246. {
  247. if(nDayOfYear <= kDaysInYear[nMonth + (13 * bLeap)])
  248. {
  249. result = nMonth;
  250. break;
  251. }
  252. }
  253. if(result == 0)
  254. result = kMonthJanuary;
  255. break;
  256. }
  257. // Refers to a day of the year, starting with 1 for January 1st.
  258. // The valid range is 1 - 366.
  259. case kParameterDayOfYear:
  260. {
  261. // get the year
  262. const uint32_t nYear = GetParameter(kParameterYear);
  263. // compute total number of days
  264. const int64_t nDays = (mnSeconds / kSecondsPerDay);
  265. // compute the day of the year
  266. const uint32_t leapYearCount = EADT_COUNT_LEAP_YEARS(nYear);
  267. result = (uint32_t)(nDays - (((nYear - 1) * 365) + leapYearCount));
  268. if(result == 0)
  269. result = 1;
  270. break;
  271. }
  272. // Refers to the day of the month, starting with 1 for the first
  273. // of the month. The valid range is 1 - 31.
  274. case kParameterDayOfMonth:
  275. {
  276. // get all the data we need
  277. const uint32_t nYear = GetParameter(kParameterYear);
  278. const uint32_t nMonth = GetParameter(kParameterMonth);
  279. const uint32_t nDayOfYear = GetParameter(kParameterDayOfYear);
  280. // get the day of the month
  281. const int isLeapYear = IsLeapYear(nYear) ? 1 : 0;
  282. result = nDayOfYear - kDaysInYear[(nMonth - 1) + (13 * isLeapYear)];
  283. break;
  284. }
  285. // Refers to the day of the week, starting with 1 for Sunday.
  286. // The valid range is 1-7.
  287. case kParameterDayOfWeek:
  288. {
  289. // compute total number of days
  290. const int64_t nDays = (mnSeconds / kSecondsPerDay);
  291. // compute the day of the week
  292. result = 1 + (uint32_t)(nDays % 7);
  293. break;
  294. }
  295. // Refers to the hour of a day in 24 hour format, starting
  296. // with 0 for midnight. The valid range is 0-23.
  297. case kParameterHour:
  298. result = (uint32_t)((mnSeconds / kSecondsPerHour) % 24);
  299. break;
  300. // Refers to the minute of the hour, starting with 0 for
  301. // the first minute. The valid range is 0-59.
  302. case kParameterMinute:
  303. result = (uint32_t)((mnSeconds / kSecondsPerMinute) % 60);
  304. break;
  305. // Refers to the second of the minute, starting with 0
  306. // for the first second. The valid range is 0-59.
  307. case kParameterSecond:
  308. result = (uint32_t)(mnSeconds % 60);
  309. break;
  310. case kParameterNanosecond:
  311. result = mnNanosecond;
  312. break;
  313. // Refers to the week of the year, starting with 1 for the
  314. // week of January 1. The valid range is 1-52.
  315. case kParameterWeekOfYear:
  316. {
  317. const uint32_t nDayOfYear = GetParameter(kParameterDayOfYear);
  318. result = 1 + (nDayOfYear - 1) / 7;
  319. break;
  320. }
  321. // Refers to the week of the month, starting with 1 for the
  322. // first week. The valid range is 1-5.
  323. case kParameterWeekOfMonth:
  324. {
  325. const uint32_t nDayOfMonth = GetParameter(kParameterDayOfMonth);
  326. result = 1 + (nDayOfMonth - 1) / 7;
  327. break;
  328. }
  329. case kParameterUnknown:
  330. default:
  331. break; // This removes compiler warnings about unused cases.
  332. }
  333. return result;
  334. }
  335. ///////////////////////////////////////////////////////////////////////
  336. // SetParameter
  337. //
  338. // Sets the given parameter. If you want to set the year to 1999,
  339. // you would call SetParameter(kParameterYear, 1999).
  340. //
  341. void DateTime::SetParameter(Parameter parameter, uint32_t nValue)
  342. {
  343. switch (parameter)
  344. {
  345. // straight-forward calls to set
  346. case kParameterYear:
  347. Set(nValue, kValueIgnored, kValueIgnored, kValueIgnored, kValueIgnored, kValueIgnored, kValueIgnored);
  348. break;
  349. case kParameterMonth:
  350. Set(kValueIgnored, nValue, kValueIgnored, kValueIgnored, kValueIgnored, kValueIgnored, kValueIgnored);
  351. break;
  352. case kParameterDayOfMonth:
  353. Set(kValueIgnored, kValueIgnored, nValue, kValueIgnored, kValueIgnored, kValueIgnored, kValueIgnored);
  354. break;
  355. case kParameterHour:
  356. Set(kValueIgnored, kValueIgnored, kValueIgnored, nValue, kValueIgnored, kValueIgnored, kValueIgnored);
  357. break;
  358. case kParameterMinute:
  359. Set(kValueIgnored, kValueIgnored, kValueIgnored, kValueIgnored, nValue, kValueIgnored, kValueIgnored);
  360. break;
  361. case kParameterSecond:
  362. Set(kValueIgnored, kValueIgnored, kValueIgnored, kValueIgnored, kValueIgnored, nValue , kValueIgnored);
  363. break;
  364. case kParameterNanosecond:
  365. Set(kValueIgnored, kValueIgnored, kValueIgnored, kValueIgnored, kValueIgnored, kValueIgnored, nValue );
  366. break;
  367. // set the day of the year
  368. case kParameterDayOfYear:
  369. {
  370. // update the total number of seconds by the offset from the current day of the year
  371. const uint32_t nDayOfYear = GetParameter(kParameterDayOfYear);
  372. mnSeconds += (int32_t)(nValue - nDayOfYear) * kSecondsPerDay;
  373. break;
  374. }
  375. // set the day of the week
  376. case kParameterDayOfWeek:
  377. {
  378. // make sure the new value is valid
  379. if((nValue >= kDayOfWeekSunday) && (nValue <= kDayOfWeekSaturday))
  380. {
  381. // update the total number of seconds by the offset from the current day of the week
  382. const uint32_t nDayOfWeek = GetParameter(kParameterDayOfWeek);
  383. mnSeconds += (int32_t)(nValue - nDayOfWeek) * kSecondsPerDay;
  384. }
  385. else
  386. {
  387. EA_FAIL_M("EAStdC DateTime");
  388. }
  389. break;
  390. }
  391. // set the week of the year or month
  392. case kParameterWeekOfYear:
  393. // Fall through.
  394. case kParameterWeekOfMonth:
  395. {
  396. // update the total number of seconds by the offset from the current week of the year
  397. const uint32_t nParameterValue = GetParameter(parameter);
  398. mnSeconds += (int32_t)(nValue - nParameterValue) * 7 * kSecondsPerDay;
  399. break;
  400. }
  401. case kParameterUnknown:
  402. default:
  403. break; // This removes compiler warnings about unused cases.
  404. }
  405. }
  406. ///////////////////////////////////////////////////////////////////////
  407. // Set
  408. //
  409. // Sets the time and date based on various inputs. If any input is
  410. // kValueIgnored, then the input is ignored and the current value is used.
  411. // If any of the cyclic inputs is beyond its valid range, the modulo
  412. // of the value is used and the division of the value is added to the
  413. // next higher bracket. For example, if the input nMinute is 65, then
  414. // the minute used is 5 and 1 is added to the current hour value.
  415. //
  416. void DateTime::Set(uint32_t nYear, uint32_t nMonth, uint32_t nDayOfMonth,
  417. uint32_t nHour, uint32_t nMinute, uint32_t nSecond, uint32_t nNanosecond)
  418. {
  419. if(nYear == kValueIgnored || !nYear)
  420. nYear = GetParameter(kParameterYear);
  421. if(nMonth == kValueIgnored || !nMonth)
  422. nMonth = GetParameter(kParameterMonth);
  423. if(nDayOfMonth == kValueIgnored || !nDayOfMonth)
  424. nDayOfMonth = GetParameter(kParameterDayOfMonth);
  425. if(nHour == kValueIgnored)
  426. nHour = GetParameter(kParameterHour);
  427. if(nMinute == kValueIgnored)
  428. nMinute = GetParameter(kParameterMinute);
  429. if(nSecond == kValueIgnored)
  430. nSecond = GetParameter(kParameterSecond);
  431. if(nNanosecond == kValueIgnored)
  432. nNanosecond = mnNanosecond;
  433. // wrap the month value
  434. if(nMonth > 12)
  435. {
  436. nYear += (nMonth - 1) / 12;
  437. nMonth = ((nMonth - 1) % 12) + 1;
  438. }
  439. // compute total number of days for the given year adding all leap days
  440. const uint32_t leapYearCount = EADT_COUNT_LEAP_YEARS(nYear);
  441. int64_t nDays = ((nYear - 1) * 365) + leapYearCount;
  442. // add month and day
  443. const int isLeapYear = (IsLeapYear(nYear) ? 1 : 0);
  444. nDays += kDaysInYear[(nMonth - 1) + (13 * isLeapYear)] + (nDayOfMonth - 0); // Should this -0 be -1?
  445. // convert the number of days to seconds
  446. mnSeconds = nDays * kSecondsPerDay;
  447. // add current time
  448. mnSeconds += nHour * kSecondsPerHour;
  449. mnSeconds += nMinute * kSecondsPerMinute;
  450. mnSeconds += nSecond;
  451. mnSeconds += (nNanosecond / 1000000000);
  452. mnNanosecond = (nNanosecond % 1000000000);
  453. }
  454. ///////////////////////////////////////////////////////////////////////
  455. // Set
  456. //
  457. // Sets the time based on the current time. If the timeFrame is
  458. // kTimeFrameUTC, the time is set to what the current UTC time is,
  459. // which will be a fixed number of hours off of what the current
  460. // local time is.
  461. //
  462. // We have the option of not reading nanoseconds, because reading nanoseconds means calling the GetTimeOfDay
  463. // function, and the GetTimeOfDay function needs to indirectly call SetTimeZoneBias, which in turn
  464. // calls Set. So we need to break the possibility of an infinite loop, and we can do that by having
  465. // SetTimeZoneBias call SetInternal(timeFrame, *false*); SetTimeZoneBias doesn't need the nanosecond
  466. // precision anyway.
  467. //
  468. void DateTime::Set(TimeFrame timeFrame, bool bSetNanoseconds)
  469. {
  470. #if defined(EA_PLATFORM_MICROSOFT)
  471. SYSTEMTIME s;
  472. if(timeFrame == kTimeFrameLocal)
  473. GetLocalTimeAlternative(&s);
  474. else
  475. GetSystemTime(&s);
  476. Set(s.wYear, // wYear: 1601 through 30827.
  477. s.wMonth, // wMonth: 1 through 12.
  478. s.wDay, // wDay: 1 through 31.
  479. s.wHour, // wHour: 0 through 23.
  480. s.wMinute, // wMinute: 0 through 59.
  481. s.wSecond, // wSecond: 0 through 59.
  482. bSetNanoseconds ? s.wMilliseconds * 1000000 : 0); // wMilliseconds: 0 through 999.
  483. #else
  484. const time_t nTime = time(NULL);
  485. struct tm* const pTime = ((timeFrame == kTimeFrameUTC) ? gmtime(&nTime) : localtime(&nTime));
  486. struct tm const tmCopy = *pTime; // Need to make a copy because calling external code below could change what pTime points to, given that pTime comes from a static pointer inside the C Standard Library.
  487. timeval tv;
  488. if(bSetNanoseconds)
  489. GetTimeOfDay(&tv, NULL, (timeFrame == kTimeFrameUTC));
  490. else
  491. tv.tv_usec = 0;
  492. Set(tmCopy.tm_year + 1900, tmCopy.tm_mon + kMonthJanuary, tmCopy.tm_mday, tmCopy.tm_hour, tmCopy.tm_min, tmCopy.tm_sec, (uint32_t)(tv.tv_usec * 1000));
  493. #endif
  494. }
  495. ///////////////////////////////////////////////////////////////////////
  496. // Compare
  497. //
  498. // This function compares this object with another DateTime object
  499. // and returns an integer result. The return value is the same as with
  500. // the strcmp string comparison function. If this object is less than
  501. // the argument dateTime, the return value is < 0. Comparison operators
  502. // are defined outside this class, though they use the Compare function
  503. // to do their work.
  504. //
  505. int DateTime::Compare(const DateTime& dateTime, bool bCompareDate, bool bCompareTime) const
  506. {
  507. // Note: I believe the code below which just does / and % is valid. The reason it wouldn't
  508. // be valid is if some days have more seconds than others. Yes there are leap seconds that
  509. // occur every few years, but leap seconds don't affect the recorded time of day; they reflect
  510. // the measurement of time passage in the real world. Also, leap years (which are in fact a
  511. // way in which the recorded days in a year differ) don't affect this, because they add whole
  512. // days as opposed to fractional ones. Daylight savings time doesn't affect this because
  513. // it too doesn't affect recorded time but rather affects real world exerpeience only.
  514. // Time zones don't affect this calculation, as this function assumes that both DateTime
  515. // objects being compared are within the same time zone.
  516. bool bCompareNanoseconds = true; // Until proven false below.
  517. // this is what we will be comparing
  518. int64_t nValueA = mnSeconds;
  519. int64_t nValueB = dateTime.GetSeconds();
  520. if(bCompareDate && !bCompareTime)
  521. {
  522. // Date only - compare the total number of days.
  523. // Make nValueA and nValueB be days instead of seconds.
  524. nValueA = nValueA / kSecondsPerDay;
  525. nValueB = nValueB / kSecondsPerDay;
  526. bCompareNanoseconds = false;
  527. }
  528. else if(!bCompareDate && bCompareTime)
  529. {
  530. // Time of day only - extract the time portion of the total seconds value.
  531. // Make nValueA and nValueB be seconds since the start of the day instead of absolute time seconds.
  532. nValueA = nValueA % kSecondsPerDay;
  533. nValueB = nValueB % kSecondsPerDay;
  534. }
  535. // else compare both date and time.
  536. if(bCompareNanoseconds && (nValueA == nValueB))
  537. {
  538. nValueA = mnNanosecond;
  539. nValueB = dateTime.mnNanosecond;
  540. }
  541. if(nValueA == nValueB)
  542. return 0;
  543. else if(nValueA < nValueB)
  544. return -1;
  545. return 1;
  546. }
  547. ///////////////////////////////////////////////////////////////////////
  548. // AddTime
  549. //
  550. // Allows you to increment (or decrement) the given parameter by the given amount.
  551. //
  552. void DateTime::AddTime(Parameter parameter, int64_t nValue)
  553. {
  554. switch (parameter)
  555. {
  556. case kParameterYear:
  557. SetParameter(kParameterYear, (uint32_t)(GetParameter(kParameterYear) + nValue));
  558. break;
  559. case kParameterMonth:
  560. {
  561. // first - compute new year
  562. uint32_t nYear = (uint32_t)(GetParameter(kParameterYear) + (nValue / 12));
  563. // now - how many months do we have left?
  564. nValue = nValue % 12;
  565. // compute new month value
  566. const uint32_t nMonth = GetParameter(kParameterMonth);
  567. nValue = (int64_t)nMonth + nValue;
  568. // did we cross a year boundary ?
  569. if(nValue < 1)
  570. {
  571. nYear--;
  572. nValue = nValue + 12; // compute new month value
  573. }
  574. else if(nValue > 12)
  575. {
  576. nYear++;
  577. nValue = nValue - 12; // compute new month value
  578. }
  579. // set the new year and month
  580. Set(nYear, (uint32_t)(uint64_t)nValue, kValueIgnored, kValueIgnored, kValueIgnored, kValueIgnored);
  581. break;
  582. }
  583. // There really is no difference between the handling of the following
  584. // 3 parameter types - each offsets the date by a given number of days.
  585. case kParameterDayOfMonth:
  586. case kParameterDayOfYear:
  587. case kParameterDayOfWeek:
  588. mnSeconds += (int64_t)(nValue * kSecondsPerDay);
  589. break;
  590. case kParameterHour:
  591. mnSeconds += (int64_t)(nValue * kSecondsPerHour);
  592. break;
  593. case kParameterMinute:
  594. mnSeconds += (int64_t)(nValue * kSecondsPerMinute);
  595. break;
  596. case kParameterSecond:
  597. mnSeconds += nValue;
  598. break;
  599. case kParameterNanosecond:
  600. {
  601. int64_t newNanoseconds = GetParameter(kParameterNanosecond) + nValue;
  602. int64_t addedSeconds = (newNanoseconds / 1000000000);
  603. newNanoseconds %= 1000000000;
  604. EA_ASSERT(addedSeconds < INT64_C(0xffffffff));
  605. AddTime(kParameterSecond, (uint32_t)addedSeconds);
  606. EA_ASSERT(newNanoseconds < INT64_C(0xffffffff));
  607. SetParameter(kParameterNanosecond, (uint32_t)newNanoseconds);
  608. break;
  609. }
  610. // kParameterWeekOfYear and kParameterWeekOfMonth act the same
  611. // for this: each offsets the date by a given number of weeks
  612. case kParameterWeekOfYear:
  613. // Fall through
  614. case kParameterWeekOfMonth:
  615. mnSeconds += (int64_t)(nValue * 7 * kSecondsPerDay);
  616. break;
  617. case kParameterUnknown:
  618. default:
  619. break; // This removes compiler warnings about unused cases.
  620. }
  621. EA_ASSERT(mnSeconds >= 0); // Verify that the operation didn't cause integer wraparound.
  622. if(mnSeconds < 0)
  623. mnSeconds = 0;
  624. }
  625. int64_t DateTime::GetSeconds() const { return mnSeconds; }
  626. void DateTime::SetSeconds(int64_t nSeconds) { mnSeconds = nSeconds; }
  627. uint64_t DateTime::GetMilliseconds() const { return (uint64_t)mnSeconds * 1000 + mnNanosecond / 1000000; }
  628. void DateTime::SetMilliseconds(uint64_t milliseconds)
  629. {
  630. mnSeconds = milliseconds / 1000;
  631. mnNanosecond = (milliseconds % 1000) * 1000000;
  632. }
  633. EA::StdC::int128_t DateTime::GetNanoseconds() const
  634. {
  635. return (EA::StdC::int128_t(mnSeconds) * 1000000000) + mnNanosecond;
  636. }
  637. void DateTime::SetNanoseconds(const EA::StdC::int128_t& nanoseconds)
  638. {
  639. EA::StdC::int128_t seconds = nanoseconds / 1000000000;
  640. EA::StdC::int128_t nanosecond = nanoseconds % 1000000000;
  641. mnSeconds = seconds.AsInt64();
  642. mnNanosecond = nanosecond.AsUint32();
  643. }
  644. ///////////////////////////////////////////////////////////////////////
  645. // IsLeapYear
  646. //
  647. // Returns true if the given year is a leap year.
  648. // Algorithm from K & R, "The C Programming Language", 1st ed.
  649. //
  650. EASTDC_API bool IsLeapYear(uint32_t nYear)
  651. {
  652. return (!(nYear & 3) && (nYear % 100)) || !(nYear % 400);
  653. // Alternative:
  654. //if((nYear % 4) == 0)
  655. //{
  656. // if((nYear % 100) == 0)
  657. // return ((nYear % 400) == 0);
  658. // else
  659. // return true;
  660. //}
  661. //return false;
  662. }
  663. ///////////////////////////////////////////////////////////////////////
  664. // GetDaysInYear
  665. //
  666. // Returns the number of days in the given year. The value will vary
  667. // based on whether the year is a leap year or not.
  668. //
  669. EASTDC_API uint32_t GetDaysInYear(uint32_t nYear)
  670. {
  671. return IsLeapYear(nYear) ? (uint32_t)366 : (uint32_t)365;
  672. }
  673. ///////////////////////////////////////////////////////////////////////
  674. // GetDaysInMonth
  675. //
  676. // Implemented by Blazej Stompel
  677. //
  678. // Returns the number of days in the given month. The value will vary
  679. // based on the month and based on whether the year is a leap year.
  680. // The input nMonth takes one of enum Month or an integer equivalent.
  681. //
  682. EASTDC_API uint32_t GetDaysInMonth(uint32_t nMonth, uint32_t nYear)
  683. {
  684. // Make sure the month value is valid
  685. if((nMonth >= kMonthJanuary) && (nMonth <= kMonthDecember))
  686. {
  687. // Special case for leap years
  688. if(nMonth == kMonthFebruary)
  689. {
  690. const bool isLeapYear = IsLeapYear(nYear);
  691. if(isLeapYear)
  692. return kDaysInMonth[nMonth - 1] + 1;
  693. }
  694. return kDaysInMonth[nMonth - 1];
  695. }
  696. return 0;
  697. }
  698. ///////////////////////////////////////////////////////////////////////
  699. // GetDayOfYear
  700. //
  701. // The input nMonth takes one of enum Month or an integer equivalent.
  702. // The input nDayOfMonth takes an integer consistent with enum DayOfMonth.
  703. //
  704. EASTDC_API uint32_t GetDayOfYear(uint32_t nMonth, uint32_t nDayOfMonth, uint32_t nYear)
  705. {
  706. const DateTime sDateTime(nYear, nMonth, nDayOfMonth, 0, 0, 0);
  707. return sDateTime.GetParameter(kParameterDayOfYear);
  708. }
  709. // Code to regenerate the kEpochSeconds array.
  710. //
  711. // kEpochSeconds[kEpochJulian] = DateTime((uint32_t)-4712, 1, 1, 12, 0, 0).GetSeconds(); // -4712/01/01/12:00:00
  712. // kEpochSeconds[kEpochModifiedJulian] = DateTime((uint32_t) 1858, 11, 17, 0, 0, 0).GetSeconds(); // 1858/11/17/00:00:00
  713. // kEpochSeconds[kEpochGregorian] = DateTime((uint32_t) 1752, 9, 14, 0, 0, 0).GetSeconds(); // 1752/09/14/00:00:00
  714. // kEpochSeconds[kEpoch1900] = DateTime((uint32_t) 1900, 1, 1, 0, 0, 0).GetSeconds(); // 1900/01/01/00:00:00
  715. // kEpochSeconds[kEpoch1950] = DateTime((uint32_t) 1950, 1, 1, 0, 0, 0).GetSeconds(); // 1950/01/01/00:00:00
  716. // kEpochSeconds[kEpoch1970] = DateTime((uint32_t) 1970, 1, 1, 0, 0, 0).GetSeconds(); // 1970/01/01/00:00:00
  717. // kEpochSeconds[kEpoch2000] = DateTime((uint32_t) 2000, 1, 1, 0, 0, 0).GetSeconds(); // 2000/01/01/00:00:00
  718. // kEpochSeconds[kEpochJ2000] = DateTime((uint32_t) 2000, 1, 1, 11, 58, 55).GetSeconds(); // 2000/01/01/11:58:55
  719. // kEpochSeconds[kEpochDateTime] = 0;
  720. static int64_t kEpochSeconds[10] =
  721. {
  722. INT64_C(0), // kEpochUnknown
  723. INT64_C(89839426968000), // kEpochJulian Began at -4712/01/01/12:00:00 (Year 1858, January 1, noon).
  724. INT64_C(55278460800), // kEpochGregorian Began at 1752/09/14/00:00:00 (Year 1752, January 1, midnight). Beginning of the Gregorian calendar.
  725. INT64_C(58628966400), // kEpochModifiedJulian Began at 1858/11/17/00:00:00 (Year 1858, January 1, midnight). 2,400,000.5 days after Julian epoch began.
  726. INT64_C(59926694400), // kEpoch1900 Began at 1900/01/01/00:00:00 (Year 1900, January 1, midnight). Same epoch used by the Network Time Protocol.
  727. INT64_C(61504531200), // kEpoch1950 Began at 1950/01/01/00:00:00 (Year 1950, January 1, midnight). Used by some gaming systems.
  728. INT64_C(62135683200), // kEpoch1970 Began at 1970/01/01/00:00:00 (Year 1970, January 1, midnight). Same epoch used by C runtime library and Unix OS.
  729. INT64_C(63082368000), // kEpoch2000 Began at 2000/01/01/00:00:00 (Year 2000, January 1, midnight). Same epoch used by Apple file systems.
  730. INT64_C(63082411135), // kEpochJ2000 Began at 2000/01/01/11:58:55 (Year 2000, January 1, ~noon). Coordinated Universal Time, also includes 816 milliseconds.
  731. INT64_C(0), // kEpochDateTime Began at 0000/01/01/00:00:00 (Year 0000, January 1, midnight).
  732. };
  733. ///////////////////////////////////////////////////////////////////////
  734. // ConvertEpochSeconds
  735. //
  736. EASTDC_API int64_t ConvertEpochSeconds(Epoch srcEpoch, int64_t srcSeconds, Epoch destEpoch)
  737. {
  738. if((srcEpoch < kEpochCount) && (destEpoch < kEpochCount))
  739. return (srcSeconds + kEpochSeconds[srcEpoch]) - kEpochSeconds[destEpoch];
  740. return 0;
  741. }
  742. ///////////////////////////////////////////////////////////////////////
  743. // GetCurrent
  744. //
  745. // Returns the current year, month, hour, etc.
  746. //
  747. EASTDC_API uint32_t GetCurrent(Parameter parameter, TimeFrame timeFrame)
  748. {
  749. const DateTime sDateTime(timeFrame);
  750. return sDateTime.GetParameter(parameter);
  751. }
  752. ///////////////////////////////////////////////////////////////////////
  753. // IsDST
  754. //
  755. // Returns true if the time is daylight savings time. This function
  756. // assumes that DST is valid for the given current locale; some locales
  757. // within the United States don't observe DST.
  758. //
  759. EASTDC_API bool IsDST()
  760. {
  761. time_t nTime = time(NULL);
  762. struct tm* const pTime = localtime(&nTime); // Find out if the local time is in DST
  763. return pTime->tm_isdst > 0;
  764. }
  765. ///////////////////////////////////////////////////////////////////////
  766. // IsDSTDateTime
  767. //
  768. EASTDC_API bool IsDSTDateTime(int64_t dateTimeSeconds)
  769. {
  770. // DateTime seconds, based on 0000/01/01/00:00:00 (Year 0000, January 1, midnight) (not time_t).
  771. int64_t timeTSeconds = DateTimeSecondsToTimeTSeconds(dateTimeSeconds);
  772. time_t time = (time_t)timeTSeconds; // For some platforms this might result in a 64 to 32 bit chop, though the list bits should be all zero.
  773. struct tm* const pTime = localtime(&time);
  774. return pTime->tm_isdst > 0;
  775. }
  776. ///////////////////////////////////////////////////////////////////////
  777. // GetDaylightSavingsBias
  778. //
  779. // Returns the number of seconds that the current time is daylight
  780. // savings adjusted from the conventional time. Adding this value
  781. // to the conventional time yields the time when adjusted for
  782. // daylight savings. Some locations implement daylight savings offsets
  783. // that are a half hour instead of an hour. We ignore these, as they
  784. // are uncommon and problematic.
  785. //
  786. EASTDC_API int64_t GetDaylightSavingsBias()
  787. {
  788. return 3600;
  789. }
  790. ///////////////////////////////////////////////////////////////////////
  791. // GetTimeZoneBias
  792. //
  793. // Returns the number of seconds that the local time zone is off of UTC.
  794. // Adding this value to the current UTC yields the current local time.
  795. // For locales in the United states, this is usually a negative number
  796. // like -28800 (8 hours behind UTC). For locales East of Europe it will
  797. // be a positive value (hours ahead of UTC).
  798. //
  799. EASTDC_API int64_t GetTimeZoneBias()
  800. {
  801. #if defined(EA_PLATFORM_WINDOWS)
  802. TIME_ZONE_INFORMATION tz;
  803. DWORD rv = GetTimeZoneInformation(&tz);
  804. EA_ASSERT(rv != TIME_ZONE_ID_INVALID);
  805. EA_UNUSED(rv);
  806. return (tz.Bias * -60);
  807. #elif defined(EA_PLATFORM_APPLE)
  808. // http://linux.die.net/man/2/gettimeofday
  809. struct timeval tv;
  810. struct timezone tz;
  811. gettimeofday(&tv, &tz);
  812. return (tz.tz_minuteswest * -60);
  813. #elif defined(EA_PLATFORM_ANDROID) && __ANDROID_API__ >= 8
  814. tzset();
  815. return -timezone; // Seconds West of GMT
  816. // Disabled until we can verify these are correct on their respective machines:
  817. //#elif defined(EA_PLATFORM_LINUX)
  818. // // http://linux.die.net/man/3/daylight
  819. // return timezone;
  820. #elif defined(EA_PLATFORM_UNIX)
  821. // BSD doesn't support the timezone parameter of gettimeofday.
  822. // However, we can deduce the time zone offset by taking an properly-chosen GM time_t
  823. // value and convert it with both localtime() and gmtime() and see what the seconds difference is.
  824. const time_t jan3rd1970 = (60 * 60 * 24 * 2);
  825. struct tm tmGM;
  826. gmtime_r(&jan3rd1970, &tmGM);
  827. time_t tLocal = mktime(&tmGM);
  828. return (jan3rd1970 - tLocal); // This will be a negative number like -28800 (PST time zone).
  829. #elif EASTDC_UTC_TIME_AVAILABLE
  830. DateTime sDateTimeLocal(0);
  831. DateTime sDateTimeUTC(0);
  832. int64_t nSecondsLocal;
  833. int64_t nSecondsUTC;
  834. bool bIsDST;
  835. // The reason the logic below exists is because this platform don't provide functions
  836. // to get the time zone bias, but often let you infer it because they provide
  837. // functions to tell the local time and the UTC time. One possibility is the
  838. // case that the second turned over between the two time readings.
  839. //Debug code.
  840. //#if defined(EA_PLATFORM_MICROSOFT)
  841. // _putenv_s("TZ", "GST-1GDT");
  842. // _tzset();
  843. //#endif
  844. sDateTimeLocal.Set(kTimeFrameLocal, false); // Intentionally use 'false' here so that SetInternal doesn't call GetTimeOfDay, which can recursively call us back here and loop indefinitely.
  845. bIsDST = IsDST(); // Intentionally call this in between the two Set calls.
  846. sDateTimeUTC.Set(kTimeFrameUTC, false);
  847. nSecondsLocal = sDateTimeLocal.GetSeconds();
  848. nSecondsUTC = sDateTimeUTC.GetSeconds();
  849. // These seconds should be an even number of minutes apart. If they aren't then
  850. // the second must have turned over between the two Set calls above. So we detect
  851. // that and handle it.
  852. const int64_t difference = (nSecondsUTC > nSecondsLocal) ? (nSecondsUTC - nSecondsLocal) : (nSecondsLocal - nSecondsUTC);
  853. const int64_t differenceMod60 = difference % 60; // Usually this will be zero.
  854. if(differenceMod60)
  855. {
  856. if(nSecondsUTC > nSecondsLocal)
  857. nSecondsUTC -= differenceMod60;
  858. else
  859. nSecondsUTC -= (60 - differenceMod60);
  860. }
  861. if(bIsDST)
  862. nSecondsLocal -= 3600;
  863. return (nSecondsLocal - nSecondsUTC);
  864. #else
  865. return 0;
  866. #endif
  867. }
  868. ///////////////////////////////////////////////////////////////////////
  869. // GetTimeZoneName
  870. //
  871. EASTDC_API bool GetTimeZoneName(char8_t* pName, bool bDaylightSavingsName)
  872. {
  873. #if defined(EA_PLATFORM_MICROSOFT) && defined(_MSC_VER) && (_MSC_VER >= 1400)
  874. EA_COMPILETIME_ASSERT(EASTDC_UTC_TIME_AVAILABLE == 1);
  875. size_t size = 0; // "The supplied pName must have a capacity of at least 8 bytes."
  876. errno_t result = _get_tzname(&size, pName, kTimeZoneNameCapacity, bDaylightSavingsName ? 1 : 0);
  877. return (result == 0);
  878. #elif defined(EA_PLATFORM_UNIX) && EASTDC_UNIX_TZNAME_AVAILABLE
  879. EA_COMPILETIME_ASSERT(EASTDC_UTC_TIME_AVAILABLE == 1); // If this assertion fails then EASTDC_UTC_TIME_AVAILABLE is 0 whereas it really could be 1.
  880. const char* pTZName = tzname[bDaylightSavingsName ? 1 : 0];
  881. Strncpy(pName, pTZName, kTimeZoneNameCapacity); // "The supplied pName must have a capacity of at least 8 bytes."
  882. pName[7] = 0;
  883. return true;
  884. #else
  885. EA_UNUSED(bDaylightSavingsName);
  886. #if EASTDC_UTC_TIME_AVAILABLE
  887. int64_t b = GetTimeZoneBias();
  888. Snprintf(pName, kTimeZoneNameCapacity, "+%6lld", b);
  889. #else
  890. Strlcpy(pName, "LT", kTimeZoneNameCapacity); // Local Time. This is merely our convention for when we have no time zone information other than to say it's the local time.
  891. #endif
  892. return true;
  893. #endif
  894. }
  895. ///////////////////////////////////////////////////////////////////////
  896. // DateTimeToTm
  897. //
  898. // Converts a DateTime to a C tm struct.
  899. //
  900. EASTDC_API void DateTimeToTm(const DateTime& dateTime, tm& time)
  901. {
  902. // time doesn't have a field for anything more precise than seconds, so kParameterNanosecond is irrelevant, as we don't apply rounding to nanoseconds.
  903. time.tm_sec = (int)dateTime.GetParameter(kParameterSecond);
  904. time.tm_min = (int)dateTime.GetParameter(kParameterMinute);
  905. time.tm_hour = (int)dateTime.GetParameter(kParameterHour);
  906. time.tm_mday = (int)dateTime.GetParameter(kParameterDayOfMonth);
  907. time.tm_mon = (int)dateTime.GetParameter(kParameterMonth) - kMonthJanuary;
  908. time.tm_year = (int)dateTime.GetParameter(kParameterYear) - 1900;
  909. time.tm_wday = (int)dateTime.GetParameter(kParameterDayOfWeek) - kDayOfWeekSunday;
  910. time.tm_yday = (int)dateTime.GetParameter(kParameterDayOfYear) - 1;
  911. time.tm_isdst = 0; // We don't have a way to tell if an arbitrary dateTime object is daylight savings time.
  912. }
  913. ///////////////////////////////////////////////////////////////////////
  914. // TmToDateTime
  915. //
  916. // Converts a C tm struct to a DateTime.
  917. //
  918. EASTDC_API void TmToDateTime(const tm& time, DateTime& dateTime)
  919. {
  920. dateTime.Set((uint32_t)(time.tm_year + 1900), (uint32_t)(time.tm_mon + kMonthJanuary),
  921. (uint32_t)time.tm_mday, (uint32_t)time.tm_hour, (uint32_t)time.tm_min, (uint32_t)time.tm_sec);
  922. }
  923. ///////////////////////////////////////////////////////////////////////
  924. // DateTimeToFileTime
  925. //
  926. // Converts a DateTime to a FILETIME struct.
  927. // A FILETIME contains a 64-bit value representing the number of
  928. // 100-nanosecond intervals since January 1, 1601 (UTC).
  929. //
  930. EASTDC_API void DateTimeToFileTime(const DateTime& dateTime, _FILETIME& time)
  931. {
  932. _SYSTEMTIME systemTime;
  933. DateTimeToSystemTime(dateTime, systemTime);
  934. #if defined(EA_PLATFORM_MICROSOFT)
  935. SystemTimeToFileTime(&systemTime, &time); // OS call.
  936. #else
  937. int64_t month, year;
  938. if(systemTime.wMonth >= 3) // If month is after a leap year day could occur)...
  939. {
  940. month = systemTime.wMonth + 1;
  941. year = systemTime.wYear;
  942. }
  943. else
  944. {
  945. month = systemTime.wMonth + 13;
  946. year = systemTime.wYear - 1;
  947. }
  948. const int64_t endOfCenturyLeapYearCount = ((3 * (year / 100) + 3) / 4); // http://en.wikipedia.org/wiki/Century_leap_year
  949. // Convert the dateTime value to 100 ns intervals since Jan 1, 1601.
  950. const int64_t day = ((36525 * year) / 100) - endOfCenturyLeapYearCount +
  951. ((1959 * month) / 64) + systemTime.wDay - 584817; // Subtract 584817 to make the time based on 1601-01-01.
  952. const int64_t time64 = ((((day * kHoursPerDay +
  953. systemTime.wHour) * kMinutesPerHour +
  954. systemTime.wMinute) * kSecondsPerMinute +
  955. systemTime.wSecond) * 1000 + // 1000 = milliseconds per second.
  956. systemTime.wMilliseconds) * 10000; // 10000 == (100 ns intervals per millisecond).
  957. time.dwLowDateTime = (uint32_t)time64;
  958. time.dwHighDateTime = (uint32_t)(time64 >> INT64_C(32));
  959. #endif
  960. }
  961. ///////////////////////////////////////////////////////////////////////
  962. // FileTimeToDateTime
  963. //
  964. // Converts a FILETIME struct to a DateTime.
  965. //
  966. EASTDC_API void FileTimeToDateTime(const _FILETIME& time, DateTime& dateTime)
  967. {
  968. #if defined(EA_PLATFORM_MICROSOFT)
  969. _SYSTEMTIME systemTime;
  970. FileTimeToSystemTime(&time, &systemTime); // OS call.
  971. SystemTimeToDateTime(systemTime, dateTime);
  972. #else
  973. // A FILETIME contains a 64-bit value representing the number of
  974. // 100-nanosecond intervals since January 1, 1601 (UTC).
  975. EA_UNUSED(time);
  976. memset(&dateTime, 0, sizeof(dateTime));
  977. // To do: Implement this.
  978. #endif
  979. }
  980. ///////////////////////////////////////////////////////////////////////
  981. // DateTimeToSystemTime
  982. //
  983. // Converts a DateTime to a SYSTEMTIME struct.
  984. //
  985. EASTDC_API void DateTimeToSystemTime(const DateTime& dateTime, _SYSTEMTIME& time)
  986. {
  987. time.wYear = (uint16_t) dateTime.GetParameter(kParameterYear);
  988. time.wMonth = (uint16_t) dateTime.GetParameter(kParameterMonth);
  989. time.wDayOfWeek = (uint16_t)(dateTime.GetParameter(kParameterDayOfWeek) - 1);
  990. time.wDay = (uint16_t) dateTime.GetParameter(kParameterDayOfMonth);
  991. time.wHour = (uint16_t) dateTime.GetParameter(kParameterHour);
  992. time.wMinute = (uint16_t) dateTime.GetParameter(kParameterMinute);
  993. time.wSecond = (uint16_t) dateTime.GetParameter(kParameterSecond);
  994. time.wMilliseconds = (uint16_t)(dateTime.GetParameter(kParameterNanosecond) / 1000000);
  995. }
  996. ///////////////////////////////////////////////////////////////////////
  997. // SystemTimeToDateTime
  998. //
  999. // Converts a SYSTEMTIME struct to a DateTime.
  1000. //
  1001. EASTDC_API void SystemTimeToDateTime(const _SYSTEMTIME& time, DateTime& dateTime)
  1002. {
  1003. dateTime = DateTime(time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond);
  1004. dateTime.SetParameter(kParameterNanosecond, time.wMilliseconds * 1000000);
  1005. }
  1006. ///////////////////////////////////////////////////////////////////////
  1007. // GetTimeOfDay
  1008. //
  1009. // This behaves the same as the Posix gettimeofday function, with the
  1010. // addition that pTZ is formally supported and that bUTC specifies to
  1011. // get the time of day in UTC instead of local time.
  1012. // pTZ is an output parameter and it's input value has no affect on the
  1013. // function behavior and return value.
  1014. // Note that a timeval has the same meaning as time_t except that it contains
  1015. // sub-second information.
  1016. //
  1017. EASTDC_API int GetTimeOfDay(timeval* pTV, timezone_* pTZ, bool bUTC)
  1018. {
  1019. timezone_ tz;
  1020. if(!pTZ)
  1021. pTZ = &tz;
  1022. #if defined(EA_PLATFORM_MICROSOFT)
  1023. #if EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP)
  1024. static bool tzsetCalled = false;
  1025. if(!tzsetCalled)
  1026. {
  1027. _tzset();
  1028. tzsetCalled = true;
  1029. }
  1030. #endif
  1031. if(pTV)
  1032. {
  1033. FILETIME ft;
  1034. LARGE_INTEGER li;
  1035. int64_t t;
  1036. GetSystemTimeAsFileTime(&ft); // Microsoft call. The information is in Coordinated Universal Time (UTC) format.
  1037. li.LowPart = ft.dwLowDateTime;
  1038. li.HighPart = ft.dwHighDateTime;
  1039. t = li.QuadPart; // In 100-nanosecond intervals
  1040. t -= INT64_C(116444736000000000); // Offset to the Epoch time
  1041. t /= 10; // In microseconds
  1042. pTV->tv_sec = (long)(t / 1000000);
  1043. pTV->tv_usec = (long)(t % 1000000);
  1044. if(!bUTC) // If should convert to local time...
  1045. pTV->tv_sec -= _timezone;
  1046. }
  1047. pTZ->tz_minuteswest = _timezone / 60; // _timezone is a positive value, as opposed to the negative value that 'time zone bias' is.
  1048. pTZ->tz_dsttime = _daylight;
  1049. return 0;
  1050. #elif EASTDC_CLOCK_GETTIME_AVAILABLE
  1051. timeval tv;
  1052. if(!pTV) // Unix gettimeofday requires a valid timeval pointer.
  1053. pTV = &tv;
  1054. timespec ts;
  1055. int result = clock_gettime(CLOCK_REALTIME, &ts);
  1056. // To do: Handle time zone. See Unix code below.
  1057. memset(pTZ, 0, sizeof(timezone_));
  1058. if((result == 0) && pTV) // If OK and if the user specified a pTV to fill in...
  1059. {
  1060. // Convert timespec to timeval. They are similar except for the tv_usec/tv_nsec member.
  1061. pTV->tv_sec = ts.tv_sec;
  1062. pTV->tv_usec = (suseconds_t)(ts.tv_nsec / 1000);
  1063. if(!bUTC) // If should convert to local time...
  1064. pTV->tv_sec -= ((pTZ->tz_minuteswest * 60) - (pTZ->tz_dsttime ? 3600 : 0));
  1065. }
  1066. return result;
  1067. #elif defined(EA_PLATFORM_UNIX) || defined(EA_PLATFORM_IPHONE)
  1068. timeval tv;
  1069. if(!pTV) // Unix gettimeofday requires a valid timeval pointer.
  1070. pTV = &tv;
  1071. int result = gettimeofday(pTV, pTZ); // The gettimeofday function obtains the current time, expressed as seconds and microseconds since 00:00 Coordinated Universal Time (UTC), January 1, 1970
  1072. #if defined(EA_PLATFORM_LINUX) && !defined(EA_PLATFORM_ANDROID) // pTZ parameter is not always supported on linux platforms. timezone not defined on some Android devices (i.e. Sony Xperia X10i).
  1073. pTZ->tz_minuteswest = timezone / 60; //timezone is seconds west of GMT
  1074. time_t nowtm = pTV->tv_sec;
  1075. tm tmResult;
  1076. tm* ptmResult;
  1077. ptmResult = localtime_r(&nowtm, &tmResult);
  1078. result = result != 0 ? result : !(ptmResult == &tmResult); //if gettimeofday failed return that error code, otherwise return 1 if localtime_r returned wrong pointer, otherwise 0
  1079. pTZ->tz_dsttime = tmResult.tm_isdst;
  1080. #endif
  1081. if((result == 0) && pTV) // If OK and if the user specified a pTV to fill in...
  1082. {
  1083. if(!bUTC) // If should convert to local time...
  1084. pTV->tv_sec -= ((pTZ->tz_minuteswest * 60) - (pTZ->tz_dsttime ? 3600 : 0));
  1085. }
  1086. return result;
  1087. #else
  1088. // We implement the most basic version which is likely to compile:
  1089. // use just the time_t function and don't support time zones.
  1090. pTZ->tz_minuteswest = (int)GetTimeZoneBias() / -60; // tz_minuteswest is a positive value in the US, as opposed to the negative value that 'time zone bias' is.
  1091. pTZ->tz_dsttime = 0; // Assume there is never daylight savings time.
  1092. if(pTV)
  1093. {
  1094. pTV->tv_sec = time(NULL); // The time function shall return the value of time in seconds since the Epoch (00:00 Coordinated Universal Time (UTC), January 1, 1970).
  1095. pTV->tv_usec = 0;
  1096. if(!bUTC) // If should convert to local time...
  1097. pTV->tv_sec -= ((pTZ->tz_minuteswest * 60) - (pTZ->tz_dsttime ? 3600 : 0));
  1098. }
  1099. return 0;
  1100. #endif
  1101. }
  1102. ///////////////////////////////////////////////////////////////////////
  1103. // TimevalDifference
  1104. //
  1105. // Calculates the result of TVA - TVB.
  1106. // Returns 1 if TVA >= TVB, 0 if TVA == TVB, -1 if TVA < TVB. Much like strcmp().
  1107. // Note that a timeval has the same meaning as time_t except that it contains
  1108. // sub-second information.
  1109. //
  1110. EASTDC_API int TimevalDifference(const timeval* pTVA, const timeval* pTVB, timeval* pTVResult)
  1111. {
  1112. timeval tva(*pTVA);
  1113. timeval tvb(*pTVB);
  1114. // Perform the carry for the later subtraction by updating y.
  1115. if(tva.tv_usec < tvb.tv_usec)
  1116. {
  1117. const int nsec = ((tvb.tv_usec - tva.tv_usec) / 1000000) + 1;
  1118. tvb.tv_usec -= 1000000 * nsec;
  1119. tvb.tv_sec += nsec;
  1120. }
  1121. if((tva.tv_usec - tvb.tv_usec) > 1000000)
  1122. {
  1123. const int nsec = (tvb.tv_usec - tva.tv_usec) / 1000000;
  1124. tvb.tv_usec += 1000000 * nsec;
  1125. tvb.tv_sec -= nsec;
  1126. }
  1127. // Compute the time remaining to wait. tv_usec is always positive.
  1128. pTVResult->tv_sec = tva.tv_sec - tvb.tv_sec;
  1129. pTVResult->tv_usec = tva.tv_usec - tvb.tv_usec;
  1130. if(tva.tv_sec == tvb.tv_sec)
  1131. {
  1132. if(tva.tv_usec == tvb.tv_usec)
  1133. return 0;
  1134. return (tva.tv_usec > tvb.tv_usec) ? 1 : -1;
  1135. }
  1136. return (tva.tv_sec > tvb.tv_sec) ? 1 : -1;
  1137. }
  1138. namespace Internal
  1139. {
  1140. #define SUNDAY_BASED_WEEK_NUMBER(pTM) (((pTM)->tm_yday + 7 - ((pTM)->tm_wday)) / 7)
  1141. #define MONDAY_BASED_WEEK_NUMBER(pTM) (((pTM)->tm_yday + 7 - ((pTM)->tm_wday ? (pTM)->tm_wday - 1 : 6)) / 7)
  1142. static const TimeLocale gDefaultTimeLocale =
  1143. {
  1144. { "Sun","Mon","Tue","Wed","Thu","Fri","Sat" },
  1145. { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" },
  1146. { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" },
  1147. { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" },
  1148. { "AM", "PM" },
  1149. "%a %b %d %H:%M:%S %Y",
  1150. "%m/%d/%y",
  1151. "%H:%M:%S",
  1152. "%I:%M:%S %p"
  1153. };
  1154. static bool Append(const char* EA_RESTRICT p, char* EA_RESTRICT & pTimeString, size_t& capacity)
  1155. {
  1156. for(; capacity; ++pTimeString, --capacity)
  1157. {
  1158. if((*pTimeString = *p++) == 0)
  1159. return true;
  1160. }
  1161. return false;
  1162. }
  1163. static bool WriteInt(int n, int digits, char pad, bool removeLeadingZeroes, char* EA_RESTRICT & pTimeString, size_t& capacity)
  1164. {
  1165. char buffer[10];
  1166. char* p;
  1167. buffer[9] = 0;
  1168. for(p = buffer + 8; (n > 0) && (p > buffer); n /= 10, --digits)
  1169. *p-- = (char)((n % 10) + '0');
  1170. while((p > buffer) && (digits-- > 0))
  1171. *p-- = (char)pad;
  1172. if(removeLeadingZeroes)
  1173. {
  1174. while((p[1] == '0') || (p[1] == ' '))
  1175. ++p;
  1176. if (p[1] == 0)
  1177. --p;
  1178. }
  1179. return Append(++p, pTimeString, capacity);
  1180. }
  1181. #if defined(EA_PLATFORM_WINDOWS)
  1182. #define EASTDC_WINDOWS_TIME_FUNCTIONS_AVAILABLE 1 // The Microsoft API supports these functions only for desktop target platforms (e.g. Win32, Win64).
  1183. #else
  1184. #define EASTDC_WINDOWS_TIME_FUNCTIONS_AVAILABLE 0
  1185. #endif
  1186. #if !EASTDC_WINDOWS_TIME_FUNCTIONS_AVAILABLE
  1187. // This function converts any "%" chars in the format string to "%#", writing the new format to the supplied buffer.
  1188. // Returns NULL for the only possible error condition: pBuffer's capacity was insufficient.
  1189. static char* ConvertFormatSpecifiersToAlternates(char* pBuffer, size_t bufferCapacity, const char* pFormat)
  1190. {
  1191. char* pBufferCurrent = pBuffer;
  1192. char* pBufferEnd = pBuffer + (bufferCapacity - 2); // -2 because we can write two chars per loop below. If bufferCapacity is < 2 then the loop below will quit right away, which is what we want.
  1193. while(*pFormat && (pBufferCurrent < pBufferEnd))
  1194. {
  1195. *pBufferCurrent++ = *pFormat;
  1196. if(*pFormat++ == '%')
  1197. *pBufferCurrent++ = '#';
  1198. }
  1199. *pBufferCurrent = 0;
  1200. return (*pFormat == 0) ? pBuffer : NULL;
  1201. }
  1202. #endif
  1203. }
  1204. // Posix alternative formats:
  1205. // "Some conversion specifiers can be modified by the E or O modifier characters
  1206. // to indicate that an alternative format or specification should be used rather
  1207. // than the one normally used by the unmodified conversion specifier. If the
  1208. // alternative format or specification does not exist for the current locale,
  1209. // (see ERA in the XBD specification, Section 5.3.5) the behaviour will be as if
  1210. // the unmodified conversion specification were used."
  1211. //
  1212. // Microsoft alternative formats:
  1213. // Also, Microsoft (alternatively to Posix E and O) supports using the # char
  1214. // after % to indicate alternative behaviour as follows:
  1215. // %#a, %#A, %#b, %#B, %#h, %#p, %#X, %#z, %#Z, %#% # flag is ignored.
  1216. // %#c Long date and time representation, appropriate for current locale. For example: "Tuesday, March 14, 1995, 12:41:29".
  1217. // %#x Long date representation, appropriate to current locale. For example: "Tuesday, March 14, 1995".
  1218. // %#d, %#H, %#I, %#j, %#m, %#M, %#S, %#U, %#w, %#W, %#y, %#Y Remove leading zeros (if any).
  1219. EASTDC_API size_t Strftime(char* EA_RESTRICT pTimeString, size_t timeStringCapacity,
  1220. const char* EA_RESTRICT pFormat, const tm* EA_RESTRICT pTM, const TimeLocale* EA_RESTRICT pTimeLocale)
  1221. {
  1222. using namespace Internal;
  1223. size_t capacity = timeStringCapacity;
  1224. bool bGMT = false; // To consider: provide a way for the user to set this to true or to directly specify the time zone.
  1225. char buffer[256];
  1226. if(!pTimeLocale)
  1227. pTimeLocale = &gDefaultTimeLocale;
  1228. for(; *pFormat; ++pFormat)
  1229. {
  1230. if(*pFormat == '%')
  1231. {
  1232. char cAlt = 0; // Alternative format specifier.
  1233. if((*++pFormat == 'E') || (*pFormat == 'O') || (*pFormat == '#'))
  1234. cAlt = *pFormat++;
  1235. switch(*pFormat)
  1236. {
  1237. case '\0': // We are at the end of the string, with a (not valid) trailing '%' char.
  1238. EA_FAIL_M("EAStdC Strftime"); // Incomplete format specifier.
  1239. --pFormat;
  1240. break;
  1241. case '%': // %% is replaced by a % char.
  1242. break;
  1243. case 'a': // is replaced by the locale's abbreviated weekday name.
  1244. if((pTM->tm_wday < 0) || (pTM->tm_wday > 6))
  1245. return 0;
  1246. if(!Append(pTimeLocale->mAbbrevDay[pTM->tm_wday], pTimeString, capacity))
  1247. return 0;
  1248. continue;
  1249. case 'A': // is replaced by the locale's full weekday name.
  1250. if((pTM->tm_wday < 0) || (pTM->tm_wday > 6))
  1251. return 0;
  1252. if(!Append(pTimeLocale->mDay[pTM->tm_wday], pTimeString, capacity))
  1253. return 0;
  1254. continue;
  1255. case 'b': // is replaced by the locale's abbreviated month name.
  1256. case 'h':
  1257. if((pTM->tm_mon < 0) || (pTM->tm_mon > 11))
  1258. return 0;
  1259. if(!Append(pTimeLocale->mAbbrevMonth[pTM->tm_mon], pTimeString, capacity))
  1260. return 0;
  1261. continue;
  1262. case 'B': // is replaced by the locale's full month name.
  1263. if((pTM->tm_mon < 0) || (pTM->tm_mon > 11))
  1264. return 0;
  1265. if(!Append(pTimeLocale->mMonth[pTM->tm_mon], pTimeString, capacity))
  1266. return 0;
  1267. continue;
  1268. case 'c': // is replaced by the locale's appropriate date and time representation.
  1269. {
  1270. #if EASTDC_WINDOWS_TIME_FUNCTIONS_AVAILABLE
  1271. const SYSTEMTIME systemTime = { WORD(pTM->tm_year + 1900), WORD(pTM->tm_mon + 1), WORD(pTM->tm_wday), WORD(pTM->tm_mday), WORD(pTM->tm_hour), WORD(pTM->tm_min), WORD(pTM->tm_sec), 0 };
  1272. const uint32_t dateFormat = (cAlt == '#') ? DATE_SHORTDATE : DATE_LONGDATE;
  1273. const char empty[2] = { ' ', 0 };
  1274. if(!EAStdCGetDateFormat(dateFormat, &systemTime, buffer, sizeof buffer) || !Append(buffer, pTimeString, capacity) || !Append(empty, pTimeString, capacity))
  1275. return 0;
  1276. if(!EAStdCGetTimeFormat(0, &systemTime, buffer, sizeof buffer) || !Append(buffer, pTimeString, capacity))
  1277. return 0;
  1278. #else
  1279. char formatBuffer[256]; formatBuffer[0] = 0;
  1280. const char* pFormatTemp = pTimeLocale->mDateTimeFormat;
  1281. if (cAlt == '#')
  1282. pFormatTemp = ConvertFormatSpecifiersToAlternates(formatBuffer, sizeof formatBuffer, pFormatTemp);
  1283. const size_t len = Strftime(pTimeString, capacity, pFormatTemp, pTM);
  1284. if(!len)
  1285. return 0;
  1286. pTimeString += len;
  1287. capacity -= len;
  1288. #endif
  1289. continue;
  1290. }
  1291. case 'C': // is replaced by the century number (the year divided by 100 and truncated to an integer) as a decimal number [00-99].
  1292. {
  1293. int year = (pTM->tm_year + 1900) / 100;
  1294. if(year == 0 && cAlt == '#')
  1295. {
  1296. if(!WriteInt(year, 1, '0', false, pTimeString, capacity))
  1297. return 0;
  1298. }
  1299. else
  1300. {
  1301. if(!WriteInt(year, 2, '0', cAlt == '#', pTimeString, capacity))
  1302. return 0;
  1303. }
  1304. continue;
  1305. }
  1306. case 'd': // is replaced by the day of the month as a decimal number [01,31].
  1307. if(!WriteInt(pTM->tm_mday, 2, '0', cAlt == '#', pTimeString, capacity))
  1308. return 0;
  1309. continue;
  1310. case 'D': // same as %m/%d/%y.
  1311. {
  1312. const size_t len = Strftime(pTimeString, capacity, "%m/%d/%y", pTM);
  1313. if(!len)
  1314. return 0;
  1315. pTimeString += len;
  1316. capacity -= len;
  1317. continue;
  1318. }
  1319. case 'e': // is replaced by the day of the month as a decimal number [1,31]; a single digit is preceded by a space.
  1320. if(!WriteInt(pTM->tm_mday, 2, ' ', cAlt == '#', pTimeString, capacity))
  1321. return 0;
  1322. continue;
  1323. case 'F': // same as %Y-%m-%d
  1324. {
  1325. const size_t len = Strftime(pTimeString, capacity, "%Y-%m-%d", pTM);
  1326. if(!len)
  1327. return 0;
  1328. pTimeString += len;
  1329. capacity -= len;
  1330. continue;
  1331. }
  1332. case 'g': //Replaced by the last 2 digits of the week-based year (see below) as a decimal number [00,99]
  1333. {
  1334. //Unsupported as of yet
  1335. continue;
  1336. }
  1337. case 'G': //Replaced by the week-based year (see below) as a decimal number (for example, 1977).
  1338. {
  1339. //Unsupported as of yet
  1340. continue;
  1341. }
  1342. case 'H': // is replaced by the hour (24-hour clock) as a decimal number [00,23].
  1343. if(!WriteInt(pTM->tm_hour, 2, '0', cAlt == '#', pTimeString, capacity))
  1344. return 0;
  1345. continue;
  1346. case 'I': // is replaced by the hour (12-hour clock) as a decimal number [01,12].
  1347. if(!WriteInt((pTM->tm_hour % 12) ? (pTM->tm_hour % 12) : 12, 2, '0', cAlt == '#', pTimeString, capacity))
  1348. return 0;
  1349. continue;
  1350. case 'j': // is replaced by the day of the year as a decimal number [001,366].
  1351. if(!WriteInt(pTM->tm_yday + 1, 3, '0', cAlt == '#', pTimeString, capacity))
  1352. return 0;
  1353. continue;
  1354. case 'M': // is replaced by the minute as a decimal number [00,59].
  1355. if(!WriteInt(pTM->tm_min, 2, '0', cAlt == '#', pTimeString, capacity))
  1356. return 0;
  1357. continue;
  1358. case 'm': // is replaced by the month as a decimal number [01,12].
  1359. if(!WriteInt(pTM->tm_mon + 1, 2, '0', cAlt == '#', pTimeString, capacity))
  1360. return 0;
  1361. continue;
  1362. case 'n': // is replaced by a newline character.
  1363. buffer[0] = '\n';
  1364. buffer[1] = 0;
  1365. if(!Append(buffer, pTimeString, capacity))
  1366. return 0;
  1367. continue;
  1368. case 'p': // is replaced by the locale's equivalent of either a.m. or p.m.
  1369. if(!Append(pTimeLocale->mAmPm[pTM->tm_hour >= 12], pTimeString, capacity))
  1370. return 0;
  1371. continue;
  1372. case 'r': // is replaced by the time in a.m. and p.m. notation; in the POSIX locale this is equivalent to %I:%M:%S %p.
  1373. {
  1374. const size_t len = Strftime(pTimeString, capacity, pTimeLocale->mTimeFormatAmPm, pTM);
  1375. if(!len)
  1376. return 0;
  1377. pTimeString += len;
  1378. capacity -= len;
  1379. continue;
  1380. }
  1381. case 'R': // is replaced by the time in 24 hour notation (%H:%M).
  1382. {
  1383. const size_t len = Strftime(pTimeString, capacity, "%H:%M", pTM);
  1384. if(!len)
  1385. return 0;
  1386. pTimeString += len;
  1387. capacity -= len;
  1388. continue;
  1389. }
  1390. case 'S': // is replaced by the second as a decimal number [00,61].
  1391. if(!WriteInt(pTM->tm_sec, 2, '0', cAlt == '#', pTimeString, capacity))
  1392. return 0;
  1393. continue;
  1394. case 't': // is replaced by a tab character.
  1395. buffer[0] = '\t';
  1396. buffer[1] = 0;
  1397. if(!Append(buffer, pTimeString, capacity))
  1398. return 0;
  1399. continue;
  1400. case 'T': // is replaced by the time (%H:%M:%S).
  1401. {
  1402. const size_t len = Strftime(pTimeString, capacity, "%H:%M:%S", pTM);
  1403. if(!len)
  1404. return 0;
  1405. pTimeString += len;
  1406. capacity -= len;
  1407. continue;
  1408. }
  1409. case 'u': // is replaced by the weekday as a decimal number [1,7], with 1 representing Monday.
  1410. if(!WriteInt(pTM->tm_wday ? pTM->tm_wday : 7, 1, '0', cAlt == '#', pTimeString, capacity))
  1411. return 0;
  1412. continue;
  1413. case 'U': // is replaced by the week number of the year (Sunday as the first day of the week) as a decimal number [00,53].
  1414. {
  1415. int wday = SUNDAY_BASED_WEEK_NUMBER(pTM);
  1416. if(wday == 0 && cAlt == '#')
  1417. {
  1418. if(!WriteInt(wday, 1, '0', false, pTimeString, capacity))
  1419. return 0;
  1420. }
  1421. else
  1422. {
  1423. if(!WriteInt(wday, 2, '0', cAlt == '#', pTimeString, capacity))
  1424. return 0;
  1425. }
  1426. continue;
  1427. }
  1428. case 'V': // is replaced by the week number of the year (Monday as the first day of the week) as a decimal number [01,53]. If the week containing 1 January has four or more days in the new year, then it is considered week 1. Otherwise, it is the last week (53) of the previous year, and the next week is week 1.
  1429. {
  1430. int week = MONDAY_BASED_WEEK_NUMBER(pTM);
  1431. int days = ((pTM->tm_yday + 7 - (pTM->tm_wday ? pTM->tm_wday - 1 : 6)) % 7);
  1432. if(days >= 4)
  1433. week++;
  1434. else if(week == 0)
  1435. week = 53;
  1436. if(!WriteInt(week, 2, '0', cAlt == '#', pTimeString, capacity))
  1437. return 0;
  1438. continue;
  1439. }
  1440. case 'w': // is replaced by the weekday as a decimal number [0,6], with 0 representing Sunday.
  1441. if(!WriteInt(pTM->tm_wday, 1, '0', false, pTimeString, capacity))
  1442. return 0;
  1443. continue;
  1444. case 'W': // %W is replaced by the week number of the year (Monday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Monday are considered to be in week 0.
  1445. {
  1446. int wday = MONDAY_BASED_WEEK_NUMBER(pTM);
  1447. if(wday == 0 && cAlt == '#')
  1448. {
  1449. if(!WriteInt(wday, 1, '0', false, pTimeString, capacity))
  1450. return 0;
  1451. }
  1452. else
  1453. {
  1454. if(!WriteInt(wday, 2, '0', cAlt == '#', pTimeString, capacity))
  1455. return 0;
  1456. }
  1457. continue;
  1458. }
  1459. case 'x': // %x is replaced by the locale's appropriate date representation.
  1460. {
  1461. #if EASTDC_WINDOWS_TIME_FUNCTIONS_AVAILABLE
  1462. const SYSTEMTIME systemTime = { WORD(pTM->tm_year + 1900), WORD(pTM->tm_mon + 1), WORD(pTM->tm_wday), WORD(pTM->tm_mday), WORD(pTM->tm_hour), WORD(pTM->tm_min), WORD(pTM->tm_sec), 0 };
  1463. const uint32_t dateFormat = (cAlt == '#') ? DATE_SHORTDATE : DATE_LONGDATE;
  1464. if(!EAStdCGetDateFormat(dateFormat, &systemTime, buffer, sizeof buffer) || !Append(buffer, pTimeString, capacity))
  1465. return 0;
  1466. #else
  1467. char formatBuffer[256]; formatBuffer[0] = 0;
  1468. const char* pFormatTemp = pTimeLocale->mDateFormat;
  1469. if (cAlt == '#')
  1470. pFormatTemp = ConvertFormatSpecifiersToAlternates(formatBuffer, sizeof formatBuffer, pFormatTemp);
  1471. const size_t len = Strftime(pTimeString, capacity, pFormatTemp, pTM);
  1472. if(!len)
  1473. return 0;
  1474. pTimeString += len;
  1475. capacity -= len;
  1476. #endif
  1477. continue;
  1478. }
  1479. case 'X': // %X is replaced by the locale's appropriate time representation.
  1480. {
  1481. #if EASTDC_WINDOWS_TIME_FUNCTIONS_AVAILABLE
  1482. const SYSTEMTIME systemTime = { WORD(pTM->tm_year + 1900), WORD(pTM->tm_mon + 1), WORD(pTM->tm_wday), WORD(pTM->tm_mday), WORD(pTM->tm_hour), WORD(pTM->tm_min), WORD(pTM->tm_sec), 0 };
  1483. if(!EAStdCGetTimeFormat(0, &systemTime, buffer, sizeof buffer) || !Append(buffer, pTimeString, capacity))
  1484. return 0;
  1485. #else
  1486. char formatBuffer[256]; formatBuffer[0] = 0;
  1487. const char* pFormatTemp = pTimeLocale->mTimeFormat;
  1488. if (cAlt == '#')
  1489. pFormatTemp = ConvertFormatSpecifiersToAlternates(formatBuffer, sizeof formatBuffer, pFormatTemp);
  1490. const size_t len = Strftime(pTimeString, capacity, pFormatTemp, pTM);
  1491. if(!len)
  1492. return 0;
  1493. pTimeString += len;
  1494. capacity -= len;
  1495. #endif
  1496. continue;
  1497. }
  1498. case 'y': // %y is replaced by the year without century as a decimal number [00,99].
  1499. {
  1500. int year = (pTM->tm_year + 1900) % 100;
  1501. if(year == 0 && cAlt == '#')
  1502. {
  1503. if(!WriteInt(year, 1 /*strlen("99")*/, '0', false, pTimeString, capacity))
  1504. return 0;
  1505. }
  1506. else
  1507. {
  1508. if(!WriteInt(year, 2 /*strlen("99")*/, '0', cAlt == '#', pTimeString, capacity))
  1509. return 0;
  1510. }
  1511. continue;
  1512. }
  1513. case 'Y': // %Y is replaced by the year with century as a decimal number.
  1514. if(!WriteInt((pTM->tm_year + 1900), 4 /*strlen("9999")*/, '0', cAlt == '#', pTimeString, capacity))
  1515. return 0;
  1516. continue;
  1517. case 'z': //Replaced by the offset from UTC in the ISO 8601:2000 standard format ( +hhmm or -hhmm ), or by no characters if no timezone is determinable.
  1518. //For example, "-0430" means 4 hours 30 minutes behind UTC (west of Greenwich). [CX] If tm_isdst is zero, the standard time offset is used.
  1519. //If tm_isdst is greater than zero, the daylight savings time offset is used. If tm_isdst is negative, no characters are returned. [ tm_isdst]
  1520. {
  1521. int tzBias = (int)GetTimeZoneBias(); // tzBias will be a negative number in the United States.
  1522. int hour = abs(tzBias / 3600);
  1523. int min = (abs(tzBias) - (hour * 3600)) / 60;
  1524. buffer[5] = '\0';
  1525. buffer[4] = (char)((min % 10) + '0');
  1526. min /= 10;
  1527. buffer[3] = (char)((min % 10) + '0');
  1528. buffer[2] = (char)((hour % 10) + '0');
  1529. hour /= 10;
  1530. buffer[1] = (char)((hour % 10) + '0');
  1531. buffer[0] = (tzBias < 0) ? '-' : '+';
  1532. if(!Append(buffer, pTimeString, capacity))
  1533. return 0;
  1534. continue;
  1535. }
  1536. case 'Z': // %Z is replaced by the timezone name or abbreviation, or by no bytes if no timezone information exists.
  1537. {
  1538. const char* pTZName = bGMT ? "GMT" : buffer;
  1539. if(!bGMT)
  1540. GetTimeZoneName(buffer, pTM->tm_isdst != 0);
  1541. if(pTZName && !Append(pTZName, pTimeString, capacity))
  1542. return 0;
  1543. continue;
  1544. }
  1545. default:
  1546. EA_FAIL_M("EAStdC Strftime"); // Unsupported format specifier. Just print it as-is.
  1547. break;
  1548. }
  1549. }
  1550. if(!capacity--)
  1551. return 0;
  1552. *pTimeString++ = *pFormat;
  1553. }
  1554. *pTimeString = 0;
  1555. return (timeStringCapacity - capacity);
  1556. } // Strftime
  1557. static bool ReadInt(const char*& pString, int& n, int nMin, int nMax)
  1558. {
  1559. int result = 0;
  1560. int rMax = nMax;
  1561. if((*pString >= '0') && (*pString <= '9'))
  1562. {
  1563. do {
  1564. result *= 10;
  1565. result += *pString++ - '0';
  1566. rMax /= 10;
  1567. } while(rMax && (*pString >= '0') && (*pString <= '9') && ((result * 10) <= nMax));
  1568. if((result >= nMin) && (result <= nMax))
  1569. {
  1570. n = result;
  1571. return true;
  1572. }
  1573. }
  1574. return false;
  1575. }
  1576. static bool ParseDate(bool bAlt, const char *&p, tm* EA_RESTRICT pTM, const TimeLocale *pTimeLocale)
  1577. {
  1578. #if EASTDC_WINDOWS_TIME_FUNCTIONS_AVAILABLE
  1579. EA_UNUSED(pTimeLocale);
  1580. const int formatSize = 256;
  1581. char buffer[formatSize]; buffer[0] = 0;
  1582. char* c;
  1583. char* tokenizerContext = NULL;
  1584. size_t index = 0;
  1585. LCTYPE localeDateType = bAlt ? LOCALE_SSHORTDATE : LOCALE_SLONGDATE;
  1586. size_t bufferSize = static_cast<size_t>(EAStdCGetLocaleInfo(localeDateType, NULL, 0));
  1587. char* dateFormat = static_cast<char *>(EAAlloca(bufferSize + 1));
  1588. char* tmp = static_cast<char *>(EAAlloca(bufferSize + 1));
  1589. EA_ANALYSIS_ASSUME(dateFormat != NULL);
  1590. EAStdCGetLocaleInfo(localeDateType, dateFormat, static_cast<int>(bufferSize));
  1591. dateFormat[bufferSize] = 0;
  1592. Strlcpy(tmp, dateFormat, bufferSize + 1);
  1593. c = Strtok(tmp, "/, -", &tokenizerContext);
  1594. while(c != 0)
  1595. {
  1596. if(Strncmp(c, "d", formatSize) == 0)
  1597. Strlcat(buffer, "%#d", formatSize);
  1598. else if(Strncmp(c, "dd", formatSize) == 0)
  1599. Strlcat(buffer, "%d", formatSize);
  1600. else if(Strncmp(c, "ddd", formatSize) == 0)
  1601. Strlcat(buffer, "%#a", formatSize);
  1602. else if(Strncmp(c, "dddd", formatSize) == 0)
  1603. Strlcat(buffer, "%a", formatSize);
  1604. else if(Strncmp(c, "M", formatSize) == 0)
  1605. Strlcat(buffer, "%#m", formatSize);
  1606. else if(Strncmp(c, "MM", formatSize) == 0)
  1607. Strlcat(buffer, "%m", formatSize);
  1608. else if(Strncmp(c, "MMM", formatSize) == 0)
  1609. Strlcat(buffer, "%#b", formatSize);
  1610. else if(Strncmp(c, "MMMM", formatSize) == 0)
  1611. Strlcat(buffer, "%b", formatSize);
  1612. else if(Strncmp(c, "y", formatSize) == 0)
  1613. Strlcat(buffer, "%#y", formatSize);
  1614. else if(Strncmp(c, "yy", formatSize) == 0)
  1615. Strlcat(buffer, "%y", formatSize);
  1616. else if(Strncmp(c, "yyyy", formatSize) == 0)
  1617. Strlcat(buffer, "%Y", formatSize);
  1618. else if(Strncmp(c, "yyyyy", formatSize) == 0)
  1619. Strlcat(buffer, "%Y", formatSize);
  1620. else
  1621. return false;
  1622. index += Strlen(c);
  1623. size_t separators = Strcspn(&dateFormat[index], "dmyM");
  1624. char copiedChar = dateFormat[index + separators];
  1625. dateFormat[index + separators] = 0;
  1626. Strlcat(buffer, &dateFormat[index], formatSize);
  1627. dateFormat[index + separators] = copiedChar;
  1628. index += separators;
  1629. c = Strtok(NULL, "/, -", &tokenizerContext);
  1630. }
  1631. if((p = Strptime(p, buffer, pTM)) == NULL)
  1632. return false;
  1633. #else
  1634. char formatBuffer[256]; formatBuffer[0] = 0;
  1635. const char* pFormatTemp = pTimeLocale->mDateFormat;
  1636. if(bAlt)
  1637. pFormatTemp = Internal::ConvertFormatSpecifiersToAlternates(formatBuffer, sizeof formatBuffer, pFormatTemp);
  1638. if((p = Strptime(p, pFormatTemp, pTM)) == NULL)
  1639. return false;
  1640. #endif
  1641. return true;
  1642. }
  1643. static bool ParseTime(bool bAlt, const char *&p, tm* EA_RESTRICT pTM, const TimeLocale *pTimeLocale)
  1644. {
  1645. #if EASTDC_WINDOWS_TIME_FUNCTIONS_AVAILABLE
  1646. EA_UNUSED(pTimeLocale);
  1647. EA_UNUSED(bAlt);
  1648. const int formatSize = 256;
  1649. char buffer[formatSize]; buffer[0] = 0;
  1650. size_t bufferSize = static_cast<size_t>(EAStdCGetLocaleInfo(LOCALE_STIMEFORMAT, NULL, 0));
  1651. char* timeFormat = static_cast<char *>(EAAlloca(bufferSize + 1));
  1652. char* tmp = static_cast<char *>(EAAlloca(bufferSize + 1));
  1653. EA_ANALYSIS_ASSUME(timeFormat != NULL);
  1654. EAStdCGetLocaleInfo(LOCALE_STIMEFORMAT, timeFormat, static_cast<int>(bufferSize));
  1655. Strlcpy(tmp, timeFormat, bufferSize);
  1656. char* tokenizerContext = NULL;
  1657. size_t index = 0;
  1658. char* c = Strtok(tmp, ": ", &tokenizerContext);
  1659. while(c != 0)
  1660. {
  1661. if(Strncmp(c, "h", formatSize) == 0)
  1662. Strlcat(buffer, "%#I", formatSize);
  1663. else if(Strncmp(c, "hh", formatSize) == 0)
  1664. Strlcat(buffer, "%I", formatSize);
  1665. else if(Strncmp(c, "H", formatSize) == 0)
  1666. Strlcat(buffer, "%#H", formatSize);
  1667. else if(Strncmp(c, "HH", formatSize) == 0)
  1668. Strlcat(buffer, "%H", formatSize);
  1669. else if(Strncmp(c, "m", formatSize) == 0)
  1670. Strlcat(buffer, "%#M", formatSize);
  1671. else if(Strncmp(c, "mm", formatSize) == 0)
  1672. Strlcat(buffer, "%M", formatSize);
  1673. else if(Strncmp(c, "s", formatSize) == 0)
  1674. Strlcat(buffer, "%#S", formatSize);
  1675. else if(Strncmp(c, "ss", formatSize) == 0)
  1676. Strlcat(buffer, "%S", formatSize);
  1677. else if(Strncmp(c, "t", formatSize) == 0)
  1678. Strlcat(buffer, "%#p", formatSize);
  1679. else if(Strncmp(c, "tt", formatSize) == 0)
  1680. Strlcat(buffer, "%p", formatSize);
  1681. else
  1682. return false;
  1683. index += Strlen(c);
  1684. size_t separators = Strcspn(&timeFormat[index], "hHmst");
  1685. char copiedChar = timeFormat[index + separators];
  1686. timeFormat[index + separators] = 0;
  1687. Strlcat(buffer, &timeFormat[index], formatSize);
  1688. timeFormat[index + separators] = copiedChar;
  1689. index += separators;
  1690. c = Strtok(NULL, ": ", &tokenizerContext);
  1691. }
  1692. if((p = Strptime(p, buffer, pTM)) == NULL)
  1693. return false;
  1694. #else
  1695. char formatBuffer[256]; formatBuffer[0] = 0;
  1696. const char* pFormatTemp = pTimeLocale->mTimeFormat;
  1697. if(bAlt)
  1698. pFormatTemp = Internal::ConvertFormatSpecifiersToAlternates(formatBuffer, sizeof formatBuffer, pFormatTemp);
  1699. if((p = Strptime(p, pFormatTemp, pTM)) == NULL)
  1700. return false;
  1701. #endif
  1702. return true;
  1703. }
  1704. EASTDC_API char* Strptime(const char* EA_RESTRICT pTimeString, const char* EA_RESTRICT pFormat, tm* EA_RESTRICT pTM, const TimeLocale* EA_RESTRICT pTimeLocale)
  1705. {
  1706. using namespace Internal;
  1707. const char* p = pTimeString;
  1708. size_t len = 0;
  1709. int i = 0;
  1710. bool bSplitYear = false;
  1711. bool bAlt = false;
  1712. char c;
  1713. if(!pTimeLocale)
  1714. pTimeLocale = &gDefaultTimeLocale;
  1715. while((c = *pFormat) != 0)
  1716. {
  1717. if(Isspace(c)) // If the current format char is whitespace...
  1718. { //
  1719. while(Isspace(*p)) // then eat any time string whitespace.
  1720. p++;
  1721. pFormat++;
  1722. continue;
  1723. }
  1724. c = *pFormat++; // c will be non-whitespace.
  1725. if(c != '%') // If we have a literal char that is outside of a % format sequence...
  1726. {
  1727. if(c != *p++)
  1728. return NULL;
  1729. continue;
  1730. }
  1731. bAlt = false;
  1732. FormatBegin:
  1733. c = *pFormat++;
  1734. switch (c)
  1735. {
  1736. case '%': // Replaced by %.
  1737. if(c != *p++)
  1738. return NULL;
  1739. break;
  1740. case 'E': // E alternate representation.
  1741. case 'O': // O alternate representation.
  1742. case '#': // # alternate representation.
  1743. bAlt = true;
  1744. goto FormatBegin;
  1745. case 'a': // The day of the week, using the locale's weekday names; either the abbreviated or full name may be specified.
  1746. case 'A': // Equivalent to %a.
  1747. {
  1748. for(i = 0; i < 7; i++)
  1749. {
  1750. len = Strlen(pTimeLocale->mDay[i]);
  1751. if(Strnicmp(pTimeLocale->mDay[i], p, len) == 0)
  1752. break;
  1753. len = Strlen(pTimeLocale->mAbbrevDay[i]);
  1754. if(Strnicmp(pTimeLocale->mAbbrevDay[i], p, len) == 0)
  1755. break;
  1756. }
  1757. // Nothing matched.
  1758. if(i == 7)
  1759. return NULL;
  1760. pTM->tm_wday = i;
  1761. p += len;
  1762. break;
  1763. }
  1764. case 'b': // The month, using the locale's month names; either the abbreviated or full name may be specified.
  1765. case 'B': // Equivalent to %b.
  1766. case 'h': // Equivalent to %b.
  1767. {
  1768. for(i = 0; i < 12; i++)
  1769. {
  1770. len = Strlen(pTimeLocale->mMonth[i]);
  1771. if(Strnicmp(pTimeLocale->mMonth[i], p, len) == 0)
  1772. break;
  1773. len = Strlen(pTimeLocale->mAbbrevMonth[i]);
  1774. if(Strnicmp(pTimeLocale->mAbbrevMonth[i], p, len) == 0)
  1775. break;
  1776. }
  1777. if(i == 12)
  1778. return NULL;
  1779. pTM->tm_mon = i;
  1780. p += len;
  1781. break;
  1782. }
  1783. case 'c': // Replaced by the locale's appropriate date and time representation.
  1784. {
  1785. #if EASTDC_WINDOWS_TIME_FUNCTIONS_AVAILABLE
  1786. if(!ParseDate(bAlt, p, pTM, pTimeLocale))
  1787. return NULL;
  1788. while(Isspace(*p))
  1789. ++p;
  1790. if(!ParseTime(bAlt, p, pTM, pTimeLocale))
  1791. return NULL;
  1792. #else
  1793. char buffer[256]; buffer[0] = 0;
  1794. const char* pFormatTemp = pTimeLocale->mDateTimeFormat;
  1795. if(bAlt)
  1796. pFormatTemp = ConvertFormatSpecifiersToAlternates(buffer, sizeof buffer, pFormatTemp);
  1797. if((p = Strptime(p, pFormatTemp, pTM)) == NULL)
  1798. return NULL;
  1799. #endif
  1800. break;
  1801. }
  1802. case 'C': // The century number [00,99]; leading zeros are permitted but not required.
  1803. if(!ReadInt(p, i, 0, 99))
  1804. return NULL;
  1805. if(bSplitYear)
  1806. pTM->tm_year = (pTM->tm_year % 100) + (i * 100);
  1807. else
  1808. {
  1809. pTM->tm_year = i * 100;
  1810. bSplitYear = true;
  1811. }
  1812. break;
  1813. case 'd': // The day of the month [01,31]; leading zeros are permitted but not required.
  1814. case 'e': // Equivalent to %d.
  1815. if(!ReadInt(p, pTM->tm_mday, 1, 31))
  1816. return NULL;
  1817. break;
  1818. case 'D': // The date as %m / %d / %y.
  1819. if((p = Strptime(p, "%m/%d/%y", pTM)) == NULL)
  1820. return NULL;
  1821. break;
  1822. case 'H': // The hour (24-hour clock) [00,23]; leading zeros are permitted but not required.
  1823. if(!ReadInt(p, pTM->tm_hour, 0, 23))
  1824. return NULL;
  1825. break;
  1826. case 'I': // The hour (12-hour clock) [01,12]; leading zeros are permitted but not required.
  1827. if(!ReadInt(p, pTM->tm_hour, 1, 12))
  1828. return NULL;
  1829. break;
  1830. case 'j': // The day number of the year [001,366]; leading zeros are permitted but not required.
  1831. if(!ReadInt(p, i, 1, 366))
  1832. return NULL;
  1833. pTM->tm_yday = i - 1;
  1834. break;
  1835. case 'M': // The minute [00,59]; leading zeros are permitted but not required.
  1836. if(!ReadInt(p, pTM->tm_min, 0, 59))
  1837. return NULL;
  1838. break;
  1839. case 'm': // The month number [01,12]; leading zeros are permitted but not required.
  1840. if(!ReadInt(p, i, 1, 12))
  1841. return NULL;
  1842. pTM->tm_mon = i - 1;
  1843. break;
  1844. case 'n': // Any white space.
  1845. case 't': // Any white space.
  1846. while(Isspace(*p))
  1847. p++;
  1848. break;
  1849. case 'p': // The locale's equivalent of a.m or p.m.
  1850. {
  1851. size_t lenAM = Strlen(pTimeLocale->mAmPm[0]);
  1852. size_t lenPM = Strlen(pTimeLocale->mAmPm[1]);
  1853. if(Strnicmp(pTimeLocale->mAmPm[0], p, lenAM) == 0) // Test AM
  1854. {
  1855. if (pTM->tm_hour == 12)
  1856. pTM->tm_hour = 0;
  1857. p += lenAM;
  1858. break;
  1859. }
  1860. else if(Strnicmp(pTimeLocale->mAmPm[1], p, lenPM) == 0) // Test PM
  1861. {
  1862. if(pTM->tm_hour <= 11)
  1863. pTM->tm_hour += 12;
  1864. if (pTM->tm_hour > 23)
  1865. return NULL;
  1866. p += lenPM;
  1867. break;
  1868. }
  1869. return NULL;
  1870. }
  1871. case 'r': // 12-hour clock time using the AM/PM notation if t_fmt_ampm is not an empty string in the LC_TIME portion of the current locale; in the POSIX locale, this shall be equivalent to %I : %M : %S %p.
  1872. if((p = Strptime(p, "%I:%M:%S %p", pTM)) == NULL)
  1873. return NULL;
  1874. break;
  1875. case 'R': // The time as %H : %M.
  1876. if((p = Strptime(p, "%H:%M", pTM)) == NULL)
  1877. return NULL;
  1878. break;
  1879. case 'S': // The seconds [00,60]; leading zeros are permitted but not required.
  1880. if(!ReadInt(p, pTM->tm_sec, 0, 61))
  1881. return NULL;
  1882. break;
  1883. case 'T': // The time as %H : %M : %S.
  1884. if((p = Strptime(p, "%H:%M:%S", pTM)) == NULL)
  1885. return NULL;
  1886. break;
  1887. case 'U': // The week number of the year (Sunday as the first day of the week) as a decimal number [00,53]; leading zeros are permitted but not required.
  1888. case 'W': // The week number of the year (Monday as the first day of the week) as a decimal number [00,53]; leading zeros are permitted but not required.
  1889. // It's hard to calculate this, as we need the rest of the information
  1890. // to implement it right. Or we could possibly delay calculation of it.
  1891. {
  1892. //Unsupported as of yet
  1893. break;
  1894. }
  1895. case 'w': // The weekday as a decimal number [0,6], with 0 representing Sunday; leading zeros are permitted but not required.
  1896. if(!ReadInt(p, pTM->tm_wday, 0, 6))
  1897. return NULL;
  1898. break;
  1899. case 'x': // The date, using the locale's date format.
  1900. {
  1901. if (!ParseDate(bAlt, p, pTM, pTimeLocale))
  1902. return NULL;
  1903. break;
  1904. }
  1905. case 'X': // The time, using the locale's time format.
  1906. {
  1907. if (!ParseTime(bAlt, p, pTM, pTimeLocale))
  1908. return NULL;
  1909. break;
  1910. }
  1911. case 'Y': // The year, including the century (for example, 1988).
  1912. if(!ReadInt(p, pTM->tm_year, 0, 9999))
  1913. return NULL;
  1914. //Set the year according to the 1900epoch
  1915. pTM->tm_year -= 1900;
  1916. break;
  1917. case 'y': // The year within century. When a century is not otherwise specified, values in the range [69,99] shall refer to years 1969 to 1999 inclusive, and values in the range [00,68] shall refer to years 2000 to 2068 inclusive; leading zeros shall be permitted but shall not be required.
  1918. if(!ReadInt(p, i, 0, 99))
  1919. return NULL;
  1920. if(bSplitYear)
  1921. {
  1922. pTM->tm_year = ((pTM->tm_year / 100) * 100) + i;
  1923. break;
  1924. }
  1925. bSplitYear = true;
  1926. if(i <= 68)
  1927. pTM->tm_year = (2000 - 1900) + i;
  1928. else
  1929. pTM->tm_year = i;
  1930. break;
  1931. default:
  1932. EA_FAIL_M("EAStdC Strptime"); // Unsupported format specifier. Just print it as-is.
  1933. return NULL;
  1934. }
  1935. }
  1936. return (char*)p;
  1937. } // Strptime
  1938. #undef EADT_COUNT_LEAP_YEARS
  1939. #undef SUNDAY_BASED_WEEK_NUMBER
  1940. #undef MONDAY_BASED_WEEK_NUMBER
  1941. } // namespace StdC
  1942. } // namespace EA
  1943. #ifdef _MSC_VER
  1944. #pragma warning(pop)
  1945. #endif