xref: /reactos/sdk/lib/crt/time/strftime.c (revision 84344399)
1 /*
2  * COPYRIGHT:   LGPL, See LGPL.txt in the top level directory
3  * PROJECT:     ReactOS CRT library
4  * FILE:        lib/sdk/crt/time/strftime.c
5  * PURPOSE:
6  * PROGRAMER:
7  */
8 #include <precomp.h>
9 
10 static inline BOOL strftime_date(char *str, size_t *pos, size_t max,
11         BOOL alternate, const struct tm *mstm, MSVCRT___lc_time_data *time_data)
12 {
13     char *format;
14     SYSTEMTIME st;
15     size_t ret;
16 
17     st.wYear = mstm->tm_year + 1900;
18     st.wMonth = mstm->tm_mon + 1;
19     st.wDayOfWeek = mstm->tm_wday;
20     st.wDay = mstm->tm_mday;
21     st.wHour = mstm->tm_hour;
22     st.wMinute = mstm->tm_min;
23     st.wSecond = mstm->tm_sec;
24     st.wMilliseconds = 0;
25 
26     format = alternate ? time_data->str.names.date : time_data->str.names.short_date;
27     ret = GetDateFormatA(time_data->lcid, 0, &st, format, NULL, 0);
28     if(ret && ret<max-*pos)
29         ret = GetDateFormatA(time_data->lcid, 0, &st, format, str+*pos, max-*pos);
30     if(!ret) {
31         *str = 0;
32         *_errno() = EINVAL;
33         return FALSE;
34     }else if(ret > max-*pos) {
35         *str = 0;
36         *_errno() = ERANGE;
37         return FALSE;
38     }
39     *pos += ret-1;
40     return TRUE;
41 }
42 
43 static inline BOOL strftime_time(char *str, size_t *pos, size_t max,
44         const struct tm *mstm, MSVCRT___lc_time_data *time_data)
45 {
46     SYSTEMTIME st;
47     size_t ret;
48 
49     st.wYear = mstm->tm_year + 1900;
50     st.wMonth = mstm->tm_mon + 1;
51     st.wDayOfWeek = mstm->tm_wday;
52     st.wDay = mstm->tm_mday;
53     st.wHour = mstm->tm_hour;
54     st.wMinute = mstm->tm_min;
55     st.wSecond = mstm->tm_sec;
56     st.wMilliseconds = 0;
57 
58     ret = GetTimeFormatA(time_data->lcid, 0, &st, time_data->str.names.time, NULL, 0);
59     if(ret && ret<max-*pos)
60         ret = GetTimeFormatA(time_data->lcid, 0, &st, time_data->str.names.time,
61                 str+*pos, max-*pos);
62     if(!ret) {
63         *str = 0;
64         *_errno() = EINVAL;
65         return FALSE;
66     }else if(ret > max-*pos) {
67         *str = 0;
68         *_errno() = ERANGE;
69         return FALSE;
70     }
71     *pos += ret-1;
72     return TRUE;
73 }
74 
75 static inline BOOL strftime_str(char *str, size_t *pos, size_t max, char *src)
76 {
77     size_t len = strlen(src);
78     if(len > max-*pos) {
79         *str = 0;
80         *_errno() = ERANGE;
81         return FALSE;
82     }
83 
84     memcpy(str+*pos, src, len);
85     *pos += len;
86     return TRUE;
87 }
88 
89 static inline BOOL strftime_int(char *str, size_t *pos, size_t max,
90         int src, int prec, int l, int h)
91 {
92     size_t len;
93 
94     if(src<l || src>h) {
95         *str = 0;
96         *_errno() = EINVAL;
97         return FALSE;
98     }
99 
100     len = _snprintf(str+*pos, max-*pos, "%0*d", prec, src);
101     if(len == -1) {
102         *str = 0;
103         *_errno() = ERANGE;
104         return FALSE;
105     }
106 
107     *pos += len;
108     return TRUE;
109 }
110 
111 /*********************************************************************
112  *		_Strftime (MSVCRT.@)
113  */
114 size_t CDECL _Strftime(char *str, size_t max, const char *format,
115         const struct tm *mstm, void *_Lc_time_arg)
116 {
117     MSVCRT___lc_time_data *time_data = (MSVCRT___lc_time_data*)_Lc_time_arg;
118     size_t ret, tmp;
119     BOOL alternate;
120 
121     TRACE("(%p %ld %s %p %p)\n", str, max, format, mstm, time_data);
122 
123     if(!str || !format) {
124         if(str && max)
125             *str = 0;
126         *_errno() = EINVAL;
127         return 0;
128     }
129 
130     if(!time_data)
131         time_data = get_locinfo()->lc_time_curr;
132 
133     for(ret=0; *format && ret<max; format++) {
134         if(*format != '%') {
135             str[ret++] = *format;
136             continue;
137         }
138 
139         format++;
140         if(*format == '#') {
141             alternate = TRUE;
142             format++;
143         }else {
144             alternate = FALSE;
145         }
146 
147         if(!mstm)
148             goto einval_error;
149 
150         switch(*format) {
151         case 'c':
152             if(!strftime_date(str, &ret, max, alternate, mstm, time_data))
153                 return 0;
154             if(ret < max)
155                 str[ret++] = ' ';
156             if(!strftime_time(str, &ret, max, mstm, time_data))
157                 return 0;
158             break;
159         case 'x':
160             if(!strftime_date(str, &ret, max, alternate, mstm, time_data))
161                 return 0;
162             break;
163         case 'X':
164             if(!strftime_time(str, &ret, max, mstm, time_data))
165                 return 0;
166             break;
167         case 'a':
168             if(mstm->tm_wday<0 || mstm->tm_wday>6)
169                 goto einval_error;
170             if(!strftime_str(str, &ret, max, time_data->str.names.short_wday[mstm->tm_wday]))
171                 return 0;
172             break;
173         case 'A':
174             if(mstm->tm_wday<0 || mstm->tm_wday>6)
175                 goto einval_error;
176             if(!strftime_str(str, &ret, max, time_data->str.names.wday[mstm->tm_wday]))
177                 return 0;
178             break;
179         case 'b':
180             if(mstm->tm_mon<0 || mstm->tm_mon>11)
181                 goto einval_error;
182             if(!strftime_str(str, &ret, max, time_data->str.names.short_mon[mstm->tm_mon]))
183                 return 0;
184             break;
185         case 'B':
186             if(mstm->tm_mon<0 || mstm->tm_mon>11)
187                 goto einval_error;
188             if(!strftime_str(str, &ret, max, time_data->str.names.mon[mstm->tm_mon]))
189                 return 0;
190             break;
191         case 'd':
192             if(!strftime_int(str, &ret, max, mstm->tm_mday, alternate ? 0 : 2, 0, 31))
193                 return 0;
194             break;
195         case 'H':
196             if(!strftime_int(str, &ret, max, mstm->tm_hour, alternate ? 0 : 2, 0, 23))
197                 return 0;
198             break;
199         case 'I':
200             tmp = mstm->tm_hour;
201             if(tmp > 12)
202                 tmp -= 12;
203             else if(!tmp)
204                 tmp = 12;
205             if(!strftime_int(str, &ret, max, tmp, alternate ? 0 : 2, 1, 12))
206                 return 0;
207             break;
208         case 'j':
209             if(!strftime_int(str, &ret, max, mstm->tm_yday+1, alternate ? 0 : 3, 1, 366))
210                 return 0;
211             break;
212         case 'm':
213             if(!strftime_int(str, &ret, max, mstm->tm_mon+1, alternate ? 0 : 2, 1, 12))
214                 return 0;
215             break;
216         case 'M':
217             if(!strftime_int(str, &ret, max, mstm->tm_min, alternate ? 0 : 2, 0, 59))
218                 return 0;
219             break;
220         case 'p':
221             if(mstm->tm_hour<0 || mstm->tm_hour>23)
222                 goto einval_error;
223             if(!strftime_str(str, &ret, max, mstm->tm_hour<12 ?
224                         time_data->str.names.am : time_data->str.names.pm))
225                 return 0;
226             break;
227         case 'S':
228             if(!strftime_int(str, &ret, max, mstm->tm_sec, alternate ? 0 : 2, 0, 59))
229                 return 0;
230             break;
231         case 'w':
232             if(!strftime_int(str, &ret, max, mstm->tm_wday, 0, 0, 6))
233                 return 0;
234             break;
235         case 'y':
236             if(!strftime_int(str, &ret, max, mstm->tm_year%100, alternate ? 0 : 2, 0, 99))
237                 return 0;
238             break;
239         case 'Y':
240             tmp = 1900+mstm->tm_year;
241             if(!strftime_int(str, &ret, max, tmp, alternate ? 0 : 4, 0, 9999))
242                 return 0;
243             break;
244         case 'z':
245         case 'Z':
246             _tzset();
247             if(_get_tzname(&tmp, str+ret, max-ret, mstm->tm_isdst ? 1 : 0))
248                 return 0;
249             ret += tmp;
250             break;
251         case 'U':
252         case 'W':
253             if(mstm->tm_wday<0 || mstm->tm_wday>6 || mstm->tm_yday<0 || mstm->tm_yday>365)
254                 goto einval_error;
255             if(*format == 'U')
256                 tmp = mstm->tm_wday;
257             else if(!mstm->tm_wday)
258                 tmp = 6;
259             else
260                 tmp = mstm->tm_wday-1;
261 
262             tmp = mstm->tm_yday/7 + (tmp <= ((unsigned)mstm->tm_yday%7));
263             if(!strftime_int(str, &ret, max, tmp, alternate ? 0 : 2, 0, 53))
264                 return 0;
265             break;
266         case '%':
267             str[ret++] = '%';
268             break;
269         default:
270             WARN("unknown format %c\n", *format);
271             goto einval_error;
272         }
273     }
274 
275     if(ret == max) {
276         if(max)
277             *str = 0;
278         *_errno() = ERANGE;
279         return 0;
280     }
281 
282     str[ret] = 0;
283     return ret;
284 
285 einval_error:
286     *str = 0;
287     *_errno() = EINVAL;
288     return 0;
289 }
290 
291 /*********************************************************************
292  *		strftime (MSVCRT.@)
293  */
294 size_t CDECL strftime( char *str, size_t max, const char *format,
295                                      const struct tm *mstm )
296 {
297     return _Strftime(str, max, format, mstm, NULL);
298 }
299 
300 /*********************************************************************
301  *		wcsftime (MSVCRT.@)
302  */
303 size_t CDECL wcsftime( wchar_t *str, size_t max,
304                                      const wchar_t *format, const struct tm *mstm )
305 {
306     char *s, *fmt;
307     size_t len;
308 
309     TRACE("%p %ld %s %p\n", str, max, debugstr_w(format), mstm );
310 
311     len = WideCharToMultiByte( CP_ACP, 0, format, -1, NULL, 0, NULL, NULL );
312     if (!(fmt = malloc( len ))) return 0;
313     WideCharToMultiByte( CP_ACP, 0, format, -1, fmt, len, NULL, NULL );
314 
315     if ((s = malloc( max*4 )))
316     {
317         if (!strftime( s, max*4, fmt, mstm )) s[0] = 0;
318         len = MultiByteToWideChar( CP_ACP, 0, s, -1, str, max );
319         if (len) len--;
320         free( s );
321     }
322     else len = 0;
323 
324     free( fmt );
325     return len;
326 }
327