summaryrefslogtreecommitdiff
path: root/src/KM_tai.cpp
blob: 16fd5d3a24db334d7f7998a502baaa76ceff6fc2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
/*

THIS IS A SUBSET OF THE FULL LIBTAI. CHANGES HAVE BEEN MADE TO SUIT
LIBKUMU STYLE AND TYPE CONVENTIONS. ALL BUGS BELONG TO JOHN HURST.
THE FOLLOWING IS FOR ATTRIBUTION, THANK YOU MR. BERNSTEIN FOR WRITING
AND DISTRIBUTING SUCH GREAT SOFTWARE:

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.

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.

*/

  /*! \file    KM_tai.cpp
    \version $Id$
    \brief   portable time functions
  */

#include <asdcp/KM_tai.h>
#ifdef KM_WIN32
#include <time.h>
#else
#include <sys/time.h>
#endif

//
void
caldate_frommjd(Kumu::TAI::caldate* cd, i32_t day)
{
  assert(cd);
  i32_t year, month, yday;

  year = day / 146097L;
  day %= 146097L;
  day += 678881L;
  while (day >= 146097L) { day -= 146097L; ++year; }

  /* year * 146097 + day - 678881 is MJD; 0 <= day < 146097 */
  /* 2000-03-01, MJD 51604, is year 5, day 0 */

  year *= 4;
  if (day == 146096L) { year += 3; day = 36524L; }
  else { year += day / 36524L; day %= 36524L; }
  year *= 25;
  year += day / 1461;
  day %= 1461;
  year *= 4;

  yday = (day < 306);
  if (day == 1460) { year += 3; day = 365; }
  else { year += day / 365; day %= 365; }
  yday += day;

  day *= 10;
  month = (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 = day + 1;
}

//
static ui32_t times365[4] = { 0, 365, 730, 1095 } ;
static ui32_t times36524[4] = { 0, 36524UL, 73048UL, 109572UL } ;
static ui32_t montab[12] =
{ 0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337 } ;
/* month length after february is (306 * m + 5) / 10 */

//
i32_t
caldate_mjd(const Kumu::TAI::caldate* cd)
{
  assert(cd);
  i32_t y, m, 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;
}


//
void
caltime_utc(Kumu::TAI::caltime* ct, const Kumu::TAI::tai* t)
{
  assert(ct&&t);
  Kumu::TAI::tai t2 = *t;
  ui64_t u = t2.x + 58486;
  i32_t s = (i32_t)(u % ui64_C(86400));

  ct->second = (s % 60); s /= 60;
  ct->minute = s % 60; s /= 60;
  ct->hour = s;

  u /= ui64_C(86400);
  caldate_frommjd(&ct->date,/*XXX*/(i32_t) (u - ui64_C(53375995543064)));

  ct->offset = 0;
}

//
void
caltime_tai(const Kumu::TAI::caltime* ct, Kumu::TAI::tai* t)
{
  assert(ct&&t);
  i32_t day, 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 * ui64_C(86400) + ui64_C(4611686014920671114) + (i64_t)s;
}

//
void
Kumu::TAI::tai::now()
{
#ifdef KM_WIN32
  SYSTEMTIME st;
  ::GetSystemTime(&st);
  TAI::caltime ct;
  ct.date.year = st.wYear;
  ct.date.month = st.wMonth;
  ct.date.day = st.wDay;
  ct.hour = st.wHour;
  ct.minute = st.wMinute;
  ct.second = st.wSecond;
  ct.offset = 0;
  caltime_tai(&ct, this);
#else
  struct timeval now;
  gettimeofday(&now, 0);
  x = ui64_C(4611686018427387914) + (ui64_t)now.tv_sec;
#endif
}


//
const Kumu::TAI::tai&
Kumu::TAI::tai::operator=(const Kumu::TAI::caltime& rhs)
{
  caltime_tai(&rhs, this);
  return *this;
}

//
const Kumu::TAI::caltime&
Kumu::TAI::caltime::operator=(const Kumu::TAI::tai& rhs)
{
  caltime_utc(this, &rhs);
  return *this;
}


//
// end KM_tai.cpp
//