4cc6c32b0a1e3ba2bbd20d422a226f53f709d27a
[asdcplib.git] / src / KM_tai.cpp
1 /*
2
3 THIS IS A SUBSET OF THE FULL LIBTAI. CHANGES HAVE BEEN MADE TO SUIT
4 LIBKUMU STYLE AND TYPE CONVENTIONS. ALL BUGS BELONG TO JOHN HURST.
5 THE FOLLOWING IS FOR ATTRIBUTION, THANK YOU MR. BERNSTEIN FOR WRITING
6 AND DISTRIBUTING SUCH GREAT SOFTWARE:
7
8 libtai 0.60, alpha.
9 19981013
10 Copyright 1998
11 D. J. Bernstein, djb@pobox.com
12 http://pobox.com/~djb/libtai.html
13
14
15 libtai is a library for storing and manipulating dates and times.
16
17 libtai supports two time scales: (1) TAI64, covering a few hundred
18 billion years with 1-second precision; (2) TAI64NA, covering the same
19 period with 1-attosecond precision. Both scales are defined in terms of
20 TAI, the current international real time standard.
21
22 libtai provides an internal format for TAI64, struct tai, designed for
23 fast time manipulations. The tai_pack() and tai_unpack() routines
24 convert between struct tai and a portable 8-byte TAI64 storage format.
25 libtai provides similar internal and external formats for TAI64NA.
26
27 libtai provides struct caldate to store dates in year-month-day form. It
28 can convert struct caldate, under the Gregorian calendar, to a modified
29 Julian day number for easy date arithmetic.
30
31 libtai provides struct caltime to store calendar dates and times along
32 with UTC offsets. It can convert from struct tai to struct caltime in
33 UTC, accounting for leap seconds, for accurate date and time display. It
34 can also convert back from struct caltime to struct tai for user input.
35 Its overall UTC-to-TAI conversion speed is 100x better than the usual
36 UNIX mktime() implementation.
37
38 This version of libtai requires a UNIX system with gettimeofday(). It
39 will be easy to port to other operating systems with compilers
40 supporting 64-bit arithmetic.
41
42 The libtai source code is in the public domain.
43
44 */
45
46   /*! \file    KM_tai.cpp
47     \version $Id$
48     \brief   portable time functions
49   */
50
51 #include <KM_tai.h>
52 #include <sys/time.h>
53
54 //
55 void
56 caldate_frommjd(Kumu::TAI::caldate* cd, i32_t day)
57 {
58   assert(cd);
59   i32_t year, month, yday;
60
61   year = day / 146097L;
62   day %= 146097L;
63   day += 678881L;
64   while (day >= 146097L) { day -= 146097L; ++year; }
65
66   /* year * 146097 + day - 678881 is MJD; 0 <= day < 146097 */
67   /* 2000-03-01, MJD 51604, is year 5, day 0 */
68
69   year *= 4;
70   if (day == 146096L) { year += 3; day = 36524L; }
71   else { year += day / 36524L; day %= 36524L; }
72   year *= 25;
73   year += day / 1461;
74   day %= 1461;
75   year *= 4;
76
77   yday = (day < 306);
78   if (day == 1460) { year += 3; day = 365; }
79   else { year += day / 365; day %= 365; }
80   yday += day;
81
82   day *= 10;
83   month = (day + 5) / 306;
84   day = (day + 5) % 306;
85   day /= 10;
86   if (month >= 10) { yday -= 306; ++year; month -= 10; }
87   else { yday += 59; month += 2; }
88
89   cd->year = year;
90   cd->month = month + 1;
91   cd->day = day + 1;
92 }
93
94 //
95 static ui32_t times365[4] = { 0, 365, 730, 1095 } ;
96 static ui32_t times36524[4] = { 0, 36524UL, 73048UL, 109572UL } ;
97 static ui32_t montab[12] =
98 { 0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337 } ;
99 /* month length after february is (306 * m + 5) / 10 */
100
101 //
102 i32_t
103 caldate_mjd(const Kumu::TAI::caldate* cd)
104 {
105   assert(cd);
106   i32_t y, m, d;
107
108   d = cd->day - 678882L;
109   m = cd->month - 1;
110   y = cd->year;
111
112   d += 146097L * (y / 400);
113   y %= 400;
114
115   if (m >= 2) m -= 2; else { m += 10; --y; }
116
117   y += (m / 12);
118   m %= 12;
119   if (m < 0) { m += 12; --y; }
120
121   d += montab[m];
122
123   d += 146097L * (y / 400);
124   y %= 400;
125   if (y < 0) { y += 400; d -= 146097L; }
126
127   d += times365[y & 3];
128   y >>= 2;
129
130   d += 1461L * (y % 25);
131   y /= 25;
132
133   d += times36524[y & 3];
134
135   return d;
136 }
137
138
139 //
140 void
141 caltime_utc(Kumu::TAI::caltime* ct, const Kumu::TAI::tai* t)
142 {
143   assert(ct&&t);
144   Kumu::TAI::tai t2 = *t;
145   ui64_t u = t2.x + 58486;
146   i32_t s = (i32_t)(u % ui64_C(86400));
147
148   ct->second = (s % 60); s /= 60;
149   ct->minute = s % 60; s /= 60;
150   ct->hour = s;
151
152   u /= ui64_C(86400);
153   caldate_frommjd(&ct->date,/*XXX*/(i32_t) (u - ui64_C(53375995543064)));
154
155   ct->offset = 0;
156 }
157
158 //
159 void
160 caltime_tai(const Kumu::TAI::caltime* ct, Kumu::TAI::tai* t)
161 {
162   assert(ct&&t);
163   i32_t day, s;
164
165   /* XXX: check for overflow? */
166
167   day = caldate_mjd(&ct->date);
168
169   s = ct->hour * 60 + ct->minute;
170   s = (s - ct->offset) * 60 + ct->second;
171
172   t->x = day * ui64_C(86400) + ui64_C(4611686014920671114) + (i64_t)s;
173 }
174
175 //
176 void
177 Kumu::TAI::tai::now()
178 {
179 #ifdef KM_WIN32
180   SYSTEMTIME st;
181   ::GetSystemTime(&st);
182   TAI::caltime ct;
183   ct.date.year = st.wYear;
184   ct.date.month = st.wMonth;
185   ct.date.day = st.wDay;
186   ct.hour = st.wHour;
187   ct.minute = st.wMinute;
188   ct.second = st.wSecond;
189   caltime_tai(&ct, this);
190 #else
191   struct timeval now;
192   gettimeofday(&now, 0);
193   x = ui64_C(4611686018427387914) + (ui64_t)now.tv_sec;
194 #endif
195 }
196
197
198 //
199 const Kumu::TAI::tai&
200 Kumu::TAI::tai::operator=(const Kumu::TAI::caltime& rhs)
201 {
202   caltime_tai(&rhs, this);
203   return *this;
204 }
205
206 //
207 const Kumu::TAI::caltime&
208 Kumu::TAI::caltime::operator=(const Kumu::TAI::tai& rhs)
209 {
210   caltime_utc(this, &rhs);
211   return *this;
212 }
213
214
215 //
216 // end KM_tai.cpp
217 //