This commit is contained in:
Vítor Santos Costa 2015-06-18 01:15:50 +01:00
parent 204f81876a
commit 14330244df
38 changed files with 1501 additions and 0 deletions

View File

@ -0,0 +1,28 @@
libtai is a library for storing and manipulating dates and times.
libtai supports two time scales: (1) TAI64, covering a few hundred
billion years with 1-second precision; (2) TAI64NA, covering the same
period with 1-attosecond precision. Both scales are defined in terms of
TAI, the current international real time standard.
libtai provides an internal format for TAI64, struct tai, designed for
fast time manipulations. The tai_pack() and tai_unpack() routines
convert between struct tai and a portable 8-byte TAI64 storage format.
libtai provides similar internal and external formats for TAI64NA.
libtai provides struct caldate to store dates in year-month-day form. It
can convert struct caldate, under the Gregorian calendar, to a modified
Julian day number for easy date arithmetic.
libtai provides struct caltime to store calendar dates and times along
with UTC offsets. It can convert from struct tai to struct caltime in
UTC, accounting for leap seconds, for accurate date and time display. It
can also convert back from struct caltime to struct tai for user input.
Its overall UTC-to-TAI conversion speed is 100x better than the usual
UNIX mktime() implementation.
This version of libtai requires a UNIX system with gettimeofday(). It
will be easy to port to other operating systems with compilers
supporting 64-bit arithmetic.
The libtai source code is in the public domain.

View File

@ -0,0 +1,3 @@
19981013 libtai 0.60, alpha.
19970908 libtai 0.56, alpha.
19970730 libtai 0.50, alpha.

View File

@ -0,0 +1,55 @@
set(LIBYAPTAI_MAJOR_VERSION 0)
set(LIBYAPTAI_MINOR_VERSION 60)
set(LIBYAPTAI_PATCH_VERSION 0)
set(LIBYAPTAI_FULL_VERSION
${LIBYAPTAI_MAJOR_VERSION}.${LIBYAPTAI_MINOR_VERSION}.${LIBYAPTAI_PATCH_VERSION})
set(LIBYAPTAI_SOURCES
tai_add.c
tai_now.c
tai_pack.c
tai_sub.c
tai_unpack.c
taia_add.c
taia_approx.c
taia_fmtfrac.c
taia_frac.c
taia_half.c
taia_less.c
taia_now.c
taia_pack.c
taia_sub.c
taia_tai.c
taia_unpack.c
caldate_fmt.c
caldate_scan.c
caldate_fmjd.c
caldate_mjd.c
caldate_norm.c
caldate_ster.c
leapsecs_read.c
leapsecs_init.c
leapsecs_add.c
leapsecs_sub.c
caltime_fmt.c
caltime_scan.c
caltime_tai.c
caltime_utc.c
)
# ugh
set (POSITION_INDEPENDENT_CODE TRUE)
add_library (libyaptai OBJECT
${LIBYAPTAI_SOURCES})
set_target_properties(libyaptai
PROPERTIES
# RPATH ${libdir} VERSION ${LIBYAPTAI_FULL_VERSION}
# SOVERSION ${LIBYAPTAI_MAJOR_VERSION}.${LIBYAPTAI_MINOR_VERSION}
POSITION_INDEPENDENT_CODE TRUE
OUTPUT_NAME yaptai
)

View File

@ -0,0 +1,72 @@
BLURB
README
INSTALL
TODO
THANKS
CHANGES
FILES
VERSION
Makefile
leapsecs.txt
leapsecs.dat
leapsecs.c
check.in
check.out
check.c
easter.c
yearcal.c
nowutc.c
caldate.3
caldate_mjd.3
caldate.h
caldate_fmt.c
caldate_scan.c
caldate_mjd.c
caldate_fmjd.c
caldate_norm.c
caldate_ster.c
tai.3
tai_now.3
tai_pack.3
tai.h
tai_add.c
tai_now.c
tai_pack.c
tai_sub.c
tai_unpack.c
leapsecs.3
leapsecs.h
leapsecs_add.c
leapsecs_sub.c
leapsecs_init.c
leapsecs_read.c
caltime.3
caltime_tai.3
caltime.h
caltime_fmt.c
caltime_scan.c
caltime_tai.c
caltime_utc.c
taia.3
taia_now.3
taia_pack.3
taia.h
taia_add.c
taia_approx.c
taia_fmtfrac.c
taia_frac.c
taia_half.c
taia_less.c
taia_now.c
taia_pack.c
taia_sub.c
taia_tai.c
taia_unpack.c
conf-cc
conf-ld
find-systype.sh
make-compile.sh
make-load.sh
make-makelib.sh
trycpp.c
warn-auto.sh

View File

@ -0,0 +1,65 @@
Like any other piece of software (and information generally), libtai
comes with NO WARRANTY.
These are UNIX installation instructions; libtai has not yet been ported
to non-UNIX systems.
To compile the programs and format the man pages:
% make
Then, as root, enable leap second support:
# cp leapsecs.dat /etc/leapsecs.dat
# chmod 644 /etc/leapsecs.dat
To use another compiler, edit conf-cc and conf-ld.
There are five test programs here. The first is leapsecs, which creates
leapsecs.dat from leapsecs.txt. Try
% ./leapsecs < leapsecs.txt | cmp - leapsecs.dat
to make sure that there are no differences.
The second is check, which prints a variety of information about an
ISO-style input date. Try
% ./check < check.in | cmp - check.out
to make sure that your test results match mine. Note that the results
for future dates will change when more leap seconds are announced.
The third is easter, which prints the date of Easter in any given year.
Try
% ./easter 1997 1998 1999 2000 2001
and check the results against tables from some other source.
The fourth is yearcal, which prints a year-long calendar. Try
% ./yearcal 1997 | ul
and check the results against another calendar. (The days in January,
March, May, July, September, and November should show up in boldface.)
The fifth is nowutc, which prints the current time in UTC. Your system's
clock must be the number of seconds since 1970-01-01 00:00:10 TAI. This
is compatible with the interpretation of time_t in the Olson time
library with leap seconds enabled. Try
% env TZ=right/Etc/GMT date; ./nowutc
if your system has the Olson library.
The libtai code is in the public domain, so you can use it in your own
programs. But keep in mind that this is a very early release. Some of
the code hasn't been tested at all! caldate_mjd() and caldate_frommjd()
are based on previous code of mine; they need outside review. It would
be particularly helpful to systematically check my results against
independent results from someone else's code.

View File

@ -0,0 +1,93 @@
################################################################
# Makefile for the libtai
#
# Libtai by: D. J. Bernstein, djb@pobox.com
# This Makefile.in by: Jan Wielemaker, wielemak@science.uva.nl
#
# This makefile fits in the SWI-Prolog (www.swi-prolog.org)
# autconf setup.
################################################################
prefix=@prefix@
exec_prefix=@exec_prefix@
srcdir=@srcdir@
bindir=$(exec_prefix)/bin
PLBASE=@libdir@/@PL@-$(PLVERSION)
man_prefix=$(prefix)/man
mansec=1
lmansec=3
mandir=$(man_prefix)/man$(mansec)
lmandir=$(man_prefix)/man$(lmansec)
plmandir=$(PLBASE)/man
CC=@CC@
AR=@AR@
RANLIB=@RANLIB@
ifeq (@PROLOG_SYSTEM@,yap)
DEFS=@DEFS@ -D_YAP_NOT_INSTALLED_=1
CFLAGS= @SHLIB_CFLAGS@ $(YAP_EXTRAS) $(DEFS) -I$(srcdir) -I../../.. -I$(srcdir)/../../include @CPPFLAGS@ @JAVAINCPATH@
else
COFLAGS=@COFLAGS@
CWFLAGS=@CWFLAGS@
CIFLAGS=@CIFLAGS@
CMFLAGS=@CMFLAGS@
CPFLAGS=
LDFLAGS=@LDFLAGS@ $(CPFLAGS)
CFLAGS= $(CWFLAGS) $(COFLAGS) $(CIFLAGS) $(CMFLAGS) $(CPFLAGS)
endif #YAP/SWI
INSTALL=@INSTALL@
INSTALL_PROGRAM=@INSTALL_PROGRAM@
INSTALL_DATA=@INSTALL_DATA@
LIBOBJ= tai_add.o tai_now.o tai_pack.o \
tai_sub.o tai_unpack.o taia_add.o taia_approx.o \
taia_fmtfrac.o taia_frac.o taia_half.o taia_less.o \
taia_now.o taia_pack.o taia_sub.o taia_tai.o taia_unpack.o \
caldate_fmt.o caldate_scan.o caldate_fmjd.o caldate_mjd.o \
caldate_norm.o caldate_ster.o leapsecs_read.o \
leapsecs_init.o leapsecs_add.o leapsecs_sub.o caltime_fmt.o \
caltime_scan.o caltime_tai.o caltime_utc.o
PROG= easter check yearcal leapsecs nowutc
all: libtai.a
progs: $(PROG)
libtai.a: $(LIBOBJ)
rm -f $@
$(AR) cq $@ $(LIBOBJ)
$(RANLIB) $@
easter: easter.o libtai.a
$(CC) $(LDFLAGS) -o $@ easter.o -L. -ltai
check: check.o libtai.a
$(CC) $(LDFLAGS) -o $@ check.o -L. -ltai
yearcal: yearcal.o libtai.a
$(CC) $(LDFLAGS) -o $@ yearcal.o -L. -ltai
leapsecs: leapsecs.o libtai.a
$(CC) $(LDFLAGS) -o $@ leapsecs.o -L. -ltai
nowutc: nowutc.o libtai.a
$(CC) $(LDFLAGS) -o $@ nowutc.o -L. -ltai
%.o: $(srcdir)/%.c
$(CC) -c -I.. $(CFLAGS) $<
################################################################
# Cleanup
################################################################
clean:
rm -f *% *~ *.o *.gcda
profclean:
rm -f *.gcda
distclean: clean profclean
rm -f Makefile
rm -f $(PROG) libtai.a

View File

@ -0,0 +1,54 @@
.TH caldate 3
.SH NAME
caldate \- calendar dates
.SH SYNTAX
.B #include <caldate.h>
unsigned int \fBcaldate_fmt\fP(\fIs\fR,&\fIcd\fR);
.br
unsigned int \fBcaldate_scan\fP(\fIs\fR,&\fIcd\fR);
struct caldate \fIcd\fR;
.br
char *\fIs\fR;
.SH DESCRIPTION
A
.B struct caldate
value is a calendar date.
It has three components:
.BR year ,
.B month
(1...12),
and
.B day
(1...31).
.B caldate_fmt
prints
.I cd
in ISO style (yyyy-mm-dd)
into the character buffer
.IR s ,
without a terminating NUL.
It returns the number of characters printed.
.I s
may be zero;
then
.B caldate_fmt
returns the number of characters that would have been printed.
.B caldate_scan
reads a calendar date in ISO style
from the beginning of the character buffer
.I s
and puts it into
.IR cd .
It returns the number of characters read.
If
.I s
does not start with an ISO-style date,
.B caldate_scan
returns 0.
.SH "SEE ALSO"
caldate_mjd(3),
caltime(3)

View File

@ -0,0 +1,19 @@
#ifndef CALDATE_H
#define CALDATE_H
struct caldate {
long year;
int month;
int day;
} ;
extern unsigned int caldate_fmt(char *s, struct caldate *cd);
extern unsigned int caldate_scan(char *s, struct caldate *cd);
extern void caldate_frommjd(struct caldate *cd, int64_t day, int *pwday, int *pyday);
extern long caldate_mjd(struct caldate *cd);
extern void caldate_normalize(struct caldate *cd);
extern void caldate_easter(struct caldate *cd);
#endif

View File

@ -0,0 +1,45 @@
#include "tai.h"
#include "caldate.h"
void caldate_frommjd(struct caldate *cd, int64_t day, int *pwday, int *pyday)
{
long year;
long month;
int yday;
year = (long)(day / LL(146097));
day %= LL(146097);
day += LL(678881);
while (day >= LL(146097)) { day -= LL(146097); ++year; }
/* year * 146097 + day - 678881 is MJD; 0 <= day < 146097 */
/* 2000-03-01, MJD 51604, is year 5, day 0 */
if (pwday) *pwday = (int)((day + 3) % 7);
year *= 4;
if (day == LL(146096)) { year += 3; day = 36524L; }
else { year += (long)(day / LL(36524)); day %= LL(36524); }
year *= 25;
year += (long)(day / 1461);
day %= 1461;
year *= 4;
yday = (day < 306);
if (day == 1460) { year += 3; day = 365; }
else { year += (long)(day / 365); day %= 365; }
yday += (long)day;
day *= 10;
month = (long)((day + 5) / 306);
day = (day + 5) % 306;
day /= 10;
if (month >= 10) { yday -= 306; ++year; month -= 10; }
else { yday += 59; month += 2; }
cd->year = year;
cd->month = month + 1;
cd->day = (long)(day + 1);
if (pyday) *pyday = yday;
}

View File

@ -0,0 +1,24 @@
#include "tai.h"
#include "caldate.h"
unsigned int caldate_fmt(char *s, struct caldate *cd)
{
long x;
int i = 0;
x = cd->year; if (x < 0) x = -x; do { ++i; x /= 10; } while(x);
if (s) {
x = cd->year;
if (x < 0) { x = -x; *s++ = '-'; }
s += i; do { *--s = '0' + (char)(x % 10); x /= 10; } while(x); s += i;
x = cd->month;
s[0] = '-'; s[2] = '0' + (char)(x % 10); x /= 10; s[1] = '0' + (char)(x % 10);
x = cd->day;
s[3] = '-'; s[5] = '0' + (char)(x % 10); x /= 10; s[4] = '0' + (char)(x % 10);
}
return (cd->year < 0) + i + 6;
}

View File

@ -0,0 +1,111 @@
.TH caldate_mjd 3
.SH NAME
caldate_mjd \- manipulate calendar dates
.SH SYNTAX
.B #include <caldate.h>
void \fBcaldate_frommjd\fP(&\fIcd\fR,\fIday\fR,\fIweekday\fR,\fIyearday\fR);
.br
long \fBcaldate_mjd\fP(&\fIcd\fR);
.br
void \fBcaldate_normalize\fP(&\fIcd\fR);
void \fBcaldate_easter\fP(&\fIcd\fR);
struct caldate \fIcd\fR;
.br
long \fIday\fR;
.br
int *\fIweekday\fR;
.br
int *\fIyearday\fR;
.SH "BASIC CONVERSIONS"
Every calendar date has a
.I modified Julian day number\fR.
The day number increases by 1 every day.
Day number 0 is 17 November 1858.
Day number 51604 is 1 March 2000.
.B caldate_frommjd
puts into
.I cd
the date corresponding to the modified Julian day number
.IR day .
.B caldate_frommjd
also computes the day of the week (0 through 6)
and the day of the year (0 through 365).
It stores the day of the week in
.B *\fIweekday
if
.I weekday
is nonzero.
It stores the day of the year in
.B *\fIyearday
if
.I yearday
is nonzero.
.B caldate_mjd
returns the modified Julian day number corresponding to the date in
.IR cd .
.B caldate_mjd
accepts days outside the range 1 to 31,
referring to days before the beginning or after the end of the month.
It also accepts months outside the range 1 to 12,
referring to months before the beginning or after the end of the year.
.B caldate_normalize
calls
.B caldate_frommjd
with the result of
.BR caldate_mjd .
This means that it accepts out-of-range months and out-of-range days in
.IR cd ,
and puts a valid calendar date back into
.IR cd .
.SH "OTHER FUNCTIONS"
.B caldate_easter
reads the year from
.I cd
and then changes
.I cd
to the date of Easter in the same year.
.SH LIMITATIONS
The
.B caldate
routines currently support the Gregorian calendar,
which was defined in 1582 and adopted at different times
in different countries.
For earlier dates the
.B caldate
routines work with
``virtual Gregorian,''
defined mathematically by the 400-year Gregorian cycle
for years before 1582.
The Julian calendar is not supported.
The Gregorian calendar will be replaced by a new calendar
within a few thousand years.
The
.B caldate_frommjd
and
.B caldate_mjd
routines will be upgraded accordingly.
The current
.B caldate_frommjd
and
.B caldate_mjd
routines are not useful for dates far in the future.
Day numbers will overflow a 32-bit
.B long
sometime after the year 5000000;
all systems should upgrade to 64-bit
.BR long s
before then.
.B caldate_mjd
does not check for overflow.
.SH "SEE ALSO"
caldate(3)

View File

@ -0,0 +1,44 @@
#include "tai.h"
#include "caldate.h"
static unsigned long times365[4] = { 0, 365, 730, 1095 } ;
static unsigned long times36524[4] = { 0, 36524UL, 73048UL, 109572UL } ;
static unsigned long montab[12] =
{ 0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337 } ;
/* month length after february is (306 * m + 5) / 10 */
long caldate_mjd(struct caldate *cd)
{
long y;
long m;
long d;
d = cd->day - 678882L;
m = cd->month - 1;
y = cd->year;
d += 146097L * (y / 400);
y %= 400;
if (m >= 2) m -= 2; else { m += 10; --y; }
y += (m / 12);
m %= 12;
if (m < 0) { m += 12; --y; }
d += montab[m];
d += 146097L * (y / 400);
y %= 400;
if (y < 0) { y += 400; d -= 146097L; }
d += times365[y & 3];
y >>= 2;
d += 1461L * (y % 25);
y /= 25;
d += times36524[y & 3];
return d;
}

View File

@ -0,0 +1,7 @@
#include "tai.h"
#include "caldate.h"
void caldate_normalize(struct caldate *cd)
{
caldate_frommjd(cd,caldate_mjd(cd),(int *) 0,(int *) 0);
}

View File

@ -0,0 +1,24 @@
#include "tai.h"
#include "caldate.h"
unsigned int caldate_scan(char *s, struct caldate *cd)
{
int sign = 1;
char *t = s;
unsigned long z;
unsigned long c;
if (*t == '-') { ++t; sign = -1; }
z = 0; while ((c = (unsigned char) (*t - '0')) <= 9) { z = z * 10 + c; ++t; }
cd->year = z * sign;
if (*t++ != '-') return 0;
z = 0; while ((c = (unsigned char) (*t - '0')) <= 9) { z = z * 10 + c; ++t; }
cd->month = z;
if (*t++ != '-') return 0;
z = 0; while ((c = (unsigned char) (*t - '0')) <= 9) { z = z * 10 + c; ++t; }
cd->day = z;
return (unsigned int)(t - s);
}

View File

@ -0,0 +1,24 @@
#include "tai.h"
#include "caldate.h"
void caldate_easter(struct caldate *cd)
{
long c;
long t;
long j;
long n;
long y;
y = cd->year;
c = (y / 100) + 1;
t = 210 - (((c * 3) / 4) % 210);
j = y % 19;
n = 57 - ((14 + j * 11 + (c * 8 + 5) / 25 + t) % 30);
if ((n == 56) && (j > 10)) --n;
if (n == 57) --n;
n -= ((((y % 28) * 5) / 4 + t + n + 2) % 7);
if (n < 32) { cd->month = 3; cd->day = n; }
else { cd->month = 4; cd->day = n - 31; }
}

View File

@ -0,0 +1,82 @@
.TH caltime 3
.SH NAME
caltime \- calendar dates and times
.SH SYNTAX
.B #include <caltime.h>
unsigned int \fBcaltime_fmt\fP(\fIs\fR,&\fIct\fR);
.br
unsigned int \fBcaltime_scan\fP(\fIs\fR,&\fIct\fR);
struct caltime \fIct\fR;
.br
char *\fIs\fR;
.SH DESCRIPTION
A
.B struct caltime
value is a calendar date and time with an offset in minutes from UTC.
It has five components:
.B date
(a
.B struct caldate\fR),
.B hour
(0...23),
.B minute
(0...59),
.B second
(0...60),
and
.B offset
(-5999...5999).
For example,
a leap second occurred
on 30 June 1997 at 23:59:60 UTC.
The local time in New York was
30 June 1997 19:59:60 -0400.
This local time is represented inside a
.B struct caltime
with
.B date
containing 1997, 6, 30;
.B hour
19;
.B minute
59;
.B second
60;
and
.B offset
\-240
(4 hours).
.SH FORMATTING
.B caltime_fmt
prints
.I ct
in ISO style (yyyy-mm-dd hh:mm:ss +oooo)
into the character buffer
.IR s ,
without a terminating NUL.
It returns the number of characters printed.
.I s
may be zero;
then
.B caltime_fmt
returns the number of characters that would have been printed.
.B caltime_scan
reads a calendar date, time, and offset in ISO style
from the beginning of the character buffer
.I s
and puts them into
.IR ct .
It returns the number of characters read.
If
.I s
does not start with an ISO-style date and time (including offset),
.B caltime_scan
returns 0.
.SH "SEE ALSO"
caltime_tai(3),
caldate(3),
tai(3)

View File

@ -0,0 +1,20 @@
#ifndef CALTIME_H
#define CALTIME_H
#include "caldate.h"
struct caltime {
struct caldate date;
int hour;
int minute;
int second;
long offset;
} ;
extern void caltime_tai(struct caltime *ct, struct tai *t);
extern void caltime_utc(struct caltime *ct, struct tai *t, int *pwday, int *pyday);
extern unsigned int caltime_fmt(char *s, struct caltime *ct);
extern unsigned int caltime_scan(char *s, struct caltime *ct);
#endif

View File

@ -0,0 +1,44 @@
#include "tai.h"
#include "caldate.h"
#include "caltime.h"
unsigned int caltime_fmt(char *s, struct caltime *ct)
{
unsigned int result;
long x;
result = caldate_fmt(s,&ct->date);
if (s) {
s += result;
x = ct->hour;
s[0] = ' ';
s[2] = '0' + (char)(x % 10); x /= 10;
s[1] = '0' + (char)(x % 10);
s += 3;
x = ct->minute;
s[0] = ':';
s[2] = '0' + (char)(x % 10); x /= 10;
s[1] = '0' + (char)(x % 10);
s += 3;
x = ct->second;
s[0] = ':';
s[2] = '0' + (char)(x % 10); x /= 10;
s[1] = '0' + (char)(x % 10);
s += 3;
s[0] = ' ';
x = ct->offset;
if (x < 0) { s[1] = '-'; x = -x; } else s[1] = '+';
s[5] = '0' + (char)(x % 10); x /= 10;
s[4] = '0' + (char)(x % 6); x /= 6;
s[3] = '0' + (char)(x % 10); x /= 10;
s[2] = '0' + (char)(x % 10);
}
return result + 15;
}

View File

@ -0,0 +1,39 @@
#include "tai.h"
#include "caltime.h"
unsigned int caltime_scan(char *s, struct caltime *ct)
{
char *t = s;
unsigned long z;
unsigned long c;
int sign;
t += caldate_scan(t,&ct->date);
while ((*t == ' ') || (*t == '\t') || (*t == 'T')) ++t;
z = 0; while ((c = (unsigned char) (*t - '0')) <= 9) { z = z * 10 + c; ++t; }
ct->hour = z;
if (*t++ != ':') return 0;
z = 0; while ((c = (unsigned char) (*t - '0')) <= 9) { z = z * 10 + c; ++t; }
ct->minute = z;
if (*t != ':')
ct->second = 0;
else {
++t;
z = 0; while ((c = (unsigned char) (*t - '0')) <= 9) { z = z * 10 + c; ++t; }
ct->second = z;
}
while ((*t == ' ') || (*t == '\t')) ++t;
if (*t == '+') sign = 1; else if (*t == '-') sign = -1; else return 0;
++t;
c = (unsigned char) (*t++ - '0'); if (c > 9) return 0; z = c;
c = (unsigned char) (*t++ - '0'); if (c > 9) return 0; z = z * 10 + c;
c = (unsigned char) (*t++ - '0'); if (c > 9) return 0; z = z * 6 + c;
c = (unsigned char) (*t++ - '0'); if (c > 9) return 0; z = z * 10 + c;
ct->offset = z * sign;
return (unsigned int)(t - s);
}

View File

@ -0,0 +1,61 @@
.TH caltime 3
.SH NAME
caltime_tai \- convert calendar dates and times
.SH SYNTAX
.B #include <caltime.h>
.br
.B #include <tai.h>
void \fBcaltime_tai\fP(&\fIct\fR,&\fIt\fR);
.br
void \fBcaltime_utc\fP(&\fIct\fR,&\fIt\fR,&\fIweekday\fR,&\fIyearday\fR);
struct caltime \fIct\fR;
.br
struct tai \fIt\fR;
.br
int *\fIweekday\fR;
.br
int *\fIyearday\fR;
.SH DESCRIPTION
.B caltime_tai
reads a date, time, and UTC offset from
.IR ct .
It puts the corresponding TAI64 label into
.IR t .
.B caltime_utc
reads a TAI64 label from
.IR t .
It puts the corresponding date and time into
.IR ct ,
with UTC offset 0.
.B caltime_utc
fills in
.I weekday
and
.I yearday
the same way as
.BR caldate_frommjd .
.SH LIMITATIONS
The sequence of TAI64 labels has been determined
for the next few hundred billion years.
The same is not true, however, for
calendar dates and times.
New leap seconds are added every year or two,
as discussed in
.BR leapsecs (3);
and the Gregorian calendar will change in a few thousand years,
as discussed in
.BR caldate_mjd (3).
This means that
.B caltime_tai
and
.B caltime_utc
are not useful for dates far in the future.
.SH "SEE ALSO"
caldate_mjd(3),
caltime(3),
tai(3),
leapsecs(3)

View File

@ -0,0 +1,23 @@
#include "tai.h"
#include "leapsecs.h"
#include "caldate.h"
#include "caltime.h"
/* XXX: breaks tai encapsulation */
void caltime_tai(struct caltime *ct, struct tai *t)
{
long day;
long s;
/* XXX: check for overflow? */
day = caldate_mjd(&ct->date);
s = ct->hour * 60 + ct->minute;
s = (s - ct->offset) * 60 + ct->second;
t->x = day * ULL(86400) + ULL(4611686014920671114) + (int64_t) s;
leapsecs_add(t,ct->second == 60);
}

View File

@ -0,0 +1,31 @@
#include "tai.h"
#include "leapsecs.h"
#include "caldate.h"
#include "caltime.h"
/* XXX: breaks tai encapsulation */
void caltime_utc(struct caltime *ct, struct tai *t, int *pwday, int *pyday)
{
struct tai t2 = *t;
uint64_t u;
int leap;
long s;
/* XXX: check for overfow? */
leap = leapsecs_sub(&t2);
u = t2.x;
u += 58486;
s = (long)(u % ULL(86400));
ct->second = (s % 60) + leap; s /= 60;
ct->minute = s % 60; s /= 60;
ct->hour = s;
u /= ULL(86400);
caldate_frommjd(&ct->date, (int64_t)u - LL(53375995543064), pwday, pyday);
ct->offset = 0;
}

View File

@ -0,0 +1,52 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "tai.h"
#include "leapsecs.h"
#include "caltime.h"
char line[100];
char *dayname[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" } ;
char out[101];
char x[TAI_PACK];
int
main(int argc, char **argv)
{
struct tai t;
struct tai t2;
struct caltime ct;
struct caltime ct2;
int weekday;
int yearday;
int i;
double packerr;
if (leapsecs_init() == -1)
printf("unable to init leapsecs\n");
while (fgets(line,sizeof line,stdin))
if (!caltime_scan(line,&ct))
printf("unable to parse\n");
else {
caltime_tai(&ct,&t);
caltime_utc(&ct2,&t,&weekday,&yearday);
tai_pack(x,&t);
tai_unpack(x,&t2);
tai_sub(&t2,&t2,&t);
packerr = tai_approx(&t2);
for (i = 0;i < TAI_PACK;++i)
printf("%2.2lx",(unsigned long) (unsigned char) x[i]);
if (packerr)
printf(" packerr=%f",packerr);
printf(" %03d %s",yearday,dayname[weekday]);
if (caltime_fmt((char *) 0,&ct2) + 1 < sizeof out) {
out[caltime_fmt(out,&ct2)] = 0;
printf(" %s",out);
}
printf("\n");
}
exit(0);
}

View File

@ -0,0 +1,44 @@
-1000000-12-31 23:59:59 +0000
-999999-1-1 0:0:0 +0000
-1-12-31 23:59:59 +0000
0-1-1 0:0:0 +0000
1-1-1 1:1:1 +0000
1000-01-01 12:04:37 +0000
1799-12-31 23:59:59 +0000
1899-12-31 23:59:59 +0000
1900-01-01 00:00:00 +0000
1969-12-31 23:59:49 +0000
1969-12-31 23:59:50 +0000
1969-12-31 23:59:59 +0000
1970-01-01 00:00:00 +0000
1970-01-01 00:00:01 +0000
1972-06-30 23:59:58 +0000
1972-06-30 23:59:59 +0000
1972-06-30 23:59:60 +0000
1972-07-01 00:00:00 +0000
1972-07-01 00:00:01 +0000
1995-12-31 23:59:58 +0000
1995-12-31 23:59:59 +0000
1995-12-31 23:59:60 +0000
1996-01-01 00:00:00 +0000
1996-01-01 00:00:01 +0000
1996-01-01 00:00:02 +0000
1997-06-30 23:59:59 +0000
1997-06-30 23:59:60 +0000
1997-07-01 00:00:00 +0000
1997-07-30 08:57:43 +0000
1997-10-03 18:14:48 +0000
1999-09-09 09:09:09 +0000
1999-12-31 23:59:59 +0000
2000-01-01 00:00:00 +0000
2000-01-01 00:00:01 +0000
2000-02-28 23:59:59 +0000
2000-02-29 23:59:59 +0000
2000-03-01 00:00:00 +0000
2000-12-31 23:59:59 +0000
2100-01-01 17:42:15 +0000
2200-01-01 17:42:15 +0000
3000-01-01 17:42:15 +0000
10000-01-01 17:42:15 +0000
999999-12-31 23:59:59 +0000
1000000-1-1 0:0:0 +0000

View File

@ -0,0 +1,44 @@
3fffe33e1b840309 365 Sun -1000000-12-31 23:59:59 +0000
3fffe33e1b84030a 000 Mon -999999-01-01 00:00:00 +0000
3ffffff1868b8409 364 Fri -1-12-31 23:59:59 +0000
3ffffff1868b840a 000 Sat 0-01-01 00:00:00 +0000
3ffffff1886e1757 000 Mon 1-01-01 01:01:01 +0000
3ffffff8df7db65f 000 Wed 1000-01-01 12:04:37 +0000
3ffffffec03dbf89 364 Tue 1799-12-31 23:59:59 +0000
3fffffff7c558189 364 Sun 1899-12-31 23:59:59 +0000
3fffffff7c55818a 000 Mon 1900-01-01 00:00:00 +0000
3fffffffffffffff 364 Wed 1969-12-31 23:59:49 +0000
4000000000000000 364 Wed 1969-12-31 23:59:50 +0000
4000000000000009 364 Wed 1969-12-31 23:59:59 +0000
400000000000000a 000 Thu 1970-01-01 00:00:00 +0000
400000000000000b 000 Thu 1970-01-01 00:00:01 +0000
4000000004b25808 181 Fri 1972-06-30 23:59:58 +0000
4000000004b25809 181 Fri 1972-06-30 23:59:59 +0000
4000000004b2580a 181 Fri 1972-06-30 23:59:60 +0000
4000000004b2580b 182 Sat 1972-07-01 00:00:00 +0000
4000000004b2580c 182 Sat 1972-07-01 00:00:01 +0000
4000000030e7241b 364 Sun 1995-12-31 23:59:58 +0000
4000000030e7241c 364 Sun 1995-12-31 23:59:59 +0000
4000000030e7241d 364 Sun 1995-12-31 23:59:60 +0000
4000000030e7241e 000 Mon 1996-01-01 00:00:00 +0000
4000000030e7241f 000 Mon 1996-01-01 00:00:01 +0000
4000000030e72420 000 Mon 1996-01-01 00:00:02 +0000
4000000033b8489d 180 Mon 1997-06-30 23:59:59 +0000
4000000033b8489e 180 Mon 1997-06-30 23:59:60 +0000
4000000033b8489f 181 Tue 1997-07-01 00:00:00 +0000
4000000033df0226 210 Wed 1997-07-30 08:57:43 +0000
4000000034353637 275 Fri 1997-10-03 18:14:48 +0000
4000000037d77955 251 Thu 1999-09-09 09:09:09 +0000
40000000386d439f 364 Fri 1999-12-31 23:59:59 +0000
40000000386d43a0 000 Sat 2000-01-01 00:00:00 +0000
40000000386d43a1 000 Sat 2000-01-01 00:00:01 +0000
4000000038bb0c1f 058 Mon 2000-02-28 23:59:59 +0000
4000000038bc5d9f 059 Tue 2000-02-29 23:59:59 +0000
4000000038bc5da0 060 Wed 2000-03-01 00:00:00 +0000
400000003a4fc89f 365 Sun 2000-12-31 23:59:59 +0000
40000000f4875017 000 Fri 2100-01-01 17:42:15 +0000
40000001b09f1217 000 Wed 2200-01-01 17:42:15 +0000
40000007915fc517 000 Wed 3000-01-01 17:42:15 +0000
4000003afff53a97 000 Sat 10000-01-01 17:42:15 +0000
40001ca4f3758a1f 364 Fri 999999-12-31 23:59:59 +0000
40001ca4f3758a20 000 Sat 1000000-01-01 00:00:00 +0000

View File

@ -0,0 +1,29 @@
#include <stdio.h>
#include <stdlib.h>
#include "caldate.h"
char *dayname[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" } ;
char out[101];
int
main(int argc, char **argv)
{
struct caldate cd;
long day;
int weekday;
int yearday;
while (*++argv) {
cd.year = atoi(*argv);
if (cd.year > 0) {
caldate_easter(&cd);
day = caldate_mjd(&cd);
caldate_frommjd(&cd,day,&weekday,&yearday);
if (caldate_fmt((char *) 0,&cd) + 1 >= sizeof out) exit(1);
out[caldate_fmt(out,&cd)] = 0;
printf("%s %s yearday %d mjd %ld\n", dayname[weekday], out, yearday, day);
}
}
exit(0);
}

View File

@ -0,0 +1,106 @@
.TH leapsecs 3
.SH NAME
leapsecs \- handle UTC leap seconds
.SH SYNTAX
.B #include <leapsecs.h>
int \fBleapsecs_sub\fP(&\fIt\fR);
.br
void \fBleapsecs_add\fP(&\fIt\fR,\fIhit\fR);
int \fBleapsecs_read\fP();
.br
int \fBleapsecs_init\fP();
struct tai \fIt\fR;
.br
int \fIhit\fR;
.SH DESCRIPTION
.B leapsecs_sub
changes a seconds-since-epoch count
into a non-leap-seconds-since-epoch count.
It interprets
.I t
as a TAI64 label,
subtracts from
.I t
the number of leap seconds that have occurred
before or at
.IR t ,
and places the result back into
.IR t .
.B leapsecs_sub
returns 1 if
.I t
was a leap second,
0 otherwise.
.B leapsecs_add
reverses the effect of
.BR leapsecs_sub .
.I hit
must be 1
for a leap second,
0 otherwise.
.SH "LEAP-SECOND TABLE"
The current implementation of
.B leapsecs_sub
and
.B leapsecs_add
uses a leap-second table read from disk.
.B leapsecs_read
reads the leap-second table from
.BR /etc/leapsecs.dat .
It returns 0 on success, -1 on error.
If
.B /etc/leapsecs.dat
does not exist,
.B leapsecs_read
treats it as an empty file.
.B leapsecs_init
is a one-time version of
.BR leapsecs_read .
Initially it is the same as
.BR leapsecs_read ;
however, once
.B leapsecs_read
returns 0,
.B leapsecs_init
will always return 0
without calling
.B leapsecs_read
again.
.B leapsecs_add
and
.B leapsecs_sub
call
.BR leapsecs_init .
.B WARNING:
If
.B leapsecs_init
returns failure,
.B leapsecs_add
and
.B leapsecs_sub
will proceed without a leap-second table.
For reliability,
all programs should call
.B leapsecs_init
at startup and check for errors.
.B WARNING:
New entries are added to the leap-second table on disk
every 12 to 18 months.
.B leapsecs_read
may be called repeatedly.
It leaves the old table alone on error.
For reliability,
all long-running programs should call
.B leapsecs_read
at least once every month.
.SH "SEE ALSO"
tai(3)

View File

@ -0,0 +1,30 @@
#include <stdio.h>
#include <stdlib.h>
#include "tai.h"
#include "leapsecs.h"
#include "caldate.h"
/* XXX: breaks tai encapsulation */
/* XXX: output here has to be binary; DOS redirection uses ASCII */
char line[100];
int
main(int argc, char**argv)
{
struct caldate cd;
struct tai t;
char x[TAI_PACK];
long leaps = 0;
while (fgets(line,sizeof line,stdin))
if (line[0] == '+')
if (caldate_scan(line + 1,&cd)) {
t.x = (caldate_mjd(&cd) + 1) * 86400ULL + 4611686014920671114ULL + leaps++;
tai_pack(x,&t);
fwrite(x,TAI_PACK,1,stdout);
}
exit(0);
}

Binary file not shown.

View File

@ -0,0 +1,16 @@
#ifndef LEAPSECS_H
#define LEAPSECS_H
extern int leapsecs_init(void);
extern int leapsecs_read(const char *file);
extern void leapsecs_add(struct tai *t, int hit);
extern int leapsecs_sub(struct tai *t);
#ifndef GLOBAL
#define GLOBAL extern
#endif
GLOBAL struct tai *leapsecs;
GLOBAL int leapsecs_num;
#endif

View File

@ -0,0 +1,38 @@
# http://pobox.com/~djb/libtai/leapsecs.txt, version 1998-10-13
#
# Official sources:
# http://maia.usno.navy.mil/leapsec.html
# http://hpiers.obspm.fr/webiers/general/earthor/utc/UTC.html
#
# Dates must be listed in sorted order.
# + means a positive leap second, inserting 23:59:60 UTC.
# - means a negative leap second, skipping 23:59:59 UTC.
#
# Note for parsers: Negative leap seconds will probably never happen, but
# the year 10000 will happen. Please don't contribute to the Y10K problem.
#
# 1972-01-01 00:00:00 UTC was 1972-01-01 00:00:10 TAI.
#
+1972-06-30
+1972-12-31
+1973-12-31
+1974-12-31
+1975-12-31
+1976-12-31
+1977-12-31
+1978-12-31
+1979-12-31
+1981-06-30
+1982-06-30
+1983-06-30
+1985-06-30
+1987-12-31
+1989-12-31
+1990-12-31
+1992-06-30
+1993-06-30
+1994-06-30
+1995-12-31
+1997-06-30
+1998-12-31
+2005-12-31

View File

@ -0,0 +1,21 @@
#include "tai.h"
#include "leapsecs.h"
/* XXX: breaks tai encapsulation */
void leapsecs_add(struct tai *t, int hit)
{
int i;
uint64_t u;
if (leapsecs_init() == -1) return;
u = t->x;
for (i = 0;i < leapsecs_num;++i) {
if (u < leapsecs[i].x) break;
if (!hit || (u > leapsecs[i].x)) ++u;
}
t->x = u;
}

View File

@ -0,0 +1,12 @@
#include "tai.h"
#include "leapsecs.h"
static int flaginit = 0;
int leapsecs_init(void)
{
if (flaginit) return 0;
if (leapsecs_read("/etc/leapsecs.dat") == -1) return -1;
flaginit = 1;
return 0;
}

View File

@ -0,0 +1,62 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#ifdef __WINDOWS__
#include <io.h>
#else
#include <unistd.h>
#endif
#include "tai.h"
#define GLOBAL
#include "leapsecs.h"
#ifndef O_BINARY
#define O_BINARY 0
#endif
#ifndef O_NDELAY
#define O_NDELAY 0
#endif
int leapsecs_read(const char *file)
{ int fd;
struct stat st;
struct tai *t;
int n;
int i;
struct tai u;
fd = open(file, O_RDONLY|O_NDELAY|O_BINARY);
if (fd == -1) {
if (errno != ENOENT) return -1;
if (leapsecs) free(leapsecs);
leapsecs = 0;
leapsecs_num = 0;
return 0;
}
if (fstat(fd,&st) == -1) { close(fd); return -1; }
t = (struct tai *) malloc(st.st_size);
if (!t) { close(fd); return -1; }
n = read(fd,(char *) t,st.st_size);
close(fd);
if (n != st.st_size) { free(t); return -1; }
n /= sizeof(struct tai);
for (i = 0;i < n;++i) {
tai_unpack((char *) &t[i],&u);
t[i] = u;
}
if (leapsecs) free(leapsecs);
leapsecs = t;
leapsecs_num = n;
return 0;
}

View File

@ -0,0 +1,25 @@
#include "tai.h"
#include "leapsecs.h"
/* XXX: breaks tai encapsulation */
int leapsecs_sub(struct tai *t)
{
int i;
uint64_t u;
int s;
if (leapsecs_init() == -1) return 0;
u = t->x;
s = 0;
for (i = 0;i < leapsecs_num;++i) {
if (u < leapsecs[i].x) break;
++s;
if (u == leapsecs[i].x) { t->x = u - s; return 1; }
}
t->x = u - s;
return 0;
}

View File

@ -0,0 +1,18 @@
#include "taia.h"
/* XXX: breaks tai encapsulation */
void taia_add(struct taia *t, struct taia *u, struct taia *v)
{
t->sec.x = u->sec.x + v->sec.x;
t->nano = u->nano + v->nano;
t->atto = u->atto + v->atto;
if (t->atto > 999999999UL) {
t->atto -= 1000000000UL;
++t->nano;
}
if (t->nano > 999999999UL) {
t->nano -= 1000000000UL;
++t->sec.x;
}
}

View File

@ -0,0 +1,5 @@
#include "taia.h"
double taia_approx(struct taia *t)
{ return tai_approx(&t->sec) + taia_frac(t);
}

View File

@ -0,0 +1,31 @@
#include "taia.h"
unsigned int taia_fmtfrac(char *s, struct taia *t)
{
unsigned long x;
if (s) {
x = t->atto;
s[17] = '0' + (int)(x % 10); x /= 10;
s[16] = '0' + (int)(x % 10); x /= 10;
s[15] = '0' + (int)(x % 10); x /= 10;
s[14] = '0' + (int)(x % 10); x /= 10;
s[13] = '0' + (int)(x % 10); x /= 10;
s[12] = '0' + (int)(x % 10); x /= 10;
s[11] = '0' + (int)(x % 10); x /= 10;
s[10] = '0' + (int)(x % 10); x /= 10;
s[9] = '0' + (int)(x % 10);
x = t->nano;
s[8] = '0' + (int)(x % 10); x /= 10;
s[7] = '0' + (int)(x % 10); x /= 10;
s[6] = '0' + (int)(x % 10); x /= 10;
s[5] = '0' + (int)(x % 10); x /= 10;
s[4] = '0' + (int)(x % 10); x /= 10;
s[3] = '0' + (int)(x % 10); x /= 10;
s[2] = '0' + (int)(x % 10); x /= 10;
s[1] = '0' + (int)(x % 10); x /= 10;
s[0] = '0' + (int)(x % 10);
}
return 18;
}