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