You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

521 lines
18 KiB

  1. /* /////////////////////////////////////////////////////////////////////////
  2. * File: winstl/time/format_functions.hpp
  3. *
  4. * Purpose: Comparison functions for Windows time structures.
  5. *
  6. * Created: 21st November 2003
  7. * Updated: 7th August 2012
  8. *
  9. * Thanks to: Mikael Pahmp, for spotting the failure to handle 24-hour
  10. * time pictures.
  11. *
  12. * Home: http://stlsoft.org/
  13. *
  14. * Copyright (c) 2003-2012, Matthew Wilson and Synesis Software
  15. * All rights reserved.
  16. *
  17. * Redistribution and use in source and binary forms, with or without
  18. * modification, are permitted provided that the following conditions are met:
  19. *
  20. * - Redistributions of source code must retain the above copyright notice, this
  21. * list of conditions and the following disclaimer.
  22. * - Redistributions in binary form must reproduce the above copyright notice,
  23. * this list of conditions and the following disclaimer in the documentation
  24. * and/or other materials provided with the distribution.
  25. * - Neither the name(s) of Matthew Wilson and Synesis Software nor the names of
  26. * any contributors may be used to endorse or promote products derived from
  27. * this software without specific prior written permission.
  28. *
  29. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  30. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  31. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  32. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  33. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  34. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  35. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  36. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  37. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  38. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  39. * POSSIBILITY OF SUCH DAMAGE.
  40. *
  41. * ////////////////////////////////////////////////////////////////////// */
  42. /** \file winstl/time/format_functions.hpp
  43. *
  44. * \brief [C, C++] Formatting functions for Windows time types
  45. * (\ref group__library__time "Time" Library).
  46. */
  47. #ifndef WINSTL_INCL_WINSTL_TIME_HPP_FORMAT_FUNCTIONS
  48. #define WINSTL_INCL_WINSTL_TIME_HPP_FORMAT_FUNCTIONS
  49. #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
  50. # define WINSTL_VER_WINSTL_TIME_HPP_FORMAT_FUNCTIONS_MAJOR 5
  51. # define WINSTL_VER_WINSTL_TIME_HPP_FORMAT_FUNCTIONS_MINOR 1
  52. # define WINSTL_VER_WINSTL_TIME_HPP_FORMAT_FUNCTIONS_REVISION 2
  53. # define WINSTL_VER_WINSTL_TIME_HPP_FORMAT_FUNCTIONS_EDIT 62
  54. #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
  55. /* /////////////////////////////////////////////////////////////////////////
  56. * Includes
  57. */
  58. #ifndef WINSTL_INCL_WINSTL_H_WINSTL
  59. # include <winstl/winstl.h>
  60. #endif /* !WINSTL_INCL_WINSTL_H_WINSTL */
  61. #ifndef STLSOFT_INCL_STLSOFT_HPP_MEMORY_AUTO_BUFFER
  62. # include <stlsoft/memory/auto_buffer.hpp>
  63. #endif /* !STLSOFT_INCL_STLSOFT_HPP_MEMORY_AUTO_BUFFER */
  64. #ifndef STLSOFT_INCL_STLSOFT_CONVERSION_HPP_INTEGER_TO_STRING
  65. # include <stlsoft/conversion/integer_to_string.hpp>
  66. #endif /* !STLSOFT_INCL_STLSOFT_CONVERSION_HPP_INTEGER_TO_STRING */
  67. #ifndef WINSTL_INCL_WINSTL_MEMORY_HPP_PROCESSHEAP_ALLOCATOR
  68. # include <winstl/memory/processheap_allocator.hpp>
  69. #endif /* !WINSTL_INCL_WINSTL_MEMORY_HPP_PROCESSHEAP_ALLOCATOR */
  70. #ifndef WINSTL_INCL_WINSTL_REGISTRY_HPP_FUNCTIONS
  71. # include <winstl/registry/functions.hpp> // for reg_get_string_value()
  72. #endif /* !WINSTL_INCL_WINSTL_REGISTRY_HPP_FUNCTIONS */
  73. /* /////////////////////////////////////////////////////////////////////////
  74. * Namespace
  75. */
  76. #ifndef _WINSTL_NO_NAMESPACE
  77. # if defined(_STLSOFT_NO_NAMESPACE) || \
  78. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  79. /* There is no stlsoft namespace, so must define ::winstl */
  80. namespace winstl
  81. {
  82. # else
  83. /* Define stlsoft::winstl_project */
  84. namespace stlsoft
  85. {
  86. namespace winstl_project
  87. {
  88. # endif /* _STLSOFT_NO_NAMESPACE */
  89. #endif /* !_WINSTL_NO_NAMESPACE */
  90. /* /////////////////////////////////////////////////////////////////////////
  91. * Helper classes
  92. */
  93. #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
  94. template <ss_typename_param_k C>
  95. struct time_format_functions_traits;
  96. STLSOFT_TEMPLATE_SPECIALISATION
  97. struct time_format_functions_traits<ws_char_a_t>
  98. {
  99. typedef ws_char_a_t char_type;
  100. static int GetTimeFormat(LCID Locale, DWORD dwFlags, SYSTEMTIME const* lpTime, char_type const* lpFormat, char_type* lpTimeStr, int cchTime)
  101. {
  102. return ::GetTimeFormatA(Locale, dwFlags, lpTime, lpFormat, lpTimeStr, cchTime);
  103. }
  104. static int GetLocaleInfo(LCID Locale, LCTYPE LCType, char_type* lpLCData, int cchData)
  105. {
  106. return ::GetLocaleInfoA(Locale, LCType, lpLCData, cchData);
  107. }
  108. static ss_size_t lstrlen(char_type const* s)
  109. {
  110. return static_cast<ss_size_t>(::lstrlenA(s));
  111. }
  112. static char_type* lstrcpy(char_type* dest, char_type const* src)
  113. {
  114. return ::lstrcpyA(dest, src);
  115. }
  116. };
  117. STLSOFT_TEMPLATE_SPECIALISATION
  118. struct time_format_functions_traits<ws_char_w_t>
  119. {
  120. typedef ws_char_w_t char_type;
  121. static int GetTimeFormat(LCID Locale, DWORD dwFlags, SYSTEMTIME const* lpTime, char_type const* lpFormat, char_type* lpTimeStr, int cchTime)
  122. {
  123. return ::GetTimeFormatW(Locale, dwFlags, lpTime, lpFormat, lpTimeStr, cchTime);
  124. }
  125. static int GetLocaleInfo(LCID Locale, LCTYPE LCType, char_type* lpLCData, int cchData)
  126. {
  127. return ::GetLocaleInfoW(Locale, LCType, lpLCData, cchData);
  128. }
  129. static ss_size_t lstrlen(char_type const* s)
  130. {
  131. return static_cast<ss_size_t>(::lstrlenW(s));
  132. }
  133. static char_type* lstrcpy(char_type* dest, char_type const* src)
  134. {
  135. return ::lstrcpyW(dest, src);
  136. }
  137. };
  138. #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
  139. /* /////////////////////////////////////////////////////////////////////////
  140. * Functions
  141. */
  142. template <ss_typename_param_k C>
  143. inline
  144. int
  145. STLSOFT_STDCALL GetTimeFormat_ms_(
  146. LCID locale // locale
  147. , DWORD dwFlags // options
  148. , CONST SYSTEMTIME* lpTime // time
  149. , C const* lpFormat // time format string
  150. , C const* const* timeMarkers // pointer to array of two pointers to time markers
  151. , C* lpTimeStr // formatted string buffer
  152. , int const cchTime // size of string buffer
  153. )
  154. {
  155. typedef C char_t;
  156. typedef time_format_functions_traits<char_t> traits_t;
  157. typedef stlsoft_ns_qual(auto_buffer_old)<
  158. char_t
  159. , processheap_allocator<char_t>
  160. > buffer_t;
  161. if(dwFlags & (TIME_NOMINUTESORSECONDS | TIME_NOSECONDS))
  162. {
  163. return traits_t::GetTimeFormat(locale, dwFlags, lpTime, lpFormat, lpTimeStr, cchTime);
  164. }
  165. if(dwFlags & LOCALE_NOUSEROVERRIDE)
  166. {
  167. locale = LOCALE_SYSTEM_DEFAULT;
  168. }
  169. buffer_t timePicture(1 + ((NULL == lpFormat) ? static_cast<ss_size_t>(::GetLocaleInfoA(locale, LOCALE_STIMEFORMAT, NULL, 0)) : 0));
  170. if(NULL == lpFormat)
  171. {
  172. ss_size_t n = static_cast<ss_size_t>(traits_t::GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STIMEFORMAT, &timePicture[0], static_cast<int>(timePicture.size())));
  173. lpFormat = &timePicture[0];
  174. if(n < timePicture.size())
  175. {
  176. timePicture[n] = static_cast<C>('\0');
  177. }
  178. }
  179. ss_size_t const cchPicture = 1 + traits_t::lstrlen(lpFormat);
  180. // Following need to be front-padded to be forward compatible with STLSoft 1.10 (which uses
  181. // 'safer' i2s functions
  182. char_t hours12_[] = { '\0', '\0', '\0', '0', '0', '\0' }; // "...00"
  183. char_t hours24_[] = { '\0', '\0', '\0', '0', '0', '\0' }; // "...00"
  184. char_t minutes_[] = { '\0', '\0', '\0', '0', '0', '\0' }; // "...00"
  185. char_t seconds_[] = { '\0', '\0', '\0', '0', '0', '.', '0', '0', '0', '\0' }; // "...00.000"
  186. uint16_t const hour12 = (lpTime->wHour > 12) ? uint16_t(lpTime->wHour - 12) : lpTime->wHour;
  187. #if defined(STLSOFT_CF_STATIC_ARRAY_SIZE_DETERMINATION_SUPPORT)
  188. char_t const* hours12 = stlsoft_ns_qual(integer_to_string)(hours12_, hour12);
  189. char_t const* hours24 = stlsoft_ns_qual(integer_to_string)(hours24_, lpTime->wHour);
  190. char_t const* minutes = stlsoft_ns_qual(integer_to_string)(minutes_, lpTime->wMinute);
  191. #else /* ? STLSOFT_CF_STATIC_ARRAY_SIZE_DETERMINATION_SUPPORT */
  192. char_t const* hours12 = stlsoft_ns_qual(integer_to_string)(&hours12_[0], STLSOFT_NUM_ELEMENTS(hours12_), hour12);
  193. char_t const* hours24 = stlsoft_ns_qual(integer_to_string)(&hours24_[0], STLSOFT_NUM_ELEMENTS(hours24_), lpTime->wHour);
  194. char_t const* minutes = stlsoft_ns_qual(integer_to_string)(&minutes_[0], STLSOFT_NUM_ELEMENTS(minutes_), lpTime->wMinute);
  195. #endif /* STLSOFT_CF_STATIC_ARRAY_SIZE_DETERMINATION_SUPPORT */
  196. stlsoft_ns_qual(integer_to_string)(&seconds_[3], STLSOFT_NUM_ELEMENTS(seconds_) - 3, lpTime->wMilliseconds);
  197. char_t const* seconds = stlsoft_ns_qual(integer_to_string)(&seconds_[0], 6, lpTime->wSecond);
  198. seconds_[5] = '.';
  199. // Get the time markers
  200. char_t const* amMarker = (NULL != timeMarkers && NULL != timeMarkers[0]) ? timeMarkers[0] : NULL;
  201. char_t const* pmMarker = (NULL != timeMarkers && NULL != timeMarkers[1]) ? timeMarkers[1] : NULL;
  202. buffer_t am(0);
  203. buffer_t pm(0);
  204. if( NULL == amMarker ||
  205. NULL == pmMarker)
  206. {
  207. HKEY hkey;
  208. LONG res = ::RegOpenKeyA(HKEY_CURRENT_USER, "Control Panel\\International", &hkey);
  209. if(ERROR_SUCCESS == res)
  210. {
  211. static char_t const s1159[] = { 's', '1', '1', '5', '9', '\0' };
  212. static char_t const s2359[] = { 's', '2', '3', '5', '9', '\0' };
  213. ws_size_t cchAM = 0;
  214. ws_size_t cchPM = 0;
  215. LONG r;
  216. if( ERROR_SUCCESS != (r = reg_get_string_value(hkey, s1159, static_cast<char_t*>(NULL), cchAM)) ||
  217. ERROR_SUCCESS != (r = (am.resize(cchAM), cchAM = am.size(), reg_get_string_value(hkey, s1159, &am[0], cchAM))))
  218. {
  219. res = r;
  220. }
  221. else if(ERROR_SUCCESS != (r = reg_get_string_value(hkey, s2359, static_cast<char_t*>(NULL), cchPM)) ||
  222. ERROR_SUCCESS != (r = (pm.resize(cchPM), cchPM = pm.size(), reg_get_string_value(hkey, s2359, &pm[0], cchPM))))
  223. {
  224. res = r;
  225. }
  226. ::RegCloseKey(hkey);
  227. }
  228. if(ERROR_SUCCESS == res)
  229. {
  230. if(NULL == amMarker)
  231. {
  232. amMarker = &am[0];
  233. }
  234. if(NULL == pmMarker)
  235. {
  236. pmMarker = &pm[0];
  237. }
  238. }
  239. }
  240. if(NULL == amMarker)
  241. {
  242. static char_t const AM[] = { 'A', 'M', '\0' };
  243. am.resize(3);
  244. amMarker = traits_t::lstrcpy(&am[0], AM);
  245. }
  246. if(NULL == pmMarker)
  247. {
  248. static char_t const PM[] = { 'P', 'M', '\0' };
  249. pm.resize(3);
  250. pmMarker = traits_t::lstrcpy(&pm[0], PM);
  251. }
  252. ws_size_t const cchAmMarker = traits_t::lstrlen(amMarker);
  253. ws_size_t const cchPmMarker = traits_t::lstrlen(pmMarker);
  254. char_t const* timeMarker = (lpTime->wHour < 12) ? amMarker : pmMarker;
  255. ws_size_t const cchMarker = (cchAmMarker < cchPmMarker) ? cchPmMarker : cchAmMarker;
  256. ws_size_t const cchTimeMax = (cchPicture - 1) + (2 - 1) + (2 - 1) + (6 - 1) + 1 + (1 + cchMarker);
  257. buffer_t buffer(1 + cchTimeMax);
  258. ws_size_t len = 0;
  259. if(!buffer.empty())
  260. {
  261. char_t const* r;
  262. char_t* w = &buffer[0];
  263. char_t prev = '\0';
  264. ws_bool_t bMarker1 = true;
  265. for(r = lpFormat; r != lpFormat + cchPicture; ++r)
  266. {
  267. char_t const ch = *r;
  268. switch(ch)
  269. {
  270. case 'h':
  271. if( 'h' == prev &&
  272. '\0' == *(hours12 + 1))
  273. {
  274. --hours12;
  275. }
  276. break;
  277. case 'H':
  278. if( 'H' == prev &&
  279. '\0' == *(hours24 + 1))
  280. {
  281. --hours24;
  282. }
  283. break;
  284. case 'm':
  285. if( 'm' == prev &&
  286. '\0' == *(minutes + 1))
  287. {
  288. --minutes;
  289. }
  290. break;
  291. case 's':
  292. if( 's' == prev &&
  293. '.' == *(seconds + 1))
  294. {
  295. --seconds;
  296. }
  297. break;
  298. case 't':
  299. if('t' == prev)
  300. {
  301. bMarker1 = false;
  302. }
  303. break;
  304. default:
  305. {
  306. static char_t const s_emptyString[] = { '\0' };
  307. char_t const* p;
  308. switch(prev)
  309. {
  310. case 'h': p = hours12; break;
  311. case 'H': p = hours24; break;
  312. case 'm': p = minutes; break;
  313. case 's': p = seconds; break;
  314. case 't':
  315. if(0 == (dwFlags & TIME_NOTIMEMARKER))
  316. {
  317. if(!bMarker1)
  318. {
  319. p = timeMarker;
  320. break;
  321. }
  322. else
  323. {
  324. // Fall through
  325. *w++ = *timeMarker;
  326. ++len;
  327. }
  328. }
  329. // Fall through
  330. default: p = s_emptyString; break;
  331. }
  332. for(; '\0' != *p; *w++ = *p++, ++len)
  333. {}
  334. }
  335. *w++ = ch;
  336. ++len;
  337. break;
  338. }
  339. if('\0' == ch)
  340. {
  341. break;
  342. }
  343. prev = ch;
  344. }
  345. }
  346. // If 0 was specified, or
  347. if( 0 == cchTime ||
  348. len <= ws_size_t(cchTime))
  349. {
  350. if(0 != cchTime)
  351. {
  352. traits_t::lstrcpy(lpTimeStr, &buffer[0]);
  353. }
  354. return static_cast<int>(len);
  355. }
  356. else
  357. {
  358. return 0;
  359. }
  360. }
  361. /** \brief Analogue to the Win32 API <code>GetTimeFormat()</code>, but also
  362. * provides milliseconds as part of the time picture.
  363. *
  364. * \param locale
  365. * \param dwFlags
  366. * \param lpTime
  367. * \param lpFormat
  368. * \param lpTimeStr The buffer into which the result will be written
  369. * \param cchTime Number of character spaces available in
  370. * <code>lpTimeStr</code>. If 0, the required length is returned, and
  371. * <code>lpTimeStr</code> is ignored.
  372. *
  373. * \return The number of characters written to <code>lpTimeStr</code>
  374. * (if <code>0 != cchTime</code>), or required
  375. * (if <code>0 == cchTime</code>).
  376. */
  377. inline
  378. int
  379. STLSOFT_STDCALL GetTimeFormat_msA(
  380. LCID locale // locale
  381. , DWORD dwFlags // options
  382. , CONST SYSTEMTIME* lpTime // time
  383. , ws_char_a_t const* lpFormat // time format string
  384. , ws_char_a_t* lpTimeStr // formatted string buffer
  385. , int cchTime // size of string buffer
  386. )
  387. {
  388. WINSTL_ASSERT(0 == cchTime || NULL != lpTimeStr);
  389. return GetTimeFormat_ms_<ws_char_a_t>(locale, dwFlags, lpTime, lpFormat, NULL, lpTimeStr, cchTime);
  390. }
  391. inline
  392. int
  393. STLSOFT_STDCALL GetTimeFormat_msW(
  394. LCID locale // locale
  395. , DWORD dwFlags // options
  396. , CONST SYSTEMTIME* lpTime // time
  397. , ws_char_w_t const* lpFormat // time format string
  398. , ws_char_w_t* lpTimeStr // formatted string buffer
  399. , int cchTime // size of string buffer
  400. )
  401. {
  402. WINSTL_ASSERT(0 == cchTime || NULL != lpTimeStr);
  403. return GetTimeFormat_ms_<ws_char_w_t>(locale, dwFlags, lpTime, lpFormat, NULL, lpTimeStr, cchTime);
  404. }
  405. inline
  406. int
  407. STLSOFT_STDCALL GetTimeFormat_msExA(
  408. LCID locale // locale
  409. , DWORD dwFlags // options
  410. , CONST SYSTEMTIME* lpTime // time
  411. , ws_char_a_t const* lpFormat // time format string
  412. , ws_char_a_t const* (*timeMarkers)[2]
  413. , ws_char_a_t* lpTimeStr // formatted string buffer
  414. , int cchTime // size of string buffer
  415. )
  416. {
  417. WINSTL_ASSERT(0 == cchTime || NULL != lpTimeStr);
  418. return GetTimeFormat_ms_<ws_char_a_t>(locale, dwFlags, lpTime, lpFormat, *timeMarkers, lpTimeStr, cchTime);
  419. }
  420. inline
  421. int
  422. STLSOFT_STDCALL GetTimeFormat_msExW(
  423. LCID locale // locale
  424. , DWORD dwFlags // options
  425. , CONST SYSTEMTIME* lpTime // time
  426. , ws_char_w_t const* lpFormat // time format string
  427. , ws_char_w_t const* (*timeMarkers)[2]
  428. , ws_char_w_t* lpTimeStr // formatted string buffer
  429. , int cchTime // size of string buffer
  430. )
  431. {
  432. WINSTL_ASSERT(0 == cchTime || NULL != lpTimeStr);
  433. return GetTimeFormat_ms_<ws_char_w_t>(locale, dwFlags, lpTime, lpFormat, *timeMarkers, lpTimeStr, cchTime);
  434. }
  435. ////////////////////////////////////////////////////////////////////////////
  436. // Unit-testing
  437. #ifdef STLSOFT_UNITTEST
  438. # include "./unittest/format_functions_unittest_.h"
  439. #endif /* STLSOFT_UNITTEST */
  440. /* ////////////////////////////////////////////////////////////////////// */
  441. #ifndef _WINSTL_NO_NAMESPACE
  442. # if defined(_STLSOFT_NO_NAMESPACE) || \
  443. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  444. } /* namespace winstl */
  445. # else
  446. } /* namespace winstl_project */
  447. } /* namespace stlsoft */
  448. # endif /* _STLSOFT_NO_NAMESPACE */
  449. #endif /* !_WINSTL_NO_NAMESPACE */
  450. /* ////////////////////////////////////////////////////////////////////// */
  451. #endif /* !WINSTL_INCL_WINSTL_TIME_HPP_FORMAT_FUNCTIONS */
  452. /* ///////////////////////////// end of file //////////////////////////// */