add tai package (and SWI interface).

This commit is contained in:
Vitor Santos Costa 2010-07-19 14:52:26 +01:00
parent 6ebc31f2d8
commit baab2cdbfe
65 changed files with 3105 additions and 0 deletions

View File

@ -17,6 +17,10 @@
'$set_source_module'/2,
'$declare_module'/5,
'$set_predicate_attribute'/3,
stamp_date_time/3,
date_time_stamp/2,
format_time/3,
format_time/4,
time_file/2,
flag/3,
current_flag/1
@ -92,6 +96,8 @@
:- load_foreign_files([libplstream], [], initIO).
:- load_foreign_files(['pl-tai'], [], install).
% Time is given as a float in SWI-Prolog.
swi_get_time(FSecs) :- datime(Datime), mktime(Datime, Secs), FSecs is Secs*1.0.
@ -101,6 +107,10 @@ goal_expansion(atom_concat(A,B,C),atomic_concat(A,B,C)).
goal_expansion(arg(A,B,C),genarg(A,B,C)).
goal_expansion(time_file(A,B),system:swi_time_file(A,B)).
goal_expansion(stamp_date_time(A,B,C),system:swi_stamp_date_time(A,B,C)).
goal_expansion(date_time_stamp(A,B),system:swi_date_time_stamp(A,B)).
goal_expansion(format_time(A,B,C),system:swi_format_time(A,B,C)).
goal_expansion(format_time(A,B,C,D),system:swi_format_time(A,B,C,D)).
goal_expansion(get_time(A),system:swi_get_time(A)).
goal_expansion(time_file(A,B),system:swi_time_file(A,B)).
goal_expansion(expand_file_name(A,B),system:swi_expand_file_name(A,B)).

58
packages/tai/Makefile.in Normal file
View File

@ -0,0 +1,58 @@
#
# default base directory for YAP installation
# (EROOT for architecture-dependent files)
#
prefix = @prefix@
exec_prefix = @exec_prefix@
ROOTDIR = $(prefix)
EROOTDIR = @exec_prefix@
abs_top_builddir = @abs_top_builddir@
#
# where the binary should be
#
BINDIR = $(EROOTDIR)/bin
#
# where YAP should look for libraries
#
LIBDIR=@libdir@
YAPLIBDIR=@libdir@/Yap
#
#
CC=@CC@
CFLAGS= @SHLIB_CFLAGS@ -D_YAP_NOT_INSTALLED_=1 $(YAP_EXTRAS) $(DEFS) -I$(srcdir) -I../.. -I$(srcdir)/../../include -I$(srcdir)/../PLStream
#
#
# You shouldn't need to change what follows.
#
INSTALL=@INSTALL@
INSTALL_DATA=@INSTALL_DATA@
INSTALL_PROGRAM=@INSTALL_PROGRAM@
SHELL=/bin/sh
RANLIB=@RANLIB@
srcdir=@srcdir@
SO=@SO@
#4.1VPATH=@srcdir@:@srcdir@/OPTYap
CWD=$(PWD)
#
OBJS=pl-tai.o
SOBJS=pl-tai.@SO@
#in some systems we just create a single object, in others we need to
# create a libray
all: $(SOBJS)
pl-tai.o: $(srcdir)/pl-tai.c
(cd libtai ; make)
$(CC) -c $(CFLAGS) $(srcdir)/pl-tai.c -o pl-tai.o
@DO_SECOND_LD@pl-tai.@SO@: pl-tai.o
@DO_SECOND_LD@ @SHLIB_LD@ -o pl-tai.@SO@ pl-tai.o libtai/libtai.a @EXTRA_LIBS_FOR_DLLS@
install: all
$(INSTALL_PROGRAM) $(SOBJS) $(DESTDIR)$(YAPLIBDIR)
clean:
rm -f *.o *~ $(OBJS) $(SOBJS) *.BAK

28
packages/tai/libtai/BLURB Normal file
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.

72
packages/tai/libtai/FILES Normal file
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,92 @@
################################################################
# 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)
CFLAGS= @SHLIB_CFLAGS@ $(YAP_EXTRAS) $(DEFS) -D_YAP_NOT_INSTALLED_=1 -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

6
packages/tai/libtai/TODO Normal 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,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,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);
}

83
packages/tai/libtai/tai.3 Normal file
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/tai/libtai/tai.h Normal 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
#ifdef __WINDOWS__
#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;
}

140
packages/tai/libtai/taia.3 Normal file
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);
}

941
packages/tai/pl-tai.c Normal file
View File

@ -0,0 +1,941 @@
/* $Id$
Part of SWI-Prolog
Author: Jan Wielemaker
E-mail: wielemak@science.uva.nl
WWW: http://www.swi-prolog.org
Copyright (C): 1985-2007, University of 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 defined(__WINDOWS__) || defined (__CYGWIN__)
#define timezone _timezone
#define HAVE_VAR_TIMEZONE
#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(const char *key)
{ GET_LD
term_t t = PL_new_term_ref();
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;
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("%E");
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':
return fmt_not_implemented("%O");
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);
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;
}
}
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)
PRED_DEF("swi_stamp_date_time", 3, stamp_date_time, 0)
PRED_DEF("swi_date_time_stamp", 2, date_time_stamp, 0)
PRED_DEF("swi_format_time", 3, format_time3, 0)
PRED_DEF("swi_format_time", 4, format_time4, 0)
EndPredDefs
#if _YAP_NOT_INSTALLED_
install_t
install(void)
{
PL_register_extensions(PL_predicates_from_tai);
}
#endif