]> pd.if.org Git - pdclib/blob - functions/time/strftime.c
Basic implementation of strftime(), untested.
[pdclib] / functions / time / strftime.c
1 /* strftime( char * restrict, size_t, const char * restrict, const struct tm * restrict )
2
3    This file is part of the Public Domain C Library (PDCLib).
4    Permission is granted to use, modify, and / or redistribute at will.
5 */
6
7 #include <time.h>
8 #include <stdlib.h>
9 #include <locale.h>
10 #include <string.h>
11
12 #ifndef REGTEST
13
14 /* TODO: Alternative representations / numerals not supported. */
15
16 /* This implementation's code is highly repetitive, but I did not really
17    care for putting it into a number of macros / helper functions.
18 */
19
20 size_t strftime( char * _PDCLIB_restrict s, size_t maxsize, const char * _PDCLIB_restrict format, const struct tm * _PDCLIB_restrict timeptr )
21 {
22     size_t rc = 0;
23
24     while ( rc < maxsize )
25     {
26         if ( *format != '%' )
27         {
28             if ( ( *s++ = *format++ ) == '\0' )
29             {
30                 return rc;
31             }
32             else
33             {
34                 ++rc;
35             }
36         }
37         else
38         {
39             /* char flag = 0; */
40             switch ( *++format )
41             {
42                 case 'E':
43                 case 'O':
44                     /* flag = *format++; */
45                     break;
46                 default:
47                     /* EMPTY */
48                     break;
49             }
50             switch( *format++ )
51             {
52                 case 'a':
53                     {
54                         /* tm_wday abbreviated */
55                         const char * day = _PDCLIB_lconv.day_name_abbr[ timeptr->tm_wday ];
56                         size_t len = strlen( day );
57                         if ( rc < ( maxsize - len ) )
58                         {
59                             strcpy( s, day );
60                             rc += len;
61                         }
62                         else
63                         {
64                             return 0;
65                         }
66                         break;
67                     }
68                 case 'A':
69                     {
70                         /* tm_wday full */
71                         const char * day = _PDCLIB_lconv.day_name_full[ timeptr->tm_wday ];
72                         size_t len = strlen( day );
73                         if ( rc < ( maxsize - len ) )
74                         {
75                             strcpy( s, day );
76                             rc += len;
77                         }
78                         else
79                         {
80                             return 0;
81                         }
82                         break;
83                     }
84                 case 'b':
85                 case 'h':
86                     {
87                         /* tm_mon abbreviated */
88                         const char * month = _PDCLIB_lconv.month_name_abbr[ timeptr->tm_mon ];
89                         size_t len = strlen( month );
90                         if ( rc < ( maxsize - len ) )
91                         {
92                             strcpy( s, month );
93                             rc += len;
94                         }
95                         else
96                         {
97                             return 0;
98                         }
99                         break;
100                     }
101                 case 'B':
102                     {
103                         /* tm_mon full */
104                         const char * month = _PDCLIB_lconv.month_name_full[ timeptr->tm_mon ];
105                         size_t len = strlen( month );
106                         if ( rc < ( maxsize - len ) )
107                         {
108                             strcpy( s, month );
109                             rc += len;
110                         }
111                         else
112                         {
113                             return 0;
114                         }
115                         break;
116                     }
117                 case 'c':
118                     {
119                         /* locale's date / time representation, %a %b %e %T %Y for C locale */
120                         /* 'E' for locale's alternative representation */
121                         size_t count = strftime( s, maxsize - rc, _PDCLIB_lconv.date_time_format, timeptr );
122                         if ( count == 0 )
123                         {
124                             return 0;
125                         }
126                         else
127                         {
128                             rc += count;
129                         }
130                         break;
131                     }
132                 case 'C':
133                     {
134                         /* tm_year divided by 100, truncated to decimal (00-99) */
135                         /* 'E' for base year (period) in locale's alternative representation */
136                         if ( rc < ( maxsize - 2 ) )
137                         {
138                             div_t period = div( ( timeptr->tm_year / 100 ), 10 );
139                             *s++ = '0' + period.quot;
140                             *s++ = '0' + period.rem;
141                             rc += 2;
142                         }
143                         else
144                         {
145                             return 0;
146                         }
147                         break;
148                     }
149                 case 'd':
150                     {
151                         /* tm_mday as decimal (01-31) */
152                         /* 'O' for locale's alternative numeric symbols */
153                         if ( rc < ( maxsize - 2 ) )
154                         {
155                             div_t day = div( timeptr->tm_mday, 10 );
156                             *s++ = '0' + day.quot;
157                             *s++ = '0' + day.rem;
158                             rc += 2;
159                         }
160                         else
161                         {
162                             return 0;
163                         }
164                         break;
165                     }
166                 case 'D':
167                     {
168                         /* %m/%d/%y */
169                         size_t count = strftime( s, maxsize - rc, "%m/%d/%y", timeptr );
170                         if ( count == 0 )
171                         {
172                             return 0;
173                         }
174                         else
175                         {
176                             rc += count;
177                         }
178                         break;
179                     }
180                 case 'e':
181                     {
182                         /* tm_mday as decimal ( 1-31) */
183                         /* 'O' for locale's alternative numeric symbols */
184                         if ( rc < ( maxsize - 2 ) )
185                         {
186                             div_t day = div( timeptr->tm_mday, 10 );
187                             *s++ = ( day.quot > 0 ) ? '0' + day.quot : ' ';
188                             *s++ = '0' + day.rem;
189                             rc += 2;
190                         }
191                         else
192                         {
193                             return 0;
194                         }
195                         break;
196                     }
197                 case 'F':
198                     {
199                         /* %Y-%m-%d */
200                         size_t count = strftime( s, maxsize - rc, "%Y-%m-%d", timeptr );
201                         if ( count == 0 )
202                         {
203                             return 0;
204                         }
205                         else
206                         {
207                             rc += count;
208                         }
209                         break;
210                     }
211                 case 'g':
212                     {
213                         /* last 2 digits of the week-based year as decimal (00-99) */
214                         /* TODO: 'g' */
215                         break;
216                     }
217                 case 'G':
218                     {
219                         /* week-based year as decimal (e.g. 1997) */
220                         /* TODO: 'G' */
221                         break;
222                     }
223                 case 'H':
224                     {
225                         /* tm_hour as 24h decimal (00-23) */
226                         /* 'O' for locale's alternative numeric symbols */
227                         if ( rc < ( maxsize - 2 ) )
228                         {
229                             div_t hour = div( timeptr->tm_hour, 10 );
230                             *s++ = '0' + hour.quot;
231                             *s++ = '0' + hour.rem;
232                             rc += 2;
233                         }
234                         else
235                         {
236                             return 0;
237                         }
238                         break;
239                     }
240                 case 'I':
241                     {
242                         /* tm_hour as 12h decimal (01-12) */
243                         /* 'O' for locale's alternative numeric symbols */
244                         if ( rc < ( maxsize - 2 ) )
245                         {
246                             div_t hour = div( ( timeptr->tm_hour + 1 ) % 12, 10 );
247                             *s++ = '0' + hour.quot;
248                             *s++ = '0' + hour.rem;
249                             rc += 2;
250                         }
251                         else
252                         {
253                             return 0;
254                         }
255                         break;
256                     }
257                 case 'j':
258                     {
259                         /* tm_yday as decimal (001-366) */
260                         if ( rc < ( maxsize - 3 ) )
261                         {
262                             div_t yday = div( timeptr->tm_yday, 100 );
263                             *s++ = '0' + yday.quot;
264                             *s++ = '0' + yday.rem / 10;
265                             *s++ = '0' + yday.rem % 10;
266                             rc += 3;
267                         }
268                         else
269                         {
270                             return 0;
271                         }
272                         break;
273                     }
274                 case 'm':
275                     {
276                         /* tm_mon as decimal (01-12) */
277                         /* 'O' for locale's alternative numeric symbols */
278                         if ( rc < ( maxsize - 2 ) )
279                         {
280                             div_t mon = div( timeptr->tm_mon + 1, 10 );
281                             *s++ = '0' + mon.quot;
282                             *s++ = '0' + mon.rem;
283                             rc += 2;
284                         }
285                         else
286                         {
287                             return 0;
288                         }
289                         break;
290                     }
291                 case 'M':
292                     {
293                         /* tm_min as decimal (00-59) */
294                         /* 'O' for locale's alternative numeric symbols */
295                         if ( rc < ( maxsize - 2 ) )
296                         {
297                             div_t min = div( timeptr->tm_min + 1, 10 );
298                             *s++ = '0' + min.quot;
299                             *s++ = '0' + min.rem;
300                             rc += 2;
301                         }
302                         else
303                         {
304                             return 0;
305                         }
306                         break;
307                     }
308                 case 'n':
309                     {
310                         /* newline */
311                         *s++ = '\n';
312                         ++rc;
313                         break;
314                     }
315                 case 'p':
316                     {
317                         /* tm_hour locale's AM/PM designations */
318                         const char * designation = _PDCLIB_lconv.am_pm[ timeptr->tm_hour > 11 ];
319                         size_t len = strlen( designation );
320                         if ( rc < ( maxsize - len ) )
321                         {
322                             strcpy( s, designation );
323                             rc += len;
324                         }
325                         else
326                         {
327                             return 0;
328                         }
329                         break;
330                     }
331                 case 'r':
332                     {
333                         /* tm_hour / tm_min / tm_sec as locale's 12-hour clock time, %I:%M:%S %p for C locale */
334                         size_t count = strftime( s, maxsize - rc, _PDCLIB_lconv.time_format_12h, timeptr );
335                         if ( count == 0 )
336                         {
337                             return 0;
338                         }
339                         else
340                         {
341                             rc += count;
342                         }
343                         break;
344                     }
345                 case 'R':
346                     {
347                         /* %H:%M */
348                         size_t count = strftime( s, maxsize - rc, "%H:%M", timeptr );
349                         if ( count == 0 )
350                         {
351                             return 0;
352                         }
353                         else
354                         {
355                             rc += count;
356                         }
357                         break;
358                     }
359                 case 'S':
360                     {
361                         /* tm_sec as decimal (00-60) */
362                         /* 'O' for locale's alternative numeric symbols */
363                         if ( rc < ( maxsize - 2 ) )
364                         {
365                             div_t sec = div( timeptr->tm_sec + 1, 10 );
366                             *s++ = '0' + sec.quot;
367                             *s++ = '0' + sec.rem;
368                             rc += 2;
369                         }
370                         else
371                         {
372                             return 0;
373                         }
374                         break;
375                     }
376                 case 't':
377                     {
378                         /* tabulator */
379                         *s++ = '\t';
380                         ++rc;
381                         break;
382                     }
383                 case 'T':
384                     {
385                         /* %H:%M:%S */
386                         size_t count = strftime( s, maxsize - rc, "%H:%M:%S", timeptr );
387                         if ( count == 0 )
388                         {
389                             return 0;
390                         }
391                         else
392                         {
393                             rc += count;
394                         }
395                         break;
396                     }
397                 case 'u':
398                     {
399                         /* tm_wday as decimal (1-7) with Monday == 1 */
400                         /* 'O' for locale's alternative numeric symbols */
401                         *s++ = ( timeptr->tm_wday == 0 ) ? '7' : '0' + timeptr->tm_wday;
402                         ++rc;
403                         break;
404                     }
405                 case 'U':
406                     {
407                         /* week number of the year (first Sunday as the first day of week 1) as decimal (00-53) */
408                         /* 'O' for locale's alternative numeric symbols */
409                         /* TODO: 'U' */
410                         break;
411                     }
412                 case 'V':
413                     {
414                         /* week number as decimal (01-53) */
415                         /* 'O' for locale's alternative numeric symbols */
416                         /* TODO: 'V' */
417                         break;
418                     }
419                 case 'w':
420                     {
421                         /* tm_wday as decimal number (0-6) with Sunday == 0 */
422                         /* 'O' for locale's alternative numeric symbols */
423                         *s++ = '0' + timeptr->tm_wday;
424                         ++rc;
425                         break;
426                     }
427                 case 'W':
428                     {
429                         /* week number of the year (first Monday as the first day of week 1) as decimal (00-53) */
430                         /* 'O' for locale's alternative numeric symbols */
431                         /* TODO: 'W' */
432                         break;
433                     }
434                 case 'x':
435                     {
436                         /* locale's date representation, %m/%d/%y for C locale */
437                         /* 'E' for locale's alternative representation */
438                         size_t count = strftime( s, maxsize - rc, _PDCLIB_lconv.date_format, timeptr );
439                         if ( count == 0 )
440                         {
441                             return 0;
442                         }
443                         else
444                         {
445                             rc += count;
446                         }
447                         break;
448                     }
449                 case 'X':
450                     {
451                         /* locale's time representation, %T for C locale */
452                         /* 'E' for locale's alternative representation */
453                         size_t count = strftime( s, maxsize - rc, _PDCLIB_lconv.time_format, timeptr );
454                         if ( count == 0 )
455                         {
456                             return 0;
457                         }
458                         else
459                         {
460                             rc += count;
461                         }
462                         break;
463                     }
464                 case 'y':
465                     {
466                         /* last 2 digits of tm_year as decimal (00-99) */
467                         /* 'E' for offset from %EC (year only) in locale's alternative representation */
468                         /* 'O' for locale's alternative numeric symbols */
469                         if ( rc < ( maxsize - 2 ) )
470                         {
471                             div_t year = div( ( timeptr->tm_year % 100 ), 10 );
472                             *s++ = '0' + year.quot;
473                             *s++ = '0' + year.rem;
474                             rc += 2;
475                         }
476                         else
477                         {
478                             return 0;
479                         }
480                         break;
481                     }
482                 case 'Y':
483                     {
484                         /* tm_year as decimal (e.g. 1997) */
485                         /* 'E' for locale's alternative representation */
486                         if ( rc < ( maxsize - 4 ) )
487                         {
488                             int year = timeptr->tm_year;
489
490                             for ( int i = 3; i >= 0; --i )
491                             {
492                                 div_t digit = div( year, 10 );
493                                 s[i] = '0' + digit.rem;
494                                 year = digit.quot;
495                             }
496
497                             rc += 4;
498                         }
499                         else
500                         {
501                             return 0;
502                         }
503                         break;
504                     }
505                 case 'z':
506                     {
507                         /* tm_isdst / UTC offset in ISO8601 format (e.g. -0430 meaning 4 hours 30 minutes behind Greenwich), or no characters */
508                         /* TODO: 'z' */
509                         break;
510                     }
511                 case 'Z':
512                     {
513                         /* tm_isdst / locale's time zone name or abbreviation, or no characters */
514                         /* TODO: 'Z' */
515                         break;
516                     }
517                 case '%':
518                     {
519                         /* '%' character */
520                         *s++ = '%';
521                         ++rc;
522                         break;
523                     }
524             }
525         }
526     }
527
528     return 0;
529 }
530
531 #endif
532
533 #ifdef TEST
534
535 #include "_PDCLIB_test.h"
536
537 int main( void )
538 {
539     TESTCASE( NO_TESTDRIVER );
540     return TEST_RESULTS;
541 }
542
543 #endif