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