Imported from ../bash-2.05b.tar.gz.
[platform/upstream/bash.git] / lib / sh / strftime.c
1 /*
2  * Modified slightly by Chet Ramey for inclusion in Bash
3  */
4
5 /*
6  * strftime.c
7  *
8  * Public-domain implementation of ISO C library routine.
9  *
10  * If you can't do prototypes, get GCC.
11  *
12  * The C99 standard now specifies just about all of the formats
13  * that were additional in the earlier versions of this file.
14  *
15  * For extensions from SunOS, add SUNOS_EXT.
16  * For extensions from HP/UX, add HPUX_EXT.
17  * For VMS dates, add VMS_EXT.
18  * For complete POSIX semantics, add POSIX_SEMANTICS.
19  *
20  * The code for %c, %x, and %X follows the C99 specification for
21  * the "C" locale.
22  *
23  * This version ignores LOCALE information.
24  * It also doesn't worry about multi-byte characters.
25  * So there.
26  *
27  * This file is also shipped with GAWK (GNU Awk), gawk specific bits of
28  * code are included if GAWK is defined.
29  *
30  * Arnold Robbins
31  * January, February, March, 1991
32  * Updated March, April 1992
33  * Updated April, 1993
34  * Updated February, 1994
35  * Updated May, 1994
36  * Updated January, 1995
37  * Updated September, 1995
38  * Updated January, 1996
39  * Updated July, 1997
40  * Updated October, 1999
41  * Updated September, 2000
42  *
43  * Fixes from ado@elsie.nci.nih.gov,
44  * February 1991, May 1992
45  * Fixes from Tor Lillqvist tml@tik.vtt.fi,
46  * May 1993
47  * Further fixes from ado@elsie.nci.nih.gov,
48  * February 1994
49  * %z code from chip@chinacat.unicom.com,
50  * Applied September 1995
51  * %V code fixed (again) and %G, %g added,
52  * January 1996
53  * %v code fixed, better configuration,
54  * July 1997
55  * Moved to C99 specification.
56  * September 2000
57  */
58 #include <config.h>
59
60 #ifndef GAWK
61 #include <stdio.h>
62 #include <ctype.h>
63 #include <time.h>
64 #endif
65 #if defined(TM_IN_SYS_TIME)
66 #include <sys/types.h>
67 #include <sys/time.h>
68 #endif
69
70 #include <stdlib.h>
71 #include <string.h>
72
73 /* defaults: season to taste */
74 #define SUNOS_EXT       1       /* stuff in SunOS strftime routine */
75 #define VMS_EXT         1       /* include %v for VMS date format */
76 #define HPUX_EXT        1       /* non-conflicting stuff in HP-UX date */
77 #ifndef GAWK
78 #define POSIX_SEMANTICS 1       /* call tzset() if TZ changes */
79 #endif
80
81 #undef strchr   /* avoid AIX weirdness */
82
83 extern void tzset(void);
84 static int weeknumber(const struct tm *timeptr, int firstweekday);
85 static int iso8601wknum(const struct tm *timeptr);
86
87 #ifdef __GNUC__
88 #define inline  __inline__
89 #else
90 #define inline  /**/
91 #endif
92
93 #define range(low, item, hi)    max(low, min(item, hi))
94
95 #if !defined(OS2) && !defined(MSDOS) && defined(HAVE_TZNAME)
96 extern char *tzname[2];
97 extern int daylight;
98 #if defined(SOLARIS) || defined(mips)
99 extern long int timezone, altzone;
100 #else
101 extern int timezone, altzone;
102 #endif
103 #endif
104
105 #undef min      /* just in case */
106
107 /* min --- return minimum of two numbers */
108
109 static inline int
110 min(int a, int b)
111 {
112         return (a < b ? a : b);
113 }
114
115 #undef max      /* also, just in case */
116
117 /* max --- return maximum of two numbers */
118
119 static inline int
120 max(int a, int b)
121 {
122         return (a > b ? a : b);
123 }
124
125 /* strftime --- produce formatted time */
126
127 size_t
128 strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr)
129 {
130         char *endp = s + maxsize;
131         char *start = s;
132         auto char tbuf[100];
133         long off;
134         int i, w, y;
135         static short first = 1;
136 #ifdef POSIX_SEMANTICS
137         static char *savetz = NULL;
138         static int savetzlen = 0;
139         char *tz;
140 #endif /* POSIX_SEMANTICS */
141 #ifndef HAVE_TM_ZONE
142 #ifndef HAVE_TM_NAME
143 #ifndef HAVE_TZNAME
144         extern char *timezone();
145         struct timeval tv;
146         struct timezone zone;
147 #endif /* HAVE_TZNAME */
148 #endif /* HAVE_TM_NAME */
149 #endif /* HAVE_TM_ZONE */
150
151         /* various tables, useful in North America */
152         static const char *days_a[] = {
153                 "Sun", "Mon", "Tue", "Wed",
154                 "Thu", "Fri", "Sat",
155         };
156         static const char *days_l[] = {
157                 "Sunday", "Monday", "Tuesday", "Wednesday",
158                 "Thursday", "Friday", "Saturday",
159         };
160         static const char *months_a[] = {
161                 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
162                 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
163         };
164         static const char *months_l[] = {
165                 "January", "February", "March", "April",
166                 "May", "June", "July", "August", "September",
167                 "October", "November", "December",
168         };
169         static const char *ampm[] = { "AM", "PM", };
170
171         if (s == NULL || format == NULL || timeptr == NULL || maxsize == 0)
172                 return 0;
173
174         /* quick check if we even need to bother */
175         if (strchr(format, '%') == NULL && strlen(format) + 1 >= maxsize)
176                 return 0;
177
178 #ifndef POSIX_SEMANTICS
179         if (first) {
180                 tzset();
181                 first = 0;
182         }
183 #else   /* POSIX_SEMANTICS */
184 #if defined (SHELL)
185         tz = get_string_value ("TZ");
186 #else
187         tz = getenv("TZ");
188 #endif
189         if (first) {
190                 if (tz != NULL) {
191                         int tzlen = strlen(tz);
192
193                         savetz = (char *) malloc(tzlen + 1);
194                         if (savetz != NULL) {
195                                 savetzlen = tzlen + 1;
196                                 strcpy(savetz, tz);
197                         }
198                 }
199                 tzset();
200                 first = 0;
201         }
202         /* if we have a saved TZ, and it is different, recapture and reset */
203         if (tz && savetz && (tz[0] != savetz[0] || strcmp(tz, savetz) != 0)) {
204                 i = strlen(tz) + 1;
205                 if (i > savetzlen) {
206                         savetz = (char *) realloc(savetz, i);
207                         if (savetz) {
208                                 savetzlen = i;
209                                 strcpy(savetz, tz);
210                         }
211                 } else
212                         strcpy(savetz, tz);
213                 tzset();
214         }
215 #endif  /* POSIX_SEMANTICS */
216
217         for (; *format && s < endp - 1; format++) {
218                 tbuf[0] = '\0';
219                 if (*format != '%') {
220                         *s++ = *format;
221                         continue;
222                 }
223         again:
224                 switch (*++format) {
225                 case '\0':
226                         *s++ = '%';
227                         goto out;
228
229                 case '%':
230                         *s++ = '%';
231                         continue;
232
233                 case 'a':       /* abbreviated weekday name */
234                         if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6)
235                                 strcpy(tbuf, "?");
236                         else
237                                 strcpy(tbuf, days_a[timeptr->tm_wday]);
238                         break;
239
240                 case 'A':       /* full weekday name */
241                         if (timeptr->tm_wday < 0 || timeptr->tm_wday > 6)
242                                 strcpy(tbuf, "?");
243                         else
244                                 strcpy(tbuf, days_l[timeptr->tm_wday]);
245                         break;
246
247                 case 'b':       /* abbreviated month name */
248                 short_month:
249                         if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11)
250                                 strcpy(tbuf, "?");
251                         else
252                                 strcpy(tbuf, months_a[timeptr->tm_mon]);
253                         break;
254
255                 case 'B':       /* full month name */
256                         if (timeptr->tm_mon < 0 || timeptr->tm_mon > 11)
257                                 strcpy(tbuf, "?");
258                         else
259                                 strcpy(tbuf, months_l[timeptr->tm_mon]);
260                         break;
261
262                 case 'c':       /* appropriate date and time representation */
263                         /*
264                          * This used to be:
265                          *
266                          * strftime(tbuf, sizeof tbuf, "%a %b %e %H:%M:%S %Y", timeptr);
267                          *
268                          * Now, per the ISO 1999 C standard, it this:
269                          */
270                         strftime(tbuf, sizeof tbuf, "%A %B %d %T %Y", timeptr);
271                         break;
272
273                 case 'C':
274                 century:
275                         sprintf(tbuf, "%02d", (timeptr->tm_year + 1900) / 100);
276                         break;
277
278                 case 'd':       /* day of the month, 01 - 31 */
279                         i = range(1, timeptr->tm_mday, 31);
280                         sprintf(tbuf, "%02d", i);
281                         break;
282
283                 case 'D':       /* date as %m/%d/%y */
284                         strftime(tbuf, sizeof tbuf, "%m/%d/%y", timeptr);
285                         break;
286
287                 case 'e':       /* day of month, blank padded */
288                         sprintf(tbuf, "%2d", range(1, timeptr->tm_mday, 31));
289                         break;
290
291                 case 'E':
292                         /* POSIX (now C99) locale extensions, ignored for now */
293                         goto again;
294
295                 case 'F':       /* ISO 8601 date representation */
296                         strftime(tbuf, sizeof tbuf, "%Y-%m-%d", timeptr);
297                         break;
298
299                 case 'g':
300                 case 'G':
301                         /*
302                          * Year of ISO week.
303                          *
304                          * If it's December but the ISO week number is one,
305                          * that week is in next year.
306                          * If it's January but the ISO week number is 52 or
307                          * 53, that week is in last year.
308                          * Otherwise, it's this year.
309                          */
310                         w = iso8601wknum(timeptr);
311                         if (timeptr->tm_mon == 11 && w == 1)
312                                 y = 1900 + timeptr->tm_year + 1;
313                         else if (timeptr->tm_mon == 0 && w >= 52)
314                                 y = 1900 + timeptr->tm_year - 1;
315                         else
316                                 y = 1900 + timeptr->tm_year;
317
318                         if (*format == 'G')
319                                 sprintf(tbuf, "%d", y);
320                         else
321                                 sprintf(tbuf, "%02d", y % 100);
322                         break;
323
324                 case 'h':       /* abbreviated month name */
325                         goto short_month;
326
327                 case 'H':       /* hour, 24-hour clock, 00 - 23 */
328                         i = range(0, timeptr->tm_hour, 23);
329                         sprintf(tbuf, "%02d", i);
330                         break;
331
332                 case 'I':       /* hour, 12-hour clock, 01 - 12 */
333                         i = range(0, timeptr->tm_hour, 23);
334                         if (i == 0)
335                                 i = 12;
336                         else if (i > 12)
337                                 i -= 12;
338                         sprintf(tbuf, "%02d", i);
339                         break;
340
341                 case 'j':       /* day of the year, 001 - 366 */
342                         sprintf(tbuf, "%03d", timeptr->tm_yday + 1);
343                         break;
344
345                 case 'm':       /* month, 01 - 12 */
346                         i = range(0, timeptr->tm_mon, 11);
347                         sprintf(tbuf, "%02d", i + 1);
348                         break;
349
350                 case 'M':       /* minute, 00 - 59 */
351                         i = range(0, timeptr->tm_min, 59);
352                         sprintf(tbuf, "%02d", i);
353                         break;
354
355                 case 'n':       /* same as \n */
356                         tbuf[0] = '\n';
357                         tbuf[1] = '\0';
358                         break;
359
360                 case 'O':
361                         /* POSIX (now C99) locale extensions, ignored for now */
362                         goto again;
363
364                 case 'p':       /* am or pm based on 12-hour clock */
365                         i = range(0, timeptr->tm_hour, 23);
366                         if (i < 12)
367                                 strcpy(tbuf, ampm[0]);
368                         else
369                                 strcpy(tbuf, ampm[1]);
370                         break;
371
372                 case 'r':       /* time as %I:%M:%S %p */
373                         strftime(tbuf, sizeof tbuf, "%I:%M:%S %p", timeptr);
374                         break;
375
376                 case 'R':       /* time as %H:%M */
377                         strftime(tbuf, sizeof tbuf, "%H:%M", timeptr);
378                         break;
379
380 #if defined(HAVE_MKTIME) || defined(GAWK)
381                 case 's':       /* time as seconds since the Epoch */
382                 {
383                         struct tm non_const_timeptr;
384
385                         non_const_timeptr = *timeptr;
386                         sprintf(tbuf, "%ld", mktime(& non_const_timeptr));
387                         break;
388                 }
389 #endif /* defined(HAVE_MKTIME) || defined(GAWK) */
390
391                 case 'S':       /* second, 00 - 60 */
392                         i = range(0, timeptr->tm_sec, 60);
393                         sprintf(tbuf, "%02d", i);
394                         break;
395
396                 case 't':       /* same as \t */
397                         tbuf[0] = '\t';
398                         tbuf[1] = '\0';
399                         break;
400
401                 case 'T':       /* time as %H:%M:%S */
402                 the_time:
403                         strftime(tbuf, sizeof tbuf, "%H:%M:%S", timeptr);
404                         break;
405
406                 case 'u':
407                 /* ISO 8601: Weekday as a decimal number [1 (Monday) - 7] */
408                         sprintf(tbuf, "%d", timeptr->tm_wday == 0 ? 7 :
409                                         timeptr->tm_wday);
410                         break;
411
412                 case 'U':       /* week of year, Sunday is first day of week */
413                         sprintf(tbuf, "%02d", weeknumber(timeptr, 0));
414                         break;
415
416                 case 'V':       /* week of year according ISO 8601 */
417                         sprintf(tbuf, "%02d", iso8601wknum(timeptr));
418                         break;
419
420                 case 'w':       /* weekday, Sunday == 0, 0 - 6 */
421                         i = range(0, timeptr->tm_wday, 6);
422                         sprintf(tbuf, "%d", i);
423                         break;
424
425                 case 'W':       /* week of year, Monday is first day of week */
426                         sprintf(tbuf, "%02d", weeknumber(timeptr, 1));
427                         break;
428
429                 case 'x':       /* appropriate date representation */
430                         strftime(tbuf, sizeof tbuf, "%A %B %d %Y", timeptr);
431                         break;
432
433                 case 'X':       /* appropriate time representation */
434                         goto the_time;
435                         break;
436
437                 case 'y':       /* year without a century, 00 - 99 */
438                 year:
439                         i = timeptr->tm_year % 100;
440                         sprintf(tbuf, "%02d", i);
441                         break;
442
443                 case 'Y':       /* year with century */
444                 fullyear:
445                         sprintf(tbuf, "%d", 1900 + timeptr->tm_year);
446                         break;
447
448                 /*
449                  * From: Chip Rosenthal <chip@chinacat.unicom.com>
450                  * Date: Sun, 19 Mar 1995 00:33:29 -0600 (CST)
451                  * 
452                  * Warning: the %z [code] is implemented by inspecting the
453                  * timezone name conditional compile settings, and
454                  * inferring a method to get timezone offsets. I've tried
455                  * this code on a couple of machines, but I don't doubt
456                  * there is some system out there that won't like it.
457                  * Maybe the easiest thing to do would be to bracket this
458                  * with an #ifdef that can turn it off. The %z feature
459                  * would be an admittedly obscure one that most folks can
460                  * live without, but it would be a great help to those of
461                  * us that muck around with various message processors.
462                  */
463                 case 'z':       /* time zone offset east of GMT e.g. -0600 */
464 #ifdef HAVE_TM_NAME
465                         /*
466                          * Systems with tm_name probably have tm_tzadj as
467                          * secs west of GMT.  Convert to mins east of GMT.
468                          */
469                         off = -timeptr->tm_tzadj / 60;
470 #else /* !HAVE_TM_NAME */
471 #ifdef HAVE_TM_ZONE
472                         /*
473                          * Systems with tm_zone probably have tm_gmtoff as
474                          * secs east of GMT.  Convert to mins east of GMT.
475                          */
476                         off = timeptr->tm_gmtoff / 60;
477 #else /* !HAVE_TM_ZONE */
478 #if HAVE_TZNAME
479                         /*
480                          * Systems with tzname[] probably have timezone as
481                          * secs west of GMT.  Convert to mins east of GMT.
482                          */
483                         off = -(daylight ? timezone : altzone) / 60;
484 #else /* !HAVE_TZNAME */
485                         off = -zone.tz_minuteswest;
486 #endif /* !HAVE_TZNAME */
487 #endif /* !HAVE_TM_ZONE */
488 #endif /* !HAVE_TM_NAME */
489                         if (off < 0) {
490                                 tbuf[0] = '-';
491                                 off = -off;
492                         } else {
493                                 tbuf[0] = '+';
494                         }
495                         sprintf(tbuf+1, "%02d%02d", off/60, off%60);
496                         break;
497
498                 case 'Z':       /* time zone name or abbrevation */
499 #ifdef HAVE_TZNAME
500                         i = (daylight && timeptr->tm_isdst > 0); /* 0 or 1 */
501                         strcpy(tbuf, tzname[i]);
502 #else
503 #ifdef HAVE_TM_ZONE
504                         strcpy(tbuf, timeptr->tm_zone);
505 #else
506 #ifdef HAVE_TM_NAME
507                         strcpy(tbuf, timeptr->tm_name);
508 #else
509                         gettimeofday(& tv, & zone);
510                         strcpy(tbuf, timezone(zone.tz_minuteswest,
511                                                 timeptr->tm_isdst > 0));
512 #endif /* HAVE_TM_NAME */
513 #endif /* HAVE_TM_ZONE */
514 #endif /* HAVE_TZNAME */
515                         break;
516
517 #ifdef SUNOS_EXT
518                 case 'k':       /* hour, 24-hour clock, blank pad */
519                         sprintf(tbuf, "%2d", range(0, timeptr->tm_hour, 23));
520                         break;
521
522                 case 'l':       /* hour, 12-hour clock, 1 - 12, blank pad */
523                         i = range(0, timeptr->tm_hour, 23);
524                         if (i == 0)
525                                 i = 12;
526                         else if (i > 12)
527                                 i -= 12;
528                         sprintf(tbuf, "%2d", i);
529                         break;
530 #endif
531
532 #ifdef HPUX_EXT
533                 case 'N':       /* Emperor/Era name */
534                         /* this is essentially the same as the century */
535                         goto century;   /* %C */
536
537                 case 'o':       /* Emperor/Era year */
538                         goto year;      /* %y */
539 #endif /* HPUX_EXT */
540
541
542 #ifdef VMS_EXT
543                 case 'v':       /* date as dd-bbb-YYYY */
544                         sprintf(tbuf, "%2d-%3.3s-%4d",
545                                 range(1, timeptr->tm_mday, 31),
546                                 months_a[range(0, timeptr->tm_mon, 11)],
547                                 timeptr->tm_year + 1900);
548                         for (i = 3; i < 6; i++)
549                                 if (islower(tbuf[i]))
550                                         tbuf[i] = toupper(tbuf[i]);
551                         break;
552 #endif
553
554                 default:
555                         tbuf[0] = '%';
556                         tbuf[1] = *format;
557                         tbuf[2] = '\0';
558                         break;
559                 }
560                 i = strlen(tbuf);
561                 if (i) {
562                         if (s + i < endp - 1) {
563                                 strcpy(s, tbuf);
564                                 s += i;
565                         } else
566                                 return 0;
567                 }
568         }
569 out:
570         if (s < endp && *format == '\0') {
571                 *s = '\0';
572                 return (s - start);
573         } else
574                 return 0;
575 }
576
577 /* isleap --- is a year a leap year? */
578
579 static int
580 isleap(int year)
581 {
582         return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
583 }
584
585
586 /* iso8601wknum --- compute week number according to ISO 8601 */
587
588 static int
589 iso8601wknum(const struct tm *timeptr)
590 {
591         /*
592          * From 1003.2:
593          *      If the week (Monday to Sunday) containing January 1
594          *      has four or more days in the new year, then it is week 1;
595          *      otherwise it is the highest numbered week of the previous
596          *      year (52 or 53), and the next week is week 1.
597          *
598          * ADR: This means if Jan 1 was Monday through Thursday,
599          *      it was week 1, otherwise week 52 or 53.
600          *
601          * XPG4 erroneously included POSIX.2 rationale text in the
602          * main body of the standard. Thus it requires week 53.
603          */
604
605         int weeknum, jan1day, diff;
606
607         /* get week number, Monday as first day of the week */
608         weeknum = weeknumber(timeptr, 1);
609
610         /*
611          * With thanks and tip of the hatlo to tml@tik.vtt.fi
612          *
613          * What day of the week does January 1 fall on?
614          * We know that
615          *      (timeptr->tm_yday - jan1.tm_yday) MOD 7 ==
616          *              (timeptr->tm_wday - jan1.tm_wday) MOD 7
617          * and that
618          *      jan1.tm_yday == 0
619          * and that
620          *      timeptr->tm_wday MOD 7 == timeptr->tm_wday
621          * from which it follows that. . .
622          */
623         jan1day = timeptr->tm_wday - (timeptr->tm_yday % 7);
624         if (jan1day < 0)
625                 jan1day += 7;
626
627         /*
628          * If Jan 1 was a Monday through Thursday, it was in
629          * week 1.  Otherwise it was last year's highest week, which is
630          * this year's week 0.
631          *
632          * What does that mean?
633          * If Jan 1 was Monday, the week number is exactly right, it can
634          *      never be 0.
635          * If it was Tuesday through Thursday, the weeknumber is one
636          *      less than it should be, so we add one.
637          * Otherwise, Friday, Saturday or Sunday, the week number is
638          * OK, but if it is 0, it needs to be 52 or 53.
639          */
640         switch (jan1day) {
641         case 1:         /* Monday */
642                 break;
643         case 2:         /* Tuesday */
644         case 3:         /* Wednesday */
645         case 4:         /* Thursday */
646                 weeknum++;
647                 break;
648         case 5:         /* Friday */
649         case 6:         /* Saturday */
650         case 0:         /* Sunday */
651                 if (weeknum == 0) {
652 #ifdef USE_BROKEN_XPG4
653                         /* XPG4 (as of March 1994) says 53 unconditionally */
654                         weeknum = 53;
655 #else
656                         /* get week number of last week of last year */
657                         struct tm dec31ly;      /* 12/31 last year */
658                         dec31ly = *timeptr;
659                         dec31ly.tm_year--;
660                         dec31ly.tm_mon = 11;
661                         dec31ly.tm_mday = 31;
662                         dec31ly.tm_wday = (jan1day == 0) ? 6 : jan1day - 1;
663                         dec31ly.tm_yday = 364 + isleap(dec31ly.tm_year + 1900);
664                         weeknum = iso8601wknum(& dec31ly);
665 #endif
666                 }
667                 break;
668         }
669
670         if (timeptr->tm_mon == 11) {
671                 /*
672                  * The last week of the year
673                  * can be in week 1 of next year.
674                  * Sigh.
675                  *
676                  * This can only happen if
677                  *      M   T  W
678                  *      29  30 31
679                  *      30  31
680                  *      31
681                  */
682                 int wday, mday;
683
684                 wday = timeptr->tm_wday;
685                 mday = timeptr->tm_mday;
686                 if (   (wday == 1 && (mday >= 29 && mday <= 31))
687                     || (wday == 2 && (mday == 30 || mday == 31))
688                     || (wday == 3 &&  mday == 31))
689                         weeknum = 1;
690         }
691
692         return weeknum;
693 }
694
695 /* weeknumber --- figure how many weeks into the year */
696
697 /* With thanks and tip of the hatlo to ado@elsie.nci.nih.gov */
698
699 static int
700 weeknumber(const struct tm *timeptr, int firstweekday)
701 {
702         int wday = timeptr->tm_wday;
703         int ret;
704
705         if (firstweekday == 1) {
706                 if (wday == 0)  /* sunday */
707                         wday = 6;
708                 else
709                         wday--;
710         }
711         ret = ((timeptr->tm_yday + 7 - wday) / 7);
712         if (ret < 0)
713                 ret = 0;
714         return ret;
715 }
716
717 #if 0
718 /* ADR --- I'm loathe to mess with ado's code ... */
719
720 Date:         Wed, 24 Apr 91 20:54:08 MDT
721 From: Michal Jaegermann <audfax!emory!vm.ucs.UAlberta.CA!NTOMCZAK>
722 To: arnold@audiofax.com
723
724 Hi Arnold,
725 in a process of fixing of strftime() in libraries on Atari ST I grabbed
726 some pieces of code from your own strftime.  When doing that it came
727 to mind that your weeknumber() function compiles a little bit nicer
728 in the following form:
729 /*
730  * firstweekday is 0 if starting in Sunday, non-zero if in Monday
731  */
732 {
733     return (timeptr->tm_yday - timeptr->tm_wday +
734             (firstweekday ? (timeptr->tm_wday ? 8 : 1) : 7)) / 7;
735 }
736 How nicer it depends on a compiler, of course, but always a tiny bit.
737
738    Cheers,
739    Michal
740    ntomczak@vm.ucs.ualberta.ca
741 #endif
742
743 #ifdef  TEST_STRFTIME
744
745 /*
746  * NAME:
747  *      tst
748  *
749  * SYNOPSIS:
750  *      tst
751  *
752  * DESCRIPTION:
753  *      "tst" is a test driver for the function "strftime".
754  *
755  * OPTIONS:
756  *      None.
757  *
758  * AUTHOR:
759  *      Karl Vogel
760  *      Control Data Systems, Inc.
761  *      vogelke@c-17igp.wpafb.af.mil
762  *
763  * BUGS:
764  *      None noticed yet.
765  *
766  * COMPILE:
767  *      cc -o tst -DTEST_STRFTIME strftime.c
768  */
769
770 /* ADR: I reformatted this to my liking, and deleted some unneeded code. */
771
772 #ifndef NULL
773 #include        <stdio.h>
774 #endif
775 #include        <sys/time.h>
776 #include        <string.h>
777
778 #define         MAXTIME         132
779
780 /*
781  * Array of time formats.
782  */
783
784 static char *array[] =
785 {
786         "(%%A)      full weekday name, var length (Sunday..Saturday)  %A",
787         "(%%B)       full month name, var length (January..December)  %B",
788         "(%%C)                                               Century  %C",
789         "(%%D)                                       date (%%m/%%d/%%y)  %D",
790         "(%%E)                           Locale extensions (ignored)  %E",
791         "(%%F)       full month name, var length (January..December)  %F",
792         "(%%H)                          hour (24-hour clock, 00..23)  %H",
793         "(%%I)                          hour (12-hour clock, 01..12)  %I",
794         "(%%M)                                       minute (00..59)  %M",
795         "(%%N)                                      Emporer/Era Name  %N",
796         "(%%O)                           Locale extensions (ignored)  %O",
797         "(%%R)                                 time, 24-hour (%%H:%%M)  %R",
798         "(%%S)                                       second (00..60)  %S",
799         "(%%T)                              time, 24-hour (%%H:%%M:%%S)  %T",
800         "(%%U)    week of year, Sunday as first day of week (00..53)  %U",
801         "(%%V)                    week of year according to ISO 8601  %V",
802         "(%%W)    week of year, Monday as first day of week (00..53)  %W",
803         "(%%X)     appropriate locale time representation (%H:%M:%S)  %X",
804         "(%%Y)                           year with century (1970...)  %Y",
805         "(%%Z) timezone (EDT), or blank if timezone not determinable  %Z",
806         "(%%a)          locale's abbreviated weekday name (Sun..Sat)  %a",
807         "(%%b)            locale's abbreviated month name (Jan..Dec)  %b",
808         "(%%c)           full date (Sat Nov  4 12:02:33 1989)%n%t%t%t  %c",
809         "(%%d)                             day of the month (01..31)  %d",
810         "(%%e)               day of the month, blank-padded ( 1..31)  %e",
811         "(%%h)                                should be same as (%%b)  %h",
812         "(%%j)                            day of the year (001..366)  %j",
813         "(%%k)               hour, 24-hour clock, blank pad ( 0..23)  %k",
814         "(%%l)               hour, 12-hour clock, blank pad ( 0..12)  %l",
815         "(%%m)                                        month (01..12)  %m",
816         "(%%o)                                      Emporer/Era Year  %o",
817         "(%%p)              locale's AM or PM based on 12-hour clock  %p",
818         "(%%r)                   time, 12-hour (same as %%I:%%M:%%S %%p)  %r",
819         "(%%u) ISO 8601: Weekday as decimal number [1 (Monday) - 7]   %u",
820         "(%%v)                                VMS date (dd-bbb-YYYY)  %v",
821         "(%%w)                       day of week (0..6, Sunday == 0)  %w",
822         "(%%x)                appropriate locale date representation  %x",
823         "(%%y)                      last two digits of year (00..99)  %y",
824         "(%%z)      timezone offset east of GMT as HHMM (e.g. -0500)  %z",
825         (char *) NULL
826 };
827
828 /* main routine. */
829
830 int
831 main(argc, argv)
832 int argc;
833 char **argv;
834 {
835         long time();
836
837         char *next;
838         char string[MAXTIME];
839
840         int k;
841         int length;
842
843         struct tm *tm;
844
845         long clock;
846
847         /* Call the function. */
848
849         clock = time((long *) 0);
850         tm = localtime(&clock);
851
852         for (k = 0; next = array[k]; k++) {
853                 length = strftime(string, MAXTIME, next, tm);
854                 printf("%s\n", string);
855         }
856
857         exit(0);
858 }
859 #endif  /* TEST_STRFTIME */