xref: /reactos/sdk/lib/ucrt/time/asctime.cpp (revision 04e0dc4a)
1 //
2 // asctime.cpp
3 //
4 //      Copyright (c) Microsoft Corporation. All rights reserved.
5 //
6 // The asctime() family of functions, which convert a tm struct into a string.
7 //
8 #include <corecrt_internal_securecrt.h>
9 #include <corecrt_internal_time.h>
10 
11 #define _ASCBUFSIZE 26
12 
13 
14 
15 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
16 //
17 // asctime_s and _wasctime_s
18 //
19 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
20 template <typename Character>
21 static Character* __cdecl common_asctime_s_write_value(
22     _Out_writes_(2) Character* p,
23     int                  const value,
24     bool                 const zero_fill
25     ) throw()
26 {
27     if (value >= 10 || zero_fill)
28     {
29         *p++ = static_cast<Character>('0' + value / 10);
30     }
31     else
32     {
33         *p++ = ' ';
34     }
35 
36     *p++ = static_cast<Character>('0' + value % 10);
37     return p;
38 }
39 
40 
41 
42 // Converts a time structure (tm) into an ASCII string.  The string is always
43 // exactly 26 characters long, in the form Tue May  1 02:34:55 1984\n\0.  The
44 // buffer 'size_in_chars' must be at least 26.  The string is generated into the
45 // buffer.  On success, zero is returned and the buffer contains the time string;
46 // on failure, an error code is returned and the contents of the buffer are
47 // indeterminate.
48 template <typename Character>
49 _Success_(return == 0)
50 static errno_t __cdecl common_asctime_s(
_Out_writes_z_(size_in_chars)51     _Out_writes_z_(size_in_chars) _Post_readable_size_(_ASCBUFSIZE) Character* const buffer,
52     _In_range_(>=, _ASCBUFSIZE)   size_t                                       const size_in_chars,
53     tm const*                                                                  const tm_value
54     ) throw()
55 {
56     _VALIDATE_RETURN_ERRCODE(
57         buffer != nullptr && size_in_chars > 0,
58         EINVAL
59     )
60 
61     _RESET_STRING(buffer, size_in_chars);
62 
63     _VALIDATE_RETURN_ERRCODE(size_in_chars >= _ASCBUFSIZE, EINVAL)
64     _VALIDATE_RETURN_ERRCODE(tm_value != nullptr,          EINVAL)
65     _VALIDATE_RETURN_ERRCODE(tm_value->tm_year >= 0,       EINVAL)
66 
67     // Month, hour, minute, and second are zero-based
68     _VALIDATE_RETURN_ERRCODE(tm_value->tm_mon  >= 0 && tm_value->tm_mon  <= 11, EINVAL)
69     _VALIDATE_RETURN_ERRCODE(tm_value->tm_hour >= 0 && tm_value->tm_hour <= 23, EINVAL)
70     _VALIDATE_RETURN_ERRCODE(tm_value->tm_min  >= 0 && tm_value->tm_min  <= 59, EINVAL)
71     _VALIDATE_RETURN_ERRCODE(tm_value->tm_sec  >= 0 && tm_value->tm_sec  <= 60, EINVAL) // including leap second
72     _VALIDATE_RETURN_ERRCODE(tm_value->tm_wday >= 0 && tm_value->tm_wday <=  6, EINVAL)
73 
74     _VALIDATE_RETURN_ERRCODE(__crt_time_is_day_valid(tm_value->tm_year, tm_value->tm_mon, tm_value->tm_mday), EINVAL)
75 
76     Character* buffer_it = buffer;
77 
78     // Copy the day name into the buffer:
79     char const* const day_first = __dnames + tm_value->tm_wday * 3;
80     char const* const day_last  = day_first + 3;
81     for (char const* day_it = day_first; day_it != day_last; ++day_it)
82         *buffer_it++ = static_cast<Character>(*day_it);
83 
84     *buffer_it++ = static_cast<Character>(' ');
85 
86     // Copy the month name into the buffer:
87     char const* const month_first = __mnames + tm_value->tm_mon * 3;
88     char const* const month_last  = month_first + 3;
89     for (char const* month_it = month_first; month_it != month_last; ++month_it)
90         *buffer_it++ = static_cast<Character>(*month_it);
91 
92     *buffer_it++ = static_cast<Character>(' ');
93 
94     // Copy the day of the month (1 - 31) into the buffer:
95     buffer_it = common_asctime_s_write_value(buffer_it, tm_value->tm_mday, false);
96     *buffer_it++ = static_cast<Character>(' ');
97 
98     // Copy the time into the buffer in HH:MM:SS form:
99     buffer_it = common_asctime_s_write_value(buffer_it, tm_value->tm_hour, true);
100     *buffer_it++ = static_cast<Character>(':');
101     buffer_it = common_asctime_s_write_value(buffer_it, tm_value->tm_min, true);
102     *buffer_it++ = static_cast<Character>(':');
103     buffer_it = common_asctime_s_write_value(buffer_it, tm_value->tm_sec, true);
104     *buffer_it++ = static_cast<Character>(' ');
105 
106     // Copy the four-digit year into the buffer:
107     buffer_it = common_asctime_s_write_value(buffer_it, __crt_get_century(tm_value->tm_year), true);
108     buffer_it = common_asctime_s_write_value(buffer_it, __crt_get_2digit_year(tm_value->tm_year), true);
109 
110     // And that's it...
111     *buffer_it++ = static_cast<Character>('\n');
112     *buffer_it++ = static_cast<Character>('\0');
113 
114     return 0;
115 }
116 
asctime_s(char * const buffer,size_t const size_in_chars,tm const * const tm_value)117 extern "C" errno_t __cdecl asctime_s(
118     char*     const buffer,
119     size_t    const size_in_chars,
120     tm const* const tm_value
121     )
122 {
123     return common_asctime_s(buffer, size_in_chars, tm_value);
124 }
125 
_wasctime_s(wchar_t * const buffer,size_t const size_in_chars,tm const * const tm_value)126 extern "C" errno_t __cdecl _wasctime_s(
127     wchar_t*  const buffer,
128     size_t    const size_in_chars,
129     tm const* const tm_value
130     )
131 {
132     return common_asctime_s(buffer, size_in_chars, tm_value);
133 }
134 
135 
136 
137 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
138 //
139 // asctime and _wasctime
140 //
141 //-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
142 // Utility functions used by common_asctime to get the per-thread actime buffer.
common_asctime_get_ptd_buffer(char)143 static char** common_asctime_get_ptd_buffer(char) throw()
144 {
145     __acrt_ptd* const ptd = __acrt_getptd_noexit();
146     if (ptd == nullptr)
147         return nullptr;
148 
149     return &ptd->_asctime_buffer;
150 }
151 
common_asctime_get_ptd_buffer(wchar_t)152 static wchar_t** common_asctime_get_ptd_buffer(wchar_t) throw()
153 {
154     __acrt_ptd* const ptd = __acrt_getptd_noexit();
155     if (ptd == nullptr)
156         return nullptr;
157 
158     return &ptd->_wasctime_buffer;
159 }
160 
161 // Converts a time structure (tm) into an ASCII string.  The string is always
162 // exactly 26 characters long, in the form Tue May  1 02:34:55 1984\n\0.
163 // The return value is a pointer to a per-thread buffer containing the
164 // generated time string.
165 template <typename Character>
166 _Success_(return != 0)
167 _Ret_writes_z_(26)
common_asctime(tm const * const tm_value)168 static Character* __cdecl common_asctime(tm const* const tm_value) throw()
169 {
170     static Character static_buffer[_ASCBUFSIZE];
171 
172     Character** ptd_buffer_address = common_asctime_get_ptd_buffer(Character());
173     if (ptd_buffer_address != nullptr && *ptd_buffer_address == nullptr)
174     {
175         *ptd_buffer_address = _calloc_crt_t(Character, _ASCBUFSIZE).detach();
176     }
177 
178     Character* const buffer = ptd_buffer_address != nullptr && *ptd_buffer_address != nullptr
179         ? *ptd_buffer_address
180         : static_buffer;
181 
182     errno_t const status = common_asctime_s(buffer, _ASCBUFSIZE, tm_value);
183     if (status != 0)
184         return nullptr;
185 
186     return buffer;
187 }
188 
asctime(tm const * const tm_value)189 extern "C" char* __cdecl asctime(tm const* const tm_value)
190 {
191     return common_asctime<char>(tm_value);
192 }
193 
_wasctime(tm const * const tm_value)194 extern "C" wchar_t* __cdecl _wasctime(tm const* const tm_value)
195 {
196     return common_asctime<wchar_t>(tm_value);
197 }
198