]> pd.if.org Git - pdclib/blob - functions/time/strftime.c
77160619a293b9aa0d896e5c5b6a0d8afe142197
[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. Multibyte support missing. */
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 static int iso_week( const struct tm * timeptr )
21 {
22     /* calculations below rely on Sunday == 7 */
23     int wday = timeptr->tm_wday;
24     if ( wday == 0 )
25     {
26         wday = 7;
27     }
28     /* https://en.wikipedia.org/wiki/ISO_week_date */
29     int week = ( timeptr->tm_yday - wday + 11 ) / 7;
30     if ( week == 53 )
31     {
32         /* date *may* belong to the *next* year, if:
33            * it is 31.12. and Monday - Wednesday
34            * it is 30.12. and Monday - Tuesday
35            * it is 29.12. and Monday
36            We can safely assume December...
37         */
38         if ( ( timeptr->tm_mday - wday ) > 27 )
39         {
40             week = 1;
41         }
42     }
43     else if ( week == 0 )
44     {
45         /* date *does* belong to *previous* year,
46            i.e. has week 52 *unless*...
47            * current year started on a Friday, or
48            * previous year is leap and this year
49              started on a Saturday.
50         */
51         int firstday = timeptr->tm_wday - ( timeptr->tm_yday % 7 );
52         if ( firstday < 0 )
53         {
54             firstday += 7;
55         }
56         if ( ( firstday == 5 ) || ( _PDCLIB_is_leap( timeptr->tm_year - 1 ) && firstday == 6 ) )
57         {
58             week = 53;
59         }
60         else
61         {
62             week = 52;
63         }
64     }
65     return week;
66 }
67
68 static int sprints( char * _PDCLIB_restrict dest, const char * _PDCLIB_restrict src, size_t maxsize, size_t * rc )
69 {
70     size_t len = strlen( src );
71     if ( *rc < ( maxsize - len ) )
72     {
73         strcpy( dest + *rc, src );
74         *rc += len;
75         return 1;
76     }
77     else
78     {
79         return 0;
80     }
81 }
82
83 size_t strftime( char * _PDCLIB_restrict s, size_t maxsize, const char * _PDCLIB_restrict format, const struct tm * _PDCLIB_restrict timeptr )
84 {
85     size_t rc = 0;
86
87     while ( rc < maxsize )
88     {
89         if ( *format != '%' )
90         {
91             if ( ( s[rc] = *format++ ) == '\0' )
92             {
93                 return rc;
94             }
95             else
96             {
97                 ++rc;
98             }
99         }
100         else
101         {
102             /* char flag = 0; */
103             switch ( *++format )
104             {
105                 case 'E':
106                 case 'O':
107                     /* flag = *format++; */
108                     break;
109                 default:
110                     /* EMPTY */
111                     break;
112             }
113             switch( *format++ )
114             {
115                 case 'a':
116                     {
117                         /* tm_wday abbreviated */
118                         if ( ! sprints( s, _PDCLIB_lconv.day_name_abbr[ timeptr->tm_wday ], maxsize, &rc ) )
119                         {
120                             return 0;
121                         }
122                         break;
123                     }
124                 case 'A':
125                     {
126                         /* tm_wday full */
127                         if ( ! sprints( s, _PDCLIB_lconv.day_name_full[ timeptr->tm_wday ], maxsize, &rc ) )
128                         {
129                             return 0;
130                         }
131                         break;
132                     }
133                 case 'b':
134                 case 'h':
135                     {
136                         /* tm_mon abbreviated */
137                         if ( ! sprints( s, _PDCLIB_lconv.month_name_abbr[ timeptr->tm_mon ], maxsize, &rc ) )
138                         {
139                             return 0;
140                         }
141                         break;
142                     }
143                 case 'B':
144                     {
145                         /* tm_mon full */
146                         if ( ! sprints( s, _PDCLIB_lconv.month_name_full[ timeptr->tm_mon ], maxsize, &rc ) )
147                         {
148                             return 0;
149                         }
150                         break;
151                     }
152                 case 'c':
153                     {
154                         /* locale's date / time representation, %a %b %e %T %Y for C locale */
155                         /* 'E' for locale's alternative representation */
156                         size_t count = strftime( s + rc, maxsize - rc, _PDCLIB_lconv.date_time_format, timeptr );
157                         if ( count == 0 )
158                         {
159                             return 0;
160                         }
161                         else
162                         {
163                             rc += count;
164                         }
165                         break;
166                     }
167                 case 'C':
168                     {
169                         /* tm_year divided by 100, truncated to decimal (00-99) */
170                         /* 'E' for base year (period) in locale's alternative representation */
171                         if ( rc < ( maxsize - 2 ) )
172                         {
173                             div_t period = div( ( ( timeptr->tm_year + 1900 ) / 100 ), 10 );
174                             s[rc++] = '0' + period.quot;
175                             s[rc++] = '0' + period.rem;
176                         }
177                         else
178                         {
179                             return 0;
180                         }
181                         break;
182                     }
183                 case 'd':
184                     {
185                         /* tm_mday as decimal (01-31) */
186                         /* 'O' for locale's alternative numeric symbols */
187                         if ( rc < ( maxsize - 2 ) )
188                         {
189                             div_t day = div( timeptr->tm_mday, 10 );
190                             s[rc++] = '0' + day.quot;
191                             s[rc++] = '0' + day.rem;
192                         }
193                         else
194                         {
195                             return 0;
196                         }
197                         break;
198                     }
199                 case 'D':
200                     {
201                         /* %m/%d/%y */
202                         size_t count = strftime( s + rc, maxsize - rc, "%m/%d/%y", timeptr );
203                         if ( count == 0 )
204                         {
205                             return 0;
206                         }
207                         else
208                         {
209                             rc += count;
210                         }
211                         break;
212                     }
213                 case 'e':
214                     {
215                         /* tm_mday as decimal ( 1-31) */
216                         /* 'O' for locale's alternative numeric symbols */
217                         if ( rc < ( maxsize - 2 ) )
218                         {
219                             div_t day = div( timeptr->tm_mday, 10 );
220                             s[rc++] = ( day.quot > 0 ) ? '0' + day.quot : ' ';
221                             s[rc++] = '0' + day.rem;
222                         }
223                         else
224                         {
225                             return 0;
226                         }
227                         break;
228                     }
229                 case 'F':
230                     {
231                         /* %Y-%m-%d */
232                         size_t count = strftime( s + rc, maxsize - rc, "%Y-%m-%d", timeptr );
233                         if ( count == 0 )
234                         {
235                             return 0;
236                         }
237                         else
238                         {
239                             rc += count;
240                         }
241                         break;
242                     }
243                 case 'g':
244                     {
245                         /* last 2 digits of the week-based year as decimal (00-99) */
246                         if ( rc < ( maxsize - 2 ) )
247                         {
248                             int week = iso_week( timeptr );
249                             int bias = 0;
250                             if ( week >= 52 && timeptr->tm_mon == 0 )
251                             {
252                                 --bias;
253                             }
254                             else if ( week == 1 && timeptr->tm_mon == 11 )
255                             {
256                                 ++bias;
257                             }
258                             div_t year = div( timeptr->tm_year % 100 + bias, 10 );
259                             s[rc++] = '0' + year.quot;
260                             s[rc++] = '0' + year.rem;
261                         }
262                         else
263                         {
264                             return 0;
265                         }
266                         break;
267                     }
268                 case 'G':
269                     {
270                         /* week-based year as decimal (e.g. 1997) */
271                         if ( rc < ( maxsize - 4 ) )
272                         {
273                             int week = iso_week( timeptr );
274                             int year = timeptr->tm_year + 1900;
275                             if ( week >= 52 && timeptr->tm_mon == 0 )
276                             {
277                                 --year;
278                             }
279                             else if ( week == 1 && timeptr->tm_mon == 11 )
280                             {
281                                 ++year;
282                             }
283                             for ( int i = 3; i >= 0; --i )
284                             {
285                                 div_t digit = div( year, 10 );
286                                 s[ rc + i ] = '0' + digit.rem;
287                                 year = digit.quot;
288                             }
289
290                             rc += 4;
291                         }
292                         else
293                         {
294                             return 0;
295                         }
296                         break;
297                     }
298                 case 'H':
299                     {
300                         /* tm_hour as 24h decimal (00-23) */
301                         /* 'O' for locale's alternative numeric symbols */
302                         if ( rc < ( maxsize - 2 ) )
303                         {
304                             div_t hour = div( timeptr->tm_hour, 10 );
305                             s[rc++] = '0' + hour.quot;
306                             s[rc++] = '0' + hour.rem;
307                         }
308                         else
309                         {
310                             return 0;
311                         }
312                         break;
313                     }
314                 case 'I':
315                     {
316                         /* tm_hour as 12h decimal (01-12) */
317                         /* 'O' for locale's alternative numeric symbols */
318                         if ( rc < ( maxsize - 2 ) )
319                         {
320                             div_t hour = div( ( timeptr->tm_hour + 11 ) % 12 + 1, 10 );
321                             s[rc++] = '0' + hour.quot;
322                             s[rc++] = '0' + hour.rem;
323                         }
324                         else
325                         {
326                             return 0;
327                         }
328                         break;
329                     }
330                 case 'j':
331                     {
332                         /* tm_yday as decimal (001-366) */
333                         if ( rc < ( maxsize - 3 ) )
334                         {
335                             div_t yday = div( timeptr->tm_yday + 1, 100 );
336                             s[rc++] = '0' + yday.quot;
337                             s[rc++] = '0' + yday.rem / 10;
338                             s[rc++] = '0' + yday.rem % 10;
339                         }
340                         else
341                         {
342                             return 0;
343                         }
344                         break;
345                     }
346                 case 'm':
347                     {
348                         /* tm_mon as decimal (01-12) */
349                         /* 'O' for locale's alternative numeric symbols */
350                         if ( rc < ( maxsize - 2 ) )
351                         {
352                             div_t mon = div( timeptr->tm_mon + 1, 10 );
353                             s[rc++] = '0' + mon.quot;
354                             s[rc++] = '0' + mon.rem;
355                         }
356                         else
357                         {
358                             return 0;
359                         }
360                         break;
361                     }
362                 case 'M':
363                     {
364                         /* tm_min as decimal (00-59) */
365                         /* 'O' for locale's alternative numeric symbols */
366                         if ( rc < ( maxsize - 2 ) )
367                         {
368                             div_t min = div( timeptr->tm_min, 10 );
369                             s[rc++] = '0' + min.quot;
370                             s[rc++] = '0' + min.rem;
371                         }
372                         else
373                         {
374                             return 0;
375                         }
376                         break;
377                     }
378                 case 'n':
379                     {
380                         /* newline */
381                         s[rc++] = '\n';
382                         break;
383                     }
384                 case 'p':
385                     {
386                         /* tm_hour locale's AM/PM designations */
387                         const char * designation = _PDCLIB_lconv.am_pm[ timeptr->tm_hour > 11 ];
388                         size_t len = strlen( designation );
389                         if ( rc < ( maxsize - len ) )
390                         {
391                             strcpy( s + rc, designation );
392                             rc += len;
393                         }
394                         else
395                         {
396                             return 0;
397                         }
398                         break;
399                     }
400                 case 'r':
401                     {
402                         /* tm_hour / tm_min / tm_sec as locale's 12-hour clock time, %I:%M:%S %p for C locale */
403                         size_t count = strftime( s + rc, maxsize - rc, _PDCLIB_lconv.time_format_12h, timeptr );
404                         if ( count == 0 )
405                         {
406                             return 0;
407                         }
408                         else
409                         {
410                             rc += count;
411                         }
412                         break;
413                     }
414                 case 'R':
415                     {
416                         /* %H:%M */
417                         size_t count = strftime( s + rc, maxsize - rc, "%H:%M", timeptr );
418                         if ( count == 0 )
419                         {
420                             return 0;
421                         }
422                         else
423                         {
424                             rc += count;
425                         }
426                         break;
427                     }
428                 case 'S':
429                     {
430                         /* tm_sec as decimal (00-60) */
431                         /* 'O' for locale's alternative numeric symbols */
432                         if ( rc < ( maxsize - 2 ) )
433                         {
434                             div_t sec = div( timeptr->tm_sec, 10 );
435                             s[rc++] = '0' + sec.quot;
436                             s[rc++] = '0' + sec.rem;
437                         }
438                         else
439                         {
440                             return 0;
441                         }
442                         break;
443                     }
444                 case 't':
445                     {
446                         /* tabulator */
447                         s[rc++] = '\t';
448                         break;
449                     }
450                 case 'T':
451                     {
452                         /* %H:%M:%S */
453                         size_t count = strftime( s + rc, maxsize - rc, "%H:%M:%S", timeptr );
454                         if ( count == 0 )
455                         {
456                             return 0;
457                         }
458                         else
459                         {
460                             rc += count;
461                         }
462                         break;
463                     }
464                 case 'u':
465                     {
466                         /* tm_wday as decimal (1-7) with Monday == 1 */
467                         /* 'O' for locale's alternative numeric symbols */
468                         s[rc++] = ( timeptr->tm_wday == 0 ) ? '7' : '0' + timeptr->tm_wday;
469                         break;
470                     }
471                 case 'U':
472                     {
473                         /* week number of the year (first Sunday as the first day of week 1) as decimal (00-53) */
474                         /* 'O' for locale's alternative numeric symbols */
475                         /* TODO: 'U' */
476                         break;
477                     }
478                 case 'V':
479                     {
480                         /* ISO week number as decimal (01-53) */
481                         /* 'O' for locale's alternative numeric symbols */
482                         if ( rc < ( maxsize - 2 ) )
483                         {
484                             div_t week = div( iso_week( timeptr ), 10 );
485                             s[rc++] = '0' + week.quot;
486                             s[rc++] = '0' + week.rem;
487                         }
488                         else
489                         {
490                             return 0;
491                         }
492                         break;
493                     }
494                 case 'w':
495                     {
496                         /* tm_wday as decimal number (0-6) with Sunday == 0 */
497                         /* 'O' for locale's alternative numeric symbols */
498                         s[rc++] = '0' + timeptr->tm_wday;
499                         break;
500                     }
501                 case 'W':
502                     {
503                         /* week number of the year (first Monday as the first day of week 1) as decimal (00-53) */
504                         /* 'O' for locale's alternative numeric symbols */
505                         /* TODO: 'W' */
506                         break;
507                     }
508                 case 'x':
509                     {
510                         /* locale's date representation, %m/%d/%y for C locale */
511                         /* 'E' for locale's alternative representation */
512                         size_t count = strftime( s + rc, maxsize - rc, _PDCLIB_lconv.date_format, timeptr );
513                         if ( count == 0 )
514                         {
515                             return 0;
516                         }
517                         else
518                         {
519                             rc += count;
520                         }
521                         break;
522                     }
523                 case 'X':
524                     {
525                         /* locale's time representation, %T for C locale */
526                         /* 'E' for locale's alternative representation */
527                         size_t count = strftime( s + rc, maxsize - rc, _PDCLIB_lconv.time_format, timeptr );
528                         if ( count == 0 )
529                         {
530                             return 0;
531                         }
532                         else
533                         {
534                             rc += count;
535                         }
536                         break;
537                     }
538                 case 'y':
539                     {
540                         /* last 2 digits of tm_year as decimal (00-99) */
541                         /* 'E' for offset from %EC (year only) in locale's alternative representation */
542                         /* 'O' for locale's alternative numeric symbols */
543                         if ( rc < ( maxsize - 2 ) )
544                         {
545                             div_t year = div( ( timeptr->tm_year % 100 ), 10 );
546                             s[rc++] = '0' + year.quot;
547                             s[rc++] = '0' + year.rem;
548                         }
549                         else
550                         {
551                             return 0;
552                         }
553                         break;
554                     }
555                 case 'Y':
556                     {
557                         /* tm_year as decimal (e.g. 1997) */
558                         /* 'E' for locale's alternative representation */
559                         if ( rc < ( maxsize - 4 ) )
560                         {
561                             int year = timeptr->tm_year + 1900;
562
563                             for ( int i = 3; i >= 0; --i )
564                             {
565                                 div_t digit = div( year, 10 );
566                                 s[ rc + i ] = '0' + digit.rem;
567                                 year = digit.quot;
568                             }
569
570                             rc += 4;
571                         }
572                         else
573                         {
574                             return 0;
575                         }
576                         break;
577                     }
578                 case 'z':
579                     {
580                         /* tm_isdst / UTC offset in ISO8601 format (e.g. -0430 meaning 4 hours 30 minutes behind Greenwich), or no characters */
581                         /* TODO: 'z' */
582                         break;
583                     }
584                 case 'Z':
585                     {
586                         /* tm_isdst / locale's time zone name or abbreviation, or no characters */
587                         /* TODO: 'Z' */
588                         break;
589                     }
590                 case '%':
591                     {
592                         /* '%' character */
593                         s[rc++] = '%';
594                         break;
595                     }
596             }
597         }
598     }
599
600     return 0;
601 }
602
603 #endif
604
605 #ifdef TEST
606
607 #include "_PDCLIB_test.h"
608
609 #define MKTIME( tm, sec, min, hour, day, month, year, wday, yday ) tm.tm_sec = sec; tm.tm_min = min; tm.tm_hour = hour; tm.tm_mday = day; tm.tm_mon = month; tm.tm_year = year; tm.tm_wday = wday; tm.tm_yday = yday; tm.tm_isdst = -1;
610
611 int main( void )
612 {
613     char buffer[100];
614     /* Basic functionality */
615     struct tm timeptr;
616     MKTIME( timeptr, 59, 30, 12, 1, 9, 72, 0, 274 );
617     TESTCASE( strftime( buffer, 100, "%a ", &timeptr ) == 4 );
618     TESTCASE( strcmp( buffer, "Sun " ) == 0 );
619     TESTCASE( strftime( buffer, 100, "%A ", &timeptr ) == 7 );
620     TESTCASE( strcmp( buffer, "Sunday " ) == 0 );
621     TESTCASE( strftime( buffer, 100, "%b ", &timeptr ) == 4 );
622     TESTCASE( strcmp( buffer, "Oct " ) == 0 );
623     TESTCASE( strftime( buffer, 100, "%h ", &timeptr ) == 4 );
624     TESTCASE( strcmp( buffer, "Oct " ) == 0 );
625     TESTCASE( strftime( buffer, 100, "%B ", &timeptr ) == 8 );
626     TESTCASE( strcmp( buffer, "October " ) == 0 );
627     TESTCASE( strftime( buffer, 100, "%c ", &timeptr ) == 25 );
628     TESTCASE( strcmp( buffer, "Sun Oct  1 12:30:59 1972 " ) == 0 );
629     TESTCASE( strftime( buffer, 100, "%C ", &timeptr ) == 3 );
630     TESTCASE( strcmp( buffer, "19 " ) == 0 );
631     TESTCASE( strftime( buffer, 100, "%d ", &timeptr ) == 3 );
632     TESTCASE( strcmp( buffer, "01 " ) == 0 );
633     TESTCASE( strftime( buffer, 100, "%D ", &timeptr ) == 9 );
634     TESTCASE( strcmp( buffer, "10/01/72 " ) == 0 );
635     TESTCASE( strftime( buffer, 100, "%e ", &timeptr ) == 3 );
636     TESTCASE( strcmp( buffer, " 1 " ) == 0 );
637     TESTCASE( strftime( buffer, 100, "%F ", &timeptr ) == 11 );
638     TESTCASE( strcmp( buffer, "1972-10-01 " ) == 0 );
639     TESTCASE( strftime( buffer, 100, "%H ", &timeptr ) == 3 );
640     TESTCASE( strcmp( buffer, "12 " ) == 0 );
641     TESTCASE( strftime( buffer, 100, "%I ", &timeptr ) == 3 );
642     TESTCASE( strcmp( buffer, "12 " ) == 0 );
643     TESTCASE( strftime( buffer, 100, "%j ", &timeptr ) == 4 );
644     TESTCASE( strcmp( buffer, "275 " ) == 0 );
645     TESTCASE( strftime( buffer, 100, "%m ", &timeptr ) == 3 );
646     TESTCASE( strcmp( buffer, "10 " ) == 0 );
647     TESTCASE( strftime( buffer, 100, "%M ", &timeptr ) == 3 );
648     TESTCASE( strcmp( buffer, "30 " ) == 0 );
649     TESTCASE( strftime( buffer, 100, "%p ", &timeptr ) == 3 );
650     TESTCASE( strcmp( buffer, "PM " ) == 0 );
651     TESTCASE( strftime( buffer, 100, "%r ", &timeptr ) == 12 );
652     TESTCASE( strcmp( buffer, "12:30:59 PM " ) == 0 );
653     TESTCASE( strftime( buffer, 100, "%R ", &timeptr ) == 6 );
654     TESTCASE( strcmp( buffer, "12:30 " ) == 0 );
655     TESTCASE( strftime( buffer, 100, "%S ", &timeptr ) == 3 );
656     TESTCASE( strcmp( buffer, "59 " ) == 0 );
657     TESTCASE( strftime( buffer, 100, "%T ", &timeptr ) == 9 );
658     TESTCASE( strcmp( buffer, "12:30:59 " ) == 0 );
659     TESTCASE( strftime( buffer, 100, "%u ", &timeptr ) == 2 );
660     TESTCASE( strcmp( buffer, "7 " ) == 0 );
661     TESTCASE( strftime( buffer, 100, "%w ", &timeptr ) == 2 );
662     TESTCASE( strcmp( buffer, "0 " ) == 0 );
663     TESTCASE( strftime( buffer, 100, "%x ", &timeptr ) == 9 );
664     TESTCASE( strcmp( buffer, "10/01/72 " ) == 0 );
665     TESTCASE( strftime( buffer, 100, "%X ", &timeptr ) == 9 );
666     TESTCASE( strcmp( buffer, "12:30:59 " ) == 0 );
667     TESTCASE( strftime( buffer, 100, "%y ", &timeptr ) == 3 );
668     TESTCASE( strcmp( buffer, "72 " ) == 0 );
669     TESTCASE( strftime( buffer, 100, "%Y ", &timeptr ) == 5 );
670     TESTCASE( strcmp( buffer, "1972 " ) == 0 );
671     TESTCASE( strftime( buffer, 100, "%% ", &timeptr ) == 2 );
672     TESTCASE( strcmp( buffer, "% " ) == 0 );
673     TESTCASE( strftime( buffer, 100, "%n ", &timeptr ) == 2 );
674     TESTCASE( strcmp( buffer, "\n " ) == 0 );
675     TESTCASE( strftime( buffer, 100, "%t ", &timeptr ) == 2 );
676     TESTCASE( strcmp( buffer, "\t " ) == 0 );
677     /* ISO week calculation */
678     MKTIME( timeptr, 0, 0, 0, 27, 11, 3, 0, 360 );
679     TESTCASE( strftime( buffer, 100, "%V ", &timeptr ) == 3 );
680     TESTCASE( strcmp( buffer, "52 " ) == 0 );
681     MKTIME( timeptr, 0, 0, 0, 28, 11, 3, 1, 361 );
682     TESTCASE( strftime( buffer, 100, "%V ", &timeptr ) == 3 );
683     TESTCASE( strcmp( buffer, "53 " ) == 0 );
684     MKTIME( timeptr, 0, 0, 0, 31, 11, 3, 4, 364 );
685     TESTCASE( strftime( buffer, 100, "%V ", &timeptr ) == 3 );
686     TESTCASE( strcmp( buffer, "53 " ) == 0 );
687     MKTIME( timeptr, 0, 0, 0, 1, 0, 4, 5, 0 );
688     TESTCASE( strftime( buffer, 100, "%V ", &timeptr ) == 3 );
689     TESTCASE( strcmp( buffer, "53 " ) == 0 );
690     MKTIME( timeptr, 0, 0, 0, 3, 0, 4, 0, 2 );
691     TESTCASE( strftime( buffer, 100, "%V ", &timeptr ) == 3 );
692     TESTCASE( strcmp( buffer, "53 " ) == 0 );
693     TESTCASE( strftime( buffer, 100, "%g ", &timeptr ) == 3 );
694     TESTCASE( strcmp( buffer, "03 " ) == 0 );
695     TESTCASE( strftime( buffer, 100, "%G ", &timeptr ) == 5 );
696     TESTCASE( strcmp( buffer, "1903 " ) == 0 );
697     MKTIME( timeptr, 0, 0, 0, 4, 0, 4, 1, 3 );
698     TESTCASE( strftime( buffer, 100, "%V ", &timeptr ) == 3 );
699     TESTCASE( strcmp( buffer, "01 " ) == 0 );
700     TESTCASE( strftime( buffer, 100, "%g ", &timeptr ) == 3 );
701     TESTCASE( strcmp( buffer, "04 " ) == 0 );
702     TESTCASE( strftime( buffer, 100, "%G ", &timeptr ) == 5 );
703     TESTCASE( strcmp( buffer, "1904 " ) == 0 );
704     MKTIME( timeptr, 0, 0, 0, 1, 0, 5, 0, 0 );
705     TESTCASE( strftime( buffer, 100, "%V ", &timeptr ) == 3 );
706     TESTCASE( strcmp( buffer, "52 " ) == 0 );
707     TESTCASE( strftime( buffer, 100, "%g ", &timeptr ) == 3 );
708     TESTCASE( strcmp( buffer, "04 " ) == 0 );
709     TESTCASE( strftime( buffer, 100, "%G ", &timeptr ) == 5 );
710     TESTCASE( strcmp( buffer, "1904 " ) == 0 );
711     MKTIME( timeptr, 0, 0, 0, 24, 11, 100, 0, 358 );
712     TESTCASE( strftime( buffer, 100, "%V ", &timeptr ) == 3 );
713     TESTCASE( strcmp( buffer, "51 " ) == 0 );
714     MKTIME( timeptr, 0, 0, 0, 25, 11, 100, 1, 359 );
715     TESTCASE( strftime( buffer, 100, "%V ", &timeptr ) == 3 );
716     TESTCASE( strcmp( buffer, "52 " ) == 0 );
717     MKTIME( timeptr, 0, 0, 0, 31, 11, 100, 0, 365 );
718     TESTCASE( strftime( buffer, 100, "%V ", &timeptr ) == 3 );
719     TESTCASE( strcmp( buffer, "52 " ) == 0 );
720     TESTCASE( strftime( buffer, 100, "%g ", &timeptr ) == 3 );
721     TESTCASE( strcmp( buffer, "00 " ) == 0 );
722     TESTCASE( strftime( buffer, 100, "%G ", &timeptr ) == 5 );
723     TESTCASE( strcmp( buffer, "2000 " ) == 0 );
724     MKTIME( timeptr, 0, 0, 0, 1, 0, 101, 1, 0 );
725     TESTCASE( strftime( buffer, 100, "%V ", &timeptr ) == 3 );
726     TESTCASE( strcmp( buffer, "01 " ) == 0 );
727     TESTCASE( strftime( buffer, 100, "%g ", &timeptr ) == 3 );
728     TESTCASE( strcmp( buffer, "01 " ) == 0 );
729     TESTCASE( strftime( buffer, 100, "%G ", &timeptr ) == 5 );
730     TESTCASE( strcmp( buffer, "2001 " ) == 0 );
731     MKTIME( timeptr, 0, 0, 0, 7, 0, 101, 7, 6 );
732     TESTCASE( strftime( buffer, 100, "%V ", &timeptr ) == 3 );
733     TESTCASE( strcmp( buffer, "01 " ) == 0 );
734     MKTIME( timeptr, 0, 0, 0, 8, 0, 101, 1, 7 );
735     TESTCASE( strftime( buffer, 100, "%V ", &timeptr ) == 3 );
736     TESTCASE( strcmp( buffer, "02 " ) == 0 );
737     return TEST_RESULTS;
738 }
739
740 #endif