View of xos/usr/lib/libc/make_time.c


XOS | Parent Directory | View | Download

/* Copyright (C) 2008  Emmanuel Varoquaux
 
   This file is part of XOS.
 
   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
 
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
 
   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
#include "time.h"
 
static const int mdays_common[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
static const int mdays_leap[12] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
 
static const int month_start_common[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
static const int month_start_leap[12] = {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335};
 
#define is_leap(year) ((((year) % 4 == 0 && (year) % 100 != 0)) || ((year) % 400 == 0))
#define ydays(year)   (is_leap(year) ? 366 : 365)
 
#define mdays(year, mon)  (is_leap(year) ? mdays_leap[mon] : mdays_common[mon])
#define month_start(year) (is_leap(year) ? month_start_leap : month_start_common)
 
#define leap_years(year, n) (((n) + 1) * ((year) - 1) / (n) - (year))
#define year_wday(year)     ((1 + (year) + (leap_years((year), 4) - leap_years((year), 100) + leap_years((year), 400))) % 7)
#define wday(year, yday)    ((year_wday(year) + (yday)) % 7)
 
#define dsecs 86400
 
/* Retourne le nombre de secondes depuis le debut de l'annee. */
static long ysec(const struct dst_date_struct *dst_date, int year)
{
  int mon;
  int mon_yday;
  long msec;
  int wday;
  int mday;
  int mweek;
  int i, mdays;
 
  mon = dst_date->mon;               /* mois */
  mon_yday = month_start(year)[mon]; /* jour de l'annee du debut du mois */
 
  msec = 0;                    /* nombre de secondes depuis le debut du mois */
  wday = wday(year, mon_yday); /* jour de la semaine */
  mday = 0;                    /* jour du mois */
  mweek = 0;                   /* semaine du mois */
  for (i = 0, mdays = mdays(year, mon); i < mdays; i++) { /* parcours du mois */
    if (wday == dst_date->wday) {
      mweek++;
      mday = i;
    }
    if (mweek == dst_date->mweek)
      break;
    msec += dsecs;
    wday = (wday + 1) % 7;
  }
  return (mon_yday + mday) * dsecs + dst_date->time; /* mois + jour + heure */
}
 
/* Dit si une date UNIX est dans la periode d'heure d'ete d'un fuseau horaire. */
static int is_dst(const time_t *timep, const struct tz_struct *tz)
{
  time_t t;
  int year;
  time_t dst_start, dst_end;
  long ysecs;
 
  if (!tz->dst)
    return 0;
 
  t = 0;
  year = 1970;
  if (*timep < 0)
    do
      year--, t -= ydays(year) * dsecs;
    while (*timep < t);
  else
    while (*timep >= t + (ysecs = ydays(year) * dsecs))
      t += ysecs, year++;
  dst_start = t + tz->offset + ysec(&tz->dst->start, year);
  dst_end = t + tz->dst->offset + ysec(&tz->dst->end, year);
  return dst_start < dst_end
    ? *timep >= dst_start && *timep < dst_end
    : *timep < dst_start || *timep >= dst_end;
}
 
struct tm *__make_tm(const time_t *timep, const struct tz_struct *tz, struct tm *result)
{
  int sec;   /* 0-59 */
  int min;   /* 0-59 */
  int hour;  /* 0-23 */
  int mday;  /* 0-30 */
  int mon;   /* 0-11 */
  int year;
  int wday;  /* 0-6 */
  int yday;  /* 0-365 */
  int isdst;
  long day, dsec;
  const int *month_start;
  register int ydays;
 
  isdst = is_dst(timep, tz);
  day = *timep / dsecs; /* nombre de jours depuis le 1er janvier 1970 */
  dsec = *timep % dsecs; /* nombre de secondes depuis le debut du jour */
  dsec -= isdst ? tz->dst->offset : tz->offset; /* decalage horaire */
  /* ajustement */
  if (dsec < 0)
    do
      day--, dsec += dsecs;
    while (dsec < 0);
  else while (dsec >= dsecs)
    dsec -= dsecs, day++;
  /* determination de l'annee */
  year = 1970;
  if (day < 0)
    do
      year--, day += ydays(year);
    while (day < 0);
  else
    while (day >= (ydays = ydays(year)))
      day -= ydays, year++;
  yday = day;
  wday = wday(year, yday);
  /* determination du mois */
  mon = 0;
  month_start = month_start(year);
  while (mon < 11 && yday >= month_start[mon + 1])
    mon++;
  /* quantieme, heure, minutes, secondes */
  mday = yday - month_start[mon];
  hour = dsec / 3600;
  min = dsec / 60 % 60;
  sec = dsec % 60;
 
  result->tm_sec = sec;
  result->tm_min = min;
  result->tm_hour = hour;
  result->tm_mday = mday + 1;
  result->tm_mon = mon;
  result->tm_year = year - 1900;
  result->tm_wday = wday;
  result->tm_yday = yday;
  result->tm_isdst = isdst;
 
  return result;
}
 
#define normalize(var1, max, var2) ({ \
  register int _tmp; \
  if (var1 < 0) { \
    _tmp = (max - var1) / max; \
    var2 -= _tmp; \
    var1 += _tmp * max; \
  } \
  else if (var1 >= max) { \
    _tmp = var1 / max; \
    var2 += _tmp; \
    var1 -= _tmp * max; \
  } \
})
 
time_t __make_time(struct tm *tm, const struct tz_struct *tz)
{
  int sec;   /* 0-59 */
  int min;   /* 0-59 */
  int hour;  /* 0-23 */
  int mday;  /* 0-30 */
  int mon;   /* 0-11 */
  int year;
  int wday;  /* 0-6 */
  int yday;  /* 0-365 */
  int isdst;
  int mdays;
  long t;
  int y;
  time_t tloc;
 
  sec = tm->tm_sec;
  min = tm->tm_min;
  hour = tm->tm_hour;
  mday = tm->tm_mday - 1;
  mon = tm->tm_mon;
  year = tm->tm_year + 1900;
  isdst = tm->tm_isdst;
 
  /* ajustements */
  normalize(sec, 60, min);
  normalize(min, 60, hour);
  normalize(hour, 24, mday);
  normalize(mon, 12, year);
  if (mday < 0)
    do {
      if (mon > 0)
        mon--;
      else
        year--, mon = 11;
      mday += mdays(year, mon);
    }
    while (mday < 0);
  else
    while (mday >= (mdays = mdays(year, mon))) {
      mday -= mdays;
      if (mon < 11)
        mon++;
      else
        year++, mon = 0;
    }
  yday = month_start(year)[mon] + mday;
  wday = wday(year, yday);
 
  t = hour * 3600 + min * 60 + sec; /* nombre de secondes depuis le debut du jour local */
  t += yday * dsecs;
  /* t est le nombre de secondes depuis le debut de l'annee locale. */
  if (year < 1970)
    for (y = 1970; y > year; y--, t -= ydays(y) * dsecs);
  else
    for (y = 1970; y < year; t += ydays(y) * dsecs, y++);
  if (isdst < 0) {
    tloc = t + tz->offset;
    isdst = is_dst(&tloc, tz);
  }
  t += isdst && tz->dst ? tz->dst->offset : tz->offset; /* decalage horaire */
 
  tm->tm_sec = sec;
  tm->tm_min = min;
  tm->tm_hour = hour;
  tm->tm_mday = mday + 1;
  tm->tm_mon = mon;
  tm->tm_year = year - 1900;
  tm->tm_wday = wday;
  tm->tm_yday = yday;
  tm->tm_isdst = isdst;
 
  return t;
}