Copy lib/libc/stdtime to contrib/tzcode/stdtime for the proper split

of contributed code and FreeBSD specific code.
This commit is contained in:
Edwin Groothuis 2010-02-25 21:21:34 +00:00
parent 97dcdbd450
commit 522a35959a
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=204333
17 changed files with 5595 additions and 0 deletions

View file

@ -0,0 +1,18 @@
# Makefile.inc,v 1.2 1994/09/13 21:26:01 wollman Exp
# $FreeBSD$
.PATH: ${.CURDIR}/stdtime ${.CURDIR}/../locale
SRCS+= asctime.c difftime.c localtime.c strftime.c strptime.c timelocal.c \
time32.c
SYM_MAPS+= ${.CURDIR}/stdtime/Symbol.map
MAN+= ctime.3 strftime.3 strptime.3 time2posix.3
MAN+= tzfile.5
MLINKS+=ctime.3 asctime.3 ctime.3 difftime.3 ctime.3 gmtime.3 \
ctime.3 localtime.3 ctime.3 mktime.3 ctime.3 timegm.3 \
ctime.3 ctime_r.3 ctime.3 localtime_r.3 ctime.3 gmtime_r.3 \
ctime.3 asctime_r.3
MLINKS+=time2posix.3 posix2time.3

View file

@ -0,0 +1,35 @@
/*
* $FreeBSD$
*/
FBSD_1.0 {
_time32_to_time;
_time_to_time32;
_time64_to_time;
_time_to_time64;
_time_to_long;
_long_to_time;
_time_to_int;
_int_to_time;
strptime;
strftime;
tzname;
tzsetwall;
tzset;
localtime;
localtime_r;
gmtime;
gmtime_r;
offtime;
ctime;
ctime_r;
mktime;
timelocal;
timegm;
timeoff;
time2posix;
posix2time;
difftime;
asctime_r;
asctime;
};

View file

@ -0,0 +1,142 @@
/*
** This file is in the public domain, so clarified as of
** 1996-06-05 by Arthur David Olson.
*/
/*
** Avoid the temptation to punt entirely to strftime;
** the output of strftime is supposed to be locale specific
** whereas the output of asctime is supposed to be constant.
*/
#include <sys/cdefs.h>
#ifndef lint
#ifndef NOID
static char elsieid[] __unused = "@(#)asctime.c 8.2";
#endif /* !defined NOID */
#endif /* !defined lint */
__FBSDID("$FreeBSD$");
/*LINTLIBRARY*/
#include "namespace.h"
#include "private.h"
#include "un-namespace.h"
#include "tzfile.h"
/*
** Some systems only handle "%.2d"; others only handle "%02d";
** "%02.2d" makes (most) everybody happy.
** At least some versions of gcc warn about the %02.2d;
** we conditionalize below to avoid the warning.
*/
/*
** All years associated with 32-bit time_t values are exactly four digits long;
** some years associated with 64-bit time_t values are not.
** Vintage programs are coded for years that are always four digits long
** and may assume that the newline always lands in the same place.
** For years that are less than four digits, we pad the output with
** leading zeroes to get the newline in the traditional place.
** The -4 ensures that we get four characters of output even if
** we call a strftime variant that produces fewer characters for some years.
** The ISO C 1999 and POSIX 1003.1-2004 standards prohibit padding the year,
** but many implementations pad anyway; most likely the standards are buggy.
*/
#ifdef __GNUC__
#define ASCTIME_FMT "%.3s %.3s%3d %2.2d:%2.2d:%2.2d %-4s\n"
#else /* !defined __GNUC__ */
#define ASCTIME_FMT "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %-4s\n"
#endif /* !defined __GNUC__ */
/*
** For years that are more than four digits we put extra spaces before the year
** so that code trying to overwrite the newline won't end up overwriting
** a digit within a year and truncating the year (operating on the assumption
** that no output is better than wrong output).
*/
#ifdef __GNUC__
#define ASCTIME_FMT_B "%.3s %.3s%3d %2.2d:%2.2d:%2.2d %s\n"
#else /* !defined __GNUC__ */
#define ASCTIME_FMT_B "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %s\n"
#endif /* !defined __GNUC__ */
#define STD_ASCTIME_BUF_SIZE 26
/*
** Big enough for something such as
** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n
** (two three-character abbreviations, five strings denoting integers,
** seven explicit spaces, two explicit colons, a newline,
** and a trailing ASCII nul).
** The values above are for systems where an int is 32 bits and are provided
** as an example; the define below calculates the maximum for the system at
** hand.
*/
#define MAX_ASCTIME_BUF_SIZE (2*3+5*INT_STRLEN_MAXIMUM(int)+7+2+1+1)
static char buf_asctime[MAX_ASCTIME_BUF_SIZE];
/*
** A la ISO/IEC 9945-1, ANSI/IEEE Std 1003.1, 2004 Edition.
*/
char *
asctime_r(timeptr, buf)
const struct tm * timeptr;
char * buf;
{
static const char wday_name[][3] = {
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};
static const char mon_name[][3] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
const char * wn;
const char * mn;
char year[INT_STRLEN_MAXIMUM(int) + 2];
char result[MAX_ASCTIME_BUF_SIZE];
if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK)
wn = "???";
else wn = wday_name[timeptr->tm_wday];
if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR)
mn = "???";
else mn = mon_name[timeptr->tm_mon];
/*
** Use strftime's %Y to generate the year, to avoid overflow problems
** when computing timeptr->tm_year + TM_YEAR_BASE.
** Assume that strftime is unaffected by other out-of-range members
** (e.g., timeptr->tm_mday) when processing "%Y".
*/
(void) strftime(year, sizeof year, "%Y", timeptr);
/*
** We avoid using snprintf since it's not available on all systems.
*/
(void) sprintf(result,
((strlen(year) <= 4) ? ASCTIME_FMT : ASCTIME_FMT_B),
wn, mn,
timeptr->tm_mday, timeptr->tm_hour,
timeptr->tm_min, timeptr->tm_sec,
year);
if (strlen(result) < STD_ASCTIME_BUF_SIZE || buf == buf_asctime) {
(void) strcpy(buf, result);
return buf;
} else {
#ifdef EOVERFLOW
errno = EOVERFLOW;
#else /* !defined EOVERFLOW */
errno = EINVAL;
#endif /* !defined EOVERFLOW */
return NULL;
}
}
/*
** A la ISO/IEC 9945-1, ANSI/IEEE Std 1003.1, 2004 Edition.
*/
char *
asctime(timeptr)
const struct tm * timeptr;
{
return asctime_r(timeptr, buf_asctime);
}

View file

@ -0,0 +1,374 @@
.\" Copyright (c) 1989, 1991, 1993
.\" The Regents of the University of California. All rights reserved.
.\"
.\" This code is derived from software contributed to Berkeley by
.\" Arthur Olson.
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 4. Neither the name of the University nor the names of its contributors
.\" may be used to endorse or promote products derived from this software
.\" without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" From: @(#)ctime.3 8.1 (Berkeley) 6/4/93
.\" $FreeBSD$
.\"
.Dd January 2, 1999
.Dt CTIME 3
.Os
.Sh NAME
.Nm asctime ,
.Nm asctime_r ,
.Nm ctime ,
.Nm ctime_r ,
.Nm difftime ,
.Nm gmtime ,
.Nm gmtime_r ,
.Nm localtime ,
.Nm localtime_r ,
.Nm mktime ,
.Nm timegm
.Nd transform binary date and time values
.Sh LIBRARY
.Lb libc
.Sh SYNOPSIS
.In time.h
.Vt extern char *tzname[2] ;
.Ft char *
.Fn ctime "const time_t *clock"
.Ft double
.Fn difftime "time_t time1" "time_t time0"
.Ft char *
.Fn asctime "const struct tm *tm"
.Ft struct tm *
.Fn localtime "const time_t *clock"
.Ft struct tm *
.Fn gmtime "const time_t *clock"
.Ft time_t
.Fn mktime "struct tm *tm"
.Ft time_t
.Fn timegm "struct tm *tm"
.Ft char *
.Fn ctime_r "const time_t *clock" "char *buf"
.Ft struct tm *
.Fn localtime_r "const time_t *clock" "struct tm *result"
.Ft struct tm *
.Fn gmtime_r "const time_t *clock" "struct tm *result"
.Ft char *
.Fn asctime_r "const struct tm *tm" "char *buf"
.Sh DESCRIPTION
The functions
.Fn ctime ,
.Fn gmtime
and
.Fn localtime
all take as an argument a time value representing the time in seconds since
the Epoch (00:00:00
.Tn UTC ,
January 1, 1970; see
.Xr time 3 ) .
.Pp
The function
.Fn localtime
converts the time value pointed at by
.Fa clock ,
and returns a pointer to a
.Dq Fa struct tm
(described below) which contains
the broken-out time information for the value after adjusting for the current
time zone (and any other factors such as Daylight Saving Time).
Time zone adjustments are performed as specified by the
.Ev TZ
environment variable (see
.Xr tzset 3 ) .
The function
.Fn localtime
uses
.Xr tzset 3
to initialize time conversion information if
.Xr tzset 3
has not already been called by the process.
.Pp
After filling in the tm structure,
.Fn localtime
sets the
.Fa tm_isdst Ns 'th
element of
.Fa tzname
to a pointer to an
.Tn ASCII
string that is the time zone abbreviation to be
used with
.Fn localtime Ns 's
return value.
.Pp
The function
.Fn gmtime
similarly converts the time value, but without any time zone adjustment,
and returns a pointer to a tm structure (described below).
.Pp
The
.Fn ctime
function
adjusts the time value for the current time zone in the same manner as
.Fn localtime ,
and returns a pointer to a 26-character string of the form:
.Bd -literal -offset indent
Thu Nov 24 18:22:48 1986\en\e0
.Ed
.Pp
All the fields have constant width.
.Pp
The
.Fn ctime_r
function
provides the same functionality as
.Fn ctime
except the caller must provide the output buffer
.Fa buf
to store the result, which must be at least 26 characters long.
The
.Fn localtime_r
and
.Fn gmtime_r
functions
provide the same functionality as
.Fn localtime
and
.Fn gmtime
respectively, except the caller must provide the output buffer
.Fa result .
.Pp
The
.Fn asctime
function
converts the broken down time in the structure
.Fa tm
pointed at by
.Fa *tm
to the form
shown in the example above.
.Pp
The
.Fn asctime_r
function
provides the same functionality as
.Fn asctime
except the caller provide the output buffer
.Fa buf
to store the result, which must be at least 26 characters long.
.Pp
The functions
.Fn mktime
and
.Fn timegm
convert the broken-down time in the structure
pointed to by tm into a time value with the same encoding as that of the
values returned by the
.Xr time 3
function (that is, seconds from the Epoch,
.Tn UTC ) .
The
.Fn mktime
function
interprets the input structure according to the current timezone setting
(see
.Xr tzset 3 ) .
The
.Fn timegm
function
interprets the input structure as representing Universal Coordinated Time
.Pq Tn UTC .
.Pp
The original values of the
.Fa tm_wday
and
.Fa tm_yday
components of the structure are ignored, and the original values of the
other components are not restricted to their normal ranges, and will be
normalized if needed.
For example,
October 40 is changed into November 9,
a
.Fa tm_hour
of \-1 means 1 hour before midnight,
.Fa tm_mday
of 0 means the day preceding the current month, and
.Fa tm_mon
of \-2 means 2 months before January of
.Fa tm_year .
(A positive or zero value for
.Fa tm_isdst
causes
.Fn mktime
to presume initially that summer time (for example, Daylight Saving Time)
is or is not in effect for the specified time, respectively.
A negative value for
.Fa tm_isdst
causes the
.Fn mktime
function to attempt to divine whether summer time is in effect for the
specified time.
The
.Fa tm_isdst
and
.Fa tm_gmtoff
members are forced to zero by
.Fn timegm . )
.Pp
On successful completion, the values of the
.Fa tm_wday
and
.Fa tm_yday
components of the structure are set appropriately, and the other components
are set to represent the specified calendar time, but with their values
forced to their normal ranges; the final value of
.Fa tm_mday
is not set until
.Fa tm_mon
and
.Fa tm_year
are determined.
The
.Fn mktime
function
returns the specified calendar time; if the calendar time cannot be
represented, it returns \-1;
.Pp
The
.Fn difftime
function
returns the difference between two calendar times,
.Pf ( Fa time1
-
.Fa time0 ) ,
expressed in seconds.
.Pp
External declarations as well as the tm structure definition are in the
.In time.h
include file.
The tm structure includes at least the following fields:
.Bd -literal -offset indent
int tm_sec; /\(** seconds (0 - 60) \(**/
int tm_min; /\(** minutes (0 - 59) \(**/
int tm_hour; /\(** hours (0 - 23) \(**/
int tm_mday; /\(** day of month (1 - 31) \(**/
int tm_mon; /\(** month of year (0 - 11) \(**/
int tm_year; /\(** year \- 1900 \(**/
int tm_wday; /\(** day of week (Sunday = 0) \(**/
int tm_yday; /\(** day of year (0 - 365) \(**/
int tm_isdst; /\(** is summer time in effect? \(**/
char \(**tm_zone; /\(** abbreviation of timezone name \(**/
long tm_gmtoff; /\(** offset from UTC in seconds \(**/
.Ed
.Pp
The
field
.Fa tm_isdst
is non-zero if summer time is in effect.
.Pp
The field
.Fa tm_gmtoff
is the offset (in seconds) of the time represented from
.Tn UTC ,
with positive
values indicating east of the Prime Meridian.
.Sh SEE ALSO
.Xr date 1 ,
.Xr gettimeofday 2 ,
.Xr getenv 3 ,
.Xr time 3 ,
.Xr tzset 3 ,
.Xr tzfile 5
.Sh STANDARDS
The
.Fn asctime ,
.Fn ctime ,
.Fn difftime ,
.Fn gmtime ,
.Fn localtime ,
and
.Fn mktime
functions conform to
.St -isoC ,
and conform to
.St -p1003.1-96
provided the selected local timezone does not contain a leap-second table
(see
.Xr zic 8 ) .
.Pp
The
.Fn asctime_r ,
.Fn ctime_r ,
.Fn gmtime_r ,
and
.Fn localtime_r
functions are expected to conform to
.St -p1003.1-96
(again provided the selected local timezone does not contain a leap-second
table).
.Pp
The
.Fn timegm
function is not specified by any standard; its function cannot be
completely emulated using the standard functions described above.
.Sh HISTORY
This manual page is derived from
the time package contributed to Berkeley by
.An Arthur Olson
and which appeared in
.Bx 4.3 .
.Sh BUGS
Except for
.Fn difftime ,
.Fn mktime ,
and the
.Fn \&_r
variants of the other functions,
these functions leaves their result in an internal static object and return
a pointer to that object.
Subsequent calls to these
function will modify the same object.
.Pp
The C Standard provides no mechanism for a program to modify its current
local timezone setting, and the
.Tn POSIX Ns No \&-standard
method is not reentrant.
(However, thread-safe implementations are provided
in the
.Tn POSIX
threaded environment.)
.Pp
The
.Va tm_zone
field of a returned
.Vt tm
structure points to a static array of characters,
which will also be overwritten by any subsequent calls (as well as by
subsequent calls to
.Xr tzset 3
and
.Xr tzsetwall 3 ) .
.Pp
Use of the external variable
.Fa tzname
is discouraged; the
.Fa tm_zone
entry in the tm structure is preferred.

View file

@ -0,0 +1,69 @@
/*
** This file is in the public domain, so clarified as of
** 1996-06-05 by Arthur David Olson.
*/
#include <sys/cdefs.h>
#ifndef lint
#ifndef NOID
static char elsieid[] __unused = "@(#)difftime.c 8.1";
#endif /* !defined NOID */
#endif /* !defined lint */
__FBSDID("$FreeBSD$");
/*LINTLIBRARY*/
#include "namespace.h"
#include "private.h" /* for time_t, TYPE_INTEGRAL, and TYPE_SIGNED */
#include "un-namespace.h"
double
difftime(time1, time0)
const time_t time1;
const time_t time0;
{
/*
** If (sizeof (double) > sizeof (time_t)) simply convert and subtract
** (assuming that the larger type has more precision).
** This is the common real-world case circa 2004.
*/
if (sizeof (double) > sizeof (time_t))
return (double) time1 - (double) time0;
if (!TYPE_INTEGRAL(time_t)) {
/*
** time_t is floating.
*/
return time1 - time0;
}
if (!TYPE_SIGNED(time_t)) {
/*
** time_t is integral and unsigned.
** The difference of two unsigned values can't overflow
** if the minuend is greater than or equal to the subtrahend.
*/
if (time1 >= time0)
return time1 - time0;
else return -((double) (time0 - time1));
}
/*
** time_t is integral and signed.
** Handle cases where both time1 and time0 have the same sign
** (meaning that their difference cannot overflow).
*/
if ((time1 < 0) == (time0 < 0))
return time1 - time0;
/*
** time1 and time0 have opposite signs.
** Punt if unsigned long is too narrow.
*/
if (sizeof (unsigned long) < sizeof (time_t))
return (double) time1 - (double) time0;
/*
** Stay calm...decent optimizers will eliminate the complexity below.
*/
if (time1 >= 0 /* && time0 < 0 */)
return (unsigned long) time1 +
(unsigned long) (-(time0 + 1)) + 1;
return -(double) ((unsigned long) time0 +
(unsigned long) (-(time1 + 1)) + 1);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,326 @@
#ifndef PRIVATE_H
#define PRIVATE_H
/*
** This file is in the public domain, so clarified as of
** 1996-06-05 by Arthur David Olson.
**
** $FreeBSD$
*/
/* Stuff moved from Makefile.inc to reduce clutter */
#ifndef TM_GMTOFF
#define TM_GMTOFF tm_gmtoff
#define TM_ZONE tm_zone
#define STD_INSPIRED 1
#define PCTS 1
#define HAVE_LONG_DOUBLE 1
#define HAVE_STRERROR 1
#define HAVE_UNISTD_H 1
#define LOCALE_HOME _PATH_LOCALE
#define TZDIR "/usr/share/zoneinfo"
#endif /* ndef TM_GMTOFF */
/*
** This header is for use ONLY with the time conversion code.
** There is no guarantee that it will remain unchanged,
** or that it will remain at all.
** Do NOT copy it to any system include directory.
** Thank you!
*/
/*
** ID
*/
#ifndef lint
#ifndef NOID
/*
static char privatehid[] = "@(#)private.h 8.6";
*/
#endif /* !defined NOID */
#endif /* !defined lint */
#define GRANDPARENTED "Local time zone must be set--see zic manual page"
/*
** Defaults for preprocessor symbols.
** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'.
*/
#ifndef HAVE_ADJTIME
#define HAVE_ADJTIME 1
#endif /* !defined HAVE_ADJTIME */
#ifndef HAVE_GETTEXT
#define HAVE_GETTEXT 0
#endif /* !defined HAVE_GETTEXT */
#ifndef HAVE_INCOMPATIBLE_CTIME_R
#define HAVE_INCOMPATIBLE_CTIME_R 0
#endif /* !defined INCOMPATIBLE_CTIME_R */
#ifndef HAVE_SETTIMEOFDAY
#define HAVE_SETTIMEOFDAY 3
#endif /* !defined HAVE_SETTIMEOFDAY */
#ifndef HAVE_SYMLINK
#define HAVE_SYMLINK 1
#endif /* !defined HAVE_SYMLINK */
#ifndef HAVE_SYS_STAT_H
#define HAVE_SYS_STAT_H 1
#endif /* !defined HAVE_SYS_STAT_H */
#ifndef HAVE_SYS_WAIT_H
#define HAVE_SYS_WAIT_H 1
#endif /* !defined HAVE_SYS_WAIT_H */
#ifndef HAVE_UNISTD_H
#define HAVE_UNISTD_H 1
#endif /* !defined HAVE_UNISTD_H */
#ifndef HAVE_UTMPX_H
#define HAVE_UTMPX_H 0
#endif /* !defined HAVE_UTMPX_H */
#ifndef LOCALE_HOME
#define LOCALE_HOME "/usr/lib/locale"
#endif /* !defined LOCALE_HOME */
#if HAVE_INCOMPATIBLE_CTIME_R
#define asctime_r _incompatible_asctime_r
#define ctime_r _incompatible_ctime_r
#endif /* HAVE_INCOMPATIBLE_CTIME_R */
/*
** Nested includes
*/
#include "sys/types.h" /* for time_t */
#include "stdio.h"
#include "errno.h"
#include "string.h"
#include "limits.h" /* for CHAR_BIT et al. */
#include "time.h"
#include "stdlib.h"
#if HAVE_GETTEXT
#include "libintl.h"
#endif /* HAVE_GETTEXT */
#if HAVE_SYS_WAIT_H
#include <sys/wait.h> /* for WIFEXITED and WEXITSTATUS */
#endif /* HAVE_SYS_WAIT_H */
#ifndef WIFEXITED
#define WIFEXITED(status) (((status) & 0xff) == 0)
#endif /* !defined WIFEXITED */
#ifndef WEXITSTATUS
#define WEXITSTATUS(status) (((status) >> 8) & 0xff)
#endif /* !defined WEXITSTATUS */
#if HAVE_UNISTD_H
#include "unistd.h" /* for F_OK, R_OK, and other POSIX goodness */
#endif /* HAVE_UNISTD_H */
#if !(HAVE_UNISTD_H)
#ifndef F_OK
#define F_OK 0
#endif /* !defined F_OK */
#ifndef R_OK
#define R_OK 4
#endif /* !defined R_OK */
#endif /* !(HAVE_UNISTD_H) */
/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
#define is_digit(c) ((unsigned)(c) - '0' <= 9)
/*
** Define HAVE_STDINT_H's default value here, rather than at the
** start, since __GLIBC__'s value depends on previously-included
** files.
** (glibc 2.1 and later have stdint.h, even with pre-C99 compilers.)
*/
#ifndef HAVE_STDINT_H
#define HAVE_STDINT_H \
(199901 <= __STDC_VERSION__ || \
2 < (__GLIBC__ + (0 < __GLIBC_MINOR__)))
#endif /* !defined HAVE_STDINT_H */
#if HAVE_STDINT_H
#include "stdint.h"
#endif /* !HAVE_STDINT_H */
#ifndef INT_FAST64_MAX
/* Pre-C99 GCC compilers define __LONG_LONG_MAX__ instead of LLONG_MAX. */
#if defined LLONG_MAX || defined __LONG_LONG_MAX__
typedef long long int_fast64_t;
#else /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */
#if (LONG_MAX >> 31) < 0xffffffff
Please use a compiler that supports a 64-bit integer type (or wider);
you may need to compile with "-DHAVE_STDINT_H".
#endif /* (LONG_MAX >> 31) < 0xffffffff */
typedef long int_fast64_t;
#endif /* ! (defined LLONG_MAX || defined __LONG_LONG_MAX__) */
#endif /* !defined INT_FAST64_MAX */
#ifndef INT32_MAX
#define INT32_MAX 0x7fffffff
#endif /* !defined INT32_MAX */
#ifndef INT32_MIN
#define INT32_MIN (-1 - INT32_MAX)
#endif /* !defined INT32_MIN */
/*
** Workarounds for compilers/systems.
*/
/*
** Some time.h implementations don't declare asctime_r.
** Others might define it as a macro.
** Fix the former without affecting the latter.
*/
#ifndef asctime_r
extern char * asctime_r(struct tm const *, char *);
#endif
/*
** Private function declarations.
*/
char * icalloc(int nelem, int elsize);
char * icatalloc(char * old, const char * new);
char * icpyalloc(const char * string);
char * imalloc(int n);
void * irealloc(void * pointer, int size);
void icfree(char * pointer);
void ifree(char * pointer);
const char * scheck(const char * string, const char * format);
/*
** Finally, some convenience items.
*/
#ifndef TRUE
#define TRUE 1
#endif /* !defined TRUE */
#ifndef FALSE
#define FALSE 0
#endif /* !defined FALSE */
#ifndef TYPE_BIT
#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
#endif /* !defined TYPE_BIT */
#ifndef TYPE_SIGNED
#define TYPE_SIGNED(type) (((type) -1) < 0)
#endif /* !defined TYPE_SIGNED */
/*
** Since the definition of TYPE_INTEGRAL contains floating point numbers,
** it cannot be used in preprocessor directives.
*/
#ifndef TYPE_INTEGRAL
#define TYPE_INTEGRAL(type) (((type) 0.5) != 0.5)
#endif /* !defined TYPE_INTEGRAL */
/*
** Since the definition of TYPE_INTEGRAL contains floating point numbers,
** it cannot be used in preprocessor directives.
*/
#ifndef TYPE_INTEGRAL
#define TYPE_INTEGRAL(type) (((type) 0.5) != 0.5)
#endif /* !defined TYPE_INTEGRAL */
#ifndef INT_STRLEN_MAXIMUM
/*
** 302 / 1000 is log10(2.0) rounded up.
** Subtract one for the sign bit if the type is signed;
** add one for integer division truncation;
** add one more for a minus sign if the type is signed.
*/
#define INT_STRLEN_MAXIMUM(type) \
((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + \
1 + TYPE_SIGNED(type))
#endif /* !defined INT_STRLEN_MAXIMUM */
/*
** INITIALIZE(x)
*/
#ifndef GNUC_or_lint
#ifdef lint
#define GNUC_or_lint
#endif /* defined lint */
#ifndef lint
#ifdef __GNUC__
#define GNUC_or_lint
#endif /* defined __GNUC__ */
#endif /* !defined lint */
#endif /* !defined GNUC_or_lint */
#ifndef INITIALIZE
#ifdef GNUC_or_lint
#define INITIALIZE(x) ((x) = 0)
#endif /* defined GNUC_or_lint */
#ifndef GNUC_or_lint
#define INITIALIZE(x)
#endif /* !defined GNUC_or_lint */
#endif /* !defined INITIALIZE */
/*
** For the benefit of GNU folk...
** `_(MSGID)' uses the current locale's message library string for MSGID.
** The default is to use gettext if available, and use MSGID otherwise.
*/
#ifndef _
#if HAVE_GETTEXT
#define _(msgid) gettext(msgid)
#else /* !HAVE_GETTEXT */
#define _(msgid) msgid
#endif /* !HAVE_GETTEXT */
#endif /* !defined _ */
#ifndef TZ_DOMAIN
#define TZ_DOMAIN "tz"
#endif /* !defined TZ_DOMAIN */
#if HAVE_INCOMPATIBLE_CTIME_R
#undef asctime_r
#undef ctime_r
char *asctime_r(struct tm const *, char *);
char *ctime_r(time_t const *, char *);
#endif /* HAVE_INCOMPATIBLE_CTIME_R */
#ifndef YEARSPERREPEAT
#define YEARSPERREPEAT 400 /* years before a Gregorian repeat */
#endif /* !defined YEARSPERREPEAT */
/*
** The Gregorian year averages 365.2425 days, which is 31556952 seconds.
*/
#ifndef AVGSECSPERYEAR
#define AVGSECSPERYEAR 31556952L
#endif /* !defined AVGSECSPERYEAR */
#ifndef SECSPERREPEAT
#define SECSPERREPEAT ((int_fast64_t) YEARSPERREPEAT * (int_fast64_t) AVGSECSPERYEAR)
#endif /* !defined SECSPERREPEAT */
#ifndef SECSPERREPEAT_BITS
#define SECSPERREPEAT_BITS 34 /* ceil(log2(SECSPERREPEAT)) */
#endif /* !defined SECSPERREPEAT_BITS */
/*
** UNIX was a registered trademark of The Open Group in 2003.
*/
#endif /* !defined PRIVATE_H */

View file

@ -0,0 +1,278 @@
.\" Copyright (c) 1989, 1991, 1993
.\" The Regents of the University of California. All rights reserved.
.\"
.\" This code is derived from software contributed to Berkeley by
.\" the American National Standards Committee X3, on Information
.\" Processing Systems.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 4. Neither the name of the University nor the names of its contributors
.\" may be used to endorse or promote products derived from this software
.\" without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" @(#)strftime.3 8.1 (Berkeley) 6/4/93
.\" $FreeBSD$
.\"
.Dd November 4, 2004
.Dt STRFTIME 3
.Os
.Sh NAME
.Nm strftime
.Nd format date and time
.Sh LIBRARY
.Lb libc
.Sh SYNOPSIS
.In time.h
.Ft size_t
.Fo strftime
.Fa "char * restrict buf"
.Fa "size_t maxsize"
.Fa "const char * restrict format"
.Fa "const struct tm * restrict timeptr"
.Fc
.Sh DESCRIPTION
The
.Fn strftime
function formats the information from
.Fa timeptr
into the buffer
.Fa buf
according to the string pointed to by
.Fa format .
.Pp
The
.Fa format
string consists of zero or more conversion specifications and
ordinary characters.
All ordinary characters are copied directly into the buffer.
A conversion specification consists of a percent sign
.Dq Ql %
and one other character.
.Pp
No more than
.Fa maxsize
characters will be placed into the array.
If the total number of resulting characters, including the terminating
NUL character, is not more than
.Fa maxsize ,
.Fn strftime
returns the number of characters in the array, not counting the
terminating NUL.
Otherwise, zero is returned and the buffer contents are indeterminate.
.Pp
The conversion specifications are copied to the buffer after expansion
as follows:-
.Bl -tag -width "xxxx"
.It Cm \&%A
is replaced by national representation of the full weekday name.
.It Cm %a
is replaced by national representation of
the abbreviated weekday name.
.It Cm \&%B
is replaced by national representation of the full month name.
.It Cm %b
is replaced by national representation of
the abbreviated month name.
.It Cm \&%C
is replaced by (year / 100) as decimal number; single
digits are preceded by a zero.
.It Cm %c
is replaced by national representation of time and date.
.It Cm \&%D
is equivalent to
.Dq Li %m/%d/%y .
.It Cm %d
is replaced by the day of the month as a decimal number (01-31).
.It Cm %E* %O*
POSIX locale extensions.
The sequences
%Ec %EC %Ex %EX %Ey %EY
%Od %Oe %OH %OI %Om %OM
%OS %Ou %OU %OV %Ow %OW %Oy
are supposed to provide alternate
representations.
.Pp
Additionally %OB implemented
to represent alternative months names
(used standalone, without day mentioned).
.It Cm %e
is replaced by the day of the month as a decimal number (1-31); single
digits are preceded by a blank.
.It Cm \&%F
is equivalent to
.Dq Li %Y-%m-%d .
.It Cm \&%G
is replaced by a year as a decimal number with century.
This year is the one that contains the greater part of
the week (Monday as the first day of the week).
.It Cm %g
is replaced by the same year as in
.Dq Li %G ,
but as a decimal number without century (00-99).
.It Cm \&%H
is replaced by the hour (24-hour clock) as a decimal number (00-23).
.It Cm %h
the same as
.Cm %b .
.It Cm \&%I
is replaced by the hour (12-hour clock) as a decimal number (01-12).
.It Cm %j
is replaced by the day of the year as a decimal number (001-366).
.It Cm %k
is replaced by the hour (24-hour clock) as a decimal number (0-23);
single digits are preceded by a blank.
.It Cm %l
is replaced by the hour (12-hour clock) as a decimal number (1-12);
single digits are preceded by a blank.
.It Cm \&%M
is replaced by the minute as a decimal number (00-59).
.It Cm %m
is replaced by the month as a decimal number (01-12).
.It Cm %n
is replaced by a newline.
.It Cm %O*
the same as
.Cm %E* .
.It Cm %p
is replaced by national representation of either
"ante meridiem" (a.m.)
or
"post meridiem" (p.m.)
as appropriate.
.It Cm \&%R
is equivalent to
.Dq Li %H:%M .
.It Cm %r
is equivalent to
.Dq Li %I:%M:%S %p .
.It Cm \&%S
is replaced by the second as a decimal number (00-60).
.It Cm %s
is replaced by the number of seconds since the Epoch, UTC (see
.Xr mktime 3 ) .
.It Cm \&%T
is equivalent to
.Dq Li %H:%M:%S .
.It Cm %t
is replaced by a tab.
.It Cm \&%U
is replaced by the week number of the year (Sunday as the first day of
the week) as a decimal number (00-53).
.It Cm %u
is replaced by the weekday (Monday as the first day of the week)
as a decimal number (1-7).
.It Cm \&%V
is replaced by the week number of the year (Monday as the first day of
the week) as a decimal number (01-53).
If the week containing January
1 has four or more days in the new year, then it is week 1; otherwise
it is the last week of the previous year, and the next week is week 1.
.It Cm %v
is equivalent to
.Dq Li %e-%b-%Y .
.It Cm \&%W
is replaced by the week number of the year (Monday as the first day of
the week) as a decimal number (00-53).
.It Cm %w
is replaced by the weekday (Sunday as the first day of the week)
as a decimal number (0-6).
.It Cm \&%X
is replaced by national representation of the time.
.It Cm %x
is replaced by national representation of the date.
.It Cm \&%Y
is replaced by the year with century as a decimal number.
.It Cm %y
is replaced by the year without century as a decimal number (00-99).
.It Cm \&%Z
is replaced by the time zone name.
.It Cm %z
is replaced by the time zone offset from UTC; a leading plus sign stands for
east of UTC, a minus sign for west of UTC, hours and minutes follow
with two digits each and no delimiter between them (common form for
RFC 822 date headers).
.It Cm %+
is replaced by national representation of the date and time
(the format is similar to that produced by
.Xr date 1 ) .
.It Cm %-*
GNU libc extension.
Do not do any padding when performing numerical outputs.
.It Cm %_*
GNU libc extension.
Explicitly specify space for padding.
.It Cm %0*
GNU libc extension.
Explicitly specify zero for padding.
.It Cm %%
is replaced by
.Ql % .
.El
.Sh SEE ALSO
.Xr date 1 ,
.Xr printf 1 ,
.Xr ctime 3 ,
.Xr printf 3 ,
.Xr strptime 3 ,
.Xr wcsftime 3
.Sh STANDARDS
The
.Fn strftime
function
conforms to
.St -isoC
with a lot of extensions including
.Ql %C ,
.Ql \&%D ,
.Ql %E* ,
.Ql %e ,
.Ql %G ,
.Ql %g ,
.Ql %h ,
.Ql %k ,
.Ql %l ,
.Ql %n ,
.Ql %O* ,
.Ql \&%R ,
.Ql %r ,
.Ql %s ,
.Ql \&%T ,
.Ql %t ,
.Ql %u ,
.Ql \&%V ,
.Ql %z ,
.Ql %+ .
.Pp
The peculiar week number and year in the replacements of
.Ql %G ,
.Ql %g
and
.Ql \&%V
are defined in ISO 8601: 1988.
.Sh BUGS
There is no conversion specification for the phase of the moon.
.Pp
The
.Fn strftime
function does not correctly handle multibyte characters in the
.Fa format
argument.

View file

@ -0,0 +1,634 @@
/*
* Copyright (c) 1989 The Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef lint
#ifndef NOID
static const char elsieid[] = "@(#)strftime.3 8.3";
/*
** Based on the UCB version with the ID appearing below.
** This is ANSIish only when "multibyte character == plain character".
*/
#endif /* !defined NOID */
#endif /* !defined lint */
#include "namespace.h"
#include "private.h"
#if defined(LIBC_SCCS) && !defined(lint)
static const char sccsid[] = "@(#)strftime.c 5.4 (Berkeley) 3/14/89";
#endif /* LIBC_SCCS and not lint */
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "tzfile.h"
#include <fcntl.h>
#include <sys/stat.h>
#include "un-namespace.h"
#include "timelocal.h"
static char * _add(const char *, char *, const char *);
static char * _conv(int, const char *, char *, const char *);
static char * _fmt(const char *, const struct tm *, char *, const char *,
int *);
static char * _yconv(int, int, int, int, char *, const char *);
extern char * tzname[];
#ifndef YEAR_2000_NAME
#define YEAR_2000_NAME "CHECK_STRFTIME_FORMATS_FOR_TWO_DIGIT_YEARS"
#endif /* !defined YEAR_2000_NAME */
#define IN_NONE 0
#define IN_SOME 1
#define IN_THIS 2
#define IN_ALL 3
#define PAD_DEFAULT 0
#define PAD_LESS 1
#define PAD_SPACE 2
#define PAD_ZERO 3
static const char* fmt_padding[][4] = {
/* DEFAULT, LESS, SPACE, ZERO */
#define PAD_FMT_MONTHDAY 0
#define PAD_FMT_HMS 0
#define PAD_FMT_CENTURY 0
#define PAD_FMT_SHORTYEAR 0
#define PAD_FMT_MONTH 0
#define PAD_FMT_WEEKOFYEAR 0
#define PAD_FMT_DAYOFMONTH 0
{ "%02d", "%d", "%2d", "%02d" },
#define PAD_FMT_SDAYOFMONTH 1
#define PAD_FMT_SHMS 1
{ "%2d", "%d", "%2d", "%02d" },
#define PAD_FMT_DAYOFYEAR 2
{ "%03d", "%d", "%3d", "%03d" },
#define PAD_FMT_YEAR 3
{ "%04d", "%d", "%4d", "%04d" }
};
size_t
strftime(char * __restrict s, size_t maxsize, const char * __restrict format,
const struct tm * __restrict t)
{
char * p;
int warn;
tzset();
warn = IN_NONE;
p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize, &warn);
#ifndef NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU
if (warn != IN_NONE && getenv(YEAR_2000_NAME) != NULL) {
(void) fprintf(stderr, "\n");
if (format == NULL)
(void) fprintf(stderr, "NULL strftime format ");
else (void) fprintf(stderr, "strftime format \"%s\" ",
format);
(void) fprintf(stderr, "yields only two digits of years in ");
if (warn == IN_SOME)
(void) fprintf(stderr, "some locales");
else if (warn == IN_THIS)
(void) fprintf(stderr, "the current locale");
else (void) fprintf(stderr, "all locales");
(void) fprintf(stderr, "\n");
}
#endif /* !defined NO_RUN_TIME_WARNINGS_ABOUT_YEAR_2000_PROBLEMS_THANK_YOU */
if (p == s + maxsize)
return 0;
*p = '\0';
return p - s;
}
static char *
_fmt(format, t, pt, ptlim, warnp)
const char * format;
const struct tm * const t;
char * pt;
const char * const ptlim;
int * warnp;
{
int Ealternative, Oalternative, PadIndex;
struct lc_time_T *tptr = __get_current_time_locale();
for ( ; *format; ++format) {
if (*format == '%') {
Ealternative = 0;
Oalternative = 0;
PadIndex = PAD_DEFAULT;
label:
switch (*++format) {
case '\0':
--format;
break;
case 'A':
pt = _add((t->tm_wday < 0 ||
t->tm_wday >= DAYSPERWEEK) ?
"?" : tptr->weekday[t->tm_wday],
pt, ptlim);
continue;
case 'a':
pt = _add((t->tm_wday < 0 ||
t->tm_wday >= DAYSPERWEEK) ?
"?" : tptr->wday[t->tm_wday],
pt, ptlim);
continue;
case 'B':
pt = _add((t->tm_mon < 0 ||
t->tm_mon >= MONSPERYEAR) ?
"?" : (Oalternative ? tptr->alt_month :
tptr->month)[t->tm_mon],
pt, ptlim);
continue;
case 'b':
case 'h':
pt = _add((t->tm_mon < 0 ||
t->tm_mon >= MONSPERYEAR) ?
"?" : tptr->mon[t->tm_mon],
pt, ptlim);
continue;
case 'C':
/*
** %C used to do a...
** _fmt("%a %b %e %X %Y", t);
** ...whereas now POSIX 1003.2 calls for
** something completely different.
** (ado, 1993-05-24)
*/
pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 0,
pt, ptlim);
continue;
case 'c':
{
int warn2 = IN_SOME;
pt = _fmt(tptr->c_fmt, t, pt, ptlim, &warn2);
if (warn2 == IN_ALL)
warn2 = IN_THIS;
if (warn2 > *warnp)
*warnp = warn2;
}
continue;
case 'D':
pt = _fmt("%m/%d/%y", t, pt, ptlim, warnp);
continue;
case 'd':
pt = _conv(t->tm_mday, fmt_padding[PAD_FMT_DAYOFMONTH][PadIndex],
pt, ptlim);
continue;
case 'E':
if (Ealternative || Oalternative)
break;
Ealternative++;
goto label;
case 'O':
/*
** C99 locale modifiers.
** The sequences
** %Ec %EC %Ex %EX %Ey %EY
** %Od %oe %OH %OI %Om %OM
** %OS %Ou %OU %OV %Ow %OW %Oy
** are supposed to provide alternate
** representations.
**
** FreeBSD extension
** %OB
*/
if (Ealternative || Oalternative)
break;
Oalternative++;
goto label;
case 'e':
pt = _conv(t->tm_mday,
fmt_padding[PAD_FMT_SDAYOFMONTH][PadIndex], pt, ptlim);
continue;
case 'F':
pt = _fmt("%Y-%m-%d", t, pt, ptlim, warnp);
continue;
case 'H':
pt = _conv(t->tm_hour, fmt_padding[PAD_FMT_HMS][PadIndex],
pt, ptlim);
continue;
case 'I':
pt = _conv((t->tm_hour % 12) ?
(t->tm_hour % 12) : 12,
fmt_padding[PAD_FMT_HMS][PadIndex], pt, ptlim);
continue;
case 'j':
pt = _conv(t->tm_yday + 1,
fmt_padding[PAD_FMT_DAYOFYEAR][PadIndex], pt, ptlim);
continue;
case 'k':
/*
** This used to be...
** _conv(t->tm_hour % 12 ?
** t->tm_hour % 12 : 12, 2, ' ');
** ...and has been changed to the below to
** match SunOS 4.1.1 and Arnold Robbins'
** strftime version 3.0. That is, "%k" and
** "%l" have been swapped.
** (ado, 1993-05-24)
*/
pt = _conv(t->tm_hour, fmt_padding[PAD_FMT_SHMS][PadIndex],
pt, ptlim);
continue;
#ifdef KITCHEN_SINK
case 'K':
/*
** After all this time, still unclaimed!
*/
pt = _add("kitchen sink", pt, ptlim);
continue;
#endif /* defined KITCHEN_SINK */
case 'l':
/*
** This used to be...
** _conv(t->tm_hour, 2, ' ');
** ...and has been changed to the below to
** match SunOS 4.1.1 and Arnold Robbin's
** strftime version 3.0. That is, "%k" and
** "%l" have been swapped.
** (ado, 1993-05-24)
*/
pt = _conv((t->tm_hour % 12) ?
(t->tm_hour % 12) : 12,
fmt_padding[PAD_FMT_SHMS][PadIndex], pt, ptlim);
continue;
case 'M':
pt = _conv(t->tm_min, fmt_padding[PAD_FMT_HMS][PadIndex],
pt, ptlim);
continue;
case 'm':
pt = _conv(t->tm_mon + 1,
fmt_padding[PAD_FMT_MONTH][PadIndex], pt, ptlim);
continue;
case 'n':
pt = _add("\n", pt, ptlim);
continue;
case 'p':
pt = _add((t->tm_hour >= (HOURSPERDAY / 2)) ?
tptr->pm :
tptr->am,
pt, ptlim);
continue;
case 'R':
pt = _fmt("%H:%M", t, pt, ptlim, warnp);
continue;
case 'r':
pt = _fmt(tptr->ampm_fmt, t, pt, ptlim,
warnp);
continue;
case 'S':
pt = _conv(t->tm_sec, fmt_padding[PAD_FMT_HMS][PadIndex],
pt, ptlim);
continue;
case 's':
{
struct tm tm;
char buf[INT_STRLEN_MAXIMUM(
time_t) + 1];
time_t mkt;
tm = *t;
mkt = mktime(&tm);
if (TYPE_SIGNED(time_t))
(void) sprintf(buf, "%ld",
(long) mkt);
else (void) sprintf(buf, "%lu",
(unsigned long) mkt);
pt = _add(buf, pt, ptlim);
}
continue;
case 'T':
pt = _fmt("%H:%M:%S", t, pt, ptlim, warnp);
continue;
case 't':
pt = _add("\t", pt, ptlim);
continue;
case 'U':
pt = _conv((t->tm_yday + DAYSPERWEEK -
t->tm_wday) / DAYSPERWEEK,
fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex], pt, ptlim);
continue;
case 'u':
/*
** From Arnold Robbins' strftime version 3.0:
** "ISO 8601: Weekday as a decimal number
** [1 (Monday) - 7]"
** (ado, 1993-05-24)
*/
pt = _conv((t->tm_wday == 0) ?
DAYSPERWEEK : t->tm_wday,
"%d", pt, ptlim);
continue;
case 'V': /* ISO 8601 week number */
case 'G': /* ISO 8601 year (four digits) */
case 'g': /* ISO 8601 year (two digits) */
/*
** From Arnold Robbins' strftime version 3.0: "the week number of the
** year (the first Monday as the first day of week 1) as a decimal number
** (01-53)."
** (ado, 1993-05-24)
**
** From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn:
** "Week 01 of a year is per definition the first week which has the
** Thursday in this year, which is equivalent to the week which contains
** the fourth day of January. In other words, the first week of a new year
** is the week which has the majority of its days in the new year. Week 01
** might also contain days from the previous year and the week before week
** 01 of a year is the last week (52 or 53) of the previous year even if
** it contains days from the new year. A week starts with Monday (day 1)
** and ends with Sunday (day 7). For example, the first week of the year
** 1997 lasts from 1996-12-30 to 1997-01-05..."
** (ado, 1996-01-02)
*/
{
int year;
int base;
int yday;
int wday;
int w;
year = t->tm_year;
base = TM_YEAR_BASE;
yday = t->tm_yday;
wday = t->tm_wday;
for ( ; ; ) {
int len;
int bot;
int top;
len = isleap_sum(year, base) ?
DAYSPERLYEAR :
DAYSPERNYEAR;
/*
** What yday (-3 ... 3) does
** the ISO year begin on?
*/
bot = ((yday + 11 - wday) %
DAYSPERWEEK) - 3;
/*
** What yday does the NEXT
** ISO year begin on?
*/
top = bot -
(len % DAYSPERWEEK);
if (top < -3)
top += DAYSPERWEEK;
top += len;
if (yday >= top) {
++base;
w = 1;
break;
}
if (yday >= bot) {
w = 1 + ((yday - bot) /
DAYSPERWEEK);
break;
}
--base;
yday += isleap_sum(year, base) ?
DAYSPERLYEAR :
DAYSPERNYEAR;
}
#ifdef XPG4_1994_04_09
if ((w == 52 &&
t->tm_mon == TM_JANUARY) ||
(w == 1 &&
t->tm_mon == TM_DECEMBER))
w = 53;
#endif /* defined XPG4_1994_04_09 */
if (*format == 'V')
pt = _conv(w, fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex],
pt, ptlim);
else if (*format == 'g') {
*warnp = IN_ALL;
pt = _yconv(year, base, 0, 1,
pt, ptlim);
} else pt = _yconv(year, base, 1, 1,
pt, ptlim);
}
continue;
case 'v':
/*
** From Arnold Robbins' strftime version 3.0:
** "date as dd-bbb-YYYY"
** (ado, 1993-05-24)
*/
pt = _fmt("%e-%b-%Y", t, pt, ptlim, warnp);
continue;
case 'W':
pt = _conv((t->tm_yday + DAYSPERWEEK -
(t->tm_wday ?
(t->tm_wday - 1) :
(DAYSPERWEEK - 1))) / DAYSPERWEEK,
fmt_padding[PAD_FMT_WEEKOFYEAR][PadIndex], pt, ptlim);
continue;
case 'w':
pt = _conv(t->tm_wday, "%d", pt, ptlim);
continue;
case 'X':
pt = _fmt(tptr->X_fmt, t, pt, ptlim, warnp);
continue;
case 'x':
{
int warn2 = IN_SOME;
pt = _fmt(tptr->x_fmt, t, pt, ptlim, &warn2);
if (warn2 == IN_ALL)
warn2 = IN_THIS;
if (warn2 > *warnp)
*warnp = warn2;
}
continue;
case 'y':
*warnp = IN_ALL;
pt = _yconv(t->tm_year, TM_YEAR_BASE, 0, 1,
pt, ptlim);
continue;
case 'Y':
pt = _yconv(t->tm_year, TM_YEAR_BASE, 1, 1,
pt, ptlim);
continue;
case 'Z':
#ifdef TM_ZONE
if (t->TM_ZONE != NULL)
pt = _add(t->TM_ZONE, pt, ptlim);
else
#endif /* defined TM_ZONE */
if (t->tm_isdst >= 0)
pt = _add(tzname[t->tm_isdst != 0],
pt, ptlim);
/*
** C99 says that %Z must be replaced by the
** empty string if the time zone is not
** determinable.
*/
continue;
case 'z':
{
int diff;
char const * sign;
if (t->tm_isdst < 0)
continue;
#ifdef TM_GMTOFF
diff = t->TM_GMTOFF;
#else /* !defined TM_GMTOFF */
/*
** C99 says that the UTC offset must
** be computed by looking only at
** tm_isdst. This requirement is
** incorrect, since it means the code
** must rely on magic (in this case
** altzone and timezone), and the
** magic might not have the correct
** offset. Doing things correctly is
** tricky and requires disobeying C99;
** see GNU C strftime for details.
** For now, punt and conform to the
** standard, even though it's incorrect.
**
** C99 says that %z must be replaced by the
** empty string if the time zone is not
** determinable, so output nothing if the
** appropriate variables are not available.
*/
if (t->tm_isdst == 0)
#ifdef USG_COMPAT
diff = -timezone;
#else /* !defined USG_COMPAT */
continue;
#endif /* !defined USG_COMPAT */
else
#ifdef ALTZONE
diff = -altzone;
#else /* !defined ALTZONE */
continue;
#endif /* !defined ALTZONE */
#endif /* !defined TM_GMTOFF */
if (diff < 0) {
sign = "-";
diff = -diff;
} else sign = "+";
pt = _add(sign, pt, ptlim);
diff /= SECSPERMIN;
diff = (diff / MINSPERHOUR) * 100 +
(diff % MINSPERHOUR);
pt = _conv(diff,
fmt_padding[PAD_FMT_YEAR][PadIndex], pt, ptlim);
}
continue;
case '+':
pt = _fmt(tptr->date_fmt, t, pt, ptlim,
warnp);
continue;
case '-':
if (PadIndex != PAD_DEFAULT)
break;
PadIndex = PAD_LESS;
goto label;
case '_':
if (PadIndex != PAD_DEFAULT)
break;
PadIndex = PAD_SPACE;
goto label;
case '0':
if (PadIndex != PAD_DEFAULT)
break;
PadIndex = PAD_ZERO;
goto label;
case '%':
/*
** X311J/88-090 (4.12.3.5): if conversion char is
** undefined, behavior is undefined. Print out the
** character itself as printf(3) also does.
*/
default:
break;
}
}
if (pt == ptlim)
break;
*pt++ = *format;
}
return pt;
}
static char *
_conv(n, format, pt, ptlim)
const int n;
const char * const format;
char * const pt;
const char * const ptlim;
{
char buf[INT_STRLEN_MAXIMUM(int) + 1];
(void) sprintf(buf, format, n);
return _add(buf, pt, ptlim);
}
static char *
_add(str, pt, ptlim)
const char * str;
char * pt;
const char * const ptlim;
{
while (pt < ptlim && (*pt = *str++) != '\0')
++pt;
return pt;
}
/*
** POSIX and the C Standard are unclear or inconsistent about
** what %C and %y do if the year is negative or exceeds 9999.
** Use the convention that %C concatenated with %y yields the
** same output as %Y, and that %Y contains at least 4 bytes,
** with more only if necessary.
*/
static char *
_yconv(a, b, convert_top, convert_yy, pt, ptlim)
const int a;
const int b;
const int convert_top;
const int convert_yy;
char * pt;
const char * const ptlim;
{
register int lead;
register int trail;
#define DIVISOR 100
trail = a % DIVISOR + b % DIVISOR;
lead = a / DIVISOR + b / DIVISOR + trail / DIVISOR;
trail %= DIVISOR;
if (trail < 0 && lead > 0) {
trail += DIVISOR;
--lead;
} else if (lead < 0 && trail > 0) {
trail -= DIVISOR;
++lead;
}
if (convert_top) {
if (lead == 0 && trail < 0)
pt = _add("-0", pt, ptlim);
else pt = _conv(lead, "%02d", pt, ptlim);
}
if (convert_yy)
pt = _conv(((trail < 0) ? -trail : trail), "%02d", pt, ptlim);
return pt;
}

View file

@ -0,0 +1,173 @@
.\"
.\" Copyright (c) 1997 Joerg Wunsch
.\"
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
.\" IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.\" $FreeBSD$
.\" "
.Dd January 4, 2003
.Dt STRPTIME 3
.Os
.Sh NAME
.Nm strptime
.Nd parse date and time string
.Sh LIBRARY
.Lb libc
.Sh SYNOPSIS
.In time.h
.Ft char *
.Fo strptime
.Fa "const char * restrict buf"
.Fa "const char * restrict format"
.Fa "struct tm * restrict timeptr"
.Fc
.Sh DESCRIPTION
The
.Fn strptime
function parses the string in the buffer
.Fa buf
according to the string pointed to by
.Fa format ,
and fills in the elements of the structure pointed to by
.Fa timeptr .
The resulting values will be relative to the local time zone.
Thus, it can be considered the reverse operation of
.Xr strftime 3 .
.Pp
The
.Fa format
string consists of zero or more conversion specifications and
ordinary characters.
All ordinary characters are matched exactly with the buffer, where
white space in the format string will match any amount of white space
in the buffer.
All conversion specifications are identical to those described in
.Xr strftime 3 .
.Pp
Two-digit year values, including formats
.Fa %y
and
.Fa \&%D ,
are now interpreted as beginning at 1969 per POSIX requirements.
Years 69-00 are interpreted in the 20th century (1969-2000), years
01-68 in the 21st century (2001-2068).
.Pp
If the
.Fa format
string does not contain enough conversion specifications to completely
specify the resulting
.Vt struct tm ,
the unspecified members of
.Va timeptr
are left untouched.
For example, if
.Fa format
is
.Dq Li "%H:%M:%S" ,
only
.Va tm_hour ,
.Va tm_sec
and
.Va tm_min
will be modified.
If time relative to today is desired, initialize the
.Fa timeptr
structure with today's date before passing it to
.Fn strptime .
.Sh RETURN VALUES
Upon successful completion,
.Fn strptime
returns the pointer to the first character in
.Fa buf
that has not been required to satisfy the specified conversions in
.Fa format .
It returns
.Dv NULL
if one of the conversions failed.
.Sh SEE ALSO
.Xr date 1 ,
.Xr scanf 3 ,
.Xr strftime 3
.Sh HISTORY
The
.Fn strptime
function appeared in
.Fx 3.0 .
.Sh AUTHORS
The
.Fn strptime
function has been contributed by Powerdog Industries.
.Pp
This man page was written by
.An J\(:org Wunsch .
.Sh BUGS
Both the
.Fa %e
and
.Fa %l
format specifiers may incorrectly scan one too many digits
if the intended values comprise only a single digit
and that digit is followed immediately by another digit.
Both specifiers accept zero-padded values,
even though they are both defined as taking unpadded values.
.Pp
The
.Fa %p
format specifier has no effect unless it is parsed
.Em after
hour-related specifiers.
Specifying
.Fa %l
without
.Fa %p
will produce undefined results.
Note that 12AM
(ante meridiem)
is taken as midnight
and 12PM
(post meridiem)
is taken as noon.
.Pp
The
.Fa \&%U
and
.Fa %W
format specifiers accept any value within the range 00 to 53
without validating against other values supplied (like month
or day of the year, for example).
.Pp
The
.Fa %Z
format specifier only accepts time zone abbreviations of the local time zone,
or the value "GMT".
This limitation is because of ambiguity due to of the over loading of time
zone abbreviations.
One such example is
.Fa EST
which is both Eastern Standard Time and Eastern Australia Summer Time.
.Pp
The
.Fn strptime
function does not correctly handle multibyte characters in the
.Fa format
argument.

View file

@ -0,0 +1,566 @@
/*
* Powerdog Industries kindly requests feedback from anyone modifying
* this function:
*
* Date: Thu, 05 Jun 1997 23:17:17 -0400
* From: Kevin Ruddy <kevin.ruddy@powerdog.com>
* To: James FitzGibbon <james@nexis.net>
* Subject: Re: Use of your strptime(3) code (fwd)
*
* The reason for the "no mod" clause was so that modifications would
* come back and we could integrate them and reissue so that a wider
* audience could use it (thereby spreading the wealth). This has
* made it possible to get strptime to work on many operating systems.
* I'm not sure why that's "plain unacceptable" to the FreeBSD team.
*
* Anyway, you can change it to "with or without modification" as
* you see fit. Enjoy.
*
* Kevin Ruddy
* Powerdog Industries, Inc.
*/
/*
* Copyright (c) 1994 Powerdog Industries. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgement:
* This product includes software developed by Powerdog Industries.
* 4. The name of Powerdog Industries may not be used to endorse or
* promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY POWERDOG INDUSTRIES ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE POWERDOG INDUSTRIES BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifndef lint
#ifndef NOID
static char copyright[] __unused =
"@(#) Copyright (c) 1994 Powerdog Industries. All rights reserved.";
static char sccsid[] __unused = "@(#)strptime.c 0.1 (Powerdog) 94/03/27";
#endif /* !defined NOID */
#endif /* not lint */
__FBSDID("$FreeBSD$");
#include "namespace.h"
#include <time.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include "un-namespace.h"
#include "libc_private.h"
#include "timelocal.h"
static char * _strptime(const char *, const char *, struct tm *, int *);
#define asizeof(a) (sizeof (a) / sizeof ((a)[0]))
static char *
_strptime(const char *buf, const char *fmt, struct tm *tm, int *GMTp)
{
char c;
const char *ptr;
int i,
len;
int Ealternative, Oalternative;
struct lc_time_T *tptr = __get_current_time_locale();
ptr = fmt;
while (*ptr != 0) {
if (*buf == 0)
break;
c = *ptr++;
if (c != '%') {
if (isspace((unsigned char)c))
while (*buf != 0 && isspace((unsigned char)*buf))
buf++;
else if (c != *buf++)
return 0;
continue;
}
Ealternative = 0;
Oalternative = 0;
label:
c = *ptr++;
switch (c) {
case 0:
case '%':
if (*buf++ != '%')
return 0;
break;
case '+':
buf = _strptime(buf, tptr->date_fmt, tm, GMTp);
if (buf == 0)
return 0;
break;
case 'C':
if (!isdigit((unsigned char)*buf))
return 0;
/* XXX This will break for 3-digit centuries. */
len = 2;
for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
i *= 10;
i += *buf - '0';
len--;
}
if (i < 19)
return 0;
tm->tm_year = i * 100 - 1900;
break;
case 'c':
buf = _strptime(buf, tptr->c_fmt, tm, GMTp);
if (buf == 0)
return 0;
break;
case 'D':
buf = _strptime(buf, "%m/%d/%y", tm, GMTp);
if (buf == 0)
return 0;
break;
case 'E':
if (Ealternative || Oalternative)
break;
Ealternative++;
goto label;
case 'O':
if (Ealternative || Oalternative)
break;
Oalternative++;
goto label;
case 'F':
buf = _strptime(buf, "%Y-%m-%d", tm, GMTp);
if (buf == 0)
return 0;
break;
case 'R':
buf = _strptime(buf, "%H:%M", tm, GMTp);
if (buf == 0)
return 0;
break;
case 'r':
buf = _strptime(buf, tptr->ampm_fmt, tm, GMTp);
if (buf == 0)
return 0;
break;
case 'T':
buf = _strptime(buf, "%H:%M:%S", tm, GMTp);
if (buf == 0)
return 0;
break;
case 'X':
buf = _strptime(buf, tptr->X_fmt, tm, GMTp);
if (buf == 0)
return 0;
break;
case 'x':
buf = _strptime(buf, tptr->x_fmt, tm, GMTp);
if (buf == 0)
return 0;
break;
case 'j':
if (!isdigit((unsigned char)*buf))
return 0;
len = 3;
for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
i *= 10;
i += *buf - '0';
len--;
}
if (i < 1 || i > 366)
return 0;
tm->tm_yday = i - 1;
break;
case 'M':
case 'S':
if (*buf == 0 || isspace((unsigned char)*buf))
break;
if (!isdigit((unsigned char)*buf))
return 0;
len = 2;
for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
i *= 10;
i += *buf - '0';
len--;
}
if (c == 'M') {
if (i > 59)
return 0;
tm->tm_min = i;
} else {
if (i > 60)
return 0;
tm->tm_sec = i;
}
if (*buf != 0 && isspace((unsigned char)*buf))
while (*ptr != 0 && !isspace((unsigned char)*ptr))
ptr++;
break;
case 'H':
case 'I':
case 'k':
case 'l':
/*
* Of these, %l is the only specifier explicitly
* documented as not being zero-padded. However,
* there is no harm in allowing zero-padding.
*
* XXX The %l specifier may gobble one too many
* digits if used incorrectly.
*/
if (!isdigit((unsigned char)*buf))
return 0;
len = 2;
for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
i *= 10;
i += *buf - '0';
len--;
}
if (c == 'H' || c == 'k') {
if (i > 23)
return 0;
} else if (i > 12)
return 0;
tm->tm_hour = i;
if (*buf != 0 && isspace((unsigned char)*buf))
while (*ptr != 0 && !isspace((unsigned char)*ptr))
ptr++;
break;
case 'p':
/*
* XXX This is bogus if parsed before hour-related
* specifiers.
*/
len = strlen(tptr->am);
if (strncasecmp(buf, tptr->am, len) == 0) {
if (tm->tm_hour > 12)
return 0;
if (tm->tm_hour == 12)
tm->tm_hour = 0;
buf += len;
break;
}
len = strlen(tptr->pm);
if (strncasecmp(buf, tptr->pm, len) == 0) {
if (tm->tm_hour > 12)
return 0;
if (tm->tm_hour != 12)
tm->tm_hour += 12;
buf += len;
break;
}
return 0;
case 'A':
case 'a':
for (i = 0; i < asizeof(tptr->weekday); i++) {
len = strlen(tptr->weekday[i]);
if (strncasecmp(buf, tptr->weekday[i],
len) == 0)
break;
len = strlen(tptr->wday[i]);
if (strncasecmp(buf, tptr->wday[i],
len) == 0)
break;
}
if (i == asizeof(tptr->weekday))
return 0;
tm->tm_wday = i;
buf += len;
break;
case 'U':
case 'W':
/*
* XXX This is bogus, as we can not assume any valid
* information present in the tm structure at this
* point to calculate a real value, so just check the
* range for now.
*/
if (!isdigit((unsigned char)*buf))
return 0;
len = 2;
for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
i *= 10;
i += *buf - '0';
len--;
}
if (i > 53)
return 0;
if (*buf != 0 && isspace((unsigned char)*buf))
while (*ptr != 0 && !isspace((unsigned char)*ptr))
ptr++;
break;
case 'w':
if (!isdigit((unsigned char)*buf))
return 0;
i = *buf - '0';
if (i > 6)
return 0;
tm->tm_wday = i;
if (*buf != 0 && isspace((unsigned char)*buf))
while (*ptr != 0 && !isspace((unsigned char)*ptr))
ptr++;
break;
case 'd':
case 'e':
/*
* The %e specifier is explicitly documented as not
* being zero-padded but there is no harm in allowing
* such padding.
*
* XXX The %e specifier may gobble one too many
* digits if used incorrectly.
*/
if (!isdigit((unsigned char)*buf))
return 0;
len = 2;
for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
i *= 10;
i += *buf - '0';
len--;
}
if (i > 31)
return 0;
tm->tm_mday = i;
if (*buf != 0 && isspace((unsigned char)*buf))
while (*ptr != 0 && !isspace((unsigned char)*ptr))
ptr++;
break;
case 'B':
case 'b':
case 'h':
for (i = 0; i < asizeof(tptr->month); i++) {
if (Oalternative) {
if (c == 'B') {
len = strlen(tptr->alt_month[i]);
if (strncasecmp(buf,
tptr->alt_month[i],
len) == 0)
break;
}
} else {
len = strlen(tptr->month[i]);
if (strncasecmp(buf, tptr->month[i],
len) == 0)
break;
len = strlen(tptr->mon[i]);
if (strncasecmp(buf, tptr->mon[i],
len) == 0)
break;
}
}
if (i == asizeof(tptr->month))
return 0;
tm->tm_mon = i;
buf += len;
break;
case 'm':
if (!isdigit((unsigned char)*buf))
return 0;
len = 2;
for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
i *= 10;
i += *buf - '0';
len--;
}
if (i < 1 || i > 12)
return 0;
tm->tm_mon = i - 1;
if (*buf != 0 && isspace((unsigned char)*buf))
while (*ptr != 0 && !isspace((unsigned char)*ptr))
ptr++;
break;
case 's':
{
char *cp;
int sverrno;
long n;
time_t t;
sverrno = errno;
errno = 0;
n = strtol(buf, &cp, 10);
if (errno == ERANGE || (long)(t = n) != n) {
errno = sverrno;
return 0;
}
errno = sverrno;
buf = cp;
gmtime_r(&t, tm);
*GMTp = 1;
}
break;
case 'Y':
case 'y':
if (*buf == 0 || isspace((unsigned char)*buf))
break;
if (!isdigit((unsigned char)*buf))
return 0;
len = (c == 'Y') ? 4 : 2;
for (i = 0; len && *buf != 0 && isdigit((unsigned char)*buf); buf++) {
i *= 10;
i += *buf - '0';
len--;
}
if (c == 'Y')
i -= 1900;
if (c == 'y' && i < 69)
i += 100;
if (i < 0)
return 0;
tm->tm_year = i;
if (*buf != 0 && isspace((unsigned char)*buf))
while (*ptr != 0 && !isspace((unsigned char)*ptr))
ptr++;
break;
case 'Z':
{
const char *cp;
char *zonestr;
for (cp = buf; *cp && isupper((unsigned char)*cp); ++cp) {/*empty*/}
if (cp - buf) {
zonestr = alloca(cp - buf + 1);
strncpy(zonestr, buf, cp - buf);
zonestr[cp - buf] = '\0';
tzset();
if (0 == strcmp(zonestr, "GMT")) {
*GMTp = 1;
} else if (0 == strcmp(zonestr, tzname[0])) {
tm->tm_isdst = 0;
} else if (0 == strcmp(zonestr, tzname[1])) {
tm->tm_isdst = 1;
} else {
return 0;
}
buf += cp - buf;
}
}
break;
case 'z':
{
int sign = 1;
if (*buf != '+') {
if (*buf == '-')
sign = -1;
else
return 0;
}
buf++;
i = 0;
for (len = 4; len > 0; len--) {
if (isdigit((unsigned char)*buf)) {
i *= 10;
i += *buf - '0';
buf++;
} else
return 0;
}
tm->tm_hour -= sign * (i / 100);
tm->tm_min -= sign * (i % 100);
*GMTp = 1;
}
break;
}
}
return (char *)buf;
}
char *
strptime(const char * __restrict buf, const char * __restrict fmt,
struct tm * __restrict tm)
{
char *ret;
int gmt;
gmt = 0;
ret = _strptime(buf, fmt, tm, &gmt);
if (ret && gmt) {
time_t t = timegm(tm);
localtime_r(&t, tm);
}
return (ret);
}

View file

@ -0,0 +1,123 @@
.\" $FreeBSD$
.\"
.Dd September 11, 2005
.Dt TIME2POSIX 3
.Os
.Sh NAME
.Nm time2posix ,
.Nm posix2time
.Nd convert seconds since the Epoch
.Sh LIBRARY
.Lb libc
.Sh SYNOPSIS
.In time.h
.Ft time_t
.Fn time2posix "time_t t"
.Ft time_t
.Fn posix2time "time_t t"
.Sh DESCRIPTION
.St -p1003.1-88
legislates that a time_t value of
536457599 shall correspond to "Wed Dec 31 23:59:59 GMT 1986."
This effectively implies that POSIX time_t's cannot include leap
seconds and,
therefore,
that the system time must be adjusted as each leap occurs.
.Pp
If the time package is configured with leap-second support
enabled,
however,
no such adjustment is needed and
time_t values continue to increase over leap events
(as a true `seconds since...' value).
This means that these values will differ from those required by POSIX
by the net number of leap seconds inserted since the Epoch.
.Pp
Typically this is not a problem as the type time_t is intended
to be
(mostly)
opaque\(emtime_t values should only be obtained-from and
passed-to functions such as
.Xr time 3 ,
.Xr localtime 3 ,
.Xr mktime 3
and
.Xr difftime 3 .
However,
.St -p1003.1-88
gives an arithmetic
expression for directly computing a time_t value from a given date/time,
and the same relationship is assumed by some
(usually older)
applications.
Any programs creating/dissecting time_t's
using such a relationship will typically not handle intervals
over leap seconds correctly.
.Pp
The
.Fn time2posix
and
.Fn posix2time
functions are provided to address this time_t mismatch by converting
between local time_t values and their POSIX equivalents.
This is done by accounting for the number of time-base changes that
would have taken place on a POSIX system as leap seconds were inserted
or deleted.
These converted values can then be used in lieu of correcting the older
applications,
or when communicating with POSIX-compliant systems.
.Pp
The
.Fn time2posix
function is single-valued.
That is,
every local time_t
corresponds to a single POSIX time_t.
The
.Fn posix2time
function is less well-behaved:
for a positive leap second hit the result is not unique,
and for a negative leap second hit the corresponding
POSIX time_t does not exist so an adjacent value is returned.
Both of these are good indicators of the inferiority of the
POSIX representation.
.Pp
The following table summarizes the relationship between time_t
and its conversion to,
and back from,
the POSIX representation over the leap second inserted at the end of June,
1993.
.Bl -column "93/06/30" "23:59:59" "A+0" "X=time2posix(T)"
.It Sy "DATE TIME T X=time2posix(T) posix2time(X)"
.It "93/06/30 23:59:59 A+0 B+0 A+0"
.It "93/06/30 23:59:60 A+1 B+1 A+1 or A+2"
.It "93/07/01 00:00:00 A+2 B+1 A+1 or A+2"
.It "93/07/01 00:00:01 A+3 B+2 A+3"
.El
.Pp
A leap second deletion would look like...
.Bl -column "??/06/30" "23:59:58" "A+0" "X=time2posix(T)"
.It Sy "DATE TIME T X=time2posix(T) posix2time(X)"
.It "??/06/30 23:59:58 A+0 B+0 A+0"
.It "??/07/01 00:00:00 A+1 B+2 A+1"
.It "??/07/01 00:00:01 A+2 B+3 A+2"
.El
.Pp
.D1 No "[Note: posix2time(B+1) => A+0 or A+1]"
.Pp
If leap-second support is not enabled,
local time_t's and
POSIX time_t's are equivalent,
and both
.Fn time2posix
and
.Fn posix2time
degenerate to the identity function.
.Sh "SEE ALSO"
.Xr difftime 3 ,
.Xr localtime 3 ,
.Xr mktime 3 ,
.Xr time 3
.\" @(#)time2posix.3 8.2
.\" This file is in the public domain, so clarified as of
.\" 1996-06-05 by Arthur David Olson.

View file

@ -0,0 +1,100 @@
/*-
* Copyright (c) 2001 FreeBSD Inc.
* All rights reserved.
*
* These routines are for converting time_t to fixed-bit representations
* for use in protocols or storage. When converting time to a larger
* representation of time_t these routines are expected to assume temporal
* locality and use the 50-year rule to properly set the msb bits. XXX
*
* Redistribution and use under the terms of the COPYRIGHT file at the
* base of the source tree.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <timeconv.h>
/*
* Convert a 32 bit representation of time_t into time_t. XXX needs to
* implement the 50-year rule to handle post-2038 conversions.
*/
time_t
_time32_to_time(__int32_t t32)
{
return((time_t)t32);
}
/*
* Convert time_t to a 32 bit representation. If time_t is 64 bits we can
* simply chop it down. The resulting 32 bit representation can be
* converted back to a temporally local 64 bit time_t using time32_to_time.
*/
__int32_t
_time_to_time32(time_t t)
{
return((__int32_t)t);
}
/*
* Convert a 64 bit representation of time_t into time_t. If time_t is
* represented as 32 bits we can simply chop it and not support times
* past 2038.
*/
time_t
_time64_to_time(__int64_t t64)
{
return((time_t)t64);
}
/*
* Convert time_t to a 64 bit representation. If time_t is represented
* as 32 bits we simply sign-extend and do not support times past 2038.
*/
__int64_t
_time_to_time64(time_t t)
{
return((__int64_t)t);
}
/*
* Convert to/from 'long'. Depending on the sizeof(long) this may or
* may not require using the 50-year rule.
*/
long
_time_to_long(time_t t)
{
if (sizeof(long) == sizeof(__int64_t))
return(_time_to_time64(t));
return((long)t);
}
time_t
_long_to_time(long tlong)
{
if (sizeof(long) == sizeof(__int32_t))
return(_time32_to_time(tlong));
return((time_t)tlong);
}
/*
* Convert to/from 'int'. Depending on the sizeof(int) this may or
* may not require using the 50-year rule.
*/
int
_time_to_int(time_t t)
{
if (sizeof(int) == sizeof(__int64_t))
return(_time_to_time64(t));
return((int)t);
}
time_t
_int_to_time(int tint)
{
if (sizeof(int) == sizeof(__int32_t))
return(_time32_to_time(tint));
return((time_t)tint);
}

View file

@ -0,0 +1,117 @@
/*-
* Copyright (c) 2001 Alexey Zelkin <phantom@FreeBSD.org>
* Copyright (c) 1997 FreeBSD Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <stddef.h>
#include "ldpart.h"
#include "timelocal.h"
static struct lc_time_T _time_locale;
static int _time_using_locale;
static char *time_locale_buf;
#define LCTIME_SIZE (sizeof(struct lc_time_T) / sizeof(char *))
static const struct lc_time_T _C_time_locale = {
{
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
}, {
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
}, {
"Sun", "Mon", "Tue", "Wed",
"Thu", "Fri", "Sat"
}, {
"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"
},
/* X_fmt */
"%H:%M:%S",
/*
* x_fmt
* Since the C language standard calls for
* "date, using locale's date format," anything goes.
* Using just numbers (as here) makes Quakers happier;
* it's also compatible with SVR4.
*/
"%m/%d/%y",
/*
* c_fmt
*/
"%a %b %e %H:%M:%S %Y",
/* am */
"AM",
/* pm */
"PM",
/* date_fmt */
"%a %b %e %H:%M:%S %Z %Y",
/* alt_month
* Standalone months forms for %OB
*/
{
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
},
/* md_order
* Month / day order in dates
*/
"md",
/* ampm_fmt
* To determine 12-hour clock format time (empty, if N/A)
*/
"%I:%M:%S %p"
};
struct lc_time_T *
__get_current_time_locale(void)
{
return (_time_using_locale
? &_time_locale
: (struct lc_time_T *)&_C_time_locale);
}
int
__time_load_locale(const char *name)
{
return (__part_load_locale(name, &_time_using_locale,
&time_locale_buf, "LC_TIME",
LCTIME_SIZE, LCTIME_SIZE,
(const char **)&_time_locale));
}

View file

@ -0,0 +1,55 @@
/*-
* Copyright (c) 1997-2002 FreeBSD Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _TIMELOCAL_H_
#define _TIMELOCAL_H_
/*
* Private header file for the strftime and strptime localization
* stuff.
*/
struct lc_time_T {
const char *mon[12];
const char *month[12];
const char *wday[7];
const char *weekday[7];
const char *X_fmt;
const char *x_fmt;
const char *c_fmt;
const char *am;
const char *pm;
const char *date_fmt;
const char *alt_month[12];
const char *md_order;
const char *ampm_fmt;
};
struct lc_time_T *__get_current_time_locale(void);
int __time_load_locale(const char *);
#endif /* !_TIMELOCAL_H_ */

View file

@ -0,0 +1,152 @@
.\" $FreeBSD$
.Dd September 13, 1994
.Dt TZFILE 5
.Os
.Sh NAME
.Nm tzfile
.Nd timezone information
.Sh SYNOPSIS
.Fd #include \&"/usr/src/lib/libc/stdtime/tzfile.h\&"
.Sh DESCRIPTION
The time zone information files used by
.Xr tzset 3
begin with the magic characters
.Dq Li TZif
to identify them as
time zone information files,
followed by a character identifying the version of the file's format
(as of 2005, either an ASCII NUL or a '2')
followed by fifteen bytes containing zeroes reserved for future use,
followed by four four-byte values
written in a ``standard'' byte order
(the high-order byte of the value is written first).
These values are,
in order:
.Pp
.Bl -tag -compact -width tzh_ttisstdcnt
.It Va tzh_ttisgmtcnt
The number of UTC/local indicators stored in the file.
.It Va tzh_ttisstdcnt
The number of standard/wall indicators stored in the file.
.It Va tzh_leapcnt
The number of leap seconds for which data is stored in the file.
.It Va tzh_timecnt
The number of ``transition times'' for which data is stored
in the file.
.It Va tzh_typecnt
The number of ``local time types'' for which data is stored
in the file (must not be zero).
.It Va tzh_charcnt
The number of characters of ``time zone abbreviation strings''
stored in the file.
.El
.Pp
The above header is followed by
.Va tzh_timecnt
four-byte values of type
.Fa long ,
sorted in ascending order.
These values are written in ``standard'' byte order.
Each is used as a transition time (as returned by
.Xr time 3 )
at which the rules for computing local time change.
Next come
.Va tzh_timecnt
one-byte values of type
.Fa "unsigned char" ;
each one tells which of the different types of ``local time'' types
described in the file is associated with the same-indexed transition time.
These values serve as indices into an array of
.Fa ttinfo
structures (with
.Fa tzh_typecnt
entries) that appears next in the file;
these structures are defined as follows:
.Pp
.Bd -literal -offset indent
struct ttinfo {
long tt_gmtoff;
int tt_isdst;
unsigned int tt_abbrind;
};
.Ed
.Pp
Each structure is written as a four-byte value for
.Va tt_gmtoff
of type
.Fa long ,
in a standard byte order, followed by a one-byte value for
.Va tt_isdst
and a one-byte value for
.Va tt_abbrind .
In each structure,
.Va tt_gmtoff
gives the number of seconds to be added to UTC,
.Li tt_isdst
tells whether
.Li tm_isdst
should be set by
.Xr localtime 3
and
.Va tt_abbrind
serves as an index into the array of time zone abbreviation characters
that follow the
.Li ttinfo
structure(s) in the file.
.Pp
Then there are
.Va tzh_leapcnt
pairs of four-byte values, written in standard byte order;
the first value of each pair gives the time
(as returned by
.Xr time 3 )
at which a leap second occurs;
the second gives the
.Em total
number of leap seconds to be applied after the given time.
The pairs of values are sorted in ascending order by time.
.Pp
Then there are
.Va tzh_ttisstdcnt
standard/wall indicators, each stored as a one-byte value;
they tell whether the transition times associated with local time types
were specified as standard time or wall clock time,
and are used when a time zone file is used in handling POSIX-style
time zone environment variables.
.Pp
Finally there are
.Va tzh_ttisgmtcnt
UTC/local indicators, each stored as a one-byte value;
they tell whether the transition times associated with local time types
were specified as UTC or local time,
and are used when a time zone file is used in handling POSIX-style
time zone environment variables.
.Pp
.Nm localtime
uses the first standard-time
.Li ttinfo
structure in the file
(or simply the first
.Li ttinfo
structure in the absence of a standard-time structure)
if either
.Li tzh_timecnt
is zero or the time argument is less than the first transition time recorded
in the file.
.Pp
For version-2-format time zone files,
the above header and data is followed by a second header and data,
identical in format except that eight bytes are used for each
transition time or leap second time.
After the second header and data comes a newline-enclosed,
POSIX-TZ-environment-variable-style string for use in handling instants
after the last transition time stored in the file
(with nothing between the newlines if there is no POSIX representation for
such instants).
.Sh SEE ALSO
.Xr ctime 3 ,
.Xr time2posix 3 ,
.Xr zic 8
.\" @(#)tzfile.5 8.3
.\" This file is in the public domain, so clarified as of
.\" 1996-06-05 by Arthur David Olson.

View file

@ -0,0 +1,184 @@
#ifndef TZFILE_H
#define TZFILE_H
/*
** This file is in the public domain, so clarified as of
** 1996-06-05 by Arthur David Olson.
**
** $FreeBSD$
*/
/*
** This header is for use ONLY with the time conversion code.
** There is no guarantee that it will remain unchanged,
** or that it will remain at all.
** Do NOT copy it to any system include directory.
** Thank you!
*/
/*
** ID
*/
#ifndef lint
#ifndef NOID
/*
static char tzfilehid[] = "@(#)tzfile.h 8.1";
*/
#endif /* !defined NOID */
#endif /* !defined lint */
/*
** Information about time zone files.
*/
#ifndef TZDIR
#define TZDIR "/usr/share/zoneinfo" /* Time zone object file directory */
#endif /* !defined TZDIR */
#ifndef TZDEFAULT
#define TZDEFAULT "/etc/localtime"
#endif /* !defined TZDEFAULT */
#ifndef TZDEFRULES
#define TZDEFRULES "posixrules"
#endif /* !defined TZDEFRULES */
/*
** Each file begins with. . .
*/
#define TZ_MAGIC "TZif"
struct tzhead {
char tzh_magic[4]; /* TZ_MAGIC */
char tzh_version[1]; /* '\0' or '2' as of 2005 */
char tzh_reserved[15]; /* reserved--must be zero */
char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */
char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
char tzh_leapcnt[4]; /* coded number of leap seconds */
char tzh_timecnt[4]; /* coded number of transition times */
char tzh_typecnt[4]; /* coded number of local time types */
char tzh_charcnt[4]; /* coded number of abbr. chars */
};
/*
** . . .followed by. . .
**
** tzh_timecnt (char [4])s coded transition times a la time(2)
** tzh_timecnt (unsigned char)s types of local time starting at above
** tzh_typecnt repetitions of
** one (char [4]) coded UTC offset in seconds
** one (unsigned char) used to set tm_isdst
** one (unsigned char) that's an abbreviation list index
** tzh_charcnt (char)s '\0'-terminated zone abbreviations
** tzh_leapcnt repetitions of
** one (char [4]) coded leap second transition times
** one (char [4]) total correction after above
** tzh_ttisstdcnt (char)s indexed by type; if TRUE, transition
** time is standard time, if FALSE,
** transition time is wall clock time
** if absent, transition times are
** assumed to be wall clock time
** tzh_ttisgmtcnt (char)s indexed by type; if TRUE, transition
** time is UTC, if FALSE,
** transition time is local time
** if absent, transition times are
** assumed to be local time
*/
/*
** If tzh_version is '2' or greater, the above is followed by a second instance
** of tzhead and a second instance of the data in which each coded transition
** time uses 8 rather than 4 chars,
** then a POSIX-TZ-environment-variable-style string for use in handling
** instants after the last transition time stored in the file
** (with nothing between the newlines if there is no POSIX representation for
** such instants).
*/
/*
** In the current implementation, "tzset()" refuses to deal with files that
** exceed any of the limits below.
*/
#ifndef TZ_MAX_TIMES
#define TZ_MAX_TIMES 1200
#endif /* !defined TZ_MAX_TIMES */
#ifndef TZ_MAX_TYPES
#ifndef NOSOLAR
#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
#endif /* !defined NOSOLAR */
#ifdef NOSOLAR
/*
** Must be at least 14 for Europe/Riga as of Jan 12 1995,
** as noted by Earl Chew.
*/
#define TZ_MAX_TYPES 20 /* Maximum number of local time types */
#endif /* !defined NOSOLAR */
#endif /* !defined TZ_MAX_TYPES */
#ifndef TZ_MAX_CHARS
#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
/* (limited by what unsigned chars can hold) */
#endif /* !defined TZ_MAX_CHARS */
#ifndef TZ_MAX_LEAPS
#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
#endif /* !defined TZ_MAX_LEAPS */
#define SECSPERMIN 60
#define MINSPERHOUR 60
#define HOURSPERDAY 24
#define DAYSPERWEEK 7
#define DAYSPERNYEAR 365
#define DAYSPERLYEAR 366
#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY)
#define MONSPERYEAR 12
#define TM_SUNDAY 0
#define TM_MONDAY 1
#define TM_TUESDAY 2
#define TM_WEDNESDAY 3
#define TM_THURSDAY 4
#define TM_FRIDAY 5
#define TM_SATURDAY 6
#define TM_JANUARY 0
#define TM_FEBRUARY 1
#define TM_MARCH 2
#define TM_APRIL 3
#define TM_MAY 4
#define TM_JUNE 5
#define TM_JULY 6
#define TM_AUGUST 7
#define TM_SEPTEMBER 8
#define TM_OCTOBER 9
#define TM_NOVEMBER 10
#define TM_DECEMBER 11
#define TM_YEAR_BASE 1900
#define EPOCH_YEAR 1970
#define EPOCH_WDAY TM_THURSDAY
#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
/*
** Since everything in isleap is modulo 400 (or a factor of 400), we know that
** isleap(y) == isleap(y % 400)
** and so
** isleap(a + b) == isleap((a + b) % 400)
** or
** isleap(a + b) == isleap(a % 400 + b % 400)
** This is true even if % means modulo rather than Fortran remainder
** (which is allowed by C89 but not C99).
** We use this to avoid addition overflow problems.
*/
#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
#endif /* !defined TZFILE_H */