add tai package (and SWI interface).
This commit is contained in:
parent
6ebc31f2d8
commit
baab2cdbfe
|
@ -17,6 +17,10 @@
|
||||||
'$set_source_module'/2,
|
'$set_source_module'/2,
|
||||||
'$declare_module'/5,
|
'$declare_module'/5,
|
||||||
'$set_predicate_attribute'/3,
|
'$set_predicate_attribute'/3,
|
||||||
|
stamp_date_time/3,
|
||||||
|
date_time_stamp/2,
|
||||||
|
format_time/3,
|
||||||
|
format_time/4,
|
||||||
time_file/2,
|
time_file/2,
|
||||||
flag/3,
|
flag/3,
|
||||||
current_flag/1
|
current_flag/1
|
||||||
|
@ -92,6 +96,8 @@
|
||||||
|
|
||||||
:- load_foreign_files([libplstream], [], initIO).
|
:- load_foreign_files([libplstream], [], initIO).
|
||||||
|
|
||||||
|
:- load_foreign_files(['pl-tai'], [], install).
|
||||||
|
|
||||||
% Time is given as a float in SWI-Prolog.
|
% Time is given as a float in SWI-Prolog.
|
||||||
swi_get_time(FSecs) :- datime(Datime), mktime(Datime, Secs), FSecs is Secs*1.0.
|
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(arg(A,B,C),genarg(A,B,C)).
|
||||||
goal_expansion(time_file(A,B),system:swi_time_file(A,B)).
|
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(get_time(A),system:swi_get_time(A)).
|
||||||
goal_expansion(time_file(A,B),system:swi_time_file(A,B)).
|
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)).
|
goal_expansion(expand_file_name(A,B),system:swi_expand_file_name(A,B)).
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
|
@ -0,0 +1,3 @@
|
||||||
|
19981013 libtai 0.60, alpha.
|
||||||
|
19970908 libtai 0.56, alpha.
|
||||||
|
19970730 libtai 0.50, alpha.
|
|
@ -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
|
|
@ -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.
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
|
@ -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
|
|
@ -0,0 +1 @@
|
||||||
|
libtai 0.60
|
|
@ -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)
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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)
|
|
@ -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;
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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; }
|
||||||
|
}
|
|
@ -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)
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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)
|
|
@ -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);
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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)
|
|
@ -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.
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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)
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
|
@ -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)
|
|
@ -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);
|
||||||
|
}
|
|
@ -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)
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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)
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
#include "taia.h"
|
||||||
|
|
||||||
|
double taia_approx(struct taia *t)
|
||||||
|
{ return tai_approx(&t->sec) + taia_frac(t);
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
#include "taia.h"
|
||||||
|
|
||||||
|
double taia_frac(struct taia *t)
|
||||||
|
{
|
||||||
|
return (t->atto * 0.000000001 + t->nano) * 0.000000001;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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)
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
#include "taia.h"
|
||||||
|
|
||||||
|
void taia_tai(struct taia *ta, struct tai *t)
|
||||||
|
{
|
||||||
|
*t = ta->sec;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
Reference in New Issue