home *** CD-ROM | disk | FTP | other *** search
/ ftp.freefriends.org / ftp.freefriends.org.tar / ftp.freefriends.org / arnold / Source / strftime-8.0.shar.gz / strftime-8.0.shar / strftime.c < prev   
C/C++ Source or Header  |  2001-07-04  |  21KB  |  851 lines

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