tzcode96b.tar.gz and ISO8601

Lawrence Kirby fred at genesis.demon.co.uk
Mon Jan 15 15:10:41 UTC 1996


Hello! I've recently been 'dabbling' in dates and in particular ISO8601
week numbers so I was interested in tzcode96b.tar.gz, in particular the
strftime.c file. First however I'll just point out something minor in
tzfile.h which is:

#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)

This is not the best way to test for a leap year since if the year is not
divisible by 4 we know it is not divisible by 400 (but it tests for this
anyway). The more usual form is:

#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))


My main interest is in the following extract from the code for the 'V'
(amongst others) conversion specifier:


#ifdef XPG4_1994_04_09
                    if (w == 52 && t->tm_mon == TM_JANUARY)
                        w = 53;
#endif /* defined XPG4_1994_04_09 */


Is it true that XPG4 has a slightly different definition of week number
than ISO8601? What this appears to be saying is that if week 52 crosses
into the new year the days in January become week 53 (but if week 53 crosses
the year boundary it stays as week 53).

The following is the function I wrote to calculate ISO8601 week numbers. I've
added an 'XPG4' option in what I believe to be an equivalent way (this means
that points 2 and 6 of the 'rules' aren't quite correct for XPG4). Since I'm
submitting something similar for inclusion in the free 'Snippets' library any
thoughts would be welcome.


/******************************************************************************
 * weeknum_ISO8601       January 1996       L.Kirby
 *
 * Released to the Public Domain
 *
 * Calculates the week number of a day in the year based on the ISO8601 week
 * numbering scheme. The arguments are:
 *
 * t - a pointer to a struct tm in which the following members must be set and
 *     normalised to the standard ranges specified (as standard library
 *     functions gmtime, localtime and mktime should do):
 *     
 *     tm_wday - The day of the week of the day in question:
 *               0-Sunday -> 6-Saturday
 *
 *     tm_yday - The day of the year of the day in question: 0 -> 365
 *               January 1st is day 0.
 *
 *     tm_year - The year since 1900.
 *
 * firstdow - This defines the day of the week on which a week starts:
 *            0-Sunday -> 6-Saturday. For normal ISO8601 weeks that start on
 *            a Monday this should be 1.
 *
 ******************************************************************************
 *
 * The week number is a value between 1 and 53 inclusive defined according to
 * the following rules:
 *
 * 1. The Gregorian calendar is assumed.
 *
 * 2. There are always 7 consecutive days with the same week number.
 *
 * 3. January 4th is defined to be in week 1. Equivalently week 1 is the
 *    first week of a year which has at least 4 days in that year.
 *
 * 4. firstdow defines the day of the week which starts a new week i.e. has
 *    a different week number from the previous day.
 *
 * 5. Week numbers increase in sequence from week 1 until the week that is
 *    defined to be week 1 of the following year.
 *
 * It follows that:
 *
 * 6. Up to January 3rd may be in either week 1 of the current year or in
 *    weeks 52 or 53 of the previous year.
 *
 * 7. 29th December onwards may be in either weeks 52 or 53 of the current
 *    year or week 1 of the following year.
 *
 ******************************************************************************
 */

#include <time.h>

#define isleap(year) (!((year) % 4) && (((year) % 100) || !((year) % 400)))

int weeknum_ISO8601(const struct tm *t, int firstdow)

{
    const int tmp1 = firstdow - t->tm_wday;
    const int tmp2 = t->tm_yday + ((tmp1 > 0) ? 3 : 10) + tmp1;
    const int fourthdaynum = tmp2 % 7;
    int     week = tmp2 / 7;

    if (week == 0) {
#ifdef XPG4_1994_04_09
        week = 53;
#else
        const int year = t->tm_year + (1900 % 400) - 1;

        week = (fourthdaynum + isleap(year) >= 6) ? 53 : 52;
#endif
    } else if (week == 53) {
        const int year = t->tm_year + (1900 % 400);

        if (fourthdaynum > isleap(year))
            week = 1;
    }

   return week;
}

-- 
-----------------------------------------
Lawrence Kirby | fred at genesis.demon.co.uk
Wilts, England | 70734.126 at compuserve.com
-----------------------------------------



More information about the tz mailing list