/* 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 . */ #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; }