fix pl-tai

This commit is contained in:
Vitor Santos Costa
2011-03-09 13:05:03 +00:00
parent 752fa4fc53
commit 5418f55d74
78 changed files with 344 additions and 419 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,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=$(prefix)/lib/@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,37 @@
################################################################
# Makefile for libtai
#
# Author: Jan Wielemaker
# E-mail: wielemak@science.uva.nl
################################################################
!include ..\rules.mk
LIBOBJ= tai_add.obj tai_now.obj tai_pack.obj tai_sub.obj \
tai_unpack.obj taia_add.obj taia_approx.obj \
taia_fmtfrac.obj taia_frac.obj taia_half.obj taia_less.obj \
taia_now.obj taia_pack.obj taia_sub.obj taia_tai.obj \
taia_unpack.obj caldate_fmt.obj caldate_scan.obj \
caldate_fmjd.obj caldate_mjd.obj caldate_norm.obj \
caldate_ster.obj leapsecs_read.obj \
leapsecs_init.obj leapsecs_add.obj leapsecs_sub.obj \
caltime_fmt.obj caltime_scan.obj caltime_tai.obj \
caltime_utc.obj
all: tai.lib
tai.lib: $(LIBOBJ)
if exist $@ del $@
$(AR) /out:$@ /nologo $(LIBOBJ)
################################################################
# Cleanup
################################################################
clean::
if exist *.obj del *.obj
if exist *~ del *~
distclean: clean
-del tai.lib *.pdb 2>nul

View File

@@ -0,0 +1,10 @@
libtai 0.60, alpha.
19981013
Copyright 1998
D. J. Bernstein, djb@pobox.com
http://pobox.com/~djb/libtai.html
libtai is a library for storing and manipulating dates and times. See
BLURB for a more detailed advertisement.
INSTALL says how to set up and test libtai.

View File

View File

@@ -0,0 +1,6 @@
test caldate_mjd handling of weird days
test caldate_mjd handling of weird months
manually verify check.out
make 32-bit version?
test, test, test!
support time zones

View File

@@ -0,0 +1 @@
libtai 0.60

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,39 @@
#include <stdio.h>
#include <stdlib.h>
#include "tai.h"
#include "leapsecs.h"
#include "taia.h"
#include "caltime.h"
struct taia now;
struct tai sec;
struct caltime ct;
char x[TAIA_FMTFRAC];
int
main(int argc, char **argv)
{
if (leapsecs_init() == -1) {
fprintf(stderr,"utcnow: fatal: unable to init leapsecs\n");
exit(111);
}
taia_now(&now);
x[taia_fmtfrac(x,&now)] = 0;
taia_tai(&now,&sec);
caltime_utc(&ct,&sec,(int *) 0,(int *) 0);
printf("%ld-%02d-%02d %02d:%02d:%02d.%s\n"
,ct.date.year
,ct.date.month
,ct.date.day
,ct.hour
,ct.minute
,ct.second
,x
);
exit(0);
}

View File

@@ -0,0 +1,83 @@
.TH tai 3
.SH NAME
tai \- manipulate times with 1-second precision
.SH SYNTAX
.B #include <tai.h>
double \fBtai_approx\fP(&\fIt\fR);
int \fBtai_less\fP(&\fIa\fR,&\fIb\fR);
.br
void \fBtai_add\fP(&\fIt\fR,&\fIa\fR,&\fIb\fR);
.br
void \fBtai_sub\fP(&\fIt\fR,&\fIa\fR,&\fIb\fR);
struct tai \fIt\fR;
.br
struct tai \fIa\fR;
.br
struct tai \fIb\fR;
.SH DESCRIPTION
A
.B struct tai
stores an integer between 0 inclusive and 2^64 exclusive.
The format of
.B struct tai
is designed to speed up common operations;
applications should not look inside
.B struct tai\fR.
A
.B struct tai
variable is commonly used to store
a TAI64 label.
Each TAI64 label refers to one second of real time.
TAI64 labels span a period of
hundreds of billions of years.
See
.B http://pobox.com/~djb/proto/tai64.txt
for more information.
A
.B struct tai
variable may also be used to store
the numerical difference between two TAI64 labels.
.SH ARITHMETIC
.B tai_approx
returns a double-precision approximation to
.IR t .
The result of
.B tai_approx
is always nonnegative.
.B tai_less
returns 1 if
.I a
is smaller than
.IR b ,
0 otherwise.
.B tai_add
adds
.I a
and
.I b
modulo 2^64
and puts the result into
.IR t .
The inputs and outputs may overlap.
.B tai_sub
subtracts
.I b
from
.I a
modulo 2^64
and puts the result into
.IR t .
The inputs and outputs may overlap.
.SH "SEE ALSO"
tai_now(3),
tai_pack(3),
taia(3),
utc(3)

36
packages/PLStream/libtai/tai.h Executable file
View File

@@ -0,0 +1,36 @@
#ifndef TAI_H
#define TAI_H
#ifdef __WINDOWS__
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
#include <inttypes.h> /* more portable than stdint.h */
#endif
#if defined(__WINDOWS__) && !defined(__GNUC__)
#define LL(x) x ## i64
#define ULL(x) x ## ui64
#else
#define LL(x) x ## LL
#define ULL(x) x ## ULL
#endif
struct tai {
uint64_t x;
} ;
extern void tai_now(struct tai *t);
/* JW: MSVC cannot convert unsigned to double :-( */
#define tai_approx(t) ((double) ((int64_t)(t)->x))
extern void tai_add(struct tai *t, struct tai *u, struct tai *v);
extern void tai_sub(struct tai *t, struct tai *u, struct tai *v);
#define tai_less(t,u) ((t)->x < (u)->x)
#define TAI_PACK 8
extern void tai_pack(char *s, struct tai *t);
extern void tai_unpack(char *s, struct tai *t);
#endif

View File

@@ -0,0 +1,6 @@
#include "tai.h"
void tai_add(struct tai *t, struct tai *u, struct tai *v)
{
t->x = u->x + v->x;
}

View File

@@ -0,0 +1,34 @@
.TH tai_now 3
.SH NAME
tai_now \- get current time, with 1-second precision
.SH SYNTAX
.B #include <tai.h>
void \fBtai_now\fP(&\fIt\fR);
struct tai \fIt\fR;
.SH DESCRIPTION
.B tai_now
puts the current time into
.IR t .
More precisely:
.B tai_now
puts into
.I t
its best guess as to the TAI64 label for the 1-second interval
that contains the current time.
.SH NOTES
This implementation of
.B tai_now
assumes that the
.B time_t
returned from the
.B time
function
represents the number of TAI seconds since
1970-01-01 00:00:10 TAI.
.SH "SEE ALSO"
tai(3),
taia_now(3),
time(3)

View File

@@ -0,0 +1,7 @@
#include <time.h>
#include "tai.h"
void tai_now(struct tai *t)
{
t->x = ULL(4611686018427387914) + (uint64_t) time((time_t *) 0);
}

View File

@@ -0,0 +1,36 @@
.TH tai_pack 3
.SH NAME
tai_pack \- convert TAI64 labels to external format
.SH SYNTAX
.B #include <tai.h>
void \fBtai_pack\fP(\fIbuf\fR,&\fIt\fR);
.br
void \fBtai_unpack\fP(\fIbuf\fR,&\fIt\fR);
char \fIbuf\fR[\fBTAI_PACK\fP];
.br
struct tai \fIt\fR;
.SH DESCRIPTION
.B tai_pack
converts a TAI64 label
from internal format in
.I t
to TAI64 format in
.IR buf .
.B tai_unpack
converts a TAI64 label
from TAI64 format in
.I buf
to internal format in
.IR t .
.B TAI_PACK
is 8.
See
.B http://pobox.com/~djb/proto/tai64.txt
for more information about TAI64 format.
.SH "SEE ALSO"
tai(3)

View File

@@ -0,0 +1,17 @@
#include "tai.h"
void tai_pack(char *s, struct tai *t)
{
uint64_t x;
x = t->x;
/* JW: Cast to int need to get MSVC6 silent */
s[7] = (int)x & 255; x >>= 8;
s[6] = (int)x & 255; x >>= 8;
s[5] = (int)x & 255; x >>= 8;
s[4] = (int)x & 255; x >>= 8;
s[3] = (int)x & 255; x >>= 8;
s[2] = (int)x & 255; x >>= 8;
s[1] = (int)x & 255; x >>= 8;
s[0] = (int)x;
}

View File

@@ -0,0 +1,6 @@
#include "tai.h"
void tai_sub(struct tai *t, struct tai *u, struct tai *v)
{
t->x = u->x - v->x;
}

View File

@@ -0,0 +1,16 @@
#include "tai.h"
void tai_unpack(char *s, struct tai *t)
{
uint64_t x;
x = (unsigned char) s[0];
x <<= 8; x += (unsigned char) s[1];
x <<= 8; x += (unsigned char) s[2];
x <<= 8; x += (unsigned char) s[3];
x <<= 8; x += (unsigned char) s[4];
x <<= 8; x += (unsigned char) s[5];
x <<= 8; x += (unsigned char) s[6];
x <<= 8; x += (unsigned char) s[7];
t->x = x;
}

View File

@@ -0,0 +1,140 @@
.TH taia 3
.SH NAME
taia \- manipulate times with 1-attosecond precision
.SH SYNTAX
.B #include <taia.h>
double \fBtaia_approx\fP(&\fIt\fR);
.br
double \fBtaia_frac\fP(&\fIt\fR);
.br
void \fBtaia_tai\fP(&\fIt\fR,&\fIsec\fR);
int \fBtaia_less\fP(&\fIa\fR,&\fIb\fR);
.br
void \fBtaia_add\fP(&\fIt\fR,&\fIa\fR,&\fIb\fR);
.br
void \fBtaia_sub\fP(&\fIt\fR,&\fIa\fR,&\fIb\fR);
.br
void \fBtaia_half\fP(&\fIt\fR,&\fIa\fR);
unsigned int \fBtaia_fmtfrac\fP(\fIs\fR,&\fIt\fR);
struct taia \fIt\fR;
.br
struct taia \fIa\fR;
.br
struct taia \fIb\fR;
.br
struct tai \fIsec\fR;
.br
char *\fIs\fR;
.SH DESCRIPTION
A
.B struct taia
stores an integer between 0 inclusive and 2^64x10^18 exclusive.
The format of
.B struct taia
is designed to speed up common operations;
applications should not look inside
.B struct taia\fR.
A
.B struct taia
variable is commonly used to store
a TAI64NA label.
Each TAI64NA label refers to one attosecond of real time;
see
.B http://pobox.com/~djb/proto/tai64.txt
for more information.
The integer in the
.B struct taia
is 10^18 times the second count,
plus 10^9 times the nanosecond count,
plus the attosecond count.
A
.B struct taia
variable may also be used to store
the numerical difference between two TAI64NA labels.
.SH ARITHMETIC
.B taia_approx
returns a double-precision approximation to
.IR t /10^18.
The result of
.B taia_approx
is always nonnegative.
.B taia_tai
places into
.I sec
the integer part of
.IR t /10^18.
.B taia_frac
returns a double-precision approximation to
the fraction part of
.IR t /10^18.
The result of
.B taia_frac
is always nonnegative.
.B taia_less
returns 1 if
.I a
is smaller than
.IR b ,
0 otherwise.
.B taia_add
adds
.I a
and
.I b
modulo 2^64x10^18
and puts the result into
.IR t .
The inputs and outputs may overlap.
.B taia_sub
subtracts
.I b
from
.I a
modulo 2^64x10^18
and puts the result into
.IR t .
The inputs and outputs may overlap.
.B taia_half
divides
.I a
by 2, rounding down,
and puts the result into
.IR t .
The input and output may overlap.
.SH "FORMATTING"
.B taia_fmtfrac
prints the remainder of
.IR t /10^18,
padded to exactly 18 digits,
into the character buffer
.IR s ,
without a terminating NUL.
It returns 18, the number of characters written.
.I s
may be zero;
then
.B taia_fmtfrac
returns 18 without printing anything.
The macro
.B TAIA_FMTFRAC
is defined as 19;
this is enough space for the output of
.B taia_fmtfrac
and a terminating NUL.
.SH "SEE ALSO"
taia_now(3),
taia_pack(3),
tai(3)

View File

@@ -0,0 +1,31 @@
#ifndef TAIA_H
#define TAIA_H
#include "tai.h"
struct taia {
struct tai sec;
unsigned long nano; /* 0...999999999 */
unsigned long atto; /* 0...999999999 */
} ;
extern void taia_tai(struct taia *ta, struct tai *t);
extern void taia_now(struct taia *t);
extern double taia_approx(struct taia *t);
extern double taia_frac(struct taia *t);
extern void taia_add(struct taia *t, struct taia *u, struct taia *v);
extern void taia_sub(struct taia *t, struct taia *u, struct taia *v);
extern void taia_half(struct taia *t, struct taia *u);
extern int taia_less(struct taia *t, struct taia *u);
#define TAIA_PACK 16
extern void taia_pack(char *s, struct taia *t);
extern void taia_unpack(char *s, struct taia *t);
#define TAIA_FMTFRAC 19
extern unsigned int taia_fmtfrac(char *s, struct taia *t);
#endif

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;
}

View File

@@ -0,0 +1,6 @@
#include "taia.h"
double taia_frac(struct taia *t)
{
return (t->atto * 0.000000001 + t->nano) * 0.000000001;
}

View File

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

View File

@@ -0,0 +1,12 @@
#include "taia.h"
/* XXX: breaks tai encapsulation */
int taia_less(struct taia *t, struct taia *u)
{
if (t->sec.x < u->sec.x) return 1;
if (t->sec.x > u->sec.x) return 0;
if (t->nano < u->nano) return 1;
if (t->nano > u->nano) return 0;
return t->atto < u->atto;
}

View File

@@ -0,0 +1,33 @@
.TH taia_now 3
.SH NAME
taia_now \- get current time, with 1-attosecond precision
.SH SYNTAX
.B #include <taia.h>
void \fBtaia_now\fP(&\fIt\fR);
struct taia \fIt\fR;
.SH DESCRIPTION
.B taia_now
puts the current time into
.IR t .
More precisely:
.B taia_now
puts into
.I t
its best guess as to the TAI64NA label for the 1-attosecond interval
that contains the current time.
.SH NOTES
This implementation of
.B tai_now
assumes that the
.B struct timeval
returned from
.B gettimeofday
represents the number of TAI seconds since
1970-01-01 00:00:10 TAI.
.SH "SEE ALSO"
gettimeofday(2),
tai_now(3),
taia(3)

View File

@@ -0,0 +1,40 @@
#include <sys/types.h>
#ifdef __WINDOWS__
#define WINDOWS_LEAN_AND_MEAN 1
#include <windows.h>
#else
#include <sys/time.h>
#endif
#include "taia.h"
/* XXX: breaks tai encapsulation */
/*-------------------------------------------------------------------------
* The Microsoft Win32 API can return the current system time in "file
* timestamp" format, which is a 64-bit value representing the number of
* 100-nanosecond ticks since {AD1601-01-01 00:00:00 Z}.
* 11644473600 is seconds offset AD1601 to AD1970
*------------------------------------------------------------------------*/
void taia_now(struct taia *t)
{
#ifdef __WINDOWS__
FILETIME ft;
int64_t cns; /* 100ns ticks */
/* Get the current system time */
GetSystemTimeAsFileTime(&ft);
/* Convert to longtime_t form */
cns = ((int64_t)ft.dwHighDateTime << 32) + ft.dwLowDateTime;
t->sec.x = cns/10000000 - 11644473600 + ULL(4611686018427387914);
t->nano = (long)((cns % 10000000))*100;
t->atto = 0;
#else
struct timeval now;
gettimeofday(&now,(struct timezone *) 0);
t->sec.x = ULL(4611686018427387914) + (uint64_t) now.tv_sec;
t->nano = 1000 * now.tv_usec + 500;
t->atto = 0;
#endif
}

View File

@@ -0,0 +1,36 @@
.TH taia_pack 3
.SH NAME
taia_pack \- convert TAI64NA labels to external format
.SH SYNTAX
.B #include <taia.h>
void \fBtaia_pack\fP(\fIbuf\fR,&\fIt\fR);
.br
void \fBtaia_unpack\fP(\fIbuf\fR,&\fIt\fR);
char \fIbuf\fR[\fBTAIA_PACK\fP];
.br
struct taia \fIt\fR;
.SH DESCRIPTION
.B taia_pack
converts a TAI64NA label
from internal format in
.I t
to TAI64NA format in
.IR buf .
.B taia_unpack
converts a TAI64NA label
from TAI64NA format in
.I buf
to internal format in
.IR t .
.B TAIA_PACK
is 16.
See
.B http://pobox.com/~djb/proto/tai64.txt
for more information about TAI64NA format.
.SH "SEE ALSO"
taia(3)

View File

@@ -0,0 +1,20 @@
#include "taia.h"
void taia_pack(char *s, struct taia *t)
{
unsigned long x;
tai_pack(s,&t->sec);
s += 8;
x = t->atto;
s[7] = (int)x & 255; x >>= 8;
s[6] = (int)x & 255; x >>= 8;
s[5] = (int)x & 255; x >>= 8;
s[4] = (int)x;
x = t->nano;
s[3] = (int)x & 255; x >>= 8;
s[2] = (int)x & 255; x >>= 8;
s[1] = (int)x & 255; x >>= 8;
s[0] = (int)x;
}

View File

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

View File

@@ -0,0 +1,6 @@
#include "taia.h"
void taia_tai(struct taia *ta, struct tai *t)
{
*t = ta->sec;
}

View File

@@ -0,0 +1,20 @@
#include "taia.h"
void taia_unpack(char *s, struct taia *t)
{
unsigned long x;
tai_unpack(s,&t->sec);
s += 8;
x = (unsigned char) s[4];
x <<= 8; x += (unsigned char) s[5];
x <<= 8; x += (unsigned char) s[6];
x <<= 8; x += (unsigned char) s[7];
t->atto = x;
x = (unsigned char) s[0];
x <<= 8; x += (unsigned char) s[1];
x <<= 8; x += (unsigned char) s[2];
x <<= 8; x += (unsigned char) s[3];
t->nano = x;
}

View File

@@ -0,0 +1,66 @@
#include <stdio.h>
#include <stdlib.h>
#include "caldate.h"
char *montab[] = {
"January"
, "February"
, "March"
, "April"
, "May"
, "June"
, "July"
, "August"
, "September"
, "October"
, "November"
, "December"
} ;
int main(int argc, char **argv)
{
int year;
long daystart;
long dayend;
long day;
int weekday;
struct caldate cd;
while (*++argv) {
year = atoi(*argv);
cd.year = year;
cd.month = 1;
cd.day = 1;
daystart = caldate_mjd(&cd);
cd.year = year + 1;
dayend = caldate_mjd(&cd);
while ((daystart + 3) % 7) --daystart;
while ((dayend + 3) % 7) ++dayend;
for (day = daystart;day < dayend;++day) {
caldate_frommjd(&cd,day,&weekday,(int *) 0);
if (cd.year != year)
printf(" ");
else {
if (cd.month & 1)
if (cd.day < 10)
printf(" %d%c%d ",cd.day % 10,8,cd.day % 10);
else
printf("%d%c%d%d%c%d ",cd.day / 10,8,cd.day / 10,cd.day % 10,8,cd.day % 10);
else
printf("%2d ",cd.day);
if (weekday == 6) {
if ((cd.day >= 15) && (cd.day < 22))
printf(" %s %d\n",montab[cd.month - 1],year);
else
printf("\n");
}
}
}
printf("\n");
}
exit(0);
}

View File

@@ -4716,6 +4716,7 @@ init_yap(void)
PL_register_extensions(PL_predicates_from_glob);
PL_register_extensions(PL_predicates_from_write);
PL_register_extensions(PL_predicates_from_read);
PL_register_extensions(PL_predicates_from_tai);
PL_register_extensions(foreigns);
fileerrors = TRUE;
SinitStreams();

View File

@@ -63,6 +63,7 @@ LastModifiedFile(const char *file)
if ( statfunc(OsPath(file, tmp), &buf) < 0 )
return (time_t)-1;
fprintf(stderr,"buf.st_mtime=%ld\n",buf.st_mtime);
return buf.st_mtime;
}

View File

@@ -840,6 +840,7 @@ extern const PL_extension PL_predicates_from_ctype[];
extern const PL_extension PL_predicates_from_file[];
extern const PL_extension PL_predicates_from_files[];
extern const PL_extension PL_predicates_from_glob[];
extern const PL_extension PL_predicates_from_write[];
extern const PL_extension PL_predicates_from_read[];
extern const PL_extension PL_predicates_from_tai[];
extern const PL_extension PL_predicates_from_write[];

958
packages/PLStream/pl-tai.c Executable file
View File

@@ -0,0 +1,958 @@
/* $Id$
Part of SWI-Prolog
Author: Jan Wielemaker
E-mail: J.Wielemaker@cs.vu.nl
WWW: http://www.swi-prolog.org
Copyright (C): 1985-2010, University of Amsterdam
VU University Amsterdam
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Solaris has asctime_r() with 3 arguments. Using _POSIX_PTHREAD_SEMANTICS
is supposed to give the POSIX standard one.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(__sun__) || defined(__sun)
#define _POSIX_PTHREAD_SEMANTICS 1
#endif
#include <math.h>
#include "pl-incl.h"
#include "libtai/taia.h"
#include "libtai/caltime.h"
#include <stdio.h>
#include <ctype.h>
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
#if defined(__WINDOWS__) || defined (__CYGWIN__)
#define timezone _timezone
#ifndef HAVE_VAR_TIMEZONE
#define HAVE_VAR_TIMEZONE
#endif
#else
extern char *tzname[2];
#ifdef HAVE_VAR_TIMEZONE
extern long timezone;
#endif
#endif
#define TAI_UTC_OFFSET LL(4611686018427387914)
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
struct ftm is a `floating' version of the system struct tm.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#define HAS_STAMP 0x0001
#define HAS_WYDAY 0x0002
typedef struct ftm
{ struct tm tm; /* System time structure */
double sec; /* float version of tm.tm_sec */
int utcoff; /* offset to UTC (seconds) */
atom_t tzname; /* Name of timezone */
int isdst; /* Daylight saving time */
double stamp; /* Time stamp (sec since 1970-1-1) */
int flags; /* Filled fields */
} ftm;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
tz_offset() returns the offset from UTC in seconds.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static void
do_tzset()
{ static int done = FALSE;
if ( !done )
{ tzset();
done = TRUE;
}
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
POSIX provides the variable timezone, providing the offset of the
current timezone WEST of GMT in seconds. Some systems (FreeBSD) do not
provide that. Instead thet provide tm_gmtoff in struct tm, but this
value is EAST and includes the DST offset.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static int
tz_offset()
{
#ifdef HAVE_VAR_TIMEZONE
do_tzset();
return timezone;
#else
#ifdef HAVE_STRUCT_TIME_TM_GMTOFF
static int offset = -1;
if ( offset == -1 )
{ time_t t = time(NULL);
struct tm tm;
localtime_r(&t, &tm);
offset = -tm.tm_gmtoff;
if ( tm.tm_isdst > 0 )
offset += 3600;
/*Use to verify on systems where we know both. In Western Europe the
offset must be -3600, both in winter and summer.*/
/*Sdprintf("timezone offset = %d (must be %d)\n", offset, timezone);*/
}
return offset;
#else
#error "Do not know how to get timezone info"
#endif
#endif
}
static char *
tz_name(int dst)
{ dst = (dst != 0);
do_tzset();
return tzname[dst];
}
static atom_t
tz_name_as_atom(int dst)
{ static atom_t a[2];
dst = (dst != 0); /* 0 or 1 */
if ( !a[dst] )
{ wchar_t wbuf[256];
const char *str = tz_name(dst);
size_t n;
if ( (n = mbstowcs(wbuf, str, sizeof(wbuf)/sizeof(wbuf[0])-1)) != (size_t)-1 )
{ a[dst] = PL_new_atom_wchars(n, wbuf);
} else
{ a[dst] = PL_new_atom(str);
}
}
return a[dst];
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
unify_taia(): Unify a TAIA date as a Prolog double using the POSIX 1970
origin;
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*
static int
unify_taia(term_t t, struct taia *taia)
{ double d = (double)((int64_t)taia->sec.x - TAI_UTC_OFFSET);
d += taia->nano / 1e9;
return PL_unify_float(t, d);
}
*/
static int
get_taia(term_t t, struct taia *taia, double *seconds)
{ double d;
if ( PL_get_float(t, &d) )
{ double fp, ip;
if ( seconds )
*seconds = d;
fp = modf(d, &ip);
if ( fp < 0 )
{ fp += 1.0;
ip -= 1.0;
}
taia->sec.x = (int64_t)ip + TAI_UTC_OFFSET;
taia->nano = (long)(fp*1e9);
taia->atto = 0L;
return TRUE;
}
return FALSE;
}
static int
get_tz_arg(int i, term_t t, term_t a, atom_t *tz)
{ GET_LD
atom_t name;
_PL_get_arg(i, t, a);
if ( !PL_get_atom_ex(a, &name) )
fail;
if ( name != ATOM_minus )
*tz = name;
succeed;
}
static int
get_int_arg(int i, term_t t, term_t a, int *val)
{ GET_LD
_PL_get_arg(i, t, a);
return PL_get_integer_ex(a, val);
}
static int
get_float_arg(int i, term_t t, term_t a, double *val)
{ GET_LD
_PL_get_arg(i, t, a);
return PL_get_float_ex(a, val);
}
static int
get_bool_arg(int i, term_t t, term_t a, int *val)
{ GET_LD
atom_t name;
_PL_get_arg(i, t, a);
if ( PL_get_atom(a, &name) )
{ if ( name == ATOM_true )
{ *val = TRUE;
return TRUE;
} else if ( name == ATOM_false || name == ATOM_minus )
{ *val = FALSE;
return TRUE;
}
}
return PL_get_bool_ex(a, val); /* generate an error */
}
static int
get_ftm(term_t t, ftm *ftm)
{ GET_LD
if ( PL_is_functor(t, FUNCTOR_date9) )
{ term_t tmp = PL_new_term_ref();
memset(ftm, 0, sizeof(*ftm));
if ( get_int_arg (1, t, tmp, &ftm->tm.tm_year) &&
get_int_arg (2, t, tmp, &ftm->tm.tm_mon) &&
get_int_arg (3, t, tmp, &ftm->tm.tm_mday) &&
get_int_arg (4, t, tmp, &ftm->tm.tm_hour) &&
get_int_arg (5, t, tmp, &ftm->tm.tm_min) &&
get_float_arg(6, t, tmp, &ftm->sec) &&
get_int_arg (7, t, tmp, &ftm->utcoff) &&
get_tz_arg (8, t, tmp, &ftm->tzname) &&
get_bool_arg (9, t, tmp, &ftm->isdst) )
{ double fp, ip;
fixup:
fp = modf(ftm->sec, &ip);
if ( fp < 0.0 )
{ fp += 1.0;
ip -= 1.0;
}
ftm->tm.tm_sec = (int)ip;
ftm->tm.tm_year -= 1900; /* 1900 based */
ftm->tm.tm_mon--; /* 0-based */
succeed;
}
} else if ( PL_is_functor(t, FUNCTOR_date3) )
{ term_t tmp = PL_new_term_ref();
memset(ftm, 0, sizeof(*ftm));
if ( get_int_arg (1, t, tmp, &ftm->tm.tm_year) &&
get_int_arg (2, t, tmp, &ftm->tm.tm_mon) &&
get_int_arg (3, t, tmp, &ftm->tm.tm_mday) )
goto fixup;
}
fail;
}
/** void cal_ftm(ftm *ftm, int required)
compute missing fields from fmt
*/
void
cal_ftm(ftm *ftm, int required)
{ int missing = ftm->flags^required;
if ( missing ) /* we need something, so we always */
{ struct caltime ct; /* need the stamp */
struct tai tai;
ct.date.year = ftm->tm.tm_year+1900;
ct.date.month = ftm->tm.tm_mon+1;
ct.date.day = ftm->tm.tm_mday;
ct.hour = ftm->tm.tm_hour;
ct.minute = ftm->tm.tm_min;
ct.second = ftm->tm.tm_sec;
ct.offset = -ftm->utcoff / 60; /* TBD: make libtai speak seconds */
caltime_tai(&ct, &tai);
ftm->stamp = (double)((int64_t)tai.x - TAI_UTC_OFFSET);
ftm->stamp -= (double)ct.second;
ftm->stamp += ftm->sec;
ftm->flags |= HAS_STAMP;
if ( missing & HAS_WYDAY )
{ caltime_utc(&ct, &tai, &ftm->tm.tm_wday, &ftm->tm.tm_yday);
ftm->flags |= HAS_WYDAY;
}
}
}
static
PRED_IMPL("stamp_date_time", 3, stamp_date_time, 0)
{ PRED_LD
struct taia taia;
term_t compound = A2;
double argsec;
if ( get_taia(A1, &taia, &argsec) )
{ struct caltime ct;
int weekday, yearday;
double sec;
int utcoffset;
int done = FALSE;
atom_t alocal;
atom_t tzatom = ATOM_minus;
atom_t dstatom = ATOM_minus;
if ( PL_get_atom(A3, &alocal) )
{ if ( alocal == ATOM_local )
{ time_t unixt;
int64_t ut64;
struct tm tm;
utcoffset = tz_offset();
ut64 = taia.sec.x - TAI_UTC_OFFSET;
unixt = (time_t) ut64;
if ( (int64_t)unixt == ut64 )
{ double ip;
localtime_r(&unixt, &tm);
sec = (double)tm.tm_sec + modf(argsec, &ip);
ct.date.year = tm.tm_year+1900;
ct.date.month = tm.tm_mon+1;
ct.date.day = tm.tm_mday;
ct.hour = tm.tm_hour;
ct.minute = tm.tm_min;
tzatom = tz_name_as_atom(tm.tm_isdst);
if ( tm.tm_isdst > 0 )
{ utcoffset -= 3600;
dstatom = ATOM_true;
} else
{ dstatom = ATOM_false;
}
done = TRUE;
}
} else if ( alocal == ATOM_utc )
{ utcoffset = 0;
tzatom = alocal;
} else
{ return PL_error(NULL, 0, NULL, ERR_DOMAIN, ATOM_timezone, A3);
}
} else if ( !PL_get_integer_ex(A3, &utcoffset) )
{ fail;
}
if ( !done )
{ taia.sec.x -= utcoffset;
caltime_utc(&ct, &taia.sec, &weekday, &yearday);
sec = (double)ct.second+(double)taia.nano/1e9;
}
return PL_unify_term(compound,
PL_FUNCTOR, FUNCTOR_date9,
PL_LONG, ct.date.year,
PL_INT, ct.date.month,
PL_INT, ct.date.day,
PL_INT, ct.hour,
PL_INT, ct.minute,
PL_FLOAT, sec,
PL_INT, utcoffset,
PL_ATOM, tzatom,
PL_ATOM, dstatom);
}
/* time_stamp */
return PL_error(NULL, 0, NULL, ERR_TYPE, ATOM_float, A1);
}
static
PRED_IMPL("date_time_stamp", 2, date_time_stamp, 0)
{ ftm ftm;
if ( !get_ftm(A1, &ftm) )
fail;
cal_ftm(&ftm, HAS_STAMP);
return PL_unify_float(A2, ftm.stamp);
}
/*******************************
* GLIBC FUNCTIONS *
*******************************/
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
These functions support strftime() %g, %G and %V. Code is copied from
glibc 2.3.5. As Glibc is LGPL, there are no license issues.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#ifndef __isleap
/* Nonzero if YEAR is a leap year (every 4 years,
except every 100th isn't, and every 400th is). */
# define __isleap(year) \
((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
#endif
/* The number of days from the first day of the first ISO week of this
year to the year day YDAY with week day WDAY. ISO weeks start on
Monday; the first ISO week has the year's first Thursday. YDAY may
be as small as YDAY_MINIMUM. */
#define ISO_WEEK_START_WDAY 1 /* Monday */
#define ISO_WEEK1_WDAY 4 /* Thursday */
#define YDAY_MINIMUM (-366)
#ifdef __GNUC__
__inline__
#endif
static int
iso_week_days(int yday, int wday)
{ /* Add enough to the first operand of % to make it nonnegative. */
int big_enough_multiple_of_7 = (-YDAY_MINIMUM / 7 + 2) * 7;
return (yday
- (yday - wday + ISO_WEEK1_WDAY + big_enough_multiple_of_7) % 7
+ ISO_WEEK1_WDAY - ISO_WEEK_START_WDAY);
}
/*******************************
* ERRORS *
*******************************/
static int
fmt_domain_error(const char *key, int value)
{ GET_LD
term_t t = PL_new_term_ref();
PL_put_integer(t, value);
return PL_error(NULL, 0, NULL, ERR_DOMAIN, PL_new_atom(key), t);
}
static int
fmt_not_implemented(int c)
{ GET_LD
term_t t = PL_new_term_ref();
char key[3];
key[0] = '%';
key[1] = c;
key[2] = 0;
PL_put_atom_chars(t, key);
return PL_error(NULL, 0, NULL, ERR_EXISTENCE, PL_new_atom("format"), t);
}
/*******************************
* FORMATTING *
*******************************/
#define OUT1DIGIT(fd, val) \
{ Sputcode('0'+(val)%10, fd); \
}
#define OUT2DIGITS(fd, val) \
{ Sputcode('0'+((val)/10)%10, fd); \
Sputcode('0'+(val)%10, fd); \
}
#define OUT3DIGITS(fd, val) \
{ Sputcode('0'+((val)/100)%10, fd); \
Sputcode('0'+((val)/10)%10, fd); \
Sputcode('0'+(val)%10, fd); \
}
#define OUT2DIGITS_SPC(fd, val) \
{ Sputcode(((val)/10 == 0 ? ' ' : '0'+((val)/10)%10), fd); \
Sputcode('0'+(val)%10, fd); \
}
#define OUTNUMBER(fd, fmt, val) \
{ Sfprintf(fd, fmt, val); \
}
#define SUBFORMAT(f) \
{ format_time(fd, f, ftm, posix); \
}
#define OUTCHR(fd, c) \
{ Sputcode(c, fd); \
}
#define OUTSTR(str) \
{ Sfputs(str, fd); \
}
#define OUTSTRA(str) \
{ foutstra(str, fd); \
}
#define OUTATOM(a) \
{ writeAtomToStream(fd, a); \
}
static void
foutstra(const char *str, IOSTREAM *fd)
{ wchar_t wbuf[256];
size_t n;
if ( (n = mbstowcs(wbuf, str, sizeof(wbuf)/sizeof(wbuf[0])-1)) != (size_t)-1 )
{ wchar_t *p;
for(p=wbuf; n-- > 0; p++)
Sputcode(*p, fd);
}
}
static const char *abbred_weekday[] =
{ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
static const char *weekday[] =
{ "Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday" };
static const char *abbred_month[] =
{ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
static const char *month[] =
{ "January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
};
#define NOARG (-1)
static int
format_time(IOSTREAM *fd, const wchar_t *format, ftm *ftm, int posix)
{ wint_t c;
while((c = *format++))
{ int arg = NOARG;
int altO = FALSE;
switch(c)
{ case '%':
arg = NOARG;
fmt_next:
switch((c = *format++))
{ case 'a': /* %a: abbreviated weekday */
case 'A': /* %A: weekday */
case 'b': /* %b: abbreviated month */
case 'B': /* %B: month */
if ( posix )
{ const char *s;
cal_ftm(ftm, HAS_STAMP|HAS_WYDAY);
switch( c )
{ case 'a':
s = abbred_weekday[ftm->tm.tm_wday];
break;
case 'A':
s = weekday[ftm->tm.tm_wday];
break;
case 'b':
s = abbred_month[ftm->tm.tm_mon];
break;
case 'B':
s = month[ftm->tm.tm_mon];
break;
default:
s = NULL;
assert(0);
}
OUTSTR(s);
break;
}
/*FALLTHROUGH*/
case 'c': /* %c: default representation */
case 'p': /* %p: AM/PM (locale) */
case 'P': /* %P: am/pm (locale) */
case 'x': /* %x: date in locale */
case 'X': /* %X: time in locale */
case_b:
{ char fmt[3];
char buf[256];
size_t n;
fmt[0] = '%';
fmt[1] = (char)c;
fmt[2] = EOS;
cal_ftm(ftm, HAS_STAMP|HAS_WYDAY);
/* conversion is not thread-safe under locale switch */
n = strftime(buf, sizeof(buf), fmt, &ftm->tm);
OUTSTRA(buf);
break;
}
case 'C': /* (year/100) as a 2-digit int */
{ int year = ftm->tm.tm_year+1900;
if ( year >= 0 && year < 10000 )
{ int century = year/100;
OUT2DIGITS(fd, century);
} else
{ return fmt_domain_error("%C", year);
}
break;
}
case 'd': /* day of the month */
OUT2DIGITS(fd, ftm->tm.tm_mday);
break;
case 'D': /* %m/%d/%y */
SUBFORMAT(L"%m/%d/%y");
break;
case 'e': /* day of the month */
OUT2DIGITS_SPC(fd, ftm->tm.tm_mday);
break;
case 'E': /* alternative format */
return fmt_not_implemented(c);
case 'F': /* ISO 8601 date format */
SUBFORMAT(L"%Y-%m-%d");
break;
case 'G':
case 'g':
case 'V':
{ int year, days;
cal_ftm(ftm, HAS_STAMP|HAS_WYDAY);
year = ftm->tm.tm_year+1900;
days = iso_week_days(ftm->tm.tm_yday, ftm->tm.tm_wday);
if ( days < 0 )
{ year--;
days = iso_week_days(ftm->tm.tm_yday + (365 + __isleap (year)),
ftm->tm.tm_wday);
} else
{ int d = iso_week_days(ftm->tm.tm_yday - (365 + __isleap (year)),
ftm->tm.tm_wday);
if (0 <= d)
{ /* This ISO week belongs to the next year. */
year++;
days = d;
}
}
switch(c)
{ case 'g':
OUT2DIGITS(fd, (year % 100 + 100) % 100);
break;
case 'G':
OUTNUMBER(fd, "%d", year);
break;
case 'V':
OUT2DIGITS(fd, days/7+1);
break;
}
break;
}
case 'h': /* Equivalent to %b. (SU) */
c = 'b';
goto case_b;
case 'H': /* 0..23 hours */
OUT2DIGITS(fd, ftm->tm.tm_hour);
break;
case 'I': /* 01..12 hours */
OUT2DIGITS(fd, (ftm->tm.tm_hour)%12);
break;
case 'j': /* yday (001..366) */
cal_ftm(ftm, HAS_WYDAY);
OUT3DIGITS(fd, ftm->tm.tm_yday+1);
break;
case 'k': /* 0..23 hours (leading space) */
OUT2DIGITS_SPC(fd, ftm->tm.tm_hour);
break;
case 'l': /* 1..12 hours (leading space) */
OUT2DIGITS_SPC(fd, (ftm->tm.tm_hour)%12);
break;
case 'm': /* 01..12 month */
OUT2DIGITS(fd, ftm->tm.tm_mon+1);
break;
case 'M': /* 00..59 minute */
OUT2DIGITS(fd, ftm->tm.tm_min);
break;
case 'n': /* newline */
OUTCHR(fd, '\n');
break;
case 'O':
case ':':
if ( format[0] == 'z' )
{ altO = TRUE;
goto fmt_next;
}
return fmt_not_implemented(c);
case 'r': /* The time in a.m./p.m. notation */
SUBFORMAT(L"%I:%M:%S %p"); /* TBD: :-separator locale handling */
break;
case 'R':
SUBFORMAT(L"%H:%M");
break;
case 'f': /* Microseconds */
{ int digits = (arg == NOARG ? 6 : arg);
if ( digits > 0 )
{ double ip;
char fmt[64];
cal_ftm(ftm, HAS_STAMP);
Ssprintf(fmt, "%%0%dlld", digits);
OUTNUMBER(fd, fmt, (long)(modf(ftm->stamp, &ip) *
pow(10, digits)));
}
break;
}
case 's': /* Seconds since 1970 */
cal_ftm(ftm, HAS_STAMP);
OUTNUMBER(fd, "%.0f", ftm->stamp);
break;
case 'S': /* Seconds */
OUT2DIGITS(fd, ftm->tm.tm_sec);
break;
case 't': /* tab */
OUTCHR(fd, '\t');
break;
case 'T':
SUBFORMAT(L"%H:%M:%S");
break;
case 'u': /* 1..7 weekday, mon=1 */
{ int wday;
cal_ftm(ftm, HAS_WYDAY);
wday = (ftm->tm.tm_wday - 1 + 7) % 7 + 1;
OUT1DIGIT(fd, wday);
break;
}
case 'U': /* 00..53 weeknumber */
{ int wk;
cal_ftm(ftm, HAS_WYDAY);
wk = (ftm->tm.tm_yday - (ftm->tm.tm_yday - ftm->tm.tm_wday + 7) % 7 + 7) / 7;
OUT2DIGITS(fd, wk);
break;
}
case 'w': /* 0..6 weekday */
cal_ftm(ftm, HAS_WYDAY);
OUT1DIGIT(fd, ftm->tm.tm_wday);
break;
case 'W': /* 00..53 monday-based week number */
{ int wk;
cal_ftm(ftm, HAS_WYDAY);
wk = (ftm->tm.tm_yday - (ftm->tm.tm_yday - ftm->tm.tm_wday + 8) % 7 + 7) / 7;
OUT2DIGITS(fd, wk);
break;
}
case 'y': /* 00..99 (year) */
OUT2DIGITS(fd, (ftm->tm.tm_year+1900) % 100);
break;
case 'Y': /* Year (decimal) */
OUTNUMBER(fd, "%d", ftm->tm.tm_year+1900);
break;
case 'z': /* Time-zone as offset */
{ int min = -ftm->utcoff/60;
if ( min >= 0 )
{ OUTCHR(fd, '+');
} else
{ min = -min;
OUTCHR(fd, '-');
}
OUT2DIGITS(fd, min/60);
if ( altO )
OUTCHR(fd, ':');
OUT2DIGITS(fd, min%60);
break;
}
case 'Z': /* Time-zone as name */
if ( ftm->tzname )
{ OUTATOM(ftm->tzname);
} else
{ OUTSTRA(tz_name(ftm->tm.tm_isdst));
}
break;
case '+':
{ char buf[26];
cal_ftm(ftm, HAS_WYDAY);
asctime_r(&ftm->tm, buf);
buf[24] = EOS;
OUTSTRA(buf);
}
break;
case '%':
OUTCHR(fd, '%');
break;
default:
if ( isdigit(c) )
{ if ( arg == NOARG )
arg = c - '0';
else
arg = arg*10+(c-'0');
goto fmt_next;
} else
{ return fmt_not_implemented(c);
}
}
break;
default:
OUTCHR(fd, c);
}
}
return TRUE;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
format_time(+Spec, +Format, +Stamp)
Issues:
* Localtime/DST
* Year is an int (not so bad)
* Portability
* Sub-second times
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static foreign_t
pl_format_time(term_t out, term_t format, term_t time, int posix)
{ struct taia taia;
struct caltime ct;
struct ftm tb;
int weekday, yearday;
wchar_t *fmt;
time_t unixt;
int64_t ut64;
size_t fmtlen;
redir_context ctx;
if ( !PL_get_wchars(format, &fmtlen, &fmt,
CVT_ATOM|CVT_STRING|CVT_LIST|CVT_EXCEPTION) )
fail;
memset(&tb, 0, sizeof(tb));
if ( get_taia(time, &taia, &tb.stamp) )
{ double ip;
ut64 = taia.sec.x - TAI_UTC_OFFSET;
unixt = (time_t) ut64;
if ( (int64_t)unixt == ut64 )
{ tb.utcoff = tz_offset();
localtime_r(&unixt, &tb.tm);
tb.sec = (double)tb.tm.tm_sec + modf(tb.stamp, &ip);
if ( tb.tm.tm_isdst > 0 )
{ tb.utcoff -= 3600;
tb.isdst = TRUE;
}
tb.tzname = tz_name_as_atom(tb.tm.tm_isdst);
tb.flags = HAS_STAMP|HAS_WYDAY;
} else
{ caltime_utc(&ct, &taia.sec, &weekday, &yearday);
tb.tm.tm_sec = ct.second;
tb.tm.tm_min = ct.minute;
tb.tm.tm_hour = ct.hour;
tb.tm.tm_mday = ct.date.day;
tb.tm.tm_mon = ct.date.month - 1;
tb.tm.tm_year = ct.date.year - 1900;
tb.tm.tm_wday = weekday;
tb.tm.tm_yday = yearday;
tb.tzname = ATOM_utc;
tb.utcoff = 0;
}
} else if ( !get_ftm(time, &tb) )
{ return PL_error(NULL, 0, NULL, ERR_TYPE, ATOM_time, time);
}
if ( !setupOutputRedirect(out, &ctx, FALSE) )
fail;
if ( format_time(ctx.stream, fmt, &tb, posix) )
return closeOutputRedirect(&ctx); /* takes care of I/O errors */
discardOutputRedirect(&ctx);
fail;
}
static
PRED_IMPL("format_time", 3, format_time3, 0)
{ return pl_format_time(A1, A2, A3, FALSE);
}
static
PRED_IMPL("format_time", 4, format_time4, 0)
{ PRED_LD
int posix = FALSE;
atom_t locale;
if ( !PL_get_atom_ex(A4, &locale) )
return FALSE;
if ( locale == ATOM_posix )
posix = TRUE;
else
return PL_error(NULL, 0, NULL, ERR_DOMAIN, ATOM_locale, A4);
return pl_format_time(A1, A2, A3, posix);
}
/*******************************
* PUBLISH PREDICATES *
*******************************/
BeginPredDefs(tai)
PRED_DEF("stamp_date_time", 3, stamp_date_time, 0)
PRED_DEF("date_time_stamp", 2, date_time_stamp, 0)
PRED_DEF("format_time", 3, format_time3, 0)
PRED_DEF("format_time", 4, format_time4, 0)
EndPredDefs