extern void clock_select (void);
extern u_long leapsec; /* seconds to next leap (proximity class) */
+extern int leapdif; /* TAI difference step at next leap second*/
extern int sys_orphan;
extern double sys_mindisp;
extern double sys_maxdist;
* than the stored ones, install the new leap
* values and recompute the signatures.
*/
- if (leapsec_add_fix(ntohl(ep->pkt[1]),
+ if (leapsec_add_fix(ntohl(ep->pkt[0]),
+ ntohl(ep->pkt[1]),
ntohl(ep->pkt[2]),
- ntohl(ep->pkt[0]),
NULL))
{
leap_signature_t lsig;
int sig, num;
const u_char *src;
- num = 0;
+ num = sig = 0;
src = (const u_char*)begp;
- while (*src && isspace(*src))
+ while (isspace(*src))
src++;
if (*src == '-') {
src++;
sig = 1;
- } else {
- sig = 0;
- src += (*src == '+');
+ } else if (*src == '+') {
+ src++;
}
if (base == 0) {
memset(&res, 0, sizeof(res));
while (*src) {
if (isdigit(*src))
- digit = (u_char)*src - '0';
+ digit = *src - '0';
else if (isupper(*src))
- digit = (u_char)*src - 'A' + 10;
+ digit = *src - 'A' + 10;
else if (islower(*src))
- digit = (u_char)*src - 'a' + 10;
+ digit = *src - 'a' + 10;
else
break;
if (digit >= base)
struct leap_info {
vint64 ttime; /* transition time (after the step, ntp scale) */
u_int32 stime; /* schedule limit (a month before transition) */
- short total; /* accumulated leap seconds from that point */
+ short taiof; /* TAI offset on and after the transition */
u_short dynls; /* dynamic: inserted on peer/clock request */
};
typedef struct leap_info leap_info_t;
struct leap_head {
- vint64 expire; /* table expiration time */
- u_short size; /* number of infos in table */
- int32 total; /* total leaps before first entry */
- vint64 when; /* begin of next leap era */
- vint64 ttime; /* nominal transition time */
- vint64 stime; /* announce leapsec 1 month ahead */
- vint64 base; /* base of this leap era */
- short this_tai; /* current TAI offset */
- short next_tai; /* TAI offset after 'when'*/
- short dynls; /* next leap is dynamic */
+ vint64 expire; /* table expiration time */
+ u_short size; /* number of infos in table */
+ short base_tai; /* total leaps before first entry */
+ short this_tai; /* current TAI offset */
+ short next_tai; /* TAI offset after 'when' */
+ vint64 dtime; /* due time (current era end) */
+ vint64 ttime; /* nominal transition time (next era start) */
+ vint64 stime; /* schedule time (when we take notice) */
+ vint64 ebase; /* base time of this leap era */
+ short dynls; /* next leap is dynamic (by peer request) */
};
typedef struct leap_head leap_head_t;
static int add_range(leap_table_t*, const leap_info_t*);
static char * get_line(leapsec_reader, void*, char*, size_t);
static char * skipws(const char*);
+static int parsefail(const char * cp, const char * ep);
static void reload_limits(leap_table_t*, const vint64*);
static int betweenu32(u_int32, u_int32, u_int32);
static void reset_times(leap_table_t*);
int use_build_limit)
{
char *cp, *ep, linebuf[50];
- vint64 tt, limit;
- int al;
+ vint64 ttime, limit;
+ long taiof;
struct calendar build;
leapsec_clear(pt);
memset(&limit, 0, sizeof(limit));
while (get_line(func, farg, linebuf, sizeof(linebuf))) {
- cp = skipws(linebuf);
+ cp = linebuf;
if (*cp == '#') {
cp++;
if (*cp == '@' || *cp == '$') {
cp = skipws(cp+1);
pt->head.expire = strtouv64(cp, &ep, 10);
- if (ep == cp || *ep > ' ')
+ if (parsefail(cp, ep))
goto fail_read;
pt->lsig.etime = pt->head.expire.D_s.lo;
}
- } else if (isdigit(*cp)) {
- tt = strtouv64(cp, &ep, 10);
- if (ep == cp || *ep > ' ')
+ } else if (isdigit((u_char)*cp)) {
+ ttime = strtouv64(cp, &ep, 10);
+ if (parsefail(cp, ep))
goto fail_read;
cp = skipws(ep);
- al = strtol(cp, &ep, 10);
- if (ep == cp || *ep > ' ')
+ taiof = strtol(cp, &ep, 10);
+ if ( parsefail(cp, ep)
+ || taiof > SHRT_MAX || taiof < SHRT_MIN)
goto fail_read;
- cp = skipws(ep);
- if (ucmpv64(&tt, &limit) >= 0) {
- if (!leapsec_raw(pt, &tt, al, FALSE))
+ if (ucmpv64(&ttime, &limit) >= 0) {
+ if (!leapsec_raw(pt, &ttime,
+ taiof, FALSE))
goto fail_insn;
} else {
- pt->head.total = al;
+ pt->head.base_tai = (short)taiof;
}
- pt->lsig.ttime = tt.D_s.lo;
- pt->lsig.taiof = al;
+ pt->lsig.ttime = ttime.D_s.lo;
+ pt->lsig.taiof = (short)taiof;
}
}
return TRUE;
ttb.year, ttb.month, ttb.monthday,
"-*"[pt->info[idx].dynls != 0],
atb.year, atb.month, atb.monthday,
- pt->info[idx].total);
+ pt->info[idx].taiof);
}
}
const time_t * pivot)
{
leap_table_t * pt;
- vint64 ts64, last;
- u_int32 when32;
+ vint64 ts64, last, next;
+ u_int32 due32;
int fired;
/* preset things we use later on... */
pt = leapsec_get_table(FALSE);
memset(qr, 0, sizeof(leap_result_t));
- if (ucmpv64(&ts64, &pt->head.base) < 0) {
- /* Ooops? Clock step backward? Oh, well... */
+ if (ucmpv64(&ts64, &pt->head.ebase) < 0) {
+ /* Most likely after leap frame reset. Could also be a
+ * backstep of the system clock. Anyway, get the new
+ * leap era frame.
+ */
reload_limits(pt, &ts64);
- } else if (ucmpv64(&ts64, &pt->head.when) >= 0) {
+ } else if (ucmpv64(&ts64, &pt->head.dtime) >= 0) {
/* Boundary crossed in forward direction. This might
* indicate a leap transition, so we prepare for that
* case.
+ *
+ * Some operations below are actually NOPs in electric
+ * mode, but having only one code path that works for
+ * both modes is easier to maintain.
*/
last = pt->head.ttime;
- qr->warped = last.D_s.lo - pt->head.when.D_s.lo;
- reload_limits(pt, &ts64);
- if (ucmpv64(&pt->head.base, &last) == 0)
- fired = TRUE;
- else
+ qr->warped = (short)(last.D_s.lo -
+ pt->head.dtime.D_s.lo);
+ next = addv64i32(&ts64, qr->warped);
+ reload_limits(pt, &next);
+ fired = ucmpv64(&pt->head.ebase, &last) == 0;
+ if (fired) {
+ ts64 = next;
+ ts32 = next.D_s.lo;
+ } else {
qr->warped = 0;
+ }
}
qr->tai_offs = pt->head.this_tai;
return fired;
/* now start to collect the remaing data */
- when32 = pt->head.when.D_s.lo;
+ due32 = pt->head.dtime.D_s.lo;
qr->tai_diff = pt->head.next_tai - pt->head.this_tai;
- qr->when = pt->head.when;
- qr->dist = when32 - ts32;
+ qr->ttime = pt->head.ttime;
+ qr->ddist = due32 - ts32;
qr->dynamic = pt->head.dynls;
qr->proximity = LSPROX_SCHEDULE;
/* if not in the last day before transition, we're done. */
- if (!betweenu32(when32 - SECSPERDAY, ts32, when32))
+ if (!betweenu32(due32 - SECSPERDAY, ts32, due32))
return fired;
qr->proximity = LSPROX_ANNOUNCE;
- if (!betweenu32(when32 - 10, ts32, when32))
+ if (!betweenu32(due32 - 10, ts32, due32))
return fired;
+ /* The last 10s before the transition. Prepare for action! */
qr->proximity = LSPROX_ALERT;
return fired;
}
+/* ------------------------------------------------------------------ */
+int/*BOOL*/
+leapsec_frame(
+ leap_result_t *qr)
+{
+ const leap_table_t * pt;
+
+ memset(qr, 0, sizeof(leap_result_t));
+ pt = leapsec_get_table(FALSE);
+ if (ucmpv64(&pt->head.ttime, &pt->head.stime) <= 0)
+ return FALSE;
+
+ qr->tai_offs = pt->head.this_tai;
+ qr->tai_diff = pt->head.next_tai - pt->head.this_tai;
+ qr->ttime = pt->head.ttime;
+ qr->dynamic = pt->head.dynls;
+
+ return TRUE;
+}
+
/* ------------------------------------------------------------------ */
/* Reset the current leap frame */
void
/* ------------------------------------------------------------------ */
int/*BOOL*/
leapsec_add_fix(
+ int total,
u_int32 ttime,
u_int32 etime,
- int total,
const time_t * pivot)
{
time_t tpiv;
tt64 = ntpcal_ntp_to_ntp(ttime, pivot);
pt = leapsec_get_table(TRUE);
- if (ucmpv64(&et64, &pt->head.expire) <= 0)
- return FALSE;
- if ( ! leapsec_raw(pt, &tt64, total, FALSE))
+ if ( ucmpv64(&et64, &pt->head.expire) <= 0
+ || !leapsec_raw(pt, &tt64, total, FALSE) )
return FALSE;
pt->lsig.etime = etime;
pt->lsig.ttime = ttime;
- pt->lsig.taiof = total;
+ pt->lsig.taiof = (short)total;
pt->head.expire = et64;
/* ------------------------------------------------------------------ */
int/*BOOL*/
leapsec_add_dyn(
- u_int32 ntpnow,
int insert,
+ u_int32 ntpnow,
const time_t * pivot )
{
leap_table_t * pt;
/* [internal] Reset / init the time window in the leap processor to
* force reload on next query. Since a leap transition cannot take place
* at an odd second, the value chosen avoids spurious leap transition
- * triggers. Making all three times equal forces a reload.
+ * triggers. Making all three times equal forces a reload. Using the
+ * maximum value for unsigned 64 bits makes finding the next leap frame
+ * a bit easier.
*/
static void
reset_times(
leap_table_t * pt)
{
- pt->head.base.D_s.hi = 0;
- pt->head.base.D_s.lo = 1;
- pt->head.stime = pt->head.base;
- pt->head.ttime = pt->head.base;
- pt->head.when = pt->head.base;
+ memset(&pt->head.ebase, 0xFF, sizeof(vint64));
+ pt->head.stime = pt->head.ebase;
+ pt->head.ttime = pt->head.ebase;
+ pt->head.dtime = pt->head.ebase;
}
/* [internal] Add raw data to the table, removing old entries on the
* entry. But remember the accumulated leap seconds!
*/
if (pt->head.size >= MAX_HIST) {
- pt->head.size = MAX_HIST - 1;
- pt->head.total = pt->info[pt->head.size].total;
+ pt->head.size = MAX_HIST - 1;
+ pt->head.base_tai = pt->info[pt->head.size].taiof;
}
/* make room in lower end and insert item */
size_t size)
{
int ch;
- char *ptr = buff;
+ char *ptr;
+
+ /* if we cannot even store the delimiter, declare failure */
+ if (buff == NULL || size == 0)
+ return NULL;
+ ptr = buff;
while (EOF != (ch = (*func)(farg)) && '\n' != ch)
if (size > 1) {
size--;
*ptr++ = (char)ch;
}
- if (size)
- *ptr = '\0';
+ /* discard trailing whitespace */
+ while (ptr != buff && isspace((u_char)ptr[-1]))
+ ptr--;
+ *ptr = '\0';
return (ptr == buff && ch == EOF) ? NULL : buff;
}
skipws(
const char *ptr)
{
- const u_char * src;
+ while (isspace((u_char)*ptr))
+ ptr++;
+ return (char*)noconst(ptr);
+}
- src = (const u_char*)ptr;
- while (isspace(*src))
- src++;
- return (char*)noconst(src);
+/* [internal] check if a strtoXYZ ended at EOL or whistespace and
+ * converted something at all. Return TRUE if something went wrong.
+ */
+static int/*BOOL*/
+parsefail(
+ const char * cp,
+ const char * ep)
+{
+ return (cp == ep)
+ || (*ep && *ep != '#' && !isspace((u_char)*ep));
}
/* [internal] reload the table limits around the given time stamp. This
* empty -- no undefined condition must arise from this code.
*/
if (idx >= pt->head.size) {
- memset(&pt->head.base, 0x00, sizeof(vint64));
- pt->head.this_tai = pt->head.total;
+ memset(&pt->head.ebase, 0x00, sizeof(vint64));
+ pt->head.this_tai = pt->head.base_tai;
} else {
- pt->head.base = pt->info[idx].ttime;
- pt->head.this_tai = pt->info[idx].total;
+ pt->head.ebase = pt->info[idx].ttime;
+ pt->head.this_tai = pt->info[idx].taiof;
}
if (--idx >= 0) {
- pt->head.next_tai = pt->info[idx].total;
+ pt->head.next_tai = pt->info[idx].taiof;
pt->head.dynls = pt->info[idx].dynls;
pt->head.ttime = pt->info[idx].ttime;
- if ( ! _electric)
- pt->head.when = addv64i32(
+ if (_electric)
+ pt->head.dtime = pt->head.ttime;
+ else
+ pt->head.dtime = addv64i32(
&pt->head.ttime,
pt->head.next_tai - pt->head.this_tai);
- else
- pt->head.when = pt->head.ttime;
pt->head.stime = subv64u32(
&pt->head.ttime, pt->info[idx].stime);
} else {
memset(&pt->head.ttime, 0xFF, sizeof(vint64));
pt->head.stime = pt->head.ttime;
- pt->head.when = pt->head.ttime;
+ pt->head.dtime = pt->head.ttime;
pt->head.next_tai = pt->head.this_tai;
pt->head.dynls = 0;
}
li.ttime = ttime;
li.stime = ttime.D_s.lo - stime.D_s.lo;
- li.total = (pt->head.size ? pt->info[0].total : pt->head.total)
+ li.taiof = (pt->head.size ? pt->info[0].taiof : pt->head.base_tai)
+ (insert ? 1 : -1);
li.dynls = 1;
return add_range(pt, &li);
leapsec_raw(
leap_table_t * pt,
const vint64 * ttime,
- int total,
+ int taiof,
int dynls)
{
vint64 stime;
stime = ntpcal_date_to_ntp64(&fts);
li.ttime = *ttime;
li.stime = ttime->D_s.lo - stime.D_s.lo;
- li.total = (short)total;
+ li.taiof = (short)taiof;
li.dynls = (dynls != 0);
return add_range(pt, &li);
}
* step forward untill *we* (that is, this module) tells the client app
* to step at the right time. This needs a slightly different type of
* processing, so switching between those two modes should not be done
- * close to a leap second. The transition might be lost in that case.
+ * too close to a leap second. The transition might be lost in that
+ * case. (The limit is actual 2 sec before transition.)
*
* OTOH, this is a system characteristic, so it's expected to be set
* properly somewhere after system start and retain the value.
/* Query result for a leap second schedule
- * 'when' tells the transition point in full time scale, but only if
- * 'tai_diff' is not zero.
- * 'dist' is the distance to the transition, in clock seconds.
- * Only valid if 'tai_diff' not zero. To get the true (elapsed
- * time) difference, add 'tai_diff' to that value.
- * 'tai_offs' is the CURRENT distance from clock (UTC) to TAI.
+ * 'ttime' is the transition point in full time scale, but only if
+ * 'tai_diff' is not zero. Nominal UTC time when the next leap
+ * era starts.
+ * 'ddist' is the distance to the transition, in clock seconds.
+ * This is the distance to the due time, which is different
+ * from the transition time if the mode is non-electric.
+ * Only valid if 'tai_diff' not zero.
+ * 'tai_offs' is the CURRENT distance from clock (UTC) to TAI. Always valid.
* 'tai_diff' is the change in TAI offset after the next leap
- * transition. Zero if nothing is pending.
+ * transition. Zero if nothing is pending or too far ahead.
* 'warped' is set only once, when the the leap second occurred between
- * two queries.
- * 'proximity' is a proximity warning. See definitions below. This might
- * be more useful than an absolute difference to the leap second.
+ * two queries. Always zero in electric mode. If non-zero,
+ * immediately step the clock.
+ * 'proximity' is a proximity warning. See definitions below. This is
+ * more useful than an absolute difference to the leap second.
* 'dynamic' != 0 if entry was requested by clock/peer
*/
struct leap_result {
- vint64 when;
- u_int32 dist;
+ vint64 ttime;
+ u_int32 ddist;
short tai_offs;
short tai_diff;
short warped;
* subsequently modified.
*/
extern leap_table_t *leapsec_get_table(int alternate);
+
/* Set the current leap table. Accepts only return values from
* 'leapsec_get_table()', so it's hard to do something wrong. Returns
- * TRUE if the table was exchanged.
+ * TRUE if the current table is the requested one.
*/
-extern int/*BOOL*/ leapsec_set_table(leap_table_t*);
+extern int/*BOOL*/ leapsec_set_table(leap_table_t *);
/* Clear all leap second data. Use it for init & cleanup */
extern void leapsec_clear(leap_table_t*);
/* Load a leap second file. If 'blimit' is set, do not store (but
* register with their TAI offset) leap entries before the build date.
+ * Update the leap signature data on the fly.
*/
extern int/*BOOL*/ leapsec_load(leap_table_t*, leapsec_reader,
void*, int blimit);
-
/* Dump the current leap table in readable format, using the provided
* dump formatter function.
*/
extern void leapsec_dump(const leap_table_t*, leapsec_dumper func, void *farg);
-/* Glue to integrate this easier into existing code */
-extern int/*BOOL*/ leapsec_load_file(FILE*, int blimit);
+/* Read a leap second file. This is a convenience wrapper around the
+ * generic load function, 'leapsec_load()'.
+ */
+extern int/*BOOL*/ leapsec_load_file(FILE * fp, int blimit);
+
+/* Get the current leap data signature. This consists of the last
+ * ransition, the table expiration, and the total TAI difference at the
+ * last transition. This is valid even if the leap transition itself was
+ * culled due to the build date limit.
+ */
extern void leapsec_getsig(leap_signature_t * psig);
+
+/* Check if the leap table is expired at the given time.
+ */
extern int/*BOOL*/ leapsec_expired(u_int32 when, const time_t * pivot);
-/* Reset the current leap frame */
+/* Reset the current leap frame, so the next query will do proper table
+ * lookup from fresh. Suppresses a possible leap era transition detection
+ * for the next query.
+ */
extern void leapsec_reset_frame(void);
/* Given a transition time, the TAI offset valid after that and an
* works if the existing table is extended. On success, updates the
* signature data.
*/
-extern int/*BOOL*/ leapsec_add_fix(u_int32 ttime, u_int32 etime, int total,
+extern int/*BOOL*/ leapsec_add_fix(int offset, u_int32 ttime, u_int32 etime,
const time_t * pivot);
/* Take a time stamp and create a leap second frame for it. This will
* 'ntp_now' is subject to era unfolding. The entry is marked
* dynamic. The leap signature is NOT updated.
*/
-extern int/*BOOL*/ leapsec_add_dyn(u_int32 ntp_now, int insert,
+extern int/*BOOL*/ leapsec_add_dyn(int insert, u_int32 ntp_now,
const time_t * pivot);
/* Take a time stamp and get the associated leap information. The time
* stamp is subject to era unfolding around the pivot or the current
* system time if pivot is NULL. Sets the information in '*qr' and
* returns TRUE if a leap second era boundary was crossed between the
- * last and the current query. In that case, qr.warped contains the
- * required clock stepping. (Which is zero in electric mode...)
+ * last and the current query. In that case, qr->warped contains the
+ * required clock stepping, which is always zero in electric mode.
*/
extern int/*BOOL*/ leapsec_query(leap_result_t *qr, u_int32 ntpts,
const time_t * pivot);
+/* Get the current leap frame info. Returns TRUE if the result contains
+ * useable data, FALSE if there is currently no leap second frame.
+ * This merely replicates some results from a previous query, but since
+ * it does not check the current time, only the following entries are
+ * meaningful:
+ * qr->ttime;
+ * qr->tai_offs;
+ * qr->tai_diff;
+ * qr->dynamic;
+ */
+extern int/*BOOL*/ leapsec_frame(leap_result_t *qr);
+
#endif /* !defined(NTP_LEAPSEC_H) */
sys_rootdisp = 0;
L_CLR(&sys_reftime);
sys_jitter = LOGTOD(sys_precision);
- leapsec = 0;
+ leapsec_reset_frame();
break;
/*
if (leap_vote_ins > leap_vote_del
&& leap_vote_ins > sys_survivors / 2) {
get_systime(&now);
- leapsec_add_dyn(now.l_ui, TRUE, NULL);
+ leapsec_add_dyn(TRUE, now.l_ui, NULL);
}
if (leap_vote_del > leap_vote_ins
&& leap_vote_del > sys_survivors / 2) {
get_systime(&now);
- leapsec_add_dyn(now.l_ui, FALSE, NULL);
+ leapsec_add_dyn(FALSE, now.l_ui, NULL);
}
}
break;
#define TC_ERR (-1)
#endif
-static void check_leapsec(u_int32, const time_t*);
+static void check_leapsec(u_int32, const time_t*, int/*BOOL*/);
/*
* These routines provide support for the event timer. The timer is
static u_long stats_timer; /* stats timer */
static u_long huffpuff_timer; /* huff-n'-puff timer */
static u_long worker_idle_timer;/* next check for idle intres */
-u_long leapsec; /* leapseconds countdown */
+u_long leapsec; /* seconds to next leap (proximity class) */
+int leapdif; /* TAI difference step at next leap second*/
u_long orphwait; /* orphan wait time */
#ifdef AUTOKEY
static u_long revoke_timer; /* keys revoke timer */
* Leapseconds. Get time and defer to worker if either something
* is imminent or every 8th second.
*/
- if (leapsec > LSPROX_NOWARN || 0 == (now.l_ui & 7)) {
- check_leapsec(now.l_ui, &tnow);
+ if (leapsec > LSPROX_NOWARN || 0 == (current_time & 7))
+ check_leapsec(now.l_ui, &tnow,
+ (sys_leap == LEAP_NOTINSYNC));
+ if (sys_leap != LEAP_NOTINSYNC) {
+ if (leapsec >= LSPROX_ANNOUNCE && leapdif) {
+ if (leapdif > 0)
+ sys_leap = LEAP_ADDSECOND;
+ else
+ sys_leap = LEAP_DELSECOND;
+ } else {
+ sys_leap = LEAP_NOWARNING;
+ }
}
/*
static void
check_leapsec(
- u_int32 now ,
- const time_t * tpiv)
+ u_int32 now ,
+ const time_t * tpiv ,
+ int/*BOOL*/ reset)
{
leap_result_t lsdata;
u_int32 lsprox;
-#if defined(SYS_WINNT)
- leapsec_electric(1); /* WinNT port has its own leap second handling */
-#elif defined(KERNEL_PLL)
+#ifndef SYS_WINNT /* WinNT port has its own leap second handling */
+# ifdef KERNEL_PLL
leapsec_electric(pll_control && kern_enable);
-#else
+# else
leapsec_electric(0);
-#endif
-
- if (sys_leap == LEAP_NOTINSYNC) {
+# endif
+#endif
+ if (reset) {
lsprox = LSPROX_NOWARN;
leapsec_reset_frame();
memset(&lsdata, 0, sizeof(lsdata));
-
} else if (leapsec_query(&lsdata, now, tpiv)) {
/* Full hit. Eventually step the clock, but always
* announce the leap event has happened.
* to piping hot in one step. If things are already that wobbly,
* we let the normal clock correction take over, even if a jump
* is involved.
- */
+ * Also make sure the alarming events are edge-triggered, that is,
+ * ceated only when the threshold is crossed.
+ */
if ( (leapsec > 0 || lsprox < LSPROX_ALERT)
&& leapsec < lsprox ) {
- if (lsprox >= LSPROX_SCHEDULE) {
+ if ( leapsec < LSPROX_SCHEDULE
+ && lsprox >= LSPROX_SCHEDULE) {
if (lsdata.dynamic)
report_event(PEVNT_ARMED, sys_peer, NULL);
else
leapsec = lsprox;
}
if (leapsec > lsprox) {
- if (lsprox < LSPROX_SCHEDULE) {
+ if ( leapsec >= LSPROX_SCHEDULE
+ && lsprox < LSPROX_SCHEDULE) {
report_event(EVNT_DISARMED, NULL, NULL);
}
leapsec = lsprox;
}
- if (sys_leap != LEAP_NOTINSYNC) {
- if (leapsec < LSPROX_ANNOUNCE)
- lsdata.tai_diff = 0;
- if (lsdata.tai_diff > 0)
- sys_leap = LEAP_ADDSECOND;
- else if (lsdata.tai_diff < 0)
- sys_leap = LEAP_DELSECOND;
- else
- sys_leap = LEAP_NOWARNING;
- }
+ if (leapsec >= LSPROX_SCHEDULE)
+ leapdif = lsdata.tai_diff;
+ else
+ leapdif = 0;
}
FILE *fp;
struct stat *sp1 = &leapseconds_file_sb1;
struct stat *sp2 = &leapseconds_file_sb2;
- leap_table_t * plt;
if (leapseconds_file) {
if ((fp = fopen(leapseconds_file, "r")) == NULL) {
if ( (sp1->st_mtime != sp2->st_mtime)
|| (sp1->st_ctime != sp2->st_ctime)) {
leapseconds_file_sb1 = leapseconds_file_sb2;
- plt = leapsec_get_table(TRUE);
- if (!leapsec_load(plt, (leapsec_reader)getc, fp, TRUE)) {
+ if (!leapsec_load_file(fp, TRUE)) {
msyslog(LOG_ERR,
"format error leapseconds file %s",
leapseconds_file);
- } else {
- leapsec_set_table(plt);
}
}
fclose(fp);
#include "ntp_unixtime.h"
#include "ntp_timer.h"
#include "ntp_assert.h"
+#include "ntp_leapsec.h"
#include "clockstuff.h"
#include "ntservice.h"
#include "ntpd.h"
double now
)
{
+ /* ntp time scale origin as ticks since 1601-01-01 */
+ static const ULONGLONG HNS_JAN_1900 = 94354848000000000ull;
+
static double adjtime_carry;
double dtemp;
u_char isneg;
SYSTEMTIME st;
ULONGLONG this_perf_count;
FT_ULL curr_ft;
+ leap_result_t lsi;
/*
* Add the residual from the previous adjustment to the new
dtemp -= TimeAdjustment * ppm_per_adjust_unit;
- /*
- * If a leap second is pending for the end of the month,
- * determine the UTC time stamp when the insertion must take
- * place
+ /* If a piping-hot close leap second is pending for the end
+ * of this day, determine the UTC time stamp when the transition
+ * must take place. (Calculated in the current leap era!)
*/
- if (0 < leapsec && leapsec < 31 * DAY) {
- if (0 == ls_ft.ull) { /* time stamp has not yet been computed */
- GetSystemTime(&st);
-
- /*
- * Accept leap announcement only 1 month in advance,
- * for end of March, June, September, or December.
- */
- if (0 == (st.wMonth % 3)) {
- /*
- * The comparison time stamp is computed according
- * to 0:00h UTC of the following day
- */
- if (++st.wMonth > 12) {
- st.wMonth -= 12;
- st.wYear++;
- }
-
- st.wDay = 1;
- st.wHour = 0;
- st.wMinute = 0;
- st.wSecond = 0;
- st.wMilliseconds = 0;
-
- SystemTimeToFileTime(&st, &ls_ft.ft);
- msyslog(LOG_NOTICE,
- "Detected positive leap second announcement "
- "for %04d-%02d-%02d %02d:%02d:%02d UTC",
- st.wYear, st.wMonth, st.wDay,
- st.wHour, st.wMinute, st.wSecond);
- }
- }
- } else {
- if (ls_ft.ull != 0) { /* Leap second has been armed before */
- /*
- * Disarm leap second only if the leap second
- * is not already in progress.
- */
- if (0 == ls_time_adjustment) {
- ls_ft.ull = 0;
- msyslog(LOG_NOTICE, "Leap second announcement disarmed");
- }
+ if (leapsec >= LSPROX_ALERT) {
+ if (0 == ls_ft.ull && leapsec_frame(&lsi)) {
+ if (lsi.tai_diff > 0) {
+ /* A leap second insert is scheduled at the end
+ * of the day. Since we have not yet computed the
+ * time stamp, do it now. Signal electric mode
+ * for this insert.
+ */
+ ls_ft.ull = lsi.ttime.Q_s * HECTONANOSECONDS
+ + HNS_JAN_1900;
+ FileTimeToSystemTime(&ls_ft.ft, &st);
+ msyslog(LOG_NOTICE,
+ "Detected positive leap second announcement "
+ "for %04d-%02d-%02d %02d:%02d:%02d UTC",
+ st.wYear, st.wMonth, st.wDay,
+ st.wHour, st.wMinute, st.wSecond);
+ leapsec_electric(TRUE);
+ } else if (lsi.tai_diff < 0) {
+ /* Do not handle negative leap seconds here. If this
+ * happens, let the system step.
+ */
+ leapsec_electric(FALSE);
+ }
+ }
+ } else {
+ /* The leap second announcement is gone. Happens primarily after
+ * the leap transition, but can also be due to a clock step.
+ * Disarm the leap second, but only if there is one scheduled
+ * and not currently in progress!
+ */
+ if (ls_ft.ull != 0 && ls_time_adjustment == 0) {
+ ls_ft.ull = 0;
+ msyslog(LOG_NOTICE, "Leap second announcement disarmed");
}
}
-
/*
* If the time stamp for the next leap second has been set
* then check if the leap second must be handled
#pragma warning(pop)
init_small_adjustment();
+ leapsec_electric(TRUE);
/*
* Get privileges needed for fiddling with the clock
/>
</FileConfiguration>
</File>
+ <File
+ RelativePath="..\..\..\ntpd\ntp_leapsec.c"
+ >
+ </File>
<File
RelativePath="..\..\..\ntpd\ntp_loopfilter.c"
>
RelativePath="..\..\..\include\ntp_io.h"
>
</File>
+ <File
+ RelativePath="..\..\..\ntpd\ntp_leapsec.h"
+ >
+ </File>
<File
RelativePath="..\include\ntp_iocompletionport.h"
>
RelativePath="..\..\ntpd\ntp_iocompletionport.c"
>
</File>
+ <File
+ RelativePath="..\..\..\..\ntpd\ntp_leapsec.c"
+ >
+ </File>
<File
RelativePath="..\..\..\..\ntpd\ntp_loopfilter.c"
>
RelativePath="..\..\..\..\ntpd\ntp_keyword.h"
>
</File>
+ <File
+ RelativePath="..\..\..\..\ntpd\ntp_leapsec.h"
+ >
+ </File>
<File
RelativePath="..\..\..\..\include\ntp_libopts.h"
>
"2950473600 28 # 1 Jul 1993\n"
"#\n";
+// Faked table with a leap second removal at 2009
+static const char leap3 [] =
+ "#\n"
+ "#@ 3610569600\n"
+ "#\n"
+ "2272060800 10 # 1 Jan 1972\n"
+ "2287785600 11 # 1 Jul 1972\n"
+ "2303683200 12 # 1 Jan 1973\n"
+ "2335219200 13 # 1 Jan 1974\n"
+ "2366755200 14 # 1 Jan 1975\n"
+ "2398291200 15 # 1 Jan 1976\n"
+ "2429913600 16 # 1 Jan 1977\n"
+ "2461449600 17 # 1 Jan 1978\n"
+ "2492985600 18 # 1 Jan 1979\n"
+ "2524521600 19 # 1 Jan 1980\n"
+ "2571782400 20 # 1 Jul 1981\n"
+ "2603318400 21 # 1 Jul 1982\n"
+ "2634854400 22 # 1 Jul 1983\n"
+ "2698012800 23 # 1 Jul 1985\n"
+ "2776982400 24 # 1 Jan 1988\n"
+ "2840140800 25 # 1 Jan 1990\n"
+ "2871676800 26 # 1 Jan 1991\n"
+ "2918937600 27 # 1 Jul 1992\n"
+ "2950473600 28 # 1 Jul 1993\n"
+ "2982009600 29 # 1 Jul 1994\n"
+ "3029443200 30 # 1 Jan 1996\n"
+ "3076704000 31 # 1 Jul 1997\n"
+ "3124137600 32 # 1 Jan 1999\n"
+ "3345062400 33 # 1 Jan 2006\n"
+ "3439756800 32 # 1 Jan 2009\n"
+ "3550089600 33 # 1 Jul 2012\n"
+ "#\n";
+
+
static u_int32 lsec2009 = 3439756800u; // 1 Jan 2009, 00:00:00 utc
static u_int32 lsec2012 = 3550089600u; // 1 Jul 2012, 00:00:00 utc
ntpcal_set_timefunc(NULL);
}
+// test number parser
TEST_F(leapsecTest, ParseVUI64) {
vint64 act, exp;
const char *sp;
EXPECT_EQ(*ep, '\0');
}
+// test table selection
TEST_F(leapsecTest, tableSelect) {
leap_table_t *pt1, *pt2, *pt3, *pt4;
EXPECT_NE(pt2, pt3);
}
-TEST_F(leapsecTest, loadFileOk) {
+// load file & checl expiration
+TEST_F(leapsecTest, loadFileExpire) {
const char *cp = leap1;
int rc;
leap_table_t * pt = leapsec_get_table(0);
EXPECT_EQ(1, rc);
rc = leapsec_expired(3439756800, NULL);
EXPECT_EQ(0, rc);
- //leapsec_dump(pt, (leapsec_dumper)fprintf, stdout);
-}
-
-TEST_F(leapsecTest, loadFileExpire) {
- const char *cp = leap1;
- int rc;
- leap_table_t * pt = leapsec_get_table(0);
-
- rc = leapsec_load(pt, stringreader, &cp, FALSE)
- && leapsec_set_table(pt);
- EXPECT_EQ(1, rc);
rc = leapsec_expired(3610569601, NULL);
EXPECT_EQ(1, rc);
}
-/* test handling of the leap second at 2009.01.01 */
+// ad-hoc jump: leap second at 2009.01.01 -60days
TEST_F(leapsecTest, ls2009faraway) {
int rc;
leap_result_t qr;
EXPECT_EQ(FALSE, rc);
EXPECT_EQ(33, qr.tai_offs);
EXPECT_EQ(0, qr.tai_diff);
- EXPECT_EQ(0, qr.dist);
EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
}
-/* test handling of the leap second at 2009.01.01 */
+// ad-hoc jump: leap second at 2009.01.01 -1week
TEST_F(leapsecTest, ls2009weekaway) {
int rc;
leap_result_t qr;
EXPECT_EQ(FALSE, rc);
EXPECT_EQ(33, qr.tai_offs);
EXPECT_EQ(1, qr.tai_diff);
- EXPECT_EQ(7*SECSPERDAY, qr.dist);
EXPECT_EQ(LSPROX_SCHEDULE, qr.proximity);
}
-/* test handling of the leap second at 2009.01.01 */
+// ad-hoc jump: leap second at 2009.01.01 -1hr
TEST_F(leapsecTest, ls2009houraway) {
int rc;
leap_result_t qr;
EXPECT_EQ(FALSE, rc);
EXPECT_EQ(33, qr.tai_offs);
EXPECT_EQ(1, qr.tai_diff);
- EXPECT_EQ(SECSPERHR, qr.dist);
EXPECT_EQ(LSPROX_ANNOUNCE, qr.proximity);
}
-/* test handling of the leap second at 2009.01.01 */
+// ad-hoc jump: leap second at 2009.01.01 -1sec
TEST_F(leapsecTest, ls2009secaway) {
int rc;
leap_result_t qr;
EXPECT_EQ(FALSE, rc);
EXPECT_EQ(33, qr.tai_offs);
EXPECT_EQ(1, qr.tai_diff);
- EXPECT_EQ(1, qr.dist);
EXPECT_EQ(LSPROX_ALERT, qr.proximity);
}
-/* test handling of the leap second at 2009.01.01 */
+// ad-hoc jump to leap second at 2009.01.01
TEST_F(leapsecTest, ls2009onspot) {
int rc;
leap_result_t qr;
EXPECT_EQ(FALSE, rc);
EXPECT_EQ(34, qr.tai_offs);
EXPECT_EQ(0, qr.tai_diff);
- EXPECT_EQ(0, qr.dist);
EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
}
-/* test handling of the leap second at 2009.01.01 */
-TEST_F(leapsecTest, ls2009sequenceElectric) {
- int rc;
- leap_result_t qr;
-
- rc = setup_load_table(leap1);
- EXPECT_EQ(1, rc);
-
- rc = leapsec_query(&qr, lsec2009 - 60*SECSPERDAY, NULL);
- EXPECT_EQ(FALSE, rc);
- EXPECT_EQ(0, qr.warped );
- EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
-
- rc = leapsec_query(&qr, lsec2009 - 7*SECSPERDAY, NULL);
- EXPECT_EQ(FALSE, rc);
- EXPECT_EQ(0, qr.warped );
- EXPECT_EQ(LSPROX_SCHEDULE, qr.proximity);
-
- rc = leapsec_query(&qr, lsec2009 - SECSPERHR, NULL);
- EXPECT_EQ(FALSE, rc);
- EXPECT_EQ(0, qr.warped );
- EXPECT_EQ(LSPROX_ANNOUNCE, qr.proximity);
-
- rc = leapsec_query(&qr, lsec2009 - 1, NULL);
- EXPECT_EQ(FALSE, rc);
- EXPECT_EQ(0, qr.warped );
- EXPECT_EQ(LSPROX_ALERT, qr.proximity);
-
- rc = leapsec_query(&qr, lsec2009, NULL);
- EXPECT_EQ(TRUE, rc);
- EXPECT_EQ(0, qr.warped );
- EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
-
- // second call, same time frame: no trigger!
- rc = leapsec_query(&qr, lsec2009, NULL);
- EXPECT_EQ(FALSE, rc);
- EXPECT_EQ(0, qr.warped );
- EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
-}
-
-/* test handling of the leap second at 2009.01.01 */
+// test handling of the leap second at 2009.01.01 without table
TEST_F(leapsecTest, ls2009nodata) {
int rc;
leap_result_t qr;
EXPECT_EQ(FALSE, rc);
EXPECT_EQ(0, qr.tai_offs);
EXPECT_EQ(0, qr.tai_diff);
- EXPECT_EQ(0, qr.dist);
+ EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
}
-/* test handling of the leap second at 2009.01.01 */
+// test handling of the leap second at 2009.01.01 with culled data
TEST_F(leapsecTest, ls2009limdata) {
int rc;
leap_result_t qr;
EXPECT_EQ(FALSE, rc);
EXPECT_EQ(35, qr.tai_offs);
EXPECT_EQ(0, qr.tai_diff);
- EXPECT_EQ(0, qr.dist);
+ EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
}
-TEST_F(leapsecTest, addManual) {
+// add dynamic leap second (like from peer/clock)
+TEST_F(leapsecTest, addDynamic) {
int rc;
leap_result_t qr;
leap_table_t * pt = leapsec_get_table(0);
for (int idx=1; insns[idx]; ++idx) {
- rc = leapsec_add_dyn(insns[idx] - 20*SECSPERDAY - 100, 1, NULL);
+ rc = leapsec_add_dyn(TRUE, insns[idx] - 20*SECSPERDAY - 100, NULL);
EXPECT_EQ(TRUE, rc);
}
// try to slip in a previous entry
- rc = leapsec_add_dyn(insns[0] - 20*SECSPERDAY - 100, 1, NULL);
+ rc = leapsec_add_dyn(TRUE, insns[0] - 20*SECSPERDAY - 100, NULL);
EXPECT_EQ(FALSE, rc);
//leapsec_dump(pt, (leapsec_dumper)fprintf, stdout);
}
+// add fixed leap seconds (like from network packet)
TEST_F(leapsecTest, addFixed) {
int rc;
leap_result_t qr;
leap_table_t * pt = leapsec_get_table(0);
// try to get in BAD time stamps...
for (int idx=0; insns[idx].tt; ++idx) {
- rc = leapsec_add_fix(insns[idx].tt - 20*SECSPERDAY - 100,
- insns[idx].tt + SECSPERDAY,
- insns[idx].of,
- NULL);
+ rc = leapsec_add_fix(
+ insns[idx].of,
+ insns[idx].tt - 20*SECSPERDAY - 100,
+ insns[idx].tt + SECSPERDAY,
+ NULL);
EXPECT_EQ(FALSE, rc);
}
// no do it right
for (int idx=0; insns[idx].tt; ++idx) {
- rc = leapsec_add_fix(insns[idx].tt,
- insns[idx].tt + SECSPERDAY,
- insns[idx].of,
- NULL);
+ rc = leapsec_add_fix(
+ insns[idx].of,
+ insns[idx].tt,
+ insns[idx].tt + SECSPERDAY,
+ NULL);
EXPECT_EQ(TRUE, rc);
}
// try to slip in a previous entry
- rc = leapsec_add_fix(insns[0].tt,
- insns[0].tt + SECSPERDAY,
- insns[0].of,
- NULL);
+ rc = leapsec_add_fix(
+ insns[0].of,
+ insns[0].tt,
+ insns[0].tt + SECSPERDAY,
+ NULL);
EXPECT_EQ(FALSE, rc);
//leapsec_dump(pt, (leapsec_dumper)fprintf, stdout);
}
-/* test handling of the leap second at 2012.07.01 */
-TEST_F(leapsecTest, ls2012sequenceElectric) {
+// =====================================================================
+// SEQUENCE TESTS
+// =====================================================================
+
+// leap second insert at 2009.01.01, electric mode
+TEST_F(leapsecTest, ls2009seqInsElectric) {
+ int rc;
+ leap_result_t qr;
+
+ rc = setup_load_table(leap1);
+ EXPECT_EQ(1, rc);
+ leapsec_electric(1);
+
+ rc = leapsec_query(&qr, lsec2009 - 60*SECSPERDAY, NULL);
+ EXPECT_EQ(FALSE, rc);
+ EXPECT_EQ(0, qr.warped );
+ EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
+
+ rc = leapsec_query(&qr, lsec2009 - 7*SECSPERDAY, NULL);
+ EXPECT_EQ(FALSE, rc);
+ EXPECT_EQ(0, qr.warped );
+ EXPECT_EQ(LSPROX_SCHEDULE, qr.proximity);
+
+ rc = leapsec_query(&qr, lsec2009 - SECSPERHR, NULL);
+ EXPECT_EQ(FALSE, rc);
+ EXPECT_EQ(0, qr.warped );
+ EXPECT_EQ(LSPROX_ANNOUNCE, qr.proximity);
+
+ rc = leapsec_query(&qr, lsec2009 - 1, NULL);
+ EXPECT_EQ(FALSE, rc);
+ EXPECT_EQ(0, qr.warped );
+ EXPECT_EQ(LSPROX_ALERT, qr.proximity);
+
+ rc = leapsec_query(&qr, lsec2009, NULL);
+ EXPECT_EQ(TRUE, rc);
+ EXPECT_EQ(0, qr.warped );
+ EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
+
+ // second call, same time frame: no trigger!
+ rc = leapsec_query(&qr, lsec2009, NULL);
+ EXPECT_EQ(FALSE, rc);
+ EXPECT_EQ(0, qr.warped );
+ EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
+}
+
+// leap second insert at 2009.01.01, dumb mode
+TEST_F(leapsecTest, ls2009seqInsDumb) {
+ int rc;
+ leap_result_t qr;
+
+ rc = setup_load_table(leap1);
+ EXPECT_EQ(1, rc);
+ leapsec_electric(0);
+
+ rc = leapsec_query(&qr, lsec2009 - 60*SECSPERDAY, NULL);
+ EXPECT_EQ(FALSE, rc);
+ EXPECT_EQ(0, qr.warped );
+ EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
+
+ rc = leapsec_query(&qr, lsec2009 - 7*SECSPERDAY, NULL);
+ EXPECT_EQ(FALSE, rc);
+ EXPECT_EQ(0, qr.warped );
+ EXPECT_EQ(LSPROX_SCHEDULE, qr.proximity);
+
+ rc = leapsec_query(&qr, lsec2009 - SECSPERHR, NULL);
+ EXPECT_EQ(FALSE, rc);
+ EXPECT_EQ(0, qr.warped );
+ EXPECT_EQ(LSPROX_ANNOUNCE, qr.proximity);
+
+ rc = leapsec_query(&qr, lsec2009 - 1, NULL);
+ EXPECT_EQ(FALSE, rc);
+ EXPECT_EQ(0, qr.warped );
+ EXPECT_EQ(LSPROX_ALERT, qr.proximity);
+
+ rc = leapsec_query(&qr, lsec2009, NULL);
+ EXPECT_EQ(FALSE, rc);
+ EXPECT_EQ(0, qr.warped );
+ EXPECT_EQ(LSPROX_ALERT, qr.proximity);
+
+ rc = leapsec_query(&qr, lsec2009+1, NULL);
+ EXPECT_EQ(TRUE, rc);
+ EXPECT_EQ(-1, qr.warped );
+ EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
+
+ // second call, same time frame: no trigger!
+ rc = leapsec_query(&qr, lsec2009, NULL);
+ EXPECT_EQ(FALSE, rc);
+ EXPECT_EQ(0, qr.warped );
+ EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
+}
+
+
+// fake leap second remove at 2009.01.01, electric mode
+TEST_F(leapsecTest, ls2009seqDelElectric) {
+ int rc;
+ leap_result_t qr;
+
+ rc = setup_load_table(leap3);
+ EXPECT_EQ(1, rc);
+ leapsec_electric(1);
+
+ rc = leapsec_query(&qr, lsec2009 - 60*SECSPERDAY, NULL);
+ EXPECT_EQ(FALSE, rc);
+ EXPECT_EQ(0, qr.warped );
+ EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
+
+ rc = leapsec_query(&qr, lsec2009 - 7*SECSPERDAY, NULL);
+ EXPECT_EQ(FALSE, rc);
+ EXPECT_EQ(0, qr.warped );
+ EXPECT_EQ(LSPROX_SCHEDULE, qr.proximity);
+
+ rc = leapsec_query(&qr, lsec2009 - SECSPERHR, NULL);
+ EXPECT_EQ(FALSE, rc);
+ EXPECT_EQ(0, qr.warped );
+ EXPECT_EQ(LSPROX_ANNOUNCE, qr.proximity);
+
+ rc = leapsec_query(&qr, lsec2009 - 1, NULL);
+ EXPECT_EQ(FALSE, rc);
+ EXPECT_EQ(0, qr.warped );
+ EXPECT_EQ(LSPROX_ALERT, qr.proximity);
+
+ rc = leapsec_query(&qr, lsec2009, NULL);
+ EXPECT_EQ(TRUE, rc);
+ EXPECT_EQ(0, qr.warped );
+ EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
+
+ // second call, same time frame: no trigger!
+ rc = leapsec_query(&qr, lsec2009, NULL);
+ EXPECT_EQ(FALSE, rc);
+ EXPECT_EQ(0, qr.warped );
+ EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
+}
+
+// fake leap second remove at 2009.01.01. dumb mode
+TEST_F(leapsecTest, ls2009seqDelDumb) {
+ int rc;
+ leap_result_t qr;
+
+ rc = setup_load_table(leap3);
+ EXPECT_EQ(1, rc);
+ leapsec_electric(0);
+
+ rc = leapsec_query(&qr, lsec2009 - 60*SECSPERDAY, NULL);
+ EXPECT_EQ(FALSE, rc);
+ EXPECT_EQ(0, qr.warped );
+ EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
+
+ rc = leapsec_query(&qr, lsec2009 - 7*SECSPERDAY, NULL);
+ EXPECT_EQ(FALSE, rc);
+ EXPECT_EQ(0, qr.warped );
+ EXPECT_EQ(LSPROX_SCHEDULE, qr.proximity);
+
+ rc = leapsec_query(&qr, lsec2009 - SECSPERHR, NULL);
+ EXPECT_EQ(FALSE, rc);
+ EXPECT_EQ(0, qr.warped );
+ EXPECT_EQ(LSPROX_ANNOUNCE, qr.proximity);
+
+ rc = leapsec_query(&qr, lsec2009 - 2, NULL);
+ EXPECT_EQ(FALSE, rc);
+ EXPECT_EQ(0, qr.warped );
+ EXPECT_EQ(LSPROX_ALERT, qr.proximity);
+
+ rc = leapsec_query(&qr, lsec2009 - 1, NULL);
+ EXPECT_EQ(TRUE, rc);
+ EXPECT_EQ(1, qr.warped );
+ EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
+
+ // second call, same time frame: no trigger!
+ rc = leapsec_query(&qr, lsec2009, NULL);
+ EXPECT_EQ(FALSE, rc);
+ EXPECT_EQ(0, qr.warped );
+ EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
+}
+
+// leap second insert at 2012.07.01, electric mode
+TEST_F(leapsecTest, ls2012seqInsElectric) {
int rc;
leap_result_t qr;
EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
}
-/* test handling of the leap second at 2012.07.01 */
-TEST_F(leapsecTest, ls2012sequenceDumb) {
+// leap second insert at 2012.07.01, dumb mode
+TEST_F(leapsecTest, ls2012seqInsDumb) {
int rc;
leap_result_t qr;
EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
}
+