ISO C 201X Proposal:
Calendar Date Functions
By David R. Tribble
david@tribble.com
Revision 2.4, 2009-10-01
Contents
Cover Sheet
Document Number: WG14 N___/X3J11 __-___
C201X 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.4, 2009-01-01
Supersedes: 2.3, 2009-08-25
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 standards addressed are ISO/IEC 9899:1999 [1]
and ISO/IEC 9899:201X (draft) [2].
1.1 Concepts
- 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.
- 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 (e.g., in April) and back again
in the Fall (e.g., in 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
(e.g., 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).
- Delta Time
-
The componentized difference between two given calendar dates values with
respect to a particular calendric system.
For example, the difference between the dates 1980-01-01 and 1982-02-02
can be specified as 2 years, 1 month, and 1 day,
or as 25 months, or as 109 weeks, or as 793 days in total.
- 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).
(As of 2008, 24 leap seconds have been added to UTC time since the
practice began in 1972, so that the difference between UTC and TAI is now
34 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].)
- 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.
- 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.
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.
Calendar limitations
-
The standard library can apply the rules of only a single calendric system,
namely the Gregorian calendar.
While this is the most widely accepted calendar, this makes it impractical
to support (in a standard manner) other regional calendric systems
(e.g., the Islamic lunar calendar, the Jewish lunar calendar,
the Greek Orthodox calendar, the Chinese National calendar, etc.).
-
There is no way to query the implementation about the range of valid dates,
specifically, the range of valid year number values for the
tm_year member of the tm structure.
-
There is no standard mandated minimum range of dates supported by
conforming implementations.
-
There is no way to retrieve or manipulate time values with subsecond
precision (irrespective of the precision of the
time_t type).
This capability is missing from the tm structure type and
the strftime() function.
-
There are no special tm structure representations to indicate
unknown or erroneous dates.
-
There is no easy way to determine if a given date object (e.g., a
set of year, month, and day numbers) represents a valid calendar date.
-
There is no member of the tm structure that specifies the week
number of the year.
This number must be calculated explicitly by user code
or by calling the strftime() function.
-
The existing functions for converting a time_t values into a
broken-down time objects return pointers to global data objects, and thus
require special handling when implemented on multithreaded systems
(typically requiring the allocation of thread-specific global memory
areas).
This applies to the following functions:
- asctime()
- ctime()
- gmtime()
- localtime()
-
There is no convenient way to compare two calendar date objects,
or to determine the difference between two calendar date objects as
a componentized date object itself.
(For example, the difference between the dates 1979-01-01 and 1980-01-02 is
exactly one year and one day, as is the difference between the dates
1980-01-01 and 1981-01-02; however, the first difference represents 366 days
while the second difference represents 367 days because 1980 is a leap
year.)
Converting calendar date objects into time_t values in order to
compare them may not work, because the complete range of date objects
may not be representable as system time values.
-
There is no convenient way to perform arithmetic on componentized
date objects, such as adding a given number of days or months to a
known date in order to derive a future or past date.
Timezone limitations
-
The standard library can only handle two timezones at the same time:
UTC (GMT) and the local timezone, as provided by the
gmtime() and localtime() functions.
The local timezone is generally established as a global variable.
-
Arbitrary timezone settings cannot be applied easily to a broken-down
time object (tm structure).
This makes it difficult to convert a broken-down time object for one given
timezone into a time for another arbitrary timezone.
-
It is possible to determine the offset of the local timezone from UTC,
but this requires explicit calculations by user code
(i.e., finding the difference between the results of the
localtime() and gmtime() functions).
-
It is not possible to retrieve a timezone by name or offset from UTC
(e.g., "PST" or "+0800").
-
Once a broken-down time value has been established, either relative to
UTC or to the local timezone, its association with that timezone setting
is lost.
A broken-down time object (tm structure) contains no indication
of what timezone was used to derive its member values.
-
There is no way to convert a broken-down time object into a system
time_t value relative to any timezone other than the local timezone
(using the mktime() function).
Daylight Saving Time (DST) limitations
-
Historically, the application of Daylight Saving Time (DST) to civil
time has varied considerably.
Rules vary according to the period of history (notably World War II)
and according to region.
Some countries employed rules that resulted in up to six different
possible DST adjustments for certain periods in time.
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 diagram above illustrates the various types and their relationships
within this proposal and its related proposals.
The types and functions described in this proposal solve many of the problems
mentioned in the previous section.
-
The proposed functions
do not rely on non-constant global data objects, and
thus do not require special handling when implemented on multithreaded
systems.
(Pointers to global data returned or used by the proposed structures and
functions refer only to constant objects, which do not require special
considerations on multithreaded implementations, nor do they require
special allocation/deallocation handling within the library.)
-
Implementations are required to support at least one calendric system,
the standard proleptic Gregorian calendar.
In addition, that calendric system is required to be able to represent a
minimum range of dates (spanning two 400-year cycles of the Gregorian
calendar).
-
Special representations for
erroneous dates are provided.
-
The proposed calendar structure
is based on the existing tm structure, but adds new members:
-
The cal_type member
allows implementations to support more than one calendric system.
-
The cal_nsec member
provides for subsecond precision of times.
(This is independent of whether or not the
time_t type is capable of representing
system time with such precision.)
-
The cal_zone member
associates a calendar date object with a timezone and Daylight Saving Time
settings.
This allows the effects of the timezone to be undone when a different
timezone setting is applied to the calendar date.
It also allows for multiple date objects to exist simultaneously, each
having its own independent timezone and DST settings.
-
The cal_week member
provides the week number within the year according to the rules of
ISO 8601.
This is identical to the semantics of the "%V" format specifier
of the strftime() function.
-
The cal_wday member has a
slightly different definition from the tm_wday member
of the tm structure, in order to bring it in line with the rules
of ISO 8601.
-
The cal_year member
has a different definition
from the tm_year member of the tm structure, in that it
represents the exact year number, instead of being an offset from a
given year (1900).
This member is also used to represent erroneous dates by having a special
indicator value reserved for it.
-
The cal_mon member
has a different definition
from the tm_mon member of the tm structure, in that it
represents the exact month number, instead of being an offset from a
given month (January).
-
The cal_leapsec member
provides for leap second handling.
-
The proposed calendar structure
is also designed to be used as
a componentized delta time, representing the difference between two
calendar dates.
When a calendar date is used in this way, the members specify
the integral differences between two dates in the appropriate units of time
(e.g., the cal_year member specifies the whole number of years
between the two dates).
In addition,
The cal_ndays,
cal_nweeks,
and cal_nmons members
are used to represent the total number of days, weeks, and months difference
between two dates.
-
The proposed calendarinfo structure
allows programs to interrogate the implementation about the characteristics
of any supported calendric system.
Specifically, the structure contains members describing the acceptable ranges
for the components of calendar date objects.
(Conforming implementations are only required to support the
Gregorian calendar.)
-
The proposed timezone structure
separates the handling of timezones from the handling of calendar dates.
Information about specific timezones can be retrieved by name,
and individual timezone settings can be applied to calendar date objects.
-
Daylight Saving Time rules are combined with the timezone information,
so that there are two (or more) timezone settings
for a given calendar date value, one without DST in effect and one (or more)
with DST in effect.
-
This proposal requires conforming implementations to provide a Gregorian
calendric system that includes inserted/deleted leap seconds.
It also allows implementations the freedom to support
multiple calendric systems;
thus an implementation could provide variations of its supported
calendric systems that both do and do not handle leap seconds.
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,
in contrast to proposing that the new names be added to the existing
<time.h> header.
The choice of the name of the new header reflects its contents, namely,
standard functions dealing with time.
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
|
The following constants are defined in the
<stdtime.h> standard header.
_CAL_ERA_COMMON
_CAL_NAME_GREGORIAN
_CAL_TYPE_GREGORIAN
_CAL_YR_ERROR
Each constant is described in detail below.
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 constant 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
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 derived from the existing tm structure,
with added 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 [1601,2400]
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,60]
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; // Applied timezone and DST
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 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.
It is recommended (but not required) that within a given supported
calendric system, the values of this member form a consecutive set of
non-negative integer values.
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 [1601,2400], 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).
The term "cycle" is a synonym for "era".
- int cal_hour
-
Specifies the hour of the day
of the 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 calendar date object.
A value of INT_MIN indicates an indeterminate number.
Conforming implementations are required to support a proleptic Gregorian
calendric system that includes inserted and deleted leap seconds.
Note that calendar dates prior to 1972 do not have any leap second
adjustments.
- int cal_mday
-
Specifies the day of the month of the calendar date object.
- int cal_min
-
Specifies the minute of the hour of the calendar date object.
- int cal_mon
-
Specifies the month of the 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 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 calendar date object represents a calendar date value,
this member has no defined meaning.
- long int cal_nmons
-
When the 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 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
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 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 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 calendar date object.
Conforming implementations are required to support a Gregorian calendric
system having a range of seconds of at least [0,60].
The value of this member may include an inserted leap second, for a maximum
value of 60.
Conforming implementations are required to support a proleptic Gregorian
calendric system that includes inserted and deleted leap seconds.
Note that calendar dates prior to 1972-01-01 do not have any leap second
adjustments.
- int cal_type
-
Specifies the calendric system employed by the 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, and also includes inserted and deleted
leap second adjustments.
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 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 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 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 (proleptic) Gregorian calendar, conforming implementations are
required to support year number values within a minimum range of [1601,2400].
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.)
Within the required proleptic Gregorian calendric system, normalized member
values are restricted to being positive year numbers falling within the
common (AD/CE) era.
Whether or not non-positive year number values have meaningful interpretations
for other non-standard calendric systems is implementation-defined.
It is recommended (but not required) that implementations favor only
non-negative normalized year numbers within each era for a given supported
calendric system.
[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.
For the required proleptic Gregorian calendric system, year numbers are
constrained to being positive numbers falling within the common (AD/CE) era.
It is expected that negative year values will either be normalized to
proper year numbers within a different era (BC/BCE), or will simply be
treated as erroneous year numbers with the common era.
Mandating that conforming implementations must support years in a range of
at least AD 1601 to 2400 seems like a reasonable minimum range, which
provides for many applications that must deal with old dates (e.g.,
still-extant mortgage schedules) while also providing for dates spanning
the next few centuries.
This also parallels the range supported by the related proposed
longtime_t type.
This range spans the current and the previous 400-year cycles in the
Gregorian calendar, from 1601-01-01 to 2001-01-01 to 2401-01-01.
It is expected that quality implementations will support a wider range
of years than this.
Support for (proleptic) dates as far back as 0001-01-01 seems like a
reasonable expectation.
Since this proposal mandates support of only the proleptic Gregorian
calendric system, calendar date values representing times prior to the
adoption of the Gregorian calendar (i.e., prior to 1585 or 1782, depending
on the region) are not required to reflect actual historical dates.
- const struct timezone * cal_zone
-
A pointer to a timezone object,
specifying the timezone and Daylight Saving Time adjustments that were last
applied to the 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 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 (1601)
int ci_year_max; // Maximum year number (2400)
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 (1)
The values shown are the minimum values required for the required default
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 [1601,2400].
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 required to support a Gregorian
calendric systems that includes leap second adjustments.
- 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,60].
(Note that the extra second value of 60 is needed in order to handle
inserted leap seconds.)
[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.
(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.
If no valid system times can be converted into a date of the calendar type,
i.e., the calendric system spans dates outside the range of dates covered
by the longtime_t type, this member compares equal to the
_LONGTIME_ERROR constant.
Conforming implementations are required to support a Gregorian
calendar that can represent system times less than at least
{2401-01-01 00:00:00 Z}.
[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.
If no valid system times can be converted into a date of the calendar type,
i.e., the calendric system spans dates outside the range of dates covered
by the longtime_t type, this member compares equal to the
_LONGTIME_ERROR constant.
Conforming implementations are required to support a proleptic Gregorian
calendar that can represent system times at least as far back as
{1601-01-01 00:00:00 Z}.
[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 2400.
[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 proleptic Gregorian
calendric system having a minimum year number not greater than 1601.
[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 functions are defined in the
<stdtime.h> standard header.
calendaradd()
calendardiff()
calendarformat()
calendarscanf()
convertcalendar()
getcalendarinfo()
initcalendar()
mklongtime()
setcalendartime()
setcalendarzone()
Each function is described in detail below.
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 value.
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
calendar 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.
[Notes]
The proper way to compare two dates of different calendar types is to
normalize and convert one of the dates into a date within the calendric
system of the other date, or to convert both dates into dates within a common
calendric system (such as the standard proleptic Gregorian calendar),
and then to compare the normalized dates.
The resulting delta time can then be converted back into one of the two
original calendric systems, if so desired.
This assumes that the two calendar date objects are representable as valid
dates within both calendric systems, or in other words that the two
calendric systems span overlapping date ranges.
If this is not the case, then dates within the two disparate calendars
cannot be compared.
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 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]
|
|
%L
|
is replaced by the accumulated leap seconds adjustments.
[cal_leapsec]
|
|
%Q
|
is replaced by the locale's era name or abbreviation.
[cal_era]
|
|
%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 "%f" 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.%3f %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.%3f %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
Daylight Saving Time 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 normalize the
members of the calendar date object, but otherwise be unable to return a
valid long time value (i.e., because the resulting date is not representable
as a long time value), both the value returned by the function and the value
of the cal_year member should be examined to determine the exact
nature of the failure.
[Notes]
It is entirely possible that some calendar date values are
not representable as valid longtime_t values because they fall
outside the range of system time values supported by the implementation.
In such cases, an error is returned.
Examples
The function below converts a calendar date into its corresponding
long time value.
#include <stdio.h>
#include <stdtime.h>
longtime_t date_to_longtime(const struct calendar *date)
{
struct calendar d2;
longtime_t t;
// Print the calendar date
printf("%04d-%02d-%02d %02d:%02d:%02d.%03ld %s",
date->cal_year, date->cal_mon, date->cal_mday,
date->cal_hour, date->cal_min, date->cal_sec,
date->cal_nsec/1000000);
// Convert the calendar date into a long time
d2 = *date;
t = mklongtime(&d2, d2.cal_zone);
printf("-> %lld\n", (long long)t);
return t;
}
The following code fragment determines if a particular
{year,mon,mday} combination constitutes a valid calendar date
within the current locale's calendric system.
#include <stdbool.h>
#include <stdtime.h>
bool check_date(int yr, int mon, int day)
{
struct calendar date;
// Set the date of a new date object
initcalendar(&date, "");
date.cal_year = yr;
date.cal_mon = mon;
date.cal_mday = day;
// Verify that the date is normalized
if (mklongtime(&date, NULL) == _LONGTIME_ERROR)
return false;
if (date.cal_year != yr ||
date.cal_mon != mon ||
date.cal_mday != day)
return false;
return true;
}
The following code fragment converts the current system into a
calendar date and then back into a system time,
and then determines the difference between the before and after values.
#include <stdio.h>
#include <stdtime.h>
void convert_and_compare(void)
{
longtime_t now;
longtime_t then;
struct calendar date;
// Get the current system time
now = getlongtime();
// Convert the time into a calendar date
initcalendar(&date, _CAL_NAME_GREGORIAN);
setcalendartime(&date, NULL, now);
// Convert the date back into a system time
then = mklongtime(&date, NULL);
// Display the difference between the two system times
printf("Difference: %lld sec\n", (long long int)then - now));
}
The function below prints the long time value corresponding to the date
{2001-07-04 12:00:00 Z}.
#include <stdio.h>
#include <stdtime.h>
#include <string.h>
longtime_t time_2001()
{
struct calendar date;
longtime_t t;
// Set the calendar date
initcalendar(&date, _CAL_NAME_GREGORIAN);
date.cal_era = _CAL_ERA_COMMON;
date.cal_year = 2001;
date.cal_mon = 7;
date.cal_mday = 4;
date.cal_hour = 12;
// Convert the calendar date into a long time
t = mklongtime(&date, NULL);
printf("04 Jul 2001: %lld\n", (long long)t);
return t;
}
The following function adds 60 days to a given date and normalizes it
into a valid calendar date.
#include <stdbool.h>
#include <stdio.h>
#include <stdtime.h>
bool add_60d(struct calendar *date)
{
// Add 60 days to the date and renormalize it
date->cal_mday += 60;
return mklongtime(date, date->cal_zone) != _LONGTIME_ERROR;
}
The following function converts a count of the number of seconds since
{1970-01-01 00:00:00 Z}, sans leap seconds,
into its corresponding calendar date.
#include <stdlib.h>
#include <stdtime.h>
struct calendar * posix_date(long secs)
{
struct calendar * date;
// Initialize a date object
date = malloc(sizeof(struct calendar));
initcalendar(date, _CAL_NAME_GREGORIAN);
// Convert the POSIX seconds count into a date
date->cal_era = _CAL_ERA_COMMON;
date->cal_year = 1970;
date->cal_mday = (int)(secs / (24L*60*60));
secs %= 24L*60*60;
date->cal_hour = (int)(secs / (60*60));
secs %= 60*60;
date->cal_sec = (int)(secs);
// Normalize the date
mklongtime(date, NULL);
return date;
}
See also
_CAL_YR_ERROR,
_LONGTIME_ERROR,
struct calendar,
longtime_t,
struct timezone,
setcalendartime().
6.9 Function setcalendartime()
Synopsis
#include <stdtime.h>
int setcalendartime(struct calendar *date, const struct timezone *zone,
longtime_t t);
Description
Converts a long system time value into a calendar date for a
given timezone.
Argument t is a long system time value.
If it is equal to the _LONGTIME_ERROR constant, the function
fails.
Argument date points to a calendar date object,
whose members are modified to reflect the date and time represented by
the long time value t.
If the cal_type member of the date object has a value that
does not designate a calendric system supported by the implementation,
the function fails.
Argument zone points to a timezone object, specifying the timezone
and Daylight Saving Time adjustments to apply to the resulting calendar date.
The cal_zone member of the date object is set to point to this
timezone object, and the cal_dsti member is modified to reflect the
index of one of the members of the tz_z member of the timezone object
that represents the appropriate zone and Daylight Saving Time offset that
applies to the resulting calendar date.
This argument may be null, in which case no timezone or DST adjustments
will be made to the converted date, i.e., it will be treated as if it
represents a date relative to UTC.
The cal_leapsec member of date is set to the total number
of accumulated leap seconds within long time
value t.
If the number of leap seconds cannot be determined, the member is set to
INT_MIN.
If the resulting converted date and time cannot be properly represented by a
calendar date object, the function fails.
Returns
On success, the function returns zero.
On failure, the cal_year member of the date object is set to a
value equal to _CAL_YR_ERROR,
and the function returns an negative value.
Except for cal_type, the remaining members of the date object
have indeterminate values.
[Notes]
This function operates similarly to the gmtime() and
localtime() functions, converting a
longtime_t value into a calendar date.
However, it performs the date conversion with respect to a specific timezone
and DST variants.
It also handles erroneous time values.
If there is no timezone setting specified, the converted date
represents a "bare" date and time irrespective of any timezone,
or in other words, a date and time with respect to UTC.
It is possible that some longtime_t values are not
representable as valid calendar dates because they fall outside
the range of dates supported by the implementation.
In such cases, the function fails.
Example
The following function retrieves the current time and converts it into a
calendar date object for the local timezone.
#include <stdlib.h>
#include <stdtime.h>
struct calendar * get_date(void)
{
struct timezone * zone;
struct calendar * date;
// Allocate and initialize a timezone object
zone = malloc(sizeof(*zone));
inittimezone(zone, "");
// Allocate and initialize a calendar date object
date = malloc(sizeof(*date));
initcalendar(date, "");
date->cal_zone = zone;
// Set the calendar date to the current system time
setcalendartime(date, zone, getlongtime());
return date;
}
See also
_LONGTIME_ERROR,
struct calendar,
struct timezone,
mklongtime(),
setcalendarzone().
6.10 Function setcalendarzone()
Synopsis
#include <stdtime.h>
int setcalendarzone(struct calendar *date,
const struct timezone *oldzone, const struct timezone *newzone);
Description
Applies a specified timezone to a calendar date object, after first undoing
the effects of another timezone previously applied to it.
Argument date points to a calendar date object to be modified.
The members of the object are modified appropriately by the timezone adjustments
(in particular, the cal_dsti and cal_zone members),
and then normalized so that they fall within their normal ranges.
Argument oldzone points to a timezone object, representing the
timezone that was previously applied to the date object.
The pointer argument may be null, in which case the date object is assumed
to have no previously applied timezone or Daylight Saving Time adjustments,
i.e., the date object is treated as if it represents a time relative to UTC.
[Notes]
This argument is typically the same as the cal_zone member of
the date object, i.e., date->cal_zone.
Argument newzone points to a timezone object, representing the
timezone and Daylight Saving Time adjustments to apply to the date object.
The pointer argument may be null, in which case no timezone or
Daylight Saving Time adjustments are applied, i.e., the timezone object is
treated as if it is equivalent to UTC.
If the cal_type member of the date object has a value that is
not supported by the implementation, the function fails.
If the adjusted calendar date cannot be normalized into a meaningful date,
the function fails.
Returns
If successful, the cal_zone member of the date object is set to
point to the timezone object pointed to by newzone,
and the function returns zero.
If the function fails, the cal_year member of the date object
is set to a value equal to _CAL_YR_ERROR,
and the function returns a negative value.
The remaining members of the date object have indeterminate values.
[Notes]
Given a date object imbued with a specific date and time, and a timezone
object for a specific timezone, that timezone object can be "applied to" the
date object, which modifies the members of the date object in the
appropriate fashion so that the resulting member values represent the same
date and time but in the given timezone.
Thus, applying a timezone to a date object is equivalent to shifting the
original calendar date value from the UTC timezone into another timezone
by adding the timezone offset.
Once a date object has had a timezone applied to it, it remembers that
particular timezone setting (by setting its cal_zone member).
Multiple date objects can share (point to) the same timezone object,
since the cal_zone member points to a const timezone object.
The effects of applying a timezone to a date object can also be undone
by calling the setcalendarzone() function
on the object, applying a different (or no) timezone.
This is equivalent to shifting a calendar date value from one particular
timezone to another.
Applying a null timezone setting to a date object essentially removes all
effects of any timezone, thus making the calendar date relative to no
specific timezone (making it a date relative to UTC).
Example
The following function reverses the Daylight Saving Time adjustments
of a given calendar date.
#include <stdtime.h>
void undo_dst(struct calendar *date)
{
int adj;
// Check the date object
if (date == NULL)
return;
if (date->cal_zone == NULL)
return;
// Undo the DST adjustment
if (date->cal_dsti > 0)
{
// Determine the DST adjustment
adj = date->cal_zone.tz_z[date->cal_dsti].z_dst;
date->cal_sec += adj;
// Renormalize the date
mklongtime(date, NULL);
}
}
See also
struct calendar,
struct timezone,
setcalendartime().
7. Examples
|
Ah, but my calculations, people say,
Have squared the year to human Compass, eh?
If so, by striking from the calendar
Unborn tomorrow and dead yesterday.
Omar Khayyám,
The Rubáiyát, ca. 1100
|
The following code examples illustrate the use of
the various proposed calendar date functions.
Example 1
The following function determines the current date and time,
converts it into a calendar date,
formats it as a printable string,
and writes it to the standard output.
#include <stdio.h>
#include <stdtime.h>
void print_time(void)
{
struct timezone zone;
struct calendar date;
char buf[80];
// Get a timezone for the locale and local timezone
inittimezone(&zone, "");
// Get a date object for the locale and local timezone
initcalendar(&date, _CAL_NAME_GREGORIAN);
// Set the calendar date to the current system time
setcalendartime(&date, &zone, getlongtime());
// Format the calendar date for printing
calendarformat(buf, sizeof(buf),
"%a %Y-%m-%d %H:%M:%S.%3f %Z", &date, &zone);
// Display the current date and time
printf("%s\n", buf);
}
Example output from this function:
Thu 2002-12-13 09:45:30.166 CST
The function above is almost equivalent to the following function,
which uses the existing C99 tm structure functions.
#include <stdio.h>
#include <stdtime.h>
void print_time2(void)
{
struct tm ts;
time_t now;
char buf[80];
// Get the current time
time(&now);
// Convert the current time to a calendar date
ts = *localtime(&now);
// Format the calendar date for printing
strftime(buf, sizeof(buf),
"%a %Y-%m-%d %H:%M:%S.000 %Z", &ts);
// Display the current date and time
printf("%s\n", buf);
}
Example 2
The following function changes the timezone setting of a calendar date
from its original timezone to a different one.
#include <stdbool.h>
#include <stdlib.h>
#include <stdtime.h>
bool change_tz(struct calendar *date, const char *tzname)
{
struct timezone * zone;
// Allocate and initialize a new timezone object
zone = malloc(sizeof(*zone));
if (inittimezone(zone, tzname) != 0)
{
free(zone);
return false;
}
// Change the timezone settings of the calendar date
return setcalendarzone(date, date->cal_zone, zone) == 0;
}
Example 3
The following function creates a calendar date of
{2001-09-11 08:50:00 EDT}
(where EDT is Eastern Daylight Time, 5 hours west of UTC),
then adds 60 days to it.
#include <stdio.h>
#include <stdtime.h>
void add_60_days(void)
{
struct timezone zone;
struct calendar date;
char buf[80];
// Build a date of 2001-09-11 08:50:00 EDT
inittimezone(&zone, "EST5EDT");
initcalendar(&date, _CAL_NAME_GREGORIAN);
date.cal_year = 2001;
date.cal_mon = 9;
date.cal_mday = 11;
date.cal_hour = 8;
date.cal_min = 50;
// Format and print the date
calendarformat(buf, sizeof(buf),
"%a %Y-%b-%d %H:%M %Z", &date, &zone);
printf("%s\n", buf);
// Add 60 days to the date and renormalize it
date.cal_mday += 60;
mklongtime(&date, &zone);
// Format and print the new date
calendarformat(buf, sizeof(buf),
"%a %Y-%b-%d %H:%M %Z", &date, &zone);
printf("%s\n", buf);
}
Adding 60 days to the original date results in a new date that is no longer
in Daylight Saving Time, so the timezone changes from EDT to EST.
The output from this function is:
Tue 2001-Sep-11 08:50 EDT
Sat 2001-Nov-10 07:50 EST
Example 4
The following function determines on what day of the week
the date 1776-07-04 occurs
(within the proleptic Gregorian calendar).
#include <stdio.h>
#include <stdtime.h>
void weekday_1776(void)
{
struct calendar date;
char buf[80];
// Build a date of 1776-07-04 12:00:00 Z
initcalendar(&date, _CAL_NAME_GREGORIAN);
date.cal_year = 1776;
date.cal_mon = 7;
date.cal_mday = 4;
date.cal_hour = 12;
// Determine the weekday by normalizing the date
mklongtime(&date, NULL);
// Print the resulting day of the week
calendarformat(buf, sizeof(buf), "%a", &date, NULL);
printf("%s\n", buf);
}
Example 5
The following function converts calendar dates
from structure type tm to structure type calendar.
#include <stdtime.h>
#include <time.h>
// Convert a 'tm' struct into a 'calendar' struct
void tm_to_calendar(struct calendar *date, const struct tm *ts)
{
int v;
/* Fill the contents of 'cal' from 'ts' */
initcalendar(date, _CAL_NAME_GREGORIAN);
// Set the calendar members
v = ts->tm_year + 1900;
date->cal_era = (v > 0 ? _CAL_ERA_COMMON : _CAL_ERA_COMMON+1);
date->cal_year = (v > 0 ? v : -v + 1);
date->cal_mon = ts->tm_mon + 1;
date->cal_mday = ts->tm_mday;
date->cal_yday = ts->tm_yday + 1;
v = (ts->tm_wday != 0 ? ts->tm_wday : 7);
date->cal_wday = v;
date->cal_week = (ts->tm_yday - v + 7+4)/7; // Simplified
date->cal_hour = ts->tm_hour;
date->cal_min = ts->tm_min;
date->cal_sec = ts->tm_sec;
date->cal_nsec = 0; // Unknown, assume zero
v = ts->tm_isdst;
date->cal_dsti = (v < 0 ? -1 : v > 0 ? 1 : 0);
date->cal_zone = NULL; // Unknown, assume UTC
}
Example 6
The following function converts calendar dates
from structure type calendar to structure type tm.
#include <stdtime.h>
#include <time.h>
// Convert a 'calendar' struct into a 'tm' struct
int calendar_to_tm(struct tm *ts, const struct calendar *date)
{
int v;
// Validate the calendar date object
if (date->cal_type != _CAL_TYPE_GREGORIAN)
return -1;
// Check for special dates
if (date->cal_year == _CAL_YR_ERROR)
return -1;
// Set the tm members
v = date->cal_year;
v = (date->cal_era == _CAL_ERA_COMMON ? v : -v+1);
ts->tm_year = v - 1900;
ts->tm_mon = date->cal_mon - 1;
ts->tm_mday = date->cal_mday;
ts->tm_yday = date->cal_yday - 1;
ts->tm_wday = date->cal_wday % 7;
ts->tm_hour = date->cal_hour;
ts->tm_min = date->cal_min;
ts->tm_sec = date->cal_sec;
v = 0;
if (date->cal_zone != NULL)
v = date->cal_zone->tz_z[date->cal_dsti].z_dst;
ts->tm_isdst = v;
return 0;
}
Appendix A – Further Discussion
- [<stdtime.h>]
-
A completely new header (<stdtime.h>) is proposed
in order to reduce the impact that the proposed functions will have on
existing code.
The new header alone does not eliminate conflicts between user code and
the standard runtime library, but it does help isolate the new additions
to the library.
The alternative is to add the proposed types, constants, and functions
to the existing <time.h> header.
- [calendar]
-
It was considered giving the calendar structure a different name,
such as tmx, tm2, or _date.
The actual name presented in this proposal is unimportant, since it is a
fairly arbitrary choice that can be made by the ISO committee at a later
time.
- [namespace]
-
The proposed type and function names in this proposal intrude into the user
namespace.
The proposed constants all begin with an underscore followed by an uppercase
letter, and do not intrude into the user namespace.
The final choice of names is arbitrary and can be
finalized by the ISO committee.
- [cal_vers]
-
A previous revision of this proposal defined xx_vers
members in each of the proposed structures, specifying the revision
number of each structure type.
This was supposed to have made it easier to make changes to these structures
in future revisions of the standard library, with an eye towards providing
binary backward compatibility.
However, it was felt that these members were a bit overdesigned,
having no basis in prior art in the existing ISO C standard,
and they were subsequently removed.
- [cal_type]
-
It was considered making the cal_type member be a pointer to a
calendarinfo structure instead of an integer type number.
However, this scheme has the disadvantage of making it more likely that a
program execution error could occur if that member has been corrupted.
(Checking for a null value is easy, but checking for a non-null but erroneous
pointer value is not a simple matter.)
While using a pointer would have made it somewhat easier to access the
calendric information for a date object, it was felt that the use of a
simple integer value was safer, and that it was not really all that difficult
to use the integer value to retrieve the calendric information (using the
getcalendarinfo() function).
- [getcalendarinfobyname()]
-
There is no proposed function for retrieving a calendarinfo
object for a given calendric system by name.
It was felt that providing such a function would be overkill, since it is
rather trivial to first initialize a calendar object by name,
and then use its cal_type member to retrieve the appropriate
calendarinfo object.
(See the example provided in the description of the getcalendarinfo()
function.)
- [calendartotm()]
[tmtocalendar()]
-
There are no proposed functions for converting a calendar object
into a tm object or vice versa.
See Example 5 and Example 6
for code that does this.
- [cal_zone]
-
The cal_zone member is defined as a pointer to a const
timezone object.
The intent is to allow multiple calendar objects to share the
same timezone object.
Defining the pointer as a const pointer simplifies the requirements by
eliminating the need for explicit malloc() and free() calls.
An alternative approach is to define the cal_zone member as a
timezone structure, being nested inside the calendar object.
This approach eliminates any possibility of dangling pointers
(which could arise if the timezone object pointed to by a given
cal_zone member is deallocated without setting the member pointer
to null), but at the cost
of making the calendar structure much larger.
It is also possible to completely remove the cal_zone member.
This is possible because all of the proposed functions that require a
timezone object for their operation in fact take a timezone parameter
(or two).
The argument passed for these parameters is usually identical to the
cal_zone member of the date object that is also passed to
the functions.
- [tm_gmtoff]
-
The BSD and GNU standard C libraries add a non-standard tm_gmtoff
member to the tm structure, which specifies the offset from UTC
of the time represented by the structure.
This same functionality is provided by the proposed cal_zone member.
- [locale]
-
The existing LC_TIME setting affects the operation of the
calendarformat() function.
There may need to be additions made to the locale to support calendar
settings and attributes, which would affect the operation of the
initcalendar() function and possibly others.
- [calendarformat()]
-
The eventual names of the calendarformat() function and the
calendarscanf() function should probably be chosen to reflect their
inverse relationship with each other.
This is similar to the existing pairing of names for the
printf()/scanf() and
strftime()/strptime() functions.
- [cal_era]
-
It was considered making the cal_era member be a pointer to a
constant static string, such as "AD" or "BCE".
However, it was felt that it would be more useful and less complicated for
the implementation if the member is simply an integer.
- [short]
-
It was considered making the members of the
calendar structure that are type int
(with the possible exception of the cal_year member)
be defined as type short instead, in order to reduce the total size
of the structure.
The complicating factor here is how to handle member values that exceed their
normal ranges, such as the values they might acquire immediately prior to
being normalized by a call to the mklongtime() function.
Also, the size of the structire seems like a relatively unimportant concern.
- [int32_t]
-
It was considered defining the various structure members to be type
int16_t and int32_t.
However, this would complicate the use of the members because
it implies the use of the <inttypes.h> header, and
it adds the need for explicit casts in many circumstances.
Consider how it would complicate the following function calls, for example:
sscanf(buf, "%d %d %d",
&c.cal_year, &c.cal_mon, &c.cal_mday);
printf("%04d-%02d-%02d",
c.cal_year, c.cal_mon, c.cal_mday);
Appendix B – Other Proposed Types
This proposal is written in concert with two other proposals which define the
timezone structure (Proposal [P1]) and the
longtime_t type (Proposal [P2]).
Some of the functions defined in this proposal make use of these types.
If these other proposed types are not adopted by ISO C,
the functions defined in this proposal can be replaced with functions taking
equivalent arguments of types that are already defined in the standard
<time.h> header.
Specifically, these functions:
longtime_t mklongtime(const struct calendar *date, const timezone *zone);
int setcalendartime(struct calendar *date,
const struct timezone *zone, longtime_t t);
can be replaced with the functionally equivalent definitions:
time_t calendartime(const struct calendar *date,
const timezone *zone);
// Returns (time_t)(-1) on failure.
int setcalendartime(struct calendar *date,
const struct timezone *zone, time_t t);
References
|
Day, n.
A period of twenty-four hours, mostly misspent.
This period is divided into two parts, the day proper and the night,
or day improper the former devoted to sins of business,
the latter consecrated to the other sort.
These two kinds of social activity overlap.
Ambrose Bierce,
The Devil's Dictionary, 1911
|
- [1]
ISO/IEC 9899:1999(E)
-
Programming Language C
Second edition, Dec 1999, ISO/IEC.
Available at
http://www.ansi.org.
- [2]
ISO/IEC 9899:201X
-
Programming Language C
Committee Draft, Mar 2009, ISO/IEC N1362.
Available in PDF format at
www.open-std.org/JTC1/SC22/WG14/www/docs/n1336.pdf.
- [3]
ISO 8601:2000(E)
-
Data elements and interchange formats – Information interchange
-
– Representation of dates and times
Second edition, Dec 2000, ISO/IEC, TC 154.
Available at
http://www.iso.org.
A draft version in PDF format is at
http://xml.coverpages.org/ISO-FDIS-8601.pdf
(see also
http://xml.coverpages.org/related.html#iso8601).
- [4]
ISO/IEC 14882:1998(E)
-
Programming Language C++
First edition, Sep 1998, ISO/IEC.
Available at
http://www.ansi.org.
- [5]
Java API Reference
-
Class java.util.Calendar,
-
Class java.util.TimeZone
Java 2 Standard Edition (SE) 1.4,
Sun Microsystems, Inc.,
2001.
http://java.sun.com/j2se/1.4/docs/api/java/util/Calendar.html,
http://java.sun.com/j2se/1.4/docs/api/java/util/TimeZone.html.
- [6]
The Calendar
-
The 5000-Year Struggle to Align the Clock and the Heavens
-
– and What Happened to the Missing Ten Days
-
David Ewing Duncan, 1998.
Fourth Estate Ltd., London,
ISBN 1-85702-979-8.
Search
http://www.amazon.com.
- [7]
Systems of Time
-
U.S. Naval Observatory – Time Service Department, Jun 1998.
http://tycho.usno.navy.mil/systime.html.
- [8]
Leap Seconds
-
U.S. Naval Observatory – Time Service Department, May 1999.
http://tycho.usno.navy.mil/leapsec.html.
- [9]
A Summary of the International Standard Date and Time Notation
-
Markus Kuhn, May 1996.
http://www.cl.cam.ac.uk/~mgk25/iso-time.html.
- [10]
The Best of Dates, The Worst Of Dates
-
Gilbert Healton, 2002-02-15
http://www.exit109.com/~ghealton/y2k/yrexamples.html.
A general summary of calendars, dates, and times from past to the present.
This includes details on using ISO 8601 in and out of computers.
Ends with information for software developers wanting to properly use dates
and times.
- [11]
Year Date and Time Calculation Index -
How people and computers use dates and times
-
Gilbert Healton.
http://www.exit109.com/~ghealton/y2k/.
- [12]
Technical Recommendation (Annex) ITU-R TF.460-5
-
Recommendation regarding the application of leap seconds to UTC time
International Telecommunication Union.
http://www.itu.int.
- [13]
Daylight Saving Time
-
The history of daylight saving, from Benjamin Franklin to the present.
http://webexhibits.org/daylightsaving.
- [14]
Calendars from the Sky
-
Institute for Dynamic Educational Advancement (IDEA),
by WebExhibits.
http://webexhibits.org/calendars.
- [15]
ISO 8601 references
-
DMOZ, The Open Directory Project.
http://dmoz.org/Science/Reference/Standards/Individual_Standards/ISO_8601.
- [16]
Frequently Asked Questions about Calendars
-
Claus Třndering, Oct 2001.
http://www.tondering.dk/claus/calendar.html.
- [17]
Wikipedia: Calendar
-
http://en.wikipedia.org/wiki/Calendar.
- [18]
Wikipedia: Leap second
-
http://en.wikipedia.org/wiki/Leap_second.
- [19]
Wikipedia: Daylight Saving Time
-
http://en.wikipedia.org/wiki/Daylight_saving_time.
- [20]
Wikipedia: ISO 8601
-
http://en.wikipedia.org/wiki/ISO_8601.
Related Proposals
- [P1]
ISO C 201X Proposal: Timezone Functions
-
David R. Tribble,
Jun 2004.
http://david.tribble.com/text/c0xtimezone.html
A related proposal specifying types and functions for managing
timezone and Daylight Saving Time information.
Defines the timezone structure used in the current proposal.
- [P2]
ISO C 201X Proposal: Long Time Type
-
David R. Tribble,
Jun 2004.
http://david.tribble.com/text/c0xlongtime.html
A related proposal specifying a long time type and supporting functions
for manipulating system time values.
Defines the longtime_t type used in the current proposal.
- [P3]
Additional Constraints on time_t
-
David R. Tribble,
Feb 2001.
http://david.tribble.com/text/c0xtimet.htm.
A related proposal describing improvements to be made to the
existing standard time_t library type.
- [P4]
Proposal for an ISO C and C++ Extended-Range Time Type
-
David R. Tribble,
Sep 1999.
http://david.tribble.com/text/c0xtime.htm.
An earlier proposal (since abandoned) for an extended-range,
extended-precision system time type.
- [P5]
Proposal for a Smoothed Coordinated Universal Time (UTS)
-
Markus Kuhn,
Oct 2000.
http://www.cl.cam.ac.uk/~mgk25/uts.txt.
- [P6]
Proposed new <time.h> for ISO C 200X
-
Markus Kuhn,
2002.
http://www.cl.cam.ac.uk/~mgk25/time/c
A proposal for a properly engineered replacement for the
<time.h> API.
- [P7]
Modernized <time.h> API for ISO C
-
Paul Eggert,
Jan 2000.
http://www.twinsun.com/tz/timeapi.html.
A proposed extension to the standard C application programming
interface (API) for time.
- [P8]
A proposal for thread-safe time zone information
-
Jonathan Lennox,
Columbia University,
Jun 2001 (Version 2).
http://www.cl.cam.ac.uk/~mgk25/time/c/proposal-lennox.txt
A proposal for thread-safe time zone information, providing a set of
extensions to ISO C99 and IEEE POSIX (200x).
Source Code
The following source files provide a proof-of-concept implementation of
this proposal.
Acknowledgments
My thanks to the people who gave helpful comments on earlier revisions of
this document, especially to those individuals who emailed me
suggestions and corrections or posted comments on the
comp.std.c
newsgroup.
Revision History
- 2.4, 2009-10-01.
-
Added the requirement for the default Gregorian calendar to support
leap seconds, to bring it in line with the longtime_t proposal.
Extended the minimum required range of years (cal_year) to cover an
earlier minimum year number (AD 1601 instead of 1900), to parallel
the range provided by the proposed longtime_t type.
Corrected the maximum required year number to AD 2400, which is the
last year of the current 400-year Gregorian cycle.
Clarified the requirements for the cal_era and cal_year
members.
Clarified the requirements for the ci_time_min and
ci_time_max members.
Fixed the "big picture" diagram in the "Solutions" section.
- 2.3, 2009-08-25.
-
Updated the title from an ISO 200X to an ISO 201X proposal.
Added the "big picture" diagram in the "Solutions" section.
Added more intra-document links.
Improved section headers and paging.
Changed the "%f" and "%s" format specifiers of the
calendarformat() function to "%Q" and "%f".
Added references to Wikipedia articles.
- 2.2, 2006-06-12.
-
Changed the semantics of the cal_week member to agree with the
rules of ISO 8601 and the "%V" format specifier of
strftime().
Cleaned up the definition and examples of the "%Z" format specifier
of the calendarformat() function.
Replaced references to "GMT" with "UTC".
Replaced most instances of "broken-down time" with "componentized date".
Fixed some example functions.
- 2.1, 2006-03-12.
-
Added member cal_leapsec to the calendar structure.
- 2.0, 2004-08-15.
-
Completely rewritten.
Simplified by removing unnecessary functions.
Removed all leading underscores from type and function names.
All proposed constants, types, variables, and functions reside in a new
<stdtime.h> header.
Integrated types and functions with the proposed longtime_t
type.
Removed the calendardelta structure, combining some of its members
into the calendar structure.
Simplified the use of the timezone structure.
- 1.6, 2002-06-10.
-
This previous revision can be found at:
http://david.tribble.com/text/c0xcalendar_1.html.
This document is in the public domain,
and is not subject to any copyright restrictions.
This document:
http://david.tribble.com/text/c0xcalendar.html.
The author can be reached by email at david@tribble.com.
The author's home web page is at
http://david.tribble.com.