From baab2cdbfec92db7ebf2272372b127b6e88a5f92 Mon Sep 17 00:00:00 2001 From: Vitor Santos Costa Date: Mon, 19 Jul 2010 14:52:26 +0100 Subject: [PATCH] add tai package (and SWI interface). --- library/dialect/swi.yap | 10 + packages/tai/Makefile.in | 58 ++ packages/tai/libtai/BLURB | 28 + packages/tai/libtai/CHANGES | 3 + packages/tai/libtai/FILES | 72 +++ packages/tai/libtai/INSTALL | 65 ++ packages/tai/libtai/Makefile.in | 92 +++ packages/tai/libtai/Makefile.mak | 37 ++ packages/tai/libtai/README | 10 + packages/tai/libtai/THANKS | 0 packages/tai/libtai/TODO | 6 + packages/tai/libtai/VERSION | 1 + packages/tai/libtai/caldate.3 | 54 ++ packages/tai/libtai/caldate.h | 19 + packages/tai/libtai/caldate_fmjd.c | 45 ++ packages/tai/libtai/caldate_fmt.c | 24 + packages/tai/libtai/caldate_mjd.3 | 111 ++++ packages/tai/libtai/caldate_mjd.c | 44 ++ packages/tai/libtai/caldate_norm.c | 7 + packages/tai/libtai/caldate_scan.c | 24 + packages/tai/libtai/caldate_ster.c | 24 + packages/tai/libtai/caltime.3 | 82 +++ packages/tai/libtai/caltime.h | 20 + packages/tai/libtai/caltime_fmt.c | 44 ++ packages/tai/libtai/caltime_scan.c | 39 ++ packages/tai/libtai/caltime_tai.3 | 61 ++ packages/tai/libtai/caltime_tai.c | 23 + packages/tai/libtai/caltime_utc.c | 31 + packages/tai/libtai/check.c | 52 ++ packages/tai/libtai/easter.c | 29 + packages/tai/libtai/leapsecs.3 | 106 ++++ packages/tai/libtai/leapsecs.c | 30 + packages/tai/libtai/leapsecs.dat | Bin 0 -> 176 bytes packages/tai/libtai/leapsecs.h | 16 + packages/tai/libtai/leapsecs_add.c | 21 + packages/tai/libtai/leapsecs_init.c | 12 + packages/tai/libtai/leapsecs_read.c | 62 ++ packages/tai/libtai/leapsecs_sub.c | 25 + packages/tai/libtai/nowutc.c | 39 ++ packages/tai/libtai/tai.3 | 83 +++ packages/tai/libtai/tai.h | 36 ++ packages/tai/libtai/tai_add.c | 6 + packages/tai/libtai/tai_now.3 | 34 + packages/tai/libtai/tai_now.c | 7 + packages/tai/libtai/tai_pack.3 | 36 ++ packages/tai/libtai/tai_pack.c | 17 + packages/tai/libtai/tai_sub.c | 6 + packages/tai/libtai/tai_unpack.c | 16 + packages/tai/libtai/taia.3 | 140 +++++ packages/tai/libtai/taia.h | 31 + packages/tai/libtai/taia_add.c | 18 + packages/tai/libtai/taia_approx.c | 5 + packages/tai/libtai/taia_fmtfrac.c | 31 + packages/tai/libtai/taia_frac.c | 6 + packages/tai/libtai/taia_half.c | 12 + packages/tai/libtai/taia_less.c | 12 + packages/tai/libtai/taia_now.3 | 33 + packages/tai/libtai/taia_now.c | 40 ++ packages/tai/libtai/taia_pack.3 | 36 ++ packages/tai/libtai/taia_pack.c | 20 + packages/tai/libtai/taia_sub.c | 21 + packages/tai/libtai/taia_tai.c | 6 + packages/tai/libtai/taia_unpack.c | 20 + packages/tai/libtai/yearcal.c | 66 ++ packages/tai/pl-tai.c | 941 ++++++++++++++++++++++++++++ 65 files changed, 3105 insertions(+) create mode 100644 packages/tai/Makefile.in create mode 100644 packages/tai/libtai/BLURB create mode 100644 packages/tai/libtai/CHANGES create mode 100644 packages/tai/libtai/FILES create mode 100644 packages/tai/libtai/INSTALL create mode 100644 packages/tai/libtai/Makefile.in create mode 100644 packages/tai/libtai/Makefile.mak create mode 100644 packages/tai/libtai/README create mode 100644 packages/tai/libtai/THANKS create mode 100644 packages/tai/libtai/TODO create mode 100644 packages/tai/libtai/VERSION create mode 100644 packages/tai/libtai/caldate.3 create mode 100644 packages/tai/libtai/caldate.h create mode 100644 packages/tai/libtai/caldate_fmjd.c create mode 100644 packages/tai/libtai/caldate_fmt.c create mode 100644 packages/tai/libtai/caldate_mjd.3 create mode 100644 packages/tai/libtai/caldate_mjd.c create mode 100644 packages/tai/libtai/caldate_norm.c create mode 100644 packages/tai/libtai/caldate_scan.c create mode 100644 packages/tai/libtai/caldate_ster.c create mode 100644 packages/tai/libtai/caltime.3 create mode 100644 packages/tai/libtai/caltime.h create mode 100644 packages/tai/libtai/caltime_fmt.c create mode 100644 packages/tai/libtai/caltime_scan.c create mode 100644 packages/tai/libtai/caltime_tai.3 create mode 100644 packages/tai/libtai/caltime_tai.c create mode 100644 packages/tai/libtai/caltime_utc.c create mode 100644 packages/tai/libtai/check.c create mode 100644 packages/tai/libtai/easter.c create mode 100644 packages/tai/libtai/leapsecs.3 create mode 100644 packages/tai/libtai/leapsecs.c create mode 100644 packages/tai/libtai/leapsecs.dat create mode 100644 packages/tai/libtai/leapsecs.h create mode 100644 packages/tai/libtai/leapsecs_add.c create mode 100644 packages/tai/libtai/leapsecs_init.c create mode 100644 packages/tai/libtai/leapsecs_read.c create mode 100644 packages/tai/libtai/leapsecs_sub.c create mode 100644 packages/tai/libtai/nowutc.c create mode 100644 packages/tai/libtai/tai.3 create mode 100644 packages/tai/libtai/tai.h create mode 100644 packages/tai/libtai/tai_add.c create mode 100644 packages/tai/libtai/tai_now.3 create mode 100644 packages/tai/libtai/tai_now.c create mode 100644 packages/tai/libtai/tai_pack.3 create mode 100644 packages/tai/libtai/tai_pack.c create mode 100644 packages/tai/libtai/tai_sub.c create mode 100644 packages/tai/libtai/tai_unpack.c create mode 100644 packages/tai/libtai/taia.3 create mode 100644 packages/tai/libtai/taia.h create mode 100644 packages/tai/libtai/taia_add.c create mode 100644 packages/tai/libtai/taia_approx.c create mode 100644 packages/tai/libtai/taia_fmtfrac.c create mode 100644 packages/tai/libtai/taia_frac.c create mode 100644 packages/tai/libtai/taia_half.c create mode 100644 packages/tai/libtai/taia_less.c create mode 100644 packages/tai/libtai/taia_now.3 create mode 100644 packages/tai/libtai/taia_now.c create mode 100644 packages/tai/libtai/taia_pack.3 create mode 100644 packages/tai/libtai/taia_pack.c create mode 100644 packages/tai/libtai/taia_sub.c create mode 100644 packages/tai/libtai/taia_tai.c create mode 100644 packages/tai/libtai/taia_unpack.c create mode 100644 packages/tai/libtai/yearcal.c create mode 100644 packages/tai/pl-tai.c diff --git a/library/dialect/swi.yap b/library/dialect/swi.yap index df0bf2ad4..2dab0bfc7 100755 --- a/library/dialect/swi.yap +++ b/library/dialect/swi.yap @@ -17,6 +17,10 @@ '$set_source_module'/2, '$declare_module'/5, '$set_predicate_attribute'/3, + stamp_date_time/3, + date_time_stamp/2, + format_time/3, + format_time/4, time_file/2, flag/3, current_flag/1 @@ -92,6 +96,8 @@ :- load_foreign_files([libplstream], [], initIO). +:- load_foreign_files(['pl-tai'], [], install). + % Time is given as a float in SWI-Prolog. swi_get_time(FSecs) :- datime(Datime), mktime(Datime, Secs), FSecs is Secs*1.0. @@ -101,6 +107,10 @@ goal_expansion(atom_concat(A,B,C),atomic_concat(A,B,C)). goal_expansion(arg(A,B,C),genarg(A,B,C)). goal_expansion(time_file(A,B),system:swi_time_file(A,B)). +goal_expansion(stamp_date_time(A,B,C),system:swi_stamp_date_time(A,B,C)). +goal_expansion(date_time_stamp(A,B),system:swi_date_time_stamp(A,B)). +goal_expansion(format_time(A,B,C),system:swi_format_time(A,B,C)). +goal_expansion(format_time(A,B,C,D),system:swi_format_time(A,B,C,D)). goal_expansion(get_time(A),system:swi_get_time(A)). goal_expansion(time_file(A,B),system:swi_time_file(A,B)). goal_expansion(expand_file_name(A,B),system:swi_expand_file_name(A,B)). diff --git a/packages/tai/Makefile.in b/packages/tai/Makefile.in new file mode 100644 index 000000000..8a2f9110a --- /dev/null +++ b/packages/tai/Makefile.in @@ -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 + diff --git a/packages/tai/libtai/BLURB b/packages/tai/libtai/BLURB new file mode 100644 index 000000000..fd829fc8b --- /dev/null +++ b/packages/tai/libtai/BLURB @@ -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. diff --git a/packages/tai/libtai/CHANGES b/packages/tai/libtai/CHANGES new file mode 100644 index 000000000..62948e1e5 --- /dev/null +++ b/packages/tai/libtai/CHANGES @@ -0,0 +1,3 @@ +19981013 libtai 0.60, alpha. +19970908 libtai 0.56, alpha. +19970730 libtai 0.50, alpha. diff --git a/packages/tai/libtai/FILES b/packages/tai/libtai/FILES new file mode 100644 index 000000000..07c19aac3 --- /dev/null +++ b/packages/tai/libtai/FILES @@ -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 diff --git a/packages/tai/libtai/INSTALL b/packages/tai/libtai/INSTALL new file mode 100644 index 000000000..80883391f --- /dev/null +++ b/packages/tai/libtai/INSTALL @@ -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. diff --git a/packages/tai/libtai/Makefile.in b/packages/tai/libtai/Makefile.in new file mode 100644 index 000000000..deeb9f7c7 --- /dev/null +++ b/packages/tai/libtai/Makefile.in @@ -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 + diff --git a/packages/tai/libtai/Makefile.mak b/packages/tai/libtai/Makefile.mak new file mode 100644 index 000000000..6046d10f5 --- /dev/null +++ b/packages/tai/libtai/Makefile.mak @@ -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 + diff --git a/packages/tai/libtai/README b/packages/tai/libtai/README new file mode 100644 index 000000000..fbe5ec6e4 --- /dev/null +++ b/packages/tai/libtai/README @@ -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. diff --git a/packages/tai/libtai/THANKS b/packages/tai/libtai/THANKS new file mode 100644 index 000000000..e69de29bb diff --git a/packages/tai/libtai/TODO b/packages/tai/libtai/TODO new file mode 100644 index 000000000..6df9aceaa --- /dev/null +++ b/packages/tai/libtai/TODO @@ -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 diff --git a/packages/tai/libtai/VERSION b/packages/tai/libtai/VERSION new file mode 100644 index 000000000..fbda0f8b9 --- /dev/null +++ b/packages/tai/libtai/VERSION @@ -0,0 +1 @@ +libtai 0.60 diff --git a/packages/tai/libtai/caldate.3 b/packages/tai/libtai/caldate.3 new file mode 100644 index 000000000..43773763a --- /dev/null +++ b/packages/tai/libtai/caldate.3 @@ -0,0 +1,54 @@ +.TH caldate 3 +.SH NAME +caldate \- calendar dates +.SH SYNTAX +.B #include + +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) diff --git a/packages/tai/libtai/caldate.h b/packages/tai/libtai/caldate.h new file mode 100644 index 000000000..440c1fb9c --- /dev/null +++ b/packages/tai/libtai/caldate.h @@ -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 diff --git a/packages/tai/libtai/caldate_fmjd.c b/packages/tai/libtai/caldate_fmjd.c new file mode 100644 index 000000000..6b5a79ade --- /dev/null +++ b/packages/tai/libtai/caldate_fmjd.c @@ -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; +} diff --git a/packages/tai/libtai/caldate_fmt.c b/packages/tai/libtai/caldate_fmt.c new file mode 100644 index 000000000..1ba7bcdee --- /dev/null +++ b/packages/tai/libtai/caldate_fmt.c @@ -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; +} diff --git a/packages/tai/libtai/caldate_mjd.3 b/packages/tai/libtai/caldate_mjd.3 new file mode 100644 index 000000000..4e6a4248e --- /dev/null +++ b/packages/tai/libtai/caldate_mjd.3 @@ -0,0 +1,111 @@ +.TH caldate_mjd 3 +.SH NAME +caldate_mjd \- manipulate calendar dates +.SH SYNTAX +.B #include + +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) diff --git a/packages/tai/libtai/caldate_mjd.c b/packages/tai/libtai/caldate_mjd.c new file mode 100644 index 000000000..7e22b8e9b --- /dev/null +++ b/packages/tai/libtai/caldate_mjd.c @@ -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; +} diff --git a/packages/tai/libtai/caldate_norm.c b/packages/tai/libtai/caldate_norm.c new file mode 100644 index 000000000..e95e1258c --- /dev/null +++ b/packages/tai/libtai/caldate_norm.c @@ -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); +} diff --git a/packages/tai/libtai/caldate_scan.c b/packages/tai/libtai/caldate_scan.c new file mode 100644 index 000000000..adbe54d79 --- /dev/null +++ b/packages/tai/libtai/caldate_scan.c @@ -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); +} diff --git a/packages/tai/libtai/caldate_ster.c b/packages/tai/libtai/caldate_ster.c new file mode 100644 index 000000000..e25bab72a --- /dev/null +++ b/packages/tai/libtai/caldate_ster.c @@ -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; } +} diff --git a/packages/tai/libtai/caltime.3 b/packages/tai/libtai/caltime.3 new file mode 100644 index 000000000..e100be088 --- /dev/null +++ b/packages/tai/libtai/caltime.3 @@ -0,0 +1,82 @@ +.TH caltime 3 +.SH NAME +caltime \- calendar dates and times +.SH SYNTAX +.B #include + +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) diff --git a/packages/tai/libtai/caltime.h b/packages/tai/libtai/caltime.h new file mode 100644 index 000000000..593bbf503 --- /dev/null +++ b/packages/tai/libtai/caltime.h @@ -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 diff --git a/packages/tai/libtai/caltime_fmt.c b/packages/tai/libtai/caltime_fmt.c new file mode 100644 index 000000000..c3f4f9375 --- /dev/null +++ b/packages/tai/libtai/caltime_fmt.c @@ -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; +} diff --git a/packages/tai/libtai/caltime_scan.c b/packages/tai/libtai/caltime_scan.c new file mode 100644 index 000000000..b90719ba4 --- /dev/null +++ b/packages/tai/libtai/caltime_scan.c @@ -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); +} diff --git a/packages/tai/libtai/caltime_tai.3 b/packages/tai/libtai/caltime_tai.3 new file mode 100644 index 000000000..49c6ab163 --- /dev/null +++ b/packages/tai/libtai/caltime_tai.3 @@ -0,0 +1,61 @@ +.TH caltime 3 +.SH NAME +caltime_tai \- convert calendar dates and times +.SH SYNTAX +.B #include +.br +.B #include + +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) diff --git a/packages/tai/libtai/caltime_tai.c b/packages/tai/libtai/caltime_tai.c new file mode 100644 index 000000000..e8bb1247b --- /dev/null +++ b/packages/tai/libtai/caltime_tai.c @@ -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); +} diff --git a/packages/tai/libtai/caltime_utc.c b/packages/tai/libtai/caltime_utc.c new file mode 100644 index 000000000..706a78c62 --- /dev/null +++ b/packages/tai/libtai/caltime_utc.c @@ -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; +} diff --git a/packages/tai/libtai/check.c b/packages/tai/libtai/check.c new file mode 100644 index 000000000..c0b1a7053 --- /dev/null +++ b/packages/tai/libtai/check.c @@ -0,0 +1,52 @@ +#include +#include +#include +#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); +} diff --git a/packages/tai/libtai/easter.c b/packages/tai/libtai/easter.c new file mode 100644 index 000000000..e7f30aa6a --- /dev/null +++ b/packages/tai/libtai/easter.c @@ -0,0 +1,29 @@ +#include +#include +#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); +} diff --git a/packages/tai/libtai/leapsecs.3 b/packages/tai/libtai/leapsecs.3 new file mode 100644 index 000000000..cdd6fa4b9 --- /dev/null +++ b/packages/tai/libtai/leapsecs.3 @@ -0,0 +1,106 @@ +.TH leapsecs 3 +.SH NAME +leapsecs \- handle UTC leap seconds +.SH SYNTAX +.B #include + +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) diff --git a/packages/tai/libtai/leapsecs.c b/packages/tai/libtai/leapsecs.c new file mode 100644 index 000000000..06aaebbb5 --- /dev/null +++ b/packages/tai/libtai/leapsecs.c @@ -0,0 +1,30 @@ +#include +#include +#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); +} diff --git a/packages/tai/libtai/leapsecs.dat b/packages/tai/libtai/leapsecs.dat new file mode 100644 index 0000000000000000000000000000000000000000..206fcbc1b45f6acc084340a07daa69105a48ddd9 GIT binary patch literal 176 zcmZ=@U|?X`6v5>Hq*<4|;Rey{ZSp-Jnln8ZEY9uG)(7HqX><33Xnr1h0T3x; + + for (i = 0;i < leapsecs_num;++i) { + if (u < leapsecs[i].x) break; + if (!hit || (u > leapsecs[i].x)) ++u; + } + + t->x = u; +} diff --git a/packages/tai/libtai/leapsecs_init.c b/packages/tai/libtai/leapsecs_init.c new file mode 100644 index 000000000..cce59bc06 --- /dev/null +++ b/packages/tai/libtai/leapsecs_init.c @@ -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; +} diff --git a/packages/tai/libtai/leapsecs_read.c b/packages/tai/libtai/leapsecs_read.c new file mode 100644 index 000000000..fc86c8400 --- /dev/null +++ b/packages/tai/libtai/leapsecs_read.c @@ -0,0 +1,62 @@ +#include +#include +#include +#include +#include +#ifdef __WINDOWS__ +#include +#else +#include +#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; +} diff --git a/packages/tai/libtai/leapsecs_sub.c b/packages/tai/libtai/leapsecs_sub.c new file mode 100644 index 000000000..a52acd5c0 --- /dev/null +++ b/packages/tai/libtai/leapsecs_sub.c @@ -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; +} diff --git a/packages/tai/libtai/nowutc.c b/packages/tai/libtai/nowutc.c new file mode 100644 index 000000000..7c6eed119 --- /dev/null +++ b/packages/tai/libtai/nowutc.c @@ -0,0 +1,39 @@ +#include +#include +#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); +} diff --git a/packages/tai/libtai/tai.3 b/packages/tai/libtai/tai.3 new file mode 100644 index 000000000..f479f8742 --- /dev/null +++ b/packages/tai/libtai/tai.3 @@ -0,0 +1,83 @@ +.TH tai 3 +.SH NAME +tai \- manipulate times with 1-second precision +.SH SYNTAX +.B #include + +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) diff --git a/packages/tai/libtai/tai.h b/packages/tai/libtai/tai.h new file mode 100644 index 000000000..b3fd2da32 --- /dev/null +++ b/packages/tai/libtai/tai.h @@ -0,0 +1,36 @@ +#ifndef TAI_H +#define TAI_H + +#ifdef __WINDOWS__ +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +#include /* 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 diff --git a/packages/tai/libtai/tai_add.c b/packages/tai/libtai/tai_add.c new file mode 100644 index 000000000..3e56468f0 --- /dev/null +++ b/packages/tai/libtai/tai_add.c @@ -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; +} diff --git a/packages/tai/libtai/tai_now.3 b/packages/tai/libtai/tai_now.3 new file mode 100644 index 000000000..5beaf9979 --- /dev/null +++ b/packages/tai/libtai/tai_now.3 @@ -0,0 +1,34 @@ +.TH tai_now 3 +.SH NAME +tai_now \- get current time, with 1-second precision +.SH SYNTAX +.B #include + +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) diff --git a/packages/tai/libtai/tai_now.c b/packages/tai/libtai/tai_now.c new file mode 100644 index 000000000..b8f52e96b --- /dev/null +++ b/packages/tai/libtai/tai_now.c @@ -0,0 +1,7 @@ +#include +#include "tai.h" + +void tai_now(struct tai *t) +{ + t->x = ULL(4611686018427387914) + (uint64_t) time((time_t *) 0); +} diff --git a/packages/tai/libtai/tai_pack.3 b/packages/tai/libtai/tai_pack.3 new file mode 100644 index 000000000..81d2605cc --- /dev/null +++ b/packages/tai/libtai/tai_pack.3 @@ -0,0 +1,36 @@ +.TH tai_pack 3 +.SH NAME +tai_pack \- convert TAI64 labels to external format +.SH SYNTAX +.B #include + +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) diff --git a/packages/tai/libtai/tai_pack.c b/packages/tai/libtai/tai_pack.c new file mode 100644 index 000000000..4701b8526 --- /dev/null +++ b/packages/tai/libtai/tai_pack.c @@ -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; +} diff --git a/packages/tai/libtai/tai_sub.c b/packages/tai/libtai/tai_sub.c new file mode 100644 index 000000000..d8f92ed96 --- /dev/null +++ b/packages/tai/libtai/tai_sub.c @@ -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; +} diff --git a/packages/tai/libtai/tai_unpack.c b/packages/tai/libtai/tai_unpack.c new file mode 100644 index 000000000..645650ed6 --- /dev/null +++ b/packages/tai/libtai/tai_unpack.c @@ -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; +} diff --git a/packages/tai/libtai/taia.3 b/packages/tai/libtai/taia.3 new file mode 100644 index 000000000..ff5d2b229 --- /dev/null +++ b/packages/tai/libtai/taia.3 @@ -0,0 +1,140 @@ +.TH taia 3 +.SH NAME +taia \- manipulate times with 1-attosecond precision +.SH SYNTAX +.B #include + +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) diff --git a/packages/tai/libtai/taia.h b/packages/tai/libtai/taia.h new file mode 100644 index 000000000..3b5234dcc --- /dev/null +++ b/packages/tai/libtai/taia.h @@ -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 diff --git a/packages/tai/libtai/taia_add.c b/packages/tai/libtai/taia_add.c new file mode 100644 index 000000000..c07c037f6 --- /dev/null +++ b/packages/tai/libtai/taia_add.c @@ -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; + } +} diff --git a/packages/tai/libtai/taia_approx.c b/packages/tai/libtai/taia_approx.c new file mode 100644 index 000000000..9f9be63f7 --- /dev/null +++ b/packages/tai/libtai/taia_approx.c @@ -0,0 +1,5 @@ +#include "taia.h" + +double taia_approx(struct taia *t) +{ return tai_approx(&t->sec) + taia_frac(t); +} diff --git a/packages/tai/libtai/taia_fmtfrac.c b/packages/tai/libtai/taia_fmtfrac.c new file mode 100644 index 000000000..1e8db1502 --- /dev/null +++ b/packages/tai/libtai/taia_fmtfrac.c @@ -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; +} diff --git a/packages/tai/libtai/taia_frac.c b/packages/tai/libtai/taia_frac.c new file mode 100644 index 000000000..89a1aacb4 --- /dev/null +++ b/packages/tai/libtai/taia_frac.c @@ -0,0 +1,6 @@ +#include "taia.h" + +double taia_frac(struct taia *t) +{ + return (t->atto * 0.000000001 + t->nano) * 0.000000001; +} diff --git a/packages/tai/libtai/taia_half.c b/packages/tai/libtai/taia_half.c new file mode 100644 index 000000000..199e0ce09 --- /dev/null +++ b/packages/tai/libtai/taia_half.c @@ -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; +} diff --git a/packages/tai/libtai/taia_less.c b/packages/tai/libtai/taia_less.c new file mode 100644 index 000000000..816fb393b --- /dev/null +++ b/packages/tai/libtai/taia_less.c @@ -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; +} diff --git a/packages/tai/libtai/taia_now.3 b/packages/tai/libtai/taia_now.3 new file mode 100644 index 000000000..31b12f59d --- /dev/null +++ b/packages/tai/libtai/taia_now.3 @@ -0,0 +1,33 @@ +.TH taia_now 3 +.SH NAME +taia_now \- get current time, with 1-attosecond precision +.SH SYNTAX +.B #include + +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) diff --git a/packages/tai/libtai/taia_now.c b/packages/tai/libtai/taia_now.c new file mode 100644 index 000000000..b244f78d7 --- /dev/null +++ b/packages/tai/libtai/taia_now.c @@ -0,0 +1,40 @@ +#include +#ifdef __WINDOWS__ +#define WINDOWS_LEAN_AND_MEAN 1 +#include +#else +#include +#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 +} diff --git a/packages/tai/libtai/taia_pack.3 b/packages/tai/libtai/taia_pack.3 new file mode 100644 index 000000000..a70bd0a21 --- /dev/null +++ b/packages/tai/libtai/taia_pack.3 @@ -0,0 +1,36 @@ +.TH taia_pack 3 +.SH NAME +taia_pack \- convert TAI64NA labels to external format +.SH SYNTAX +.B #include + +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) diff --git a/packages/tai/libtai/taia_pack.c b/packages/tai/libtai/taia_pack.c new file mode 100644 index 000000000..bda23f128 --- /dev/null +++ b/packages/tai/libtai/taia_pack.c @@ -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; +} diff --git a/packages/tai/libtai/taia_sub.c b/packages/tai/libtai/taia_sub.c new file mode 100644 index 000000000..4149daa59 --- /dev/null +++ b/packages/tai/libtai/taia_sub.c @@ -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; + } +} diff --git a/packages/tai/libtai/taia_tai.c b/packages/tai/libtai/taia_tai.c new file mode 100644 index 000000000..4d45dea9c --- /dev/null +++ b/packages/tai/libtai/taia_tai.c @@ -0,0 +1,6 @@ +#include "taia.h" + +void taia_tai(struct taia *ta, struct tai *t) +{ + *t = ta->sec; +} diff --git a/packages/tai/libtai/taia_unpack.c b/packages/tai/libtai/taia_unpack.c new file mode 100644 index 000000000..89903ea39 --- /dev/null +++ b/packages/tai/libtai/taia_unpack.c @@ -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; +} diff --git a/packages/tai/libtai/yearcal.c b/packages/tai/libtai/yearcal.c new file mode 100644 index 000000000..de16bce7d --- /dev/null +++ b/packages/tai/libtai/yearcal.c @@ -0,0 +1,66 @@ +#include +#include +#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); +} diff --git a/packages/tai/pl-tai.c b/packages/tai/pl-tai.c new file mode 100644 index 000000000..ca1f441d1 --- /dev/null +++ b/packages/tai/pl-tai.c @@ -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 +#include "pl-incl.h" +#include "libtai/taia.h" +#include "libtai/caltime.h" +#include +#include + +#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 +