-
Notifications
You must be signed in to change notification settings - Fork 0
/
lstab.c
206 lines (182 loc) · 5.01 KB
/
lstab.c
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
/**
* @file lstab.c
* @note Copyright (C) 2012 Richard Cochran <richardcochran@gmail.com>
* @note SPDX-License-Identifier: GPL-2.0+
*/
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include "lstab.h"
/*
* Keep a history of the TAI - UTC offset in a lookup table.
*
* Each entry gives the NTP time when a new TAI offset came into
* effect. This is always the second immediately after a leap second.
*
* The size of the table is the number of entries from the NIST table,
* plus room for two hundred more entries to be added at run time.
* Since there can be at most two leap seconds per year, this allows
* for at least one hundred years.
*
* The table data are available from
*
* https://www.ietf.org/timezones/data/leap-seconds.list
*
* ftp://ftp.nist.gov/pub/time/leap-seconds.list
*
* When updating this table, do not forget to set N_HISTORICAL_LEAPS
* and the expiration date.
*/
#define BASE_TAI_OFFSET 10
#define N_HISTORICAL_LEAPS 28
#define N_LEAPS (N_HISTORICAL_LEAPS + 200)
#define NTP_UTC_OFFSET 2208988800ULL
struct epoch_marker {
int offset; /* TAI - UTC offset of epoch */
uint64_t ntp; /* NTP time of epoch */
uint64_t tai; /* TAI time of epoch */
uint64_t utc; /* UTC time of epoch */
};
struct lstab {
struct epoch_marker lstab[N_LEAPS];
uint64_t expiration_utc;
int length;
};
static const uint64_t expiration_date_ntp = 3818102400ULL; /* 28 December 2020 */
static const uint64_t offset_table[N_LEAPS * 2] = {
2272060800ULL, 10, /* 1 Jan 1972 */
2287785600ULL, 11, /* 1 Jul 1972 */
2303683200ULL, 12, /* 1 Jan 1973 */
2335219200ULL, 13, /* 1 Jan 1974 */
2366755200ULL, 14, /* 1 Jan 1975 */
2398291200ULL, 15, /* 1 Jan 1976 */
2429913600ULL, 16, /* 1 Jan 1977 */
2461449600ULL, 17, /* 1 Jan 1978 */
2492985600ULL, 18, /* 1 Jan 1979 */
2524521600ULL, 19, /* 1 Jan 1980 */
2571782400ULL, 20, /* 1 Jul 1981 */
2603318400ULL, 21, /* 1 Jul 1982 */
2634854400ULL, 22, /* 1 Jul 1983 */
2698012800ULL, 23, /* 1 Jul 1985 */
2776982400ULL, 24, /* 1 Jan 1988 */
2840140800ULL, 25, /* 1 Jan 1990 */
2871676800ULL, 26, /* 1 Jan 1991 */
2918937600ULL, 27, /* 1 Jul 1992 */
2950473600ULL, 28, /* 1 Jul 1993 */
2982009600ULL, 29, /* 1 Jul 1994 */
3029443200ULL, 30, /* 1 Jan 1996 */
3076704000ULL, 31, /* 1 Jul 1997 */
3124137600ULL, 32, /* 1 Jan 1999 */
3345062400ULL, 33, /* 1 Jan 2006 */
3439756800ULL, 34, /* 1 Jan 2009 */
3550089600ULL, 35, /* 1 Jul 2012 */
3644697600ULL, 36, /* 1 Jul 2015 */
3692217600ULL, 37, /* 1 Jan 2017 */
};
static void epoch_marker_init(struct epoch_marker *ls, uint64_t val, int offset)
{
ls->ntp = val;
ls->utc = val - NTP_UTC_OFFSET;
ls->tai = val - NTP_UTC_OFFSET + offset;
ls->offset = offset;
}
static void lstab_init(struct lstab *lstab)
{
struct epoch_marker *ls;
uint64_t offset, val;
int i;
for (i = 0; i < N_HISTORICAL_LEAPS; i++) {
ls = lstab->lstab + i;
val = offset_table[2 * i];
offset = offset_table[2 * i + 1];
epoch_marker_init(ls, val, offset);
}
lstab->expiration_utc = expiration_date_ntp - NTP_UTC_OFFSET;
lstab->length = i;
}
void lstab_print(struct lstab *lstab, FILE *fp)
{
int i, len = lstab->length;
fprintf(fp, "%3s%12s%12s%12s%4s\n", "idx", "NTP", "TAI", "UTC", "OFF");
for (i = 0; i < len; i++) {
fprintf(fp, "%3d" "%12" PRIu64 "%12" PRIu64 "%12" PRIu64 "%4d\n", i,
lstab->lstab[i].ntp, lstab->lstab[i].tai,
lstab->lstab[i].utc, lstab->lstab[i].offset);
}
}
static int lstab_read(struct lstab *lstab, const char *name)
{
uint64_t expiration, val;
struct epoch_marker *ls;
int index = 0, offset;
char buf[1024];
FILE *fp;
fp = fopen(name, "r");
if (!fp) {
fprintf(stderr, "failed to open '%s' for reading: %m\n", name);
return -1;
}
while (1) {
if (!fgets(buf, sizeof(buf), fp)) {
break;
}
if (1 == sscanf(buf, "#@ %" PRIu64, &expiration)) {
lstab->expiration_utc = expiration - NTP_UTC_OFFSET;
continue;
}
if (2 == sscanf(buf, "%" PRIu64 " %d", &val, &offset)) {
ls = lstab->lstab + index;
epoch_marker_init(ls, val, offset);
index++;
}
}
if (!lstab->expiration_utc) {
fprintf(stderr, "missing expiration date in '%s'\n", name);
return -1;
}
lstab->length = index;
return 0;
}
struct lstab *lstab_create(const char *filename)
{
struct lstab *lstab = calloc(1, sizeof(*lstab));
if (!lstab) {
return NULL;
}
if (filename && filename[0]) {
if (lstab_read(lstab, filename)) {
free(lstab);
return NULL;
}
} else {
lstab_init(lstab);
}
return lstab;
}
void lstab_destroy(struct lstab *lstab)
{
free(lstab);
}
enum lstab_result lstab_utc2tai(struct lstab *lstab, uint64_t utctime,
int *tai_offset)
{
int epoch = -1, index, next;
if (utctime > lstab->expiration_utc) {
return LSTAB_UNKNOWN;
}
for (index = lstab->length - 1; index > -1; index--) {
if (utctime >= lstab->lstab[index].utc) {
epoch = index;
break;
}
}
if (epoch == -1) {
return LSTAB_UNKNOWN;
}
*tai_offset = lstab->lstab[epoch].offset;
next = epoch + 1;
if (next < lstab->length && utctime == lstab->lstab[next].utc - 1) {
return LSTAB_AMBIGUOUS;
}
return LSTAB_OK;
}