ISO C 200X Proposal:
Calendar Date Functions


By David R. Tribble
david@tribble.com

Revision 2.2, 2006-06-12

 
 Contents

 
 Cover Sheet


  Document Number: WG14 N___/X3J11 __-___ C200X Revision Proposal ======================= Title: Calendar Date Functions Author: David R. Tribble Author Affiliation: Self Postal Address: Plano, TX USA E-mail Address: david@tribble.com Web URL: http://david.tribble.com Telephone Number: *************** Sponsor: ________________________________________ Revision: 2.2, 2006-06-12 Supersedes: 2.1, 2006-03-12 Proposal Category: __ Editorial change/non-normative contribution __ Correction X_ New feature __ Addition to obsolescent feature list __ Addition to Future Directions __ Other (please specify) _____________________________ Area of Standard Affected: __ Environment __ Language __ Preprocessor X_ Library X_ Macro/typedef/tag name X_ Function X_ Header __ Other (please specify) _____________________________   Prior Art: The Java standard library contains 'Calendar' and 'TimeZone' classes, which are integerated with its 'Date' class to handle various calendric systems. Target Audience: C/C++ programmers and implementers. Related Documents (if any): None Proposal Attached: X_ Yes __ No, but what's your interest? Abstract: Enhancements to the existing 'struct tm' capabilities. The addition of new constants, types, and functions in a new <stdtime.h> header for calendar computations, intended to augment the existing capabilities of the 'tm' structure and its related functions. Date conversions are also brought closer in line with the rules of ISO 8601.  

 
 1. Introduction
 

Our units of temporal measurement, from seconds on up to months, are so complicated, asymmetrical and disjunctive so as to make coherent mental reckoning in time all but impossible. Indeed, had some tyrannical god contrived to enslave our minds to time, to make it all but impossible for us to escape subjection to sodden routines and unpleasant surprises, he could hardly have done better than handing down our present system. It is like a set of trapezoidal building blocks, with no vertical or horizontal surfaces, like a language in which the simplest thought demands ornate constructions, useless particles and lengthy circumlocutions. Unlike the more successful patterns of language and science, which enable us to face experience boldly or at least level-headedly, our system of temporal calculation silently and persistently encourages our terror of time.

It is as though architects had to measure length in feet, width in meters and height in ells; as though basic instruction manuals demanded a knowledge of five different languages. It is no wonder then that we often look into our own immediate past or future, last Tuesday or a week from Sunday, with feelings of helpless confusion.

— Robert Grudin, Time and the Art of Living, 1982

This proposal presents enhancements to the standard C library affecting the time and date handling functions. The standard addressed is ISO/IEC 9899:1999 [1].


1.1 Concepts

System time
This is the implementation's notion of the current date and time during program execution. In the standard library, this is embodied by the time_t type.

Typically, this type represents an interval of time from a specific predefined date (sometimes known as the system epoch). For example, POSIX systems implement the time_t type as a signed integer count of the number of seconds since {1970-01-01 00:00:00 Z}. Other systems encode the date and time as bitfields within time_t values, and still other systems define time_t as a floating-point type. The current (C99) definition of time_t establishes no constraints other than it must be an arithmetic type.

Calendric system
This is the set of rules that specify how a given time value (of type time_t) is converted into a set of values representing the year, month, day, hour, etc., of the calendar. The most common calendric system in use is the Gregorian calendar, which was first established in 1582 by an edict from Pope Gregory XIII in several Catholic European countries, and by the end of the 20th century had been adopted by almost all of the countries of the world.

Timezone
The world is divided into timezones in order to deal with the fact that the Earth is round, such that 12:00 noon in London is not simultaneously noon in New York. Every country has rules governing its use of timezones. Most regions use an integer number of hours offset from the UTC timezone as the basis of their timezone calculations, but a few regions use offsets with fractional hours.

Daylight Saving Time (DST)
Some countries employ a set of timekeeping rules whereby the clock is advanced ahead a specific amount of time (typically one hour) during the Spring (usually in April) and back again in the Fall (usually October) as a way of increasing the number of working daylight hours during the summer months. These rules thus make it possible for times within a certain interval (typically between 02:00:00 and 03:00:00) to occur not at all on one particular night of the year (when DST takes effect) and to occur twice on another particular night of the year (when DST reverts back to normal).

Leap second
A leap second is a second of time added or subtracted from civil time to adjust for the rotation of the Earth. Some implementations are capable of keeping track of leap seconds, and some are not. UT1 (Universal Time 1) time is based on the rotation of the Earth. TAI (International Atomic Time) time is independent of the rotation of the Earth and is maintained worldwide by atomic clocks. UTC (Universal Civil Time) time is based on TAI atomic time. Periodically as needed, an additional leap second is added to UTC time to align it with UT1 time, so that the two never differ by more than 0.9 seconds. This adjustment is necessary to account for the fact that the Earth rotates about two milliseconds slower per day than calendars allow for, which adds an additional second every 500 days or so (although this drift rate is not constant and appears to be decreasing). (Currently, 23 leap seconds have been added to UTC time since the practice began in 1972, so that the difference between UTC and TAI is now 33 seconds.) There have been discussions about discontinuing the practice of leap seconds, at least in regards to UTC time, and even possibly using leap hours instead of seconds, but the debate has not been settled.

Printable representation
Calendar dates and times should be convertible into printable character (string) representations. Functions should exist to convert calendar dates into printable strings and vice versa. (The ISO 8601 specifies a particular standard character representation for dates, times, and durations, but it is not the only accepted representation. See reference [3].)

Standards
The ISO 8601 international standard defines character representations for dates, times, and durations. It also defines the numbering of weeks within years, the numbering of days within weeks, and the specification of timezone offsets. (See reference [3].)

1.2 Existing Deficiencies

There are several deficiencies and limitations with the existing (C99) standard library time and date functions, either because of missing functionality or because some operations are difficult (or impossible) to achieve.

System time limitations

Calendar limitations

Timezone limitations

Daylight Saving Time (DST) limitations

 
 2. Solutions
 

The calendar is intolerable to all wisdom, the horror of all astronomy, and a laughing-stock from a mathematician's point of view.

— Roger Bacon, 1267

The types and functions described in this proposal solve many of the problems mentioned in the previous section.

 
 3. Library Header

A new standard library header is to be added, named <stdtime.h>. This header contains the constants, types, and functions described in this proposal.

[Note]
A newly invented standard header file is proposed in order to minimize the impact that these new names will have on existing code.

The choice of the name of the new header reflects its contents, namely, standard functions dealing with time.

It is recommended that the <stdtime.h> header also contain the entire contents (constant, type, variable, and function definitions) of the existing <time.h> standard library header.

[Note]
While this creates a duplicate source for the existing names in the <time.h> header, it makes it easier to transition to the new header. This implies that the <time.h> header may be deprecated in some future version of ISO C.

 
 4. Constants
 

Our obsession with time is itself timeless. After self-awareness, it may be our most distinctive trait as a species, since undoubtedly one of the first things we become self-aware about is our own mortality - the fact that we live and die in a set amount of time.

— David Ewing Duncan, The Calendar, 1998

This section describes constants that are defined in the <stdtime.h> header file of every conforming implementation.

Unless specifically stated otherwise, these constants do not necessarily have to be implemented as preprocessor macros.


4.1 Constant _CAL_ERA_COMMON

Synopsis

    #include <stdtime.h>

    #define _CAL_ERA_COMMON  integer-expression

Description

This is an integer constant representing the AD (CE) era in the Gregorian calendric system.

The interpretation of this member for other calendric systems is implementation-defined.

See also

struct calendar, calendar.cal_era.

4.2 Constant _CAL_NAME_GREGORIAN

Synopsis

    #include <stdtime.h>

    #define _CAL_NAME_GREGORIAN  "Gregorian"

Description

This is a string constant equal to the string "Gregorian". It specifies the name of the standard Gregorian calendric system, which must be supported by all conforming implementations when passed as the name argument to the initcalendar() function.

An implementation is free to provide other constants with names of the form '_CAL_NAME_XXX', representing additional calendric systems supported by the implementation.

See also

struct calendar, struct calendarinfo, calendar.cal_name, calendarinfo.ci_name, initcalendar().

4.3 Constant _CAL_TYPE_GREGORIAN

Synopsis

    #include <stdtime.h>

    #define _CAL_TYPE_GREGORIAN  integer-expression

Description

This is an integer constant which specifies the standard Gregorian calendric system supported by the implementation, and can be passed as the type argument to the getcalendarinfo() function.

An implementation is free to provide other constants with names of the form '_CAL_TYPE_XXX', representing additional calendric systems supported by the implementation.

See also

struct calendar, struct calendarinfo, calendar.cal_type, calendarinfo.ci_type, getcalendarinfo().

4.4 Constant _CAL_YR_ERROR

Synopsis

    #include <stdtime.h>

    #define _CAL_YR_ERROR  integer-expression

Description

This is an integer constant that, when assigned to the cal_year member of a calendar date object, indicates an erroneous (invalid) date.

The arithmetic value of this constant must not compare equal to any valid year number of any calendric system supported by the implementation.

See also

struct calendar, calendar.cal_year.

 
 5. Types
 

Consider the geometry of how we measure time. It can be divided into circle time and square time: clock time and calendar time.

— David Ewing Duncan, The Calendar, 1998

The following types are defined in the <stdtime.h> standard header file:

    typedef longtime_t
    typedef time_t

    struct calendar
    struct calendarinfo
    struct timezone

Each type is described in detail below.


5.1 Typedef longtime_t

Synopsis

    #include <stdtime.h>

    typedef integer-type  longtime_t;

Description

This type represents a long system time as reckoned by the implementation during program execution.

The contents of this type are discussed in a separate proposal (see Proposal [P2] for more details.)

See also

time_t.

5.2 Structure calendar

Synopsis

    #include <stdtime.h>

    struct calendar;

Description

This structure represents a componentized date and time value according to a specific calendric system. It is also used to represent the difference between two calendar dates.

[Notes]
This structure is an extension of the existing tm  structure, with enhancements for better handling of timezones, multiple calendric systems, and subsecond time resolution.

The calendar structure contains the following members, in any order. These members together represent a date and time within a particular calendric system. The semantics of the members and their normal ranges are expressed in the comments, and each member is described in further detail in the sections below. The value ranges shown for each member are the minimum conforming ranges for the calendar type named "Gregorian", but do not necessarily apply to other calendar types.

    int             cal_type;       // Calendar type
    int             cal_era;        // Era number
    int             cal_year;       // Year number [1900,2399]
    int             cal_mon;        // Month of the year [1,12]
    int             cal_week;       // Week of the year [1,53]
    int             cal_mday;       // Day of the month [1,31]
    int             cal_yday;       // Day of the year [1,366]
    int             cal_wday;       // Day of the week [1,7]
    int             cal_hour;       // Hour of the day [0,23]
    int             cal_min;        // Minute of the hour [0,59]
    int             cal_sec;        // Second of the minute [0,59]
    long int        cal_nsec;       // Nanosecond [0,999999999]
    int             cal_dsti;       // Daylight Saving Time index
    int             cal_leapsec;    // Accumulated leap seconds
    const struct timezone *
                    cal_zone;       // Timezone and DST applied

The calendar structure also contains the following members, in any order. These members, together with the members defined above, represent a delta time, i.e., a componentized difference between two given date and time values with respect to a particular calendric system.

    long int        cal_nmons;      // Total months difference
    long int        cal_nweeks;     // Total weeks difference
    long int        cal_ndays;      // Total days difference

Additional implementation-defined members may exist within this structure (but any use of them is not portable). (For example, there may exist members for specifying holidays, the effects of intercalary months, religious holidays, phases of the moon, etc.) Such members must not be pointers to non-const objects.

[Notes]
Since calendar structure objects may be allocated by the programmer by any means at his disposal, all calendric systems must use the same calendar structure type. This implies that any additional members required to implement the rules for any particular calendric system supported by the implementation must be declared in this structure type, and thus shared by all of the supported calendric systems.

All of the members of pointer type are restricted to being null or pointing to const (presumably static) data objects, so that there are no additional memory management requirements for the library (such as requiring calls to malloc() or free() for any of the objects pointed to by these members). This allows date objects to be easily allocated statically, on the stack, or on the heap without any complicated memory allocation constraints. It also simplifies the semantics of copying date objects, passing them as arguments to functions, and returning them from functions.

Members

int cal_dsti

Specifies a Daylight Saving Time adjustment that has been applied to the date represented by the calendar date object.

If this member is zero, no DST adjustment is in effect; if it is a positive value, it specifies the index of the member of the cal_zone->tz_z[] array that has been applied as a DST adjustment to the date (provided that the cal_zone member is not null); otherwise the member is equal to -1, specifying that the DST adjustment is indeterminate.

This member in combination with the cal_zone member specifies the difference between the time represented by the calendar date object and UTC time. If the cal_zone member is null, this member has no defined meaning.

[Notes]
DST adjustments are always applied in the context of a specific timezone setting. Thus the combination of the cal_zone and the cal_dsti members specifies a particular timezone setting plus a DST adjustment for a given calendar date object.

If the cal_zone member is null, then it is assumed that there is no timezone/DST combination applied to the date object.

int cal_era

Specifies the era (or cycle) of the parent calendar date object. Valid ranges for this member are implementation-defined.

The value of this member depends on the calendric system utilized by the date object (as specified by the cal_type member) as well as the settings of the other structure members.

Conforming implementations are required to support, at a minimum, a single era within the Gregorian calendric system having a value equal to the _CAL_ERA_COMMON constant, representing the AD (CE) era.

[Notes]
For the Gregorian calendar, this member specifies which era is represented by the date object, i.e., AD (CE) or BC (BCE).

The combination of this member and the cal_year member specifies a specific year within the calendric system employed by the calendar date object.

Since conforming implementations are only required to support a Gregorian calendar with year numbers in the range [1900,2399], they are only required to support a single era value (representing the AD/CE era), which is represented by the _CAL_ERA_COMMON constant.

This member is useful for the calendarformat() function, which could simply employ this member as an index into an array of static strings (e.g., "AD" et al).

There is no requirement that the supported values of this member form a set of consecutive integers, nor that they must be strictly positive values.

The term "cycle" is a synonym for "era".

int cal_hour

Specifies the hour of the day of the parent calendar date object.

For the Gregorian calendar, this represents the number of hours since midnight.

int cal_leapsec

Specifies the number of accumulated leap seconds included in the date represented by the parent calendar date object. A value of INT_MIN indicates an indeterminate number.

int cal_mday

Specifies the day of the month of the parent calendar date object.

int cal_min

Specifies the minute of the hour of the parent calendar date object.

int cal_mon

Specifies the month of the parent calendar date object.

[Notes]
The definition differs from that of the tm_mon member of the tm structure in that it encodes the exact month number, rather than the number of months since the first month of the year. Thus January in the Gregorian calendar is month number 1 (rather than 0). This is intended to simplify its usage, and to make its semantics consistent across different calendric systems.

For other calendric systems, it is implementation-defined how intercalary months are distinguished from other months.

long int cal_ndays

When the parent calendar date object represents the difference between two calendar dates, this member specifies the total integral (whole) number of days difference between the two dates (see function calendardiff()).

When the parent calendar date object represents a calendar date value, this member has no defined meaning.

long int cal_nmons

When the parent calendar date object represents the difference between two calendar dates, this member specifies the total integral (whole) number of months difference between the two dates (see function calendardiff()).

When the parent calendar date object represents a calendar date value, this member has no defined meaning.

long cal_nsec

Specifies the number of nanoseconds within the current second of the parent calendar date object.

[Notes]
This member fills a void in the capabilities of the current tm  structure by allowing for time representations with subsecond precision.

This draws on prior art from POSIX and BSD Unix systems, which define the timeval and timespec structures and the gettimeofday() system function, which provide system time values with microsecond and nanosecond precisions.

long int cal_nweeks

When the parent calendar date object represents the difference between two calendar dates, this member specifies the total integral (whole) number of weeks difference between the two dates (see function calendardiff()).

When the parent calendar date object represents a calendar date value, this member has no defined meaning.

int cal_sec

Specifies the second of the minute of the parent calendar date object.

Conforming implementations are required to support a Gregorian calendric system having a range of seconds of at least [0,59].

If leap seconds are supported by the implementation, the value of this member may include an inserted leap second, for a maximum value of 60.

int cal_type

Specifies the calendric system employed by the parent calendar date object.

Conforming implementations are required to support at least one calendar type named "Gregorian", which corresponds to the _CAL_TYPE_GREGORIAN constant and which implements the date calculation rules of the worldwide Gregorian calendric system. Whether or not the implementation recognizes leap seconds is implementation-defined.

This member is initialized by a call to the initcalendar() function. A program shall expect that this member is set to a value corresponding in some implementation-defined manner to the calendar name passed to the initcalendar() function.

All of the possible values that this member may be assigned by a call to the initcalendar() function should be defined as constants with names of the form '_CAL_TYPE_XXX'  in the <stdtime.h> standard header file.

[Notes]
This member allows implementations to support multiple calendric systems, each having its own unique type number. The value of this member indicates to the calendar library functions what kind of calendric system is utilized by a date object, and thus what particular date and time calculation rules to apply to that object.

It is possible that implementations may support more than one variant of the standard Gregorian calendar, e.g., those with different era names ("CE" instead of "AD") or those with gaps reflecting the historical acceptance of the Gregorian calendar in 1582, 1752, 1917, or other dates. Other variants include Gregorian calendars that observe leap second rules and those that do not.

An obvious implementation of this member is to use it as a small integer index into an array of calendric calculation tables or function pointers, which could be static data or be dynamically loaded into memory on demand, but this is by no means the only possible approach.

int cal_wday

Specifies the day of the week of the parent calendar date object. A value of 1 specifies the first day of the week.

For the Gregorian calendar, the first day of the week is a Monday, as per the rules of ISO 8601 (see reference [3]).

The range of values and meaning of this member may be different for other calendric systems.

[Notes]
ISO 8601 rules state that weeks begin on Mondays, and that weekday numbers range from 1 to 7.

This member is defined differently than the tm_wday member of the tm structure. It is fairly trivial, however, to convert between equivalent structure member values:

    t.tm_wday = c.cal_wday % 7;
    c.cal_wday = (t.tm_wday == 0 ? 7 : t.tm_wday);

int cal_week

Specifies the week number in the year of the parent calendar date object.

For the Gregorian calendar, week number 1 is the first week of January having at least four days (which also contains the first Thursday, and also contains January 4th). The week number for days ocurring prior to week number 1 is the number of the last week of the previous year, and the week number for days ocurring after the last week of the year (week 52 or 53) is week 1 of the subsequent year, as per the rules of ISO 8601 (see reference [3]).

[Notes]
The definition of this member corresponds to the semantics of the "%V" format specifier of the strftime() function.

ISO 8601 rules state that weeks begin on Mondays, and week number 1 of the year is the week that includes January 4th, which is also the week that includes the first Thursday of the year, and is also the first week containing at least four days in the year.

Examples

The following dates are shown with their corresponding ISO 8601 week and weekday designations and their corresponding {cal_week, cal_wday} pair.

    2000-01-01:   1999-W52-6    {week=52, wday=6}
    2001-12-31:   2002-W01-1    {week=1,  wday=1}
    2003-12-31:   2004-W01-3    {week=1,  wday=3}
    2005-01-01:   2004-W53-6    {week=53, wday=6}

int cal_yday

Specifies the day of the year of the date represented by the calendar date object.

For the Gregorian calendar, the first day of the year (i.e., the first day of January) is day number 1.

[Notes]
The definition differs from that of the tm_yday member of the tm structure in that it encodes the exact day number, rather than the number of days since the first day of the year. Thus January 1st in the Gregorian calendar is day number 1 (rather than 0). This is intended to simplify its usage, and to make its semantics consistent across different calendric systems.

int cal_year

Specifies the year of the parent calendar date object. The combination of this member and the cal_era member specifies a given year within the calendric system employed by the calendar date object.

Conforming implementations are required to support a reasonably wide range of year values for each calendar type they support. For the Gregorian calendar, conforming implementations are required to support year number values at least within the range [1900,2399].

This member may have a value equal to the _CAL_YR_ERROR constant, indicating that the calendar date object represents an erroneous date, i.e., that it does not represent a meaningful date. (A date object having this member set to such a value implies that the rest of the members, except for the cal_type member, do not have meaningful values.)

Whether or not non-positive year number values have meaningful interpretations is implementation-defined.

[Notes]
The definition differs from that of the tm_year member of the tm structure in that it encodes the exact year number rather than an offset from some arbitrary year. This is intended to simplify its usage and make its semantics consistent across different calendric systems.

This member also encodes erroneous dates. The special marker value (_CAL_YR_ERROR) must be a value outside the range of valid year numbers for all calendric systems.

Negative year values could be used to represent different eras within the calendric system utilized. For the Gregorian calendar, negative year numbers could represent dates in the BC (BCE) era, and positive year numbers would represent dates in the AD (CE) era. Note that the Gregorian calendar has no zero (0) year; thus it is implementation-defined whether this member should have the value 0 or -1 to represent the year BC 0001. (Thus the dates AD 1300 and BC 1300 could be represented as {era=AD, year=+1300} and {era=BC, year=-1299}, respectively.)

An alternate representation could allow the cal_year member to specify the year number and the cal_era member to specify the era, such that the dates AD 1300 and BC 1300 both have a cal_year value of +1300, but different cal_era values (represented as {era=AD, year=+1300} and {era=BC, year=+1300}, respectively). This representational scheme is almost certainly better for calendric systems having more than two eras (e.g., historical calendars representing year numbers relative to the reigning timespans of multiple emperors).

Mandating that conforming implementations must support years in a range of at least AD 1900 to 2399 seems like a reasonable minimum range, which provides for many applications that must deal with old dates (e.g., extant mortgage schedules) while also providing for dates spanning the next few centuries.

It is expected that quality implementations will support a wider range of years than this. For comparison, the built-in date functions of ANSI COBOL support dates going back to AD 1601, on the assumption that there are financial applications that must deal with very old mortgage and lease agreements that are still in force. The date 1601-01-01 has the convenient property of being the first day of the previous 400-year cycle of the Gregorian calendar (the current cycle having begun on 2001-01-01). That day is also a Monday, when using a strictly linear (proleptic) representation of the Gregorian calendar rules and ignoring the various historical calendar changes.

const struct timezone * cal_zone

A pointer to a timezone object, specifying the timezone and DST adjustments that were last applied to the parent calendar date object.

The combination of this member and the cal_dsti member specifies the timezone offset and DST adjustment that has been applied to the parent date object. This pointer can be null, which indicates that no timezone/DST combination has been applied to the date object, or if the timezone information has been removed from the date object.

[Notes]
This member specifies how the members of the date object have been adjusted for a particular timezone, which allows the setcalendarzone() function to change or undo such adjustments.

This member also provides a means of retrieving the name of the timezone applied to the date object, by calling the calendarformat() function.

Timezone information is represented by an entirely separate timezone structure.

The timezone object is used in conjunction with the Daylight Saving Time setting specified by the cal_dsti member to completely specify the offset from UTC represented by the calendar date object.

The technique of storing a timezone within each date object allows for the manipulating of many date objects simultaneously, each having its own independent timezone setting.

The allocation of the timezone object pointed to by this member is entirely the responsibility of the programmer. Thus, no calls to malloc() or free() are required of the standard library.

It is intended that multiple date objects can share (i.e., point to) the same timezone object, since each cal_zone member is a pointer to a const object (and thus should not be used to modify the contents of the pointed-to timezone object). This also allows date objects to be copied in a straightforward manner.

See also

_CAL_ERA_COMMON, _CAL_NAME_GREGORIAN, _CAL_TYPE_GREGORIAN, _CAL_YR_ERROR, struct calendarinfo, longtime_t, struct timezone, time_t.

5.3 Structure calendarinfo

Synopsis

    #include <stdtime.h>

    struct calendarinfo;

Description

This structure represents information about how a specific calendric system is implemented, providing valid ranges for each of the members of componentized calendar date objects with respect to calendric system.

The calendarinfo structure contains at least the following members, in any order. The semantics of the members and their normal ranges are expressed in the comments, and each member is described in further detail in the sections below.

    int             ci_type;            // Calendar type number
    const char *    ci_name;            // Calendar name
    longtime_t      ci_time_min;        // Earliest time value
    longtime_t      ci_time_max;        // Latest time value
    int             ci_era_min;         // Minimum era number
    int             ci_era_max;         // Maximum era number
    int             ci_year_min;        // Minimum year number (1900)
    int             ci_year_max;        // Maximum year number (2399)
    int             ci_mon_min;         // Minimum month number (1)
    int             ci_mon_max;         // Maximum month number (12)
    int             ci_week_min;        // Minimum week of the year (0)
    int             ci_week_max;        // Maximum week of the year (53)
    int             ci_mday_min;        // Minimum day of the month (1)
    int             ci_mday_max;        // Maximum day of the month (31)
    int             ci_yday_min;        // Minimum day of the year (1)
    int             ci_yday_max;        // Maximum day of the year (366)
    int             ci_wday_min;        // Minimum day of the week (1)
    int             ci_wday_max;        // Maximum day of the week (7)
    int             ci_wday1;           // Weekday present in the 1st week (4)
    int             ci_hour_min;        // Minimum hour number (0)
    int             ci_hour_max;        // Maximum hour number (23)
    int             ci_min_max;         // Maximum minute number (59)
    int             ci_sec_max;         // Maximum second number (59)
    int             ci_leap_sec;        // Leap seconds supported (0)

The values shown are the minimum values required for the Gregorian calendar, which might not necessarily apply to other calendar types.

Additional members may exist within this structure, but are implementation-specific (and thus use of them is not portable).

Conforming implementations are required to support at least one calendar information object for the calendar named "Gregorian".

[Notes]
This structure allows programs to determine the characteristics of the calendric systems supported by the implementation. In particular, the range of valid date components (e.g., valid year numbers) and whether or not leap seconds are supported are two useful pieces of information.

Members

int ci_era_max
Specifies the maximum value that the cal_era member can have under the supported calendric system.

[Notes]
The Gregorian calendar has two eras, AD and BC (also known as CE and BCE, respectively). Conforming implementations are only required to support the AD (CE) era of the Gregorian calendar, which corresponds to the _CAL_ERA_COMMON constant, since they are only required to support Gregorian calendar year numbers within the range [1900,2399].

Implementations that support a larger range of years that spans both the AD (CE) and BC (BCE) eras, however, should support two distinct era numbers.

int ci_era_min
Specifies the minimum value that the cal_era member can have under the supported calendric system.

[Notes]
Implementations that support multiple eras may support negative era numbers.

int ci_hour_max
Specifies the maximum value that the cal_hour member can have under the supported calendric system.

Conforming implementations are required to support a Gregorian calendric system having hour numbers that fall within the range [0,23], where 0 represents midnight (12:00 AM).

int ci_hour_min
Specifies the minimum value that the cal_hour member can have under the supported calendric system.

Conforming implementations are required to support a Gregorian calendric system having hour numbers that fall within the range [0,23], where 0 represents midnight (12:00 AM).

int ci_leap_sec
This member has a nonzero value if the supported calendric system recognizes leap seconds, otherwise it has a value of zero.

Conforming implementations are not required to support calendric systems that are capable of recognizing leap seconds.

int ci_mday_max
Specifies the maximum value that the cal_mday member can have under the supported calendric system.

Conforming implementations are required to support a Gregorian calendric system having day of the month numbers that fall within the range [1,31].

int ci_mday_min
Specifies the minimum value that the cal_mday member can have under the supported calendric system.

Conforming implementations are required to support a Gregorian calendric system having day of the month numbers that fall within the range [1,31].

int ci_min_max
Specifies the maximum value that the cal_min member can have under the supported calendric system. (The minimum value is 0.)

Conforming implementations are required to support a Gregorian calendric system having minute numbers that fall within the range [0,59].

int ci_mon_max
Specifies the maximum value that the cal_mon member can have under the supported calendric system.

Conforming implementations are required to support a Gregorian calendric system having month numbers that fall within the range [1,12].

int ci_mon_min
Specifies the minimum value that the cal_mon member can have under the supported calendric system.

Conforming implementations are required to support a Gregorian calendric system having month numbers that fall within the range [1,12].

const char * ci_name
Points to a statically allocated null-terminated string containing the name of the calendric system implemented by the calendar information structure. This corresponds to the calendar name argument passed to the initcalendar() function in some implementation-defined manner, but is not required to be identical to it.

All calendar types that are supported by an implementation should, in general, have a corresponding name constant defined in the <stdtime.h> header file with a name of the form '_CAL_NAME_XXX'.

A program shall expect that this member is initialized to the address of a null-terminated string containing only printable characters, but whose contents are otherwise implementation-defined.

int ci_sec_max
Specifies the maximum value that the cal_sec member can have under the supported calendric system. (The minimum value is 0.)

Conforming implementations are required to support a Gregorian calendric system having second numbers that fall at least within the range [0,59].

[Notes]
Implementations that support leap seconds will initialize this member to a value of 60 to indicate that one additional (positive) leap second can occur on certain days.

Conforming implementations are not required to recognize leap seconds, however. (See the ci_leap_sec member.)

longtime_t ci_time_max
Specifies the latest longtime_t value that is representable by the calendar type. In other words, this is the latest valid system time that can be converted into a meaningful calendar date object within the calendric system represented by the calendar information structure.

[Notes]
Note that this value does not necessarily indicate anything about the complete range of valid calendar dates representable by the calendar type.

longtime_t ci_time_min
Specifies the earliest longtime_t value that is representable by the calendar type. In other words, this is the earliest valid system time that can be converted into a meaningful calendar date object within the calendric system represented by the calendar information structure.

[Notes]
Note that this value does not necessarily indicate anything about the complete range of valid calendar dates representable by the calendar type.

int ci_type
Specifies the calendar type of the calendar information structure. Conforming implementations are required to support at least one calendar type named "Gregorian", which implements the date calculation rules of the worldwide standard (proleptic) Gregorian calendric system.

A program shall expect that this member is set to a value corresponding in some implementation-defined manner to the calendar name passed to the getcalendarinfo() function.

All of the possible values that this member may be assigned by a call to the getcalendarinfo() function should be defined as constants with names of the form '_CAL_TYPE_XXX'  in the <stdtime.h> standard header file.

[Notes]
This member allows implementations to support multiple calendric systems, each having its own unique type number. The value of this member indicates to the library functions what kind of calendric system is utilized by a date object, and thus what particular date and time calculation rules to apply to that object.

An obvious implementation of this member is to use it as a small integer index into an array of calendric calculation tables or function pointers, which could be static data or be dynamically loaded into memory on demand, but this is by no means the only possible approach.

int ci_wday_max
Specifies the maximum value that the cal_wday member can have under the supported calendric system.

Conforming implementations are required to support a Gregorian calendric system having weekday numbers that fall within the range [1,7] where 1 represents Monday, as per the rules of ISO 8601 (see reference [3]).

[Notes]
ISO 8601 rules state that weeks begin on Mondays, and that weekday numbers range from 1 to 7.

int ci_wday_min
Specifies the minimum value that the cal_wday member can have under the supported calendric system.

Conforming implementations are required to support a Gregorian calendric system having weekday numbers that fall within the range [1,7] where 1 represents Monday, as per the rules of ISO 8601 (see reference [3]).

[Notes]
ISO 8601 rules state that weeks begin on Mondays and that weekday numbers range from 1 to 7.

int ci_wday1
Specifies the day of the week that is always present in the first week of the year. If this value is indeterminate, the member is set to -1.

Conforming implementations are required to support a Gregorian calendric system having a first week of the year containing a Thursday (day number 4), as per the rules of ISO 8601 (see reference [3]).

int ci_week_max
Specifies the maximum value that the cal_week member can have under the supported calendric system.

Conforming implementations are required to support a Gregorian calendric system having week numbers that fall within the range [1,53], as per the rules of ISO 8601 (see reference [3]).

int ci_week_min
Specifies the minimum value that the cal_week member can have under the supported calendric system.

Conforming implementations are required to support a Gregorian calendric system having week numbers that fall within the range [1,53], as per the rules of ISO 8601 (see reference [3]).

int ci_yday_max
Specifies the maximum value that the cal_yday member can have (within any valid year) under the supported calendric system.

Conforming implementations are required to support a Gregorian calendric system having day of the year numbers that fall within the range [1,366] as per the rules of ISO 8601 (see reference [3]).

[Notes]
ISO 8601 rules state that days of the year are numbered in the range from 1 to 366, with January 1st being day number 1.

int ci_yday_min
Specifies the minimum value that the cal_yday member can have (within any valid year) under the supported calendric system.

Conforming implementations are required to support a Gregorian calendric system having day of the year numbers that fall within the range [1,366] as per the rules of ISO 8601 (see reference [3]).

[Notes]
ISO 8601 rules state that days of the year are numbered in the range from 1 to 366, with January 1st being day number 1.

int ci_year_max
Specifies the maximum value that the cal_year member can have (within any valid era) under the supported calendric system.

Conforming implementations are required to support a Gregorian calendric system having a maximum year number not less than 2399.

[Notes]
Implementations that support a Gregorian calendar with a wider range of year numbers are encouraged to support all of the calendar year numbers that are convertible into valid longtime_t values.

int ci_year_min
Specifies the minimum value that the cal_year member can have (within any valid era) under the supported calendric system.

Conforming implementations are required to support a Gregorian calendric system having a minimum year number not greater than 1900.

[Notes]
Implementations that support a Gregorian calendar with a wider range of year numbers are encouraged to implement the proleptic Gregorian calendar, which assumes that the rules for leap days are in effect for all dates prior to the present date. While this does not reflect the true historical dates, it is a much simpler and more predictable model to implement and use.

See also

_CAL_ERA_COMMON, _CAL_NAME_GREGORIAN, _CAL_TYPE_GREGORIAN, _CAL_YR_ERROR, struct calendar, longtime_t, struct timezone, time_t.

5.4 Structure timezone

Synopsis

    #include <stdtime.h>

    struct timezone;

Description

This type represents a timezone offset and its Daylight Saving Time variants.

The contents of this structure are discussed in a separate proposal see Proposal [P1] for more details.)

See also

struct calendar.

 
 6. Functions
 

It is given to us to live for the most part under the guidance of the discipline of Mathematics. If we learn the hours by it, if we calculate the courses of the moon, if we take note of the time lapsed in the recurring year, we will be taught by numbers and preserved from confusion. Remove the computus from the world, and everything is given over to blind ignorance.

— Cassiodorus, ca. 550

The following sections describe the proposed functions to be added to the <stdtime.h> standard library header.


6.1 Function calendaradd()

Synopsis

    #include <stdtime.h>

    int calendaradd(struct calendar *date, const struct calendar *dif, int add);

Description

Adds or subtracts a componentized delta time to a calendar date.

Argument date points to a calendar date object to be modified. If the cal_type member of this object does not designate a calendric system supported by the implementation, the function fails. If the cal_year member of this object has a value equal to _CAL_YR_ERROR, the function fails.

Argument dif points to a componentized delta time. The values of the members of the delta time object are not constrained to fall within their normal ranges. (The delta time object may be the result of a call calendardiff().)

If argument add is zero, the delta time is added to the calendar date, otherwise it is subtracted.

The following members of the delta time are added to or subtracted from the calendar date:

    cal_era         // Total eras difference
    cal_year        // Total years difference
    cal_mon         // Months difference less than a year
    cal_mday        // Days difference less than a month
    cal_hour        // Hours difference less than a day
    cal_min         // Minutes difference less than an hour
    cal_sec         // Seconds difference less than a minute
    cal_nsec        // Nanoseconds difference less than a second
    cal_leapsec     // Leap seconds difference

The following members of the delta time are also used, depending on the values of some of the members above:

    cal_nmons       // Total months difference,
                    //  if cal_mon and cal_mday are both zero
    cal_nweeks      // Total weeks difference,
                    //  if cal_mon, cal_mday, and cal_nmons are all zero
    cal_ndays       // Total days difference,
                    //  if cal_mon, cal_mday, cal_nmons, and cal_nweeks are all zero

Other members may be used if the cal_type member designates a calendric system other than the standard Gregorian calendar. The remaining members are ignored.

After the calendar date is modified by the delta time, its members are normalized so that their values fall within their normal ranges.

Returns

The function returns zero on success.

If the cal_type member has a value that is not supported by the implementation, the function fails and returns -1.

If the cal_year member of the calendar date object has a value equal to the _CAL_YR_ERROR constant, indicating that the date object represents an erroneous date, the function fails and returns -1.

If the resulting adjusted date cannot be represented as a valid date within the calendric system employed by the date object, the function fails and returns a negative value.

Example

The following function adds a specified number of months to a given date object.

    #include <stdtime.h>

    int add_months(struct calendar *date, int months)
    {
        struct calendar     dif;

        // Convert months into a delta time
        initcalendar(&dif, "");
        dif.cal_year =  0;
        dif.cal_nmons = months;

        // Add the delta time to the given date
        return calendaradd(date, &dif, 0);
    }

See also

_CAL_YR_ERROR, calendar.cal_type, calendar.cal_year, calendardiff().

6.2 Function calendardiff()

Synopsis

    #include <stdtime.h>

    int calendardiff(struct calendar *dif,
            const struct calendar *a, const struct calendar *b);

Description

Determines the difference between two calendar dates as a componentized delta time representing the absolute difference between the two dates.

[Notes]
While it is fairly easy to determine the difference between two calendar dates as the number of seconds between the two (using the mktime()/mklongtime() and difftime() functions, and assuming that the dates can be converted into valid system time values), it is nontrivial to determine the difference as a componentized date.

For example, the difference between 1979-01-01 and 1980-02-02 is exactly one year, one month, and one day when represented as a componentized date. By the same token, the difference between 1980-01-01 and 1981-02-02 is also exactly one year, one month, and one day. However, the former difference represents a total span of 397 (365+31+1) days, while the latter difference represents a total span of 398 (366+31+1) days because 1980 is a leap year. Hence the need for this function.

Arguments a and b point to a calendar date objects. The resulting delta time is the absolute difference between date a and date b. If the cal_type member of either calendar date object does not designate a calendric system supported by the implementation, the function fails. Undefined behavior results if the cal_type members have different values. (This implies that the difference between two calendar dates can be determined portably only if the date objects represent dates within the same calendric system.) Undefined behavior results if the date objects contain unnormalized member values.

If the cal_year member of either date object has a value equal to _CAL_YR_ERROR, the function fails.

Argument dif points to a componentized delta time object, the members of which are modified to represent the absolute (non-negative) difference between the two calendar dates. This argument may be null, in which case the difference is computed but no componentized difference is returned. (Passing a null pointer for the dif argument thus serves simply to compare the calendar dates pointed to by a and b.)

The members of the delta time object pointed to by dif are set to values as described below. Each member is set to the least possible non-negative value that is less than its maximum normalized value, except for the cal_era and cal_year members, which may be set to values that exceed their normal ranges.

    cal_type        // Same calendar type as 'a' and 'b'
    cal_era         // Total eras difference
    cal_year        // Total years difference
    cal_mon         // Months difference of less than a year
    cal_week        // Weeks difference of less than a year
    cal_mday        // Days difference of less than a month
    cal_yday        // Days difference of less than a year
    cal_wday        // Weekdays difference of less than a week
    cal_hour        // Hours difference of less than a day
    cal_min         // Minutes difference of less than an hour
    cal_sec         // Seconds difference of less than a minute
    cal_nsec        // Nanoseconds difference of less than a second
    cal_nmons       // Total months difference
    cal_nweeks      // Total weeks difference
    cal_ndays       // Total days difference
    cal_dsti        // Zero
    cal_leapsec     // Leap seconds difference
    cal_zone        // Null

The cal_type member of the delta time object is set to the same value as the cal_type member of either of the calendar date objects.

If the cal_year member of either of the date objects pointed to by a or b has a value equal to the _CAL_YR_ERROR constant, the cal_year member of the date object pointed to by dif is set to a value equal to the _CAL_YR_ERROR constant, indicating that an erroneous date value was encountered, and the function returns a nonzero value.

Returns

If successful, the function modifies the delta time object appropriately and returns a signed value, which is zero if date a is equivalent to date b, a negative value if date a occurs before date b, or a positive value if date a occurs after date b. Note that some calendric systems may allow multiple representations of the same (equivalent) date.

On failure, the function sets the cal_year member of the delta time object to a value equal to _CAL_YR_ERROR and returns a negative value equal to INT_MIN.

Example

The following function determines the componentized difference between two calendar dates and prints the result to the standard output.

    #include <limits.h>
    #include <stdio.h>
    #include <stdtime.h>

    void print_diff(const struct calendar *a, const struct calendar *b)
    {
        struct calendar     dif;
        int                 r;

        // Compute the difference between the two dates
        r = calendardiff(&dif, a, b);

        if (r != INT_MIN)
        {
            // Print the componentized difference
            printf("%dy %dm %dd and %02d:%02:%02d.%03ld\n",
                dif.cal_year, dif.cal_mon, dif.cal_mday,
                dif.cal_hour, dif.cal_min, dif.cal_sec,
                dif.cal_nsec/1000000);
            printf("%d years and %d weeks\n",
                dif.cal_year, dif.cal_week);
            printf("%d years and %d days\n",
                dif.cal_year, dif.cal_yday);
            printf("%ld months, %ld weeks, %ld days total\n",
                dif.cal_nmons, dir.cal_nweeks, dif.cal_ndays);
        }
        else
        {
            // An error occurred
            printf("Error\n");
        }
    }

Given the two dates:

    a = 1980-01-01 01:02:16.555 Z 
    b = 1982-02-02 02:04:03.000 Z 

the output of this function is:

    2y 1m 1d and 01:01:46.445
    2 years and 4 weeks
    2 years and 32 days
    25 months, 109 weeks, 763 days total

See also

_CAL_YR_ERROR, struct calendar.

6.3 Function calendarformat()

Synopsis

    #include <stdtime.h>

    int calendarformat(char *buf, size_t max, const char *fmt,
            const struct calendar *date, const struct timezone *zone);

Description

Formats a calendar date, converting it into a printable representation.

[Notes]
This function is equivalent to strftime() except for the calendar structure type and the additional timezone parameter. It also deals with erroneous date values.

Pointer buf points to a character array that will be filled with the formatted date. No more than max characters, including a terminating null character, will be written into array buf.

Pointer date points to a calendar date object. If the cal_type member of this object does not designate a calendric system supported by the implementation, the function fails. If the cal_year member of this object has a value equal to _CAL_YR_ERROR, the function fails.

Pointer zone points to a timezone object. This may be null, in which case no timezone or Daylight Saving Time adjustment is assumed, i.e., the date object is treated as if it represents a time relative to UTC.

[Notes]
This parameter typically points to the same object as date->cal_zone, but is not required to.

The formatting string pointed to by fmt contains characters and conversion specifier sequences beginning with a '%' character. The conversion specifiers recognized are the same as for the strftime() function except for the "%Z" specifier, and following specifiers are also recognized:

  %f is replaced by the locale's era name or abbreviation. [cal_era]
  %L is replaced by the accumulated leap seconds. [cal_leapsec]
  %s is replaced by the nanosecond as a decimal number (000000000-999999999). An optional sequence of decimal digits may follow the '%', specifying the exact number of digits, including leading zeros, to insert into the formatted string. (Any digits occurring to the right of the last digit inserted are truncated, as per the ISO 8601 rules for representating decimal fractions.) [cal_nsec]
  %Z is replaced by the name of the time zone as specified by the zone object, or by no characters if zone is null. [cal_zone, cal_dsti]

[Notes]
The "%s" format specifier allows the rightmost least significant digits of the nanosecond count to be truncated (instead of allowing for rounding). This follows the ISO 8601 rules for the representation of decimal fractions and for reduced precision formats. (See section 5.3.1.3 of reference [3].) It also agrees with the practice of truncating portions of date and time values on the right to produce shorter values of reduced precision, e.g., specifying hh:mm as an abbreviated form of hh:mm:ss.

The "%Z" format specifier takes its value from the timezone object instead from (presumably) global data.

Some of the format specifiers in the string pointed to by fmt are affected by the current LC_TIME locale setting.

Returns

The function returns the number of characters written to the string pointed to by buf if successful. If an error occurs, a negative value is returned which is the negated index of the character within the string pointed to by fmt where the error was first detected.

If the cal_year member of the calendar date object has a value equal to the _CAL_YR_ERROR constant, indicating that the date object represents an erroneous date, the contents of the string pointed to by buf are not modified, and the function returns -1.

If the cal_type member has a value that is not supported by the implementation, the function fails and returns -1.

Example

The following function formats a calendar date and writes it to the standard output.

    #include <stdio.h>
    #include <stdtime.h>

    void print_date(const struct calendar *date)
    {
        char    buf[80];

        // Format the calendar date
        calendarformat(buf, sizeof(buf),
            "%a %Y-%m-%d %H:%M:%S.%3s %Z", date, date->cal_zone);

        // Print the formatted calendar date
        printf("%s\n", buf);
    }

Example output from this function:

    Tue 2001-09-11 08:50:23.166 EDT

See also

_CAL_YR_ERROR, struct calendar, struct timezone.

6.4 Function calendarscanf()

Synopsis

    #include <stdtime.h>

    int calendarscanf(const char *buf, const char *fmt,
            struct calendar *date, struct timezone *zone);

Description

Parses the contents of the string pointed to by buf, which contains a series of characters that form a date (such as one produced by a prior call to the calendarformat() function), filling the members of the date object pointed to by date and the members of the timezone object pointed to by zone appropriately. The interpretation of the contents of string buf is controlled by the format string fmt.

[Notes]
This function is similar to the strptime() function provided by POSIX.

Argument date points to a calendar date object. If the cal_type member of this object does not designate a calendric system supported by the implementation, the function fails.

Argument zone points to a timezone object. This argument may be null, even if timezone format specifiers are present within the format string fmt.

The contents of the string pointed to by fmt are composed of whitespace characters, format specifiers (which begin with a "%" character), and regular characters.

The parsing of the source string pointed to by buf proceeds by scanning from left to right, while proceeding in a corresponding fashion interpreting the formatting control string, attempting to match characters and format specifiers. If a character in the source string does not match the corresponding character or format specifier in the format control string, the parsing fails and an error is returned.

Whitespace characters in the formatting string match one or more whitespace characters, of any type, in the source string. Regular characters in the formatting string match the exact same character in the source string. Format specifiers match one or more non-whitespace characters in the source string, as expected by the rules described for the calendarformat() function, except that alphabetic characters are matched without regard to case.

In addition to handling the format specifiers of the calendarformat() function, the following specifier is also recognized:

  %? skips any character in the input string. An optional sequence of decimal digits may follow the '%', specifying the exact number of characters to skip; or an optional '*' character may follow the '%', specifying that all non-whitespace characters are to be skipped in the source string up to the next whitespace character or the terminating null character.

[Notes]
The "%?" format specifier is a new invention. It is felt that there is a need to ignore sequences of non-whitespace characters that are otherwise unpredictable when a source string is being parsed.

The "%n" and "%t" format specifiers match exactly one whitespace character (a newline or a tab character, respectively) in the source string.

The timezone format specifiers cause the source string to be scanned for timezone names. Conforming implementations are required to support, at a minimum, the timezone name "Z" designating UTC, names specified by the following syntax:

    timezone-offset:
        [+|-] [h]h [ mm ]

A timezone name and/or offset scanned from the source string is copied into the first element of the tz_z member of the timezone object if zone is not null. In addition, the cal_dsti member of the date object is set to zero.

Additional (non-portable) timezone names and syntax may be supported by the implementation.

Returns

Returns the number of characters read from buf, or a negative value if an error occurs.

If the scanned source string specifies a date that cannot be represented as a valid calendar date, or if the source string is ill-formed, then the cal_year member of the date object is set to a value equal to the _CAL_YR_ERROR constant, indicating that the date object represents an erroneous date, and the function returns a negative value. It is unspecified whether any of the other members of the date object or the timezone object will be set to meaningful values in such a case.

If the cal_type member does not designate a calendric system supported by the implementation, the function fails and returns a negative value.

[Notes]
The cal_zone and cal_dsti members of the date object are not modified.

Example

The following function parses a string containing a formatted date, producing a calendar date and printing its contents to the standard output.

    #include <stdio.h>
    #include <stdtime.h>

    void parse_date(const char *buf)
    {
        struct calendar     date;
        struct timezone     zone;
        int                 rc;

        // Parse the formatted date string
        rc = calendarscanf(buf,
                "%a %Y-%b-%d %2? %H:%M:%S.%3s %Z", &date, &zone);
        setcalendarzone(&date, NULL, &zone);

        // Print the resulting calendar date
        if (rc >= 0)
        {
            printf("%dwd, %dy %dm %dd, %dh %dm %d.%03lds %s\n",
                date.cal_wday,
                date.cal_year, date.cal_mon, date.cal_mday,
                date.cal_hour, date.cal_min, date.cal_sec,
                date.cal_nsec/1000000,
                zone.tz_name);
        }
        else
            printf("error\n");
    }

Given an input string of:

    buf = "Thu 1776-Jul-04 at 09:45:03.074 CDT"

the output from this function is:

    4wd, 1776y 7m 4d, 9h 45m 3.074s CDT

See also

_CAL_YR_ERROR, struct calendar, struct timezone, calendarformat(), initcalendar().

6.5 Function convertcalendar()

Synopsis

    #include <stdtime.h>

    int convertcalendar(struct calendar *dst, const struct calendar *src);

Description

Copies the calendar date object pointed to by src to the calendar date object pointed to by dst, converting between different calendric systems as appropriate.

The date object pointed to by dst must have been properly initialized by a prior call to the initcalendar() function (i.e., its cal_type member must have a properly initialized value.)

If the date object pointed to by src represents an erroneous date (i.e., if its cal_year member has a value equal to the _CAL_YR_ERROR constant), the date object pointed to by dst is set to represent an erroneous date also, i.e., its cal_year member is set to a value equal to the _CAL_YR_ERROR constant.

Implementations are only required to support conversions to and from the standard (proleptic) Gregorian calendric system (i.e., calender date objects having a cal_type equal to _CAL_TYPE_GREGORIAN).

Returns

Returns zero if the calendar date was successfully converted, or a negative value if the calendar date could not be converted, or a positive nonzero value if the resulting converted calendar date represents an erroneous date.

See also

_CAL_YR_ERROR, struct calendar, initcalendar().

6.6 Function getcalendarinfo()

Synopsis

    #include <stdtime.h>

    const struct calendarinfo * getcalendarinfo(int type);

Description

Retrieves a calendar information object corresponding to the specified calendric system. The calendar information object contains information about the supported ranges of values for the members of the calendar structure.

The type argument specifies the numeric type of a calendric system (which can be the value of the cal_type member of a calendar object). The value of such a number is implementation-defined. All conforming implementations, however, must support at least one calendric system with a type equal to the _CAL_TYPE_GREGORIAN constant. The type argument may be equal to zero, in which case it specifies the default calendric system of the current locale.

Returns

Returns a pointer to a (static) calendar information object corresponding to the specified calendric system type, or null if no matching calendric information object is supported by the implementation.

[Notes]
The structure returned by this function provides information about the valid range of values for various members of the calendar structure, such as the minimum and maximum year number values for valid dates.

The calendar information objects returned are expected to comprise a reasonably small set of supported calendric systems. They are intended to be treated as though they reside in static memory as non-modifiable data. As such, they do not require the use of malloc() or free() calls.

Example

The following function retrieves the information for a given calendric system by name.

    #include <stdtime.h>

    const struct calendarinfo * info_by_name(const char *name)
    {
        struct calendar     date;

        // Initialize a date object for the given calendric system
        if (initcalendar(&date, name) < 0)
            return NULL;

        // Retrieve the named calendric system info
        return getcalendarinfo(date.cal_type);
    }

See also

struct calendar, calendar.cal_type, struct calendarinfo.

6.7 Function initcalendar()

Synopsis

    #include <stdtime.h>

    int initcalendar(struct calendar *date, const char *name);

Description

Initializes a calendar date object and sets it to use a specified calendric system.

Argument date points to a calendar date object, the members of which are reset to default values. The cal_type member is set to a value corresponding to the calendric system specified by the name argument. The cal_year member is set to a value equal to the _CAL_YR_ERROR constant, indicating that the date object does not (yet) represent a valid date. All other members of the date object are set to zero or null.

The name argument points to a null-terminated string containing the name of a calendric system. The format and contents of such a string are implementation-defined. The manner in which the name argument corresponds to the names of calendric systems is implementation-defined. The name argument may point to an empty string (""), in which case it specifies the default calendric system of the current locale. All conforming implementations must support at least one calendric system matching the name "Gregorian". (See also the _CAL_NAME_GREGORIAN constant.)

If the specified name does not correspond to a calendric system supported by the implementation, the function fails and the contents of the date object are left in an indeterminate state.

Returns

The function returns zero on success, or a negative value if an error occurs.

[Notes]
The cal_type member allows implementations to support multiple calendric systems, each having its own unique type number. The value of this member indicates to the calendar library functions what kind of calendric system is utilized by a date object, and thus what particular date and time calculation rules to apply to that object.

It is conceivable that implementations may support more than one variant of the standard Gregorian calendar, e.g., those with different era names ("CE" instead of "AD") or those with gaps reflecting the historical acceptance of the Gregorian calendar in 1582, 1752, 1917, etc. Other variants include Gregorian calendars that observe leap second rules and those that do not.

Implementations could allow the calendar name to specify the name of a dynamic shared library, allowing programs to load calendric systems dynamically at runtime.

Examples

The following function initializes a calendar date object with the Gregorian calendar and the local timezone, but does not set it to any particular date value.

    #include <stdlib.h>
    #include <stdtime.h>

    int init_date(struct calendar *date)
    {
        struct timezone *   zone;

        // Initialize the calendar date object
        if (initcalendar(date, _CAL_NAME_GREGORIAN) != 0)
        {
            printf("Bad calendar initialization\n");
            return -1;
        }

        // Initialize the local timezone
        zone = malloc(sizeof(*zone));
        if (inittimezone(zone, "") != 0)
        {
            free(zone);
            printf("Bad timezone initialization\n");
            return -1;
        }

        // Set the timezone of the date object
        date->cal_zone = zone;
        return 0;
    }

The following function duplicates (clones) the contents of a calendar date object.

    #include <stdlib.h>
    #include <stdtime.h>
    #include <string.h>

    struct calendar * clone_date(const struct calendar *date)
    {
        struct calendar *   dup;

        // Duplicate the date object
        dup = malloc(sizeof(*date));
        memcpy(dup, date, sizeof(*date));
        return dup;
    }

See also

_CAL_NAME_GREGORIAN, _CAL_YR_ERROR, struct calendar, getcalendarinfo().

6.8 Function mklongtime()

Synopsis

    #include <stdtime.h>

    longtime_t mklongtime(struct calendar *date, const struct timezone *zone);

Description

Normalizes a calendar date and converts it into its equivalent long system time value.

[Notes]
This function provides the same functionality for longtime_t and calendar structure values as function mktime() does for time_t and tm structure values. It has a different interface, however.

This function makes use of the longtime_t type, which is defined in another related proposal (see Proposal [P2]).

Argument date points to a calendar date object. The members of the date object are normalized by modifying their values to fall within their normal ranges, while maintaining the same date value represented by the date object (if possible). The values of the members of the date object are not constrained to fall within their normal ranges upon entry to the function, but are so constrained upon returning from the function.

The values of the following members of the structure are used to perform the conversion:

    cal_type
    cal_era
    cal_year
    cal_mon
    cal_mday
    cal_hour
    cal_min
    cal_sec
    cal_nsec
    cal_dsti
    cal_leapsec

If the cal_type member of the date object designates a calendric system different from the standard Gregorian calendar, other members may be used to perform the conversion. Otherwise, the remaining members of the structure are ignored for the purposes of conversion.

If the cal_type member of the date object specifies a calendar type that is not supported by the implementation, the function fails.

If the cal_year member of the date object has a value equal to _CAL_YR_ERROR (indicating that the calendar date is invalid), the function fails.

The cal_leapsec member specifies the number of leap seconds to be included in the resulting converted long time value. If this member is INT_MIN, the number of accumulated leap seconds is determined from the long time value after the conversion takes place, based on historical leap second insertions and deletions. If the member is zero, no leap seconds are included in the resulting value. Any other value specifies an exact number of leap seconds to add to the resulting long time value.

Argument zone points to a timezone object containing timezone and DST adjustments that have been made to the date represented by the calendar date object. (This is typically equal to the cal_zone member of the calendar date object, but is not required to be.) The timezone and DST adjustments are applied in reverse (i.e., undone) on the calendar date before it is converted into a long time value. This argument can be null, in which case no timezone or DST adjustments are applied to the calendar date, i.e., the conversion is done with respect to UTC.

If the normalized calendar date does not represent a meaningful date, or cannot be converted into a valid long time value after adjustments have been applied, the function fails.

Returns

If successful, the function returns the resulting long time value.

On failure, the function returns a value equal to _LONGTIME_ERROR. In addition, if the date object cannot be normalized into a meaningful date, the cal_year member of the date object is set to a value equal to _CAL_YR_ERROR, and the remaining members of the date object have indeterminate values.

Because it is possible for the function to successfully normal