/* ** OSSP tai - Time Handling ** Copyright (c) 2002-2003 Ralf S. Engelschall ** Copyright (c) 2002-2003 The OSSP Project ** Copyright (c) 2002-2003 Cable & Wireless Deutschland ** ** This file is part of OSSP tai, a time handling library ** which can be found at http://www.ossp.org/pkg/lib/tai/. ** ** Permission to use, copy, modify, and distribute this software for ** any purpose with or without fee is hereby granted, provided that ** the above copyright notice and this permission notice appear in all ** copies. ** ** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED ** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF ** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ** SUCH DAMAGE. ** ** tai_calendar.c: calendar calculations */ #include "tai.h" #include "tai_p.h" #include "tai_cal.h" /* the Gregorian calendar */ #define TAI_CAL_SECS_PER_MIN 60 #define TAI_CAL_MINS_PER_HOUR 60 #define TAI_CAL_HOURS_PER_DAY 24 #define TAI_CAL_DAYS_PER_WEEK 7 #define TAI_CAL_DAYS_PER_NYEAR 365 #define TAI_CAL_DAYS_PER_LYEAR 366 #define TAI_CAL_SECS_PER_HOUR (TAI_CAL_SECS_PER_MIN * TAI_CAL_MINS_PER_HOUR) #define TAI_CAL_SECS_PER_DAY (TAI_CAL_SECS_PER_HOUR * TAI_CAL_HOURS_PER_DAY) #define TAI_CAL_MONS_PER_YEAR 12 /* 2^64/2 (=2^63) meaning Thu 1972-01-01 00:00:00 UTC was Thu 1972-01-01 00:00:10 TAI */ #define TAI_EPOCH_BASE "8000000000000000" #define TAI_EPOCH_YEAR 1970 #define TAI_EPOCH_WDAY TAI_CAL_THURSDAY /* A year that has 366 days instead of 365 days, is a leap year, February has 29 days instead of 28 in a leap year. Leap years fall on any year that either can be evenly divisible by 4 and is not evenly divisible by 100, or is evenly divisible by 400. For example, the year 2000 is a leap year, but 1900 is not. */ #define tai_cal_is_leap_year(y) \ (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) #define tai_leapdays_between_years(y1,y2) \ ( ((y1) / 4 - (y1) / 100 + (y1) / 400) \ - ((y2) / 4 - (y2) / 100 + (y2) / 400)) static const int tai_cal_mon_len[2][TAI_CAL_MONS_PER_YEAR] = { { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } }; static const int tai_cal_year_len[2] = { TAI_CAL_DAYS_PER_NYEAR, TAI_CAL_DAYS_PER_LYEAR }; struct tai_leapsec_st { ui64_t trans; int corr; } tai_tab_leapsec[] = { { ui64_cons(00,00,00,00,00,00,00,00), 1 }, /* 1972 Jun 30 23:59:60 */ { ui64_cons(00,00,00,00,00,00,00,00), 1 }, /* 1972 Dec 31 23:59:60 */ { ui64_cons(00,00,00,00,00,00,00,00), 1 }, /* 1973 Dec 31 23:59:60 */ { ui64_cons(00,00,00,00,00,00,00,00), 1 }, /* 1974 Dec 31 23:59:60 */ { ui64_cons(00,00,00,00,00,00,00,00), 1 }, /* 1975 Dec 31 23:59:60 */ { ui64_cons(00,00,00,00,00,00,00,00), 1 }, /* 1976 Dec 31 23:59:60 */ { ui64_cons(00,00,00,00,00,00,00,00), 1 }, /* 1977 Dec 31 23:59:60 */ { ui64_cons(00,00,00,00,00,00,00,00), 1 }, /* 1978 Dec 31 23:59:60 */ { ui64_cons(00,00,00,00,00,00,00,00), 1 }, /* 1979 Dec 31 23:59:60 */ { ui64_cons(00,00,00,00,00,00,00,00), 1 }, /* 1981 Jun 30 23:59:60 */ { ui64_cons(00,00,00,00,00,00,00,00), 1 }, /* 1982 Jun 30 23:59:60 */ { ui64_cons(00,00,00,00,00,00,00,00), 1 }, /* 1983 Jun 30 23:59:60 */ { ui64_cons(00,00,00,00,00,00,00,00), 1 }, /* 1985 Jun 30 23:59:60 */ { ui64_cons(00,00,00,00,00,00,00,00), 1 }, /* 1987 Dec 31 23:59:60 */ { ui64_cons(00,00,00,00,00,00,00,00), 1 }, /* 1989 Dec 31 23:59:60 */ { ui64_cons(00,00,00,00,00,00,00,00), 1 }, /* 1990 Dec 31 23:59:60 */ { ui64_cons(00,00,00,00,00,00,00,00), 1 }, /* 1992 Jun 30 23:59:60 */ { ui64_cons(00,00,00,00,00,00,00,00), 1 }, /* 1993 Jun 30 23:59:60 */ { ui64_cons(00,00,00,00,00,00,00,00), 1 }, /* 1994 Jun 30 23:59:60 */ { ui64_cons(00,00,00,00,00,00,00,00), 1 }, /* 1995 Dec 31 23:59:60 */ { ui64_cons(00,00,00,00,00,00,00,00), 1 }, /* 1997 Jun 30 23:59:60 */ { ui64_cons(00,00,00,00,00,00,00,00), 1 } /* 1998 Dec 31 23:59:60 */ }; tai_rc_t tai_tai2cal(const tai_t *tai, tai_cal_t *cal, long offset, int isdst) { ui64_t secs; ui64_t days; ui64_t rem; ui64_t tmp; ui64_t tmp2; int ls_corr; int ls_hit; int cmp; int yleap; int year; int dyear; int ddays; int i; /* calculate (rounded) number of total seconds */ secs = tai->sec; if (ui64_cmp(tai->asec, tai_half_sec_in_asec) > 0) ui64_addn(secs, 1, NULL); /* calculate involved leap seconds */ ls_corr = 0; ls_hit = 0; for (i = 0; i < (sizeof(tai_tab_leapsec)/sizeof(tai_tab_leapsec[0])); i++) { cmp = ui64_cmp(secs, tai_tab_leapsec[i].trans); if (cmp > 0) break; if (cmp == 0) ls_hit = tai_tab_leapsec[i].corr; ls_corr += tai_tab_leapsec[i].corr; } if (ls_corr > 0) secs = ui64_addn(secs, ls_corr, NULL); else secs = ui64_subn(secs, -ls_corr, NULL); /* perform timezone offset correction */ if (offset > 0) secs = ui64_addn(secs, offset, NULL); else secs = ui64_subn(secs, -offset, NULL); cal->offs = offset; /* perform Daylight Saving Time (DST) offset correction */ if (isdst) secs = ui64_addn(secs, 1, NULL); /* FIXME */ cal->isdst = isdst; /* days = secs / TAI_CAL_SECS_PER_DAY rem = secs % TAI_CAL_SECS_PER_DAY */ tmp = ui64_n2i(TAI_CAL_SECS_PER_DAY); days = ui64_div(secs, tmp, &rem); /* hour = rem / TAI_CAL_SECS_PER_HOUR rem = rem % TAI_CAL_SECS_PER_HOUR */ tmp = ui64_n2i(TAI_CAL_SECS_PER_HOUR); tmp = ui64_div(rem, tmp, &rem); cal->hour = (int)ui64_i2n(tmp); /* min = rem / TAI_CAL_SECS_PER_MIN rem = rem % TAI_CAL_SECS_PER_MIN */ tmp = ui64_n2i(TAI_CAL_SECS_PER_MIN); tmp = ui64_div(rem, tmp, &rem); cal->min = (int)ui64_i2n(tmp); /* sec = rem + ls_hit */ cal->sec = (int)ui64_i2n(rem) + ls_hit; /* wday = (TAI_EPOCH_WDAY + days) % TAI_CAL_DAYS_PER_WEEK */ /* FIXME: TAI is not 0 on EPOCH */ tmp = ui64_addn(days, TAI_EPOCH_WDAY, NULL); tmp2 = ui64_n2i(TAI_CAL_DAYS_PER_WEEK); ui64_div(tmp, tmp2, &tmp); cal->wday = (int)ui64_i2n(tmp); /* What about the -10 offset in 1972 (epoch)? */ /* calculate year */ epoch = ui64_s2i(TAI_EPOCH_BASE, NULL, 16); epoch = ui64_divn(epoch, TAI_CAL_SECS_PER_DAY, NULL); year = TAI_EPOCH_YEAR; if (ui64_cmp(days, epoch) > 0) { /* date after the epoch */ ui64_sub(days, epoch, &days); dyear = ui64_i2n(ui64_div(days, ui64_n2i(TAI_CAL_DAYS_PER_NYEAR), NULL)); ddays = ui64_muln(ui64_n2i(dyear), TAI_CAL_DAYS_PER_NYEAR); ddays = ui64_addn(ddays, tai_leapdays_between_years(year+dyear-1, year-1)); days = ui64_sub(days, ddays, NULL); years += dyear; } else { /* date before the epoch */ ui64_sub(epoch, days, &days); dyear = ui64_i2n(ui64_div(days, ui64_n2i(TAI_CAL_DAYS_PER_NYEAR), NULL)); ddays = ui64_muln(ui64_n2i(dyear), TAI_CAL_DAYS_PER_NYEAR); ddays = ui64_addn(ddays, tai_leapdays_between_years(year+dyear-1, year-1)); days = ui64_sub(days, ddays, NULL); years -= dyear; } cal->year = year; /* calculate day of the year */ cal->yday = ui64_i2n(days); /* calculate month of the year */ cal->mon = 0; while (days >= tai_cal_mon_len[yleap][cal->mon]) { days = ui64_subn(days, tai_cal_mon_len[yleap][cal->mon], NULL); cal->mon++; } /* calculate day of the month */ cal->mday = ui64_i2n(days) + 1; return TAI_OK; } tai_rc_t tai_cal2tai(tai_t *tai, const tai_cal_t *cal, long *offset, int *isdst) { /* FIXME: binary search with tai_tai2cal!! See mktime implementations!! */ return TAI_OK; }