154359Sroberto/*
254359Sroberto * ntp_calendar.h - definitions for the calendar time-of-day routine
354359Sroberto */
454359Sroberto#ifndef NTP_CALENDAR_H
554359Sroberto#define NTP_CALENDAR_H
654359Sroberto
7290000Sglebius#include <time.h>
8290000Sglebius
954359Sroberto#include "ntp_types.h"
1054359Sroberto
11290000Sglebius/* gregorian calendar date */
1254359Srobertostruct calendar {
13290000Sglebius	uint16_t year;		/* year (A.D.) */
14290000Sglebius	uint16_t yearday;	/* day of year, 1 = January 1 */
15290000Sglebius	uint8_t  month;		/* month, 1 = January */
16290000Sglebius	uint8_t  monthday;	/* day of month */
17290000Sglebius	uint8_t  hour;		/* hour of day, midnight = 0 */
18290000Sglebius	uint8_t  minute;	/* minute of hour */
19290000Sglebius	uint8_t  second;	/* second of minute */
20290000Sglebius	uint8_t  weekday;	/* 0..7, 0=Sunday */
2154359Sroberto};
2254359Sroberto
23290000Sglebius/* ISO week calendar date */
24290000Sglebiusstruct isodate {
25290000Sglebius	uint16_t year;		/* year (A.D.) */
26290000Sglebius	uint8_t	 week;		/* 1..53, week in year */
27290000Sglebius	uint8_t	 weekday;	/* 1..7, 1=Monday */
28290000Sglebius	uint8_t	 hour;		/* hour of day, midnight = 0 */
29290000Sglebius	uint8_t	 minute;	/* minute of hour */
30290000Sglebius	uint8_t	 second;	/* second of minute */
31290000Sglebius};
32290000Sglebius
33290000Sglebius/* general split representation */
34290000Sglebiustypedef struct {
35290000Sglebius	int32_t hi;
36290000Sglebius	int32_t lo;
37290000Sglebius} ntpcal_split;
38290000Sglebius
39290000Sglebiustypedef time_t (*systime_func_ptr)(time_t *);
40290000Sglebius
4154359Sroberto/*
42290000Sglebius * set the function for getting the system time. This is mostly used for
43290000Sglebius * unit testing to provide a fixed / shifted time stamp. Setting the
44290000Sglebius * value to NULL restores the original function, that is, 'time()',
45290000Sglebius * which is also the automatic default.
4654359Sroberto */
47290000Sglebiusextern systime_func_ptr ntpcal_set_timefunc(systime_func_ptr);
48290000Sglebius
49290000Sglebius/*
50290000Sglebius * days-of-week
51290000Sglebius */
52290000Sglebius#define CAL_SUNDAY	0
53290000Sglebius#define CAL_MONDAY	1
54290000Sglebius#define CAL_TUESDAY	2
55290000Sglebius#define CAL_WEDNESDAY	3
56290000Sglebius#define CAL_THURSDAY	4
57290000Sglebius#define CAL_FRIDAY	5
58290000Sglebius#define CAL_SATURDAY	6
59290000Sglebius#define CAL_SUNDAY7	7	/* also sunday */
60290000Sglebius
61290000Sglebius/*
62290000Sglebius * Days in each month.	30 days hath September...
63290000Sglebius */
6454359Sroberto#define	JAN	31
6554359Sroberto#define	FEB	28
6654359Sroberto#define	FEBLEAP	29
6754359Sroberto#define	MAR	31
6854359Sroberto#define	APR	30
6954359Sroberto#define	MAY	31
7054359Sroberto#define	JUN	30
7154359Sroberto#define	JUL	31
7254359Sroberto#define	AUG	31
7354359Sroberto#define	SEP	30
7454359Sroberto#define	OCT	31
7554359Sroberto#define	NOV	30
7654359Sroberto#define	DEC	31
7754359Sroberto
7854359Sroberto/*
79290000Sglebius * We deal in a 4 year cycle starting at March 1, 1900.	 We assume
8054359Sroberto * we will only want to deal with dates since then, and not to exceed
8154359Sroberto * the rollover day in 2036.
8254359Sroberto */
8354359Sroberto#define	SECSPERMIN	(60)			/* seconds per minute */
8454359Sroberto#define	MINSPERHR	(60)			/* minutes per hour */
8554359Sroberto#define	HRSPERDAY	(24)			/* hours per day */
86290000Sglebius#define	DAYSPERWEEK	(7)			/* days per week */
8754359Sroberto#define	DAYSPERYEAR	(365)			/* days per year */
8854359Sroberto
89290000Sglebius#define	SECSPERHR	(SECSPERMIN * MINSPERHR)
90290000Sglebius#define	SECSPERDAY	(SECSPERHR * HRSPERDAY)
91290000Sglebius#define	SECSPERWEEK	(DAYSPERWEEK * SECSPERDAY)
92290000Sglebius#define	SECSPERYEAR	(365 * SECSPERDAY)	/* regular year */
9354359Sroberto#define	SECSPERLEAPYEAR	(366 * SECSPERDAY)	/* leap year */
94290000Sglebius#define	SECSPERAVGYEAR	31556952		/* mean year length over 400yrs */
9554359Sroberto
9654359Sroberto/*
97290000Sglebius * Gross hacks.	 I have illicit knowlege that there won't be overflows
9854359Sroberto * here, the compiler often can't tell this.
9954359Sroberto */
100290000Sglebius#define	TIMES60(val)	((((val)<<4) - (val))<<2)	/* *(16 - 1) * 4 */
10154359Sroberto#define	TIMES24(val)	(((val)<<4) + ((val)<<3))	/* *16 + *8 */
102290000Sglebius#define	TIMES7(val)	(((val)<<3) - (val))		/* *8  - *1 */
10354359Sroberto#define	TIMESDPERC(val)	(((val)<<10) + ((val)<<8) \
10454359Sroberto			+ ((val)<<7) + ((val)<<5) \
10554359Sroberto			+ ((val)<<4) + ((val)<<2) + (val))	/* *big* hack */
10654359Sroberto
107290000Sglebius
108290000Sglebiusextern	const char * const months[12];
109290000Sglebiusextern	const char * const daynames[7];
110290000Sglebius
111290000Sglebiusextern	void	 caljulian	(uint32_t, struct calendar *);
112290000Sglebiusextern	uint32_t caltontp	(const struct calendar *);
113290000Sglebius
11454359Sroberto/*
115290000Sglebius * Convert between 'time_t' and 'vint64'
11654359Sroberto */
117290000Sglebiusextern vint64 time_to_vint64(const time_t *);
118290000Sglebiusextern time_t vint64_to_time(const vint64 *);
11954359Sroberto
12054359Sroberto/*
121290000Sglebius * Get the build date & time. ATTENTION: The time zone is not specified!
122290000Sglebius * This depends entirely on the C compilers' capabilities to properly
123290000Sglebius * expand the '__TIME__' and '__DATE__' macros, as required by the C
124290000Sglebius * standard.
12554359Sroberto */
126290000Sglebiusextern int
127290000Sglebiusntpcal_get_build_date(struct calendar * /* jd */);
12854359Sroberto
129290000Sglebius/*
130290000Sglebius * Convert a timestamp in NTP scale to a time_t value in the UN*X
131290000Sglebius * scale with proper epoch unfolding around a given pivot or the
132290000Sglebius * current system time.
133290000Sglebius */
134290000Sglebiusextern vint64
135290000Sglebiusntpcal_ntp_to_time(uint32_t /* ntp */, const time_t * /* pivot */);
13654359Sroberto
137290000Sglebius/*
138290000Sglebius * Convert a timestamp in NTP scale to a 64bit seconds value in the NTP
139290000Sglebius * scale with proper epoch unfolding around a given pivot or the current
140290000Sglebius * system time.
141290000Sglebius * Note: The pivot must be given in UN*X time scale!
142290000Sglebius */
143290000Sglebiusextern vint64
144290000Sglebiusntpcal_ntp_to_ntp(uint32_t /* ntp */, const time_t * /* pivot */);
14554359Sroberto
14654359Sroberto/*
147290000Sglebius * Split a time stamp in seconds into elapsed days and elapsed seconds
148290000Sglebius * since midnight.
149290000Sglebius */
150290000Sglebiusextern ntpcal_split
151290000Sglebiusntpcal_daysplit(const vint64 *);
152290000Sglebius
153290000Sglebius/*
154290000Sglebius * Merge a number of days and a number of seconds into seconds,
155290000Sglebius * expressed in 64 bits to avoid overflow.
156290000Sglebius */
157290000Sglebiusextern vint64
158290000Sglebiusntpcal_dayjoin(int32_t /* days */, int32_t /* seconds */);
159290000Sglebius
160290000Sglebius/* Get the number of leap years since epoch for the number of elapsed
161290000Sglebius * full years
162290000Sglebius */
163290000Sglebiusextern int32_t
164290000Sglebiusntpcal_leapyears_in_years(int32_t /* years */);
165290000Sglebius
166290000Sglebius/*
167290000Sglebius * Convert elapsed years in Era into elapsed days in Era.
168290000Sglebius */
169290000Sglebiusextern int32_t
170290000Sglebiusntpcal_days_in_years(int32_t /* years */);
171290000Sglebius
172290000Sglebius/*
173290000Sglebius * Convert a number of elapsed month in a year into elapsed days
174290000Sglebius * in year.
175290000Sglebius *
176290000Sglebius * The month will be normalized, and 'res.hi' will contain the
177290000Sglebius * excessive years that must be considered when converting the years,
178290000Sglebius * while 'res.lo' will contain the days since start of the
179290000Sglebius * year. (Expect the resulting days to be negative, with a positive
180290000Sglebius * excess! But then, we need no leap year flag, either...)
181290000Sglebius */
182290000Sglebiusextern ntpcal_split
183290000Sglebiusntpcal_days_in_months(int32_t /* months */);
184290000Sglebius
185290000Sglebius/*
186290000Sglebius * Convert ELAPSED years/months/days of gregorian calendar to elapsed
187290000Sglebius * days in Gregorian epoch. No range checks done here!
188290000Sglebius */
189290000Sglebiusextern int32_t
190290000Sglebiusntpcal_edate_to_eradays(int32_t /* years */, int32_t /* months */, int32_t /* mdays */);
191290000Sglebius
192290000Sglebius/*
193290000Sglebius * Convert a time spec to seconds. No range checks done here!
194290000Sglebius */
195290000Sglebiusextern int32_t
196290000Sglebiusntpcal_etime_to_seconds(int32_t /* hours */, int32_t /* minutes */, int32_t /* seconds */);
197290000Sglebius
198290000Sglebius/*
199290000Sglebius * Convert ELAPSED years/months/days of gregorian calendar to elapsed
200290000Sglebius * days in year.
201290000Sglebius *
202290000Sglebius * Note: This will give the true difference to the start of the given year,
203290000Sglebius * even if months & days are off-scale.
204290000Sglebius */
205290000Sglebiusextern int32_t
206290000Sglebiusntpcal_edate_to_yeardays(int32_t /* years */, int32_t /* months */, int32_t /* mdays */);
207290000Sglebius
208290000Sglebius/*
209290000Sglebius * Convert the date part of a 'struct tm' (that is, year, month,
210290000Sglebius * day-of-month) into the RataDie of that day.
211290000Sglebius */
212290000Sglebiusextern int32_t
213290000Sglebiusntpcal_tm_to_rd(const struct tm * /* utm */);
214290000Sglebius
215290000Sglebius/*
216290000Sglebius * Convert the date part of a 'struct calendar' (that is, year, month,
217290000Sglebius * day-of-month) into the RataDie of that day.
218290000Sglebius */
219290000Sglebiusextern int32_t
220290000Sglebiusntpcal_date_to_rd(const struct calendar * /* jt */);
221290000Sglebius
222290000Sglebius/*
223290000Sglebius * Given the number of elapsed days in the calendar era, split this
224290000Sglebius * number into the number of elapsed years in 'res.quot' and the
225290000Sglebius * number of elapsed days of that year in 'res.rem'.
226290000Sglebius *
227290000Sglebius * if 'isleapyear' is not NULL, it will receive an integer that is 0
228290000Sglebius * for regular years and a non-zero value for leap years.
229290000Sglebius *
230290000Sglebius * The input is limited to [-2^30, 2^30-1]. If the days exceed this
231290000Sglebius * range, errno is set to EDOM and the result is saturated.
232290000Sglebius */
233290000Sglebiusextern ntpcal_split
234290000Sglebiusntpcal_split_eradays(int32_t /* days */, int/*BOOL*/ * /* isleapyear */);
235290000Sglebius
236290000Sglebius/*
237290000Sglebius * Given a number of elapsed days in a year and a leap year indicator,
238290000Sglebius * split the number of elapsed days into the number of elapsed months
239290000Sglebius * in 'res.quot' and the number of elapsed days of that month in
240290000Sglebius * 'res.rem'.
241290000Sglebius */
242290000Sglebiusextern ntpcal_split
243290000Sglebiusntpcal_split_yeardays(int32_t /* eyd */, int/*BOOL*/ /* isleapyear */);
244290000Sglebius
245290000Sglebius/*
246290000Sglebius * Convert a RataDie number into the date part of a 'struct
247290000Sglebius * calendar'. Return 0 if the year is regular year, !0 if the year is
248290000Sglebius * a leap year.
249290000Sglebius */
250290000Sglebiusextern int/*BOOL*/
251290000Sglebiusntpcal_rd_to_date(struct calendar * /* jt */, int32_t /* rd */);
252290000Sglebius
253290000Sglebius/*
254290000Sglebius * Convert a RataDie number into the date part of a 'struct
255290000Sglebius * tm'. Return 0 if the year is regular year, !0 if the year is a leap
256290000Sglebius * year.
257290000Sglebius */
258290000Sglebiusextern int/*BOOL*/
259290000Sglebiusntpcal_rd_to_tm(struct tm * /* utm */, int32_t /* rd */);
260290000Sglebius
261290000Sglebius/*
262290000Sglebius * Take a value of seconds since midnight and split it into hhmmss in
263290000Sglebius * a 'struct calendar'. Return excessive days.
264290000Sglebius */
265290000Sglebiusextern int32_t
266290000Sglebiusntpcal_daysec_to_date(struct calendar * /* jt */, int32_t /* secs */);
267290000Sglebius
268290000Sglebius/*
269290000Sglebius * Take the time part of a 'struct calendar' and return the seconds
270290000Sglebius * since midnight.
271290000Sglebius */
272290000Sglebiusextern int32_t
273290000Sglebiusntpcal_date_to_daysec(const struct calendar *);
274290000Sglebius
275290000Sglebius/*
276290000Sglebius * Take a value of seconds since midnight and split it into hhmmss in
277290000Sglebius * a 'struct tm'. Return excessive days.
278290000Sglebius */
279290000Sglebiusextern int32_t
280290000Sglebiusntpcal_daysec_to_tm(struct tm * /* utm */, int32_t /* secs */);
281290000Sglebius
282290000Sglebiusextern int32_t
283290000Sglebiusntpcal_tm_to_daysec(const struct tm * /* utm */);
284290000Sglebius
285290000Sglebius/*
286290000Sglebius * convert a year number to rata die of year start
287290000Sglebius */
288290000Sglebiusextern int32_t
289290000Sglebiusntpcal_year_to_ystart(int32_t /* year */);
290290000Sglebius
291290000Sglebius/*
292290000Sglebius * For a given RataDie, get the RataDie of the associated year start,
293290000Sglebius * that is, the RataDie of the last January,1st on or before that day.
294290000Sglebius */
295290000Sglebiusextern int32_t
296290000Sglebiusntpcal_rd_to_ystart(int32_t /* rd */);
297290000Sglebius
298290000Sglebius/*
299290000Sglebius * convert a RataDie to the RataDie of start of the calendar month.
300290000Sglebius */
301290000Sglebiusextern int32_t
302290000Sglebiusntpcal_rd_to_mstart(int32_t /* year */);
303290000Sglebius
304290000Sglebius
305290000Sglebiusextern int
306290000Sglebiusntpcal_daysplit_to_date(struct calendar * /* jt */,
307290000Sglebius			const ntpcal_split * /* ds */, int32_t /* dof */);
308290000Sglebius
309290000Sglebiusextern int
310290000Sglebiusntpcal_daysplit_to_tm(struct tm * /* utm */, const ntpcal_split * /* ds */,
311290000Sglebius		      int32_t /* dof */);
312290000Sglebius
313290000Sglebiusextern int
314290000Sglebiusntpcal_time_to_date(struct calendar * /* jd */, const vint64 * /* ts */);
315290000Sglebius
316290000Sglebiusextern int32_t
317290000Sglebiusntpcal_periodic_extend(int32_t /* pivot */, int32_t /* value */,
318290000Sglebius		       int32_t /* cycle */);
319290000Sglebius
320290000Sglebiusextern int
321290000Sglebiusntpcal_ntp64_to_date(struct calendar * /* jd */, const vint64 * /* ntp */);
322290000Sglebius
323290000Sglebiusextern int
324290000Sglebiusntpcal_ntp_to_date(struct calendar * /* jd */,	uint32_t /* ntp */,
325290000Sglebius		   const time_t * /* pivot */);
326290000Sglebius
327290000Sglebiusextern vint64
328290000Sglebiusntpcal_date_to_ntp64(const struct calendar * /* jd */);
329290000Sglebius
330290000Sglebiusextern uint32_t
331290000Sglebiusntpcal_date_to_ntp(const struct calendar * /* jd */);
332290000Sglebius
333290000Sglebiusextern time_t
334290000Sglebiusntpcal_date_to_time(const struct calendar * /* jd */);
335290000Sglebius
336290000Sglebius/*
337290000Sglebius * ISO week-calendar conversions
338290000Sglebius */
339290000Sglebiusextern int32_t
340290000Sglebiusisocal_weeks_in_years(int32_t  /* years */);
341290000Sglebius
342290000Sglebius/*
343290000Sglebius * The input is limited to [-2^30, 2^30-1]. If the weeks exceed this
344290000Sglebius * range, errno is set to EDOM and the result is saturated.
345290000Sglebius */
346290000Sglebiusextern ntpcal_split
347290000Sglebiusisocal_split_eraweeks(int32_t /* weeks */);
348290000Sglebius
349290000Sglebiusextern int
350290000Sglebiusisocal_ntp64_to_date(struct isodate * /* id */, const vint64 * /* ntp */);
351290000Sglebius
352290000Sglebiusextern int
353290000Sglebiusisocal_ntp_to_date(struct isodate * /* id */, uint32_t /* ntp */,
354290000Sglebius		   const time_t * /* pivot */);
355290000Sglebius
356290000Sglebiusextern vint64
357290000Sglebiusisocal_date_to_ntp64(const struct isodate * /* id */);
358290000Sglebius
359290000Sglebiusextern uint32_t
360290000Sglebiusisocal_date_to_ntp(const struct isodate * /* id */);
361290000Sglebius
362290000Sglebius
363290000Sglebius/*
364290000Sglebius * day-of-week calculations
365290000Sglebius *
366290000Sglebius * Given a RataDie and a day-of-week, calculate a RDN that is reater-than,
367290000Sglebius * greater-or equal, closest, less-or-equal or less-than the given RDN
368290000Sglebius * and denotes the given day-of-week
369290000Sglebius */
370290000Sglebiusextern int32_t
371290000Sglebiusntpcal_weekday_gt(int32_t  /* rdn */, int32_t /* dow */);
372290000Sglebius
373290000Sglebiusextern int32_t
374290000Sglebiusntpcal_weekday_ge(int32_t /* rdn */, int32_t /* dow */);
375290000Sglebius
376290000Sglebiusextern int32_t
377290000Sglebiusntpcal_weekday_close(int32_t /* rdn */, int32_t  /* dow */);
378290000Sglebius
379290000Sglebiusextern int32_t
380290000Sglebiusntpcal_weekday_le(int32_t /* rdn */, int32_t /* dow */);
381290000Sglebius
382290000Sglebiusextern int32_t
383290000Sglebiusntpcal_weekday_lt(int32_t /* rdn */, int32_t /* dow */);
384290000Sglebius
385290000Sglebius/*
38654359Sroberto * Additional support stuff for Ed Rheingold's calendrical calculations
38754359Sroberto */
38854359Sroberto
38954359Sroberto/*
39054359Sroberto * Start day of NTP time as days past the imaginary date 12/1/1 BC.
391290000Sglebius * (This is the beginning of the Christian Era, or BCE.)
39254359Sroberto */
393290000Sglebius#define	DAY_NTP_STARTS 693596
394290000Sglebius
39554359Sroberto/*
396290000Sglebius * Start day of the UNIX epoch. This is the Rata Die of 1970-01-01.
39754359Sroberto */
398290000Sglebius#define DAY_UNIX_STARTS 719163
39954359Sroberto
40054359Sroberto/*
401290000Sglebius * Difference between UN*X and NTP epoch (25567).
40254359Sroberto */
403290000Sglebius#define NTP_TO_UNIX_DAYS (DAY_UNIX_STARTS - DAY_NTP_STARTS)
40454359Sroberto
40554359Sroberto/*
406290000Sglebius * Days in a normal 4 year leap year calendar cycle (1461).
40754359Sroberto */
408290000Sglebius#define	GREGORIAN_NORMAL_LEAP_CYCLE_DAYS	(3 * 365 + 366)
40954359Sroberto
410290000Sglebius/*
411290000Sglebius * Days in a normal 100 year leap year calendar (36524).  We lose a
412290000Sglebius * leap day in years evenly divisible by 100 but not by 400.
413290000Sglebius */
414290000Sglebius#define	GREGORIAN_NORMAL_CENTURY_DAYS	\
415290000Sglebius			(25 * GREGORIAN_NORMAL_LEAP_CYCLE_DAYS - 1)
41654359Sroberto
417290000Sglebius/*
418290000Sglebius * The Gregorian calendar is based on a 400 year cycle. This is the
419290000Sglebius * number of days in each cycle (146097).  We gain a leap day in years
420290000Sglebius * divisible by 400 relative to the "normal" century.
421290000Sglebius */
422290000Sglebius#define	GREGORIAN_CYCLE_DAYS (4 * GREGORIAN_NORMAL_CENTURY_DAYS + 1)
423290000Sglebius
424290000Sglebius/*
425290000Sglebius * Number of weeks in 400 years (20871).
426290000Sglebius */
427290000Sglebius#define	GREGORIAN_CYCLE_WEEKS (GREGORIAN_CYCLE_DAYS / 7)
428290000Sglebius
429290000Sglebius#define	is_leapyear(y)	(!((y) % 4) && !(!((y) % 100) && (y) % 400))
430290000Sglebius
43154359Sroberto#endif
432