OSSP CVS Repository

ossp - ossp-pkg/tai/tai_cal.c
Not logged in
[Honeypot]  [Browse]  [Directory]  [Home]  [Login
[Reports]  [Search]  [Ticket]  [Timeline
  [Raw

ossp-pkg/tai/tai_cal.c
/*
**  OSSP tai - Time Handling
**  Copyright (c) 2002-2003 Ralf S. Engelschall <rse@engelschall.com>
**  Copyright (c) 2002-2003 The OSSP Project <http://www.ossp.org/>
**  Copyright (c) 2002-2003 Cable & Wireless Deutschland <http://www.cw.com/de/>
**
**  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;
}


CVSTrac 2.0.1