#include "machdefs.h" #include #include #include #include #include #include #include #include "perrors.h" #include "dates.h" //++ // Class TimeZone // Lib Outils++ // include dates.h // // Classe de fuseau horaire. Permet les conversion // GMT <-> temps local, en gérant les changements // d'heure hiver-été. // Deux fuseaux horaires sont prédéfinis, "France" // et "Chili". //-- TimeZone* gTimeZone = NULL; //++ // Titre Constructeurs //-- //++ TimeZone::TimeZone() // // Constructeur par défaut. Il lit la variable // d'environnement ACQ_TZ pour choisir le fuseau // horaire. Si la variable n'est pas définie, nous // sommes en France... //-- { char* p = getenv("ACQ_TZ"); #if defined(__DECCXX) || defined(__KCC__) || defined(__aCC__) if (!p) p = const_cast("France"); #else if (!p) p = "France"; #endif SetZone(p); END_CONSTRUCTOR; } //++ TimeZone::TimeZone(const char* nom) // // Constructeur à partir du nom d'un fuseau horaire. //-- { SetZone(nom); END_CONSTRUCTOR } //++ void TimeZone::SetZone(const char* nom) // // Choisit un fuseau horaire. Contient la définition // du fuseau "France" (GMT+1, DST : dernier dimanche // de mars - dernier dimanche de septembre, c'est-à-dire // pré-gouvernement Juppé), et Chili (GMT-4, DST : // deuxième dimanche d'octobre - deuxième dimanche de mars). //-- { if (!strcmp(nom,"France")) { strcpy(code,"MET"); strcpy(name,nom); gmtOffset = 1; hasDST = 1; dstByWeekDay = 1; dstStartDay = Date::jour_Dimanche; // dernier dimanche de mars dstStartNum = -1; dstStartMonth = Date::mois_Mars; dstStopDay = Date::jour_Dimanche; // dernier dimanche de septembre dstStopNum = -1; dstStopMonth = Date::mois_Septembre; dstOffset = 1; } else if (!strcmp(nom,"Chili") || !strcmp(nom,"Chile")) { // # Chile has 2nd Sunday in October to 2nd Sunday in March DST strcpy(code,"CST"); strcpy(name,"Chili"); gmtOffset = -4; hasDST = 1; dstByWeekDay = 1; dstStartDay = Date::jour_Dimanche; // 2e dimanche d'octobre dstStartNum = 2; dstStartMonth = Date::mois_Octobre; dstStopDay = Date::jour_Dimanche; // 2e dimanche de mars dstStopNum = 2; dstStopMonth = Date::mois_Mars; dstOffset = 1; } else THROW(parmErr); } //++ int TimeZone::IsDst(const Date& date) // // Teste si une date est en heure d'été (DST). //-- { Date dt = date; if (dt.MM < 0 || dt.JJ < 0) THROW(inconsistentErr) if (dt.hh >= 0) dt += gmtOffset/24.0; // date "fictive" en local d'hiver else dt.ss = dt.hh = dt.mm = 0; // minuit local si heure indeterminee // On est en-dehors des mois frontiere ? if (dstStartMonth < dstStopMonth) { if (dt.MM < dstStartMonth) return 0; if (dt.MM > dstStopMonth) return 0; if (dt.MM > dstStartMonth && dt.MM < dstStopMonth) return 1; } else { if (dt.MM < dstStopMonth) return 1; if (dt.MM > dstStartMonth) return 1; if (dt.MM > dstStopMonth && dt.MM < dstStartMonth) return 0; } // Nous sommes dans un mois-frontiere DBASSERT(dt.MM == dstStopMonth || dt.MM == dstStartMonth); if (dt.MM == dstStartMonth) { int jour; if (dstByWeekDay) jour = Date::NthMonthDay(dstStartNum,dstStartDay, dt.MM, dt.AA); else jour = (dstStartNum>0) ? dstStartNum : Date::MonthDays(dt.MM, dt.AA)+1-dstStartNum; if (dt.JJ < jour) return 0; if (dt.JJ > jour) return 1; if (dt.hh > 1) return 1; return 0; } else { int jour; if (dstByWeekDay) jour = Date::NthMonthDay(dstStopNum,dstStopDay, dt.MM, dt.AA); else jour = (dstStopNum>0) ? dstStopNum : Date::MonthDays(dt.MM, dt.AA)+1-dstStopNum; if (dt.JJ < jour) return 1; if (dt.JJ > jour) return 0; if (dt.hh < 2) return 0; return 1; } } //++ int TimeZone::GetOffset(const Date& date) // // Retourne la difference TL-GMT pour une date donnée, // en tenant compte de l'heure d'été éventuelle. //-- { return IsDst(date) ? gmtOffset+dstOffset : gmtOffset; } //++ // Class Date // Lib Outils++ // include dates.h // // Une classe date comme une autre, avec gestion // temps local / GMT / changement d'heure, et un // jour temps sidéral. // // Une partie de la date (heure, jour...) peut être // indéterminée. Les comparaisons sont alors faites // de façon astucieuse... // // La date peut être déterminée de minuit à minuit // ou de midi à midi (nuit d'observation). Logique // complexe et peut-être foireuse pour passer de // l'un à l'autre, mais pas encore d'ennuis pour le // moment... Une date-nuit doit avoir une heure // indéterminée. // // Il faut que Date::gTimeZone soit initialisé pour // éviter des gros pépins, mais PeidaInit() le fait. // // An 2000 : Lorsqu'on gère des dates sous forme de chaine : // * En sortie, 2 caractères pour l'année entre 1950 et 1999 // et 4 caractères sinon // * En entrée, si AA<100 on ajoute 1900. //-- //++ short Date::MonthDays(short mois, short annee) // // Retourne le nombre de jours dans le mois //-- { if (mois<1 || mois>12) THROW(inconsistentErr); switch(mois) { case mois_Janvier: case mois_Mars: case mois_Mai: case mois_Juillet: case mois_Aout: case mois_Octobre: case mois_Decembre: return 31; case mois_Avril: case mois_Juin: case mois_Septembre: case mois_Novembre: return 30; case mois_Fevrier: return (((annee%4 == 0) && (annee%100 != 0)) || (annee%400 == 0)) ? 29 : 28; } return -1; } //++ short Date::YearDays(short annee) // // Retourne le nombre de jours dans l'année //-- { return (((annee%4 == 0) && (annee%100 != 0)) || (annee%400 == 0)) ? 366 : 365; } //++ bool Date::UndetDate() const // // Retourne true si une partie de la date (jour // ou mois ou année) est indéterminée. //-- { return ((AA == -1) || (MM == -1) || (JJ == -1)); } //++ bool Date::AllUndetDate() const // // Retourne true si toute la date (jour // et mois et année) est indéterminée. //-- { return ((AA == -1) && (MM == -1) && (JJ == -1)); } //++ bool Date::UndetTime() const // // Retourne true si une partie de l'heure (heure // ou minutes ou secondes) est indéterminée. //-- { return ((hh == -1) || (mm == -1) || (ss == -1)); } //++ bool Date::AllUndetTime() const // // Retourne true si toute l'heure (heure // et minutes et secondes) est indéterminée. //-- { return ((hh == -1) && (mm == -1) && (ss == -1)); } //Date::operator double() const //++ double Date::GetDays() const // // Jours écoulés depuis le 0 janvier 1901 0h TU //-- { if (UndetTime() && !AllUndetTime()) THROW(inconsistentErr); if (UndetDate() && !AllUndetDate()) THROW(inconsistentErr); double t=0; if (!UndetDate()) { int nban = AA-1901; if (nban >= 0) t = nban*365 + (nban/4) - (nban/100) + ((nban+300)/400); else t = nban*365 + (nban/4) - (nban/100) + ((nban-100)/400); for (int i=1; i= 0) t -= nban*365 + ((nban)/4) - ((nban)/100) + ((nban+300)/400); else t -= nban*365 + ((nban)/4) - ((nban)/100) + ((nban-100)/400); AA = 1901 + nban; if (t > Date::YearDays(AA)+1) { t -= Date::YearDays(AA); AA++; } else if (t<1) { AA--; t += Date::YearDays(AA); } MM = 1; while (t > Date::MonthDays(MM,AA)+1) { t -= Date::MonthDays(MM,AA); MM++; } JJ = int(t); t -= JJ; t *= 24; hh = int(t); t = (t-hh)*60; mm = int(t); t = (t-mm)*60; ss = t; nuit = 0; } //++ Date::Date() // // Constructeur. Prend l'heure courante... //-- : timeZone(gTimeZone) { time_t t = time(NULL); struct tm* TM = gmtime(&t); AA = TM->tm_year + 1900; MM = TM->tm_mon+1; JJ = TM->tm_mday; hh = TM->tm_hour; mm = TM->tm_min; ss = TM->tm_sec; nuit = 0; END_CONSTRUCTOR } //++ Date::Date(int J, int M, int A, int h, int m, double s) // // Constructeur simple. //-- : JJ(J), MM(M), AA(A), hh(h), mm(m), ss(s), timeZone(gTimeZone), nuit(0) { END_CONSTRUCTOR } //++ Date::Date(double t) // // Constructeur à partir des // jours écoulés depuis le 0 janvier 1901 0h TU // //-- : timeZone(gTimeZone) { Set(t); END_CONSTRUCTOR } //++ void Date::SetTimeZone(TimeZone* tz) // // On change de fuseau horaire. //-- { timeZone = tz; } //++ Date::Date(const char* date, const char* heure, int tOpt) // // Constructeur à partir de la date sous la forme // 'DD/MM/YYYY' 'HH/MM/SS', et tOpt est Date::kGMTTime // (par défaut) ou Date::kLocalTime. // // Tout ou partie de la date peut être indéterminée ('??'). //-- : timeZone(gTimeZone) { Set(date,heure,tOpt); END_CONSTRUCTOR } //++ Date::Date(string const& date, string const& heure, int tOpt) // // Constructeur à partir de la date sous la forme // 'DD/MM/YYYY' 'HH/MM/SS', et tOpt est Date::kGMTTime // (par défaut) ou Date::kLocalTime. // // Tout ou partie de la date peut être indéterminée ('??'). //-- : timeZone(gTimeZone) { Set(date,heure,tOpt); END_CONSTRUCTOR } //++ void Date::Set(string const& date, string const& heure, int tOpt) // // Positionne la date sous la forme // 'DD/MM/YYYY' 'HH/MM/SS', et tOpt est Date::kGMTTime // (par défaut) ou Date::kLocalTime. // // Tout ou partie de la date peut être indéterminée ('??'). //-- { Set(date.c_str(), heure == "" ? (char*)NULL : heure.c_str(), tOpt); } //++ void Date::Set(const char* date, const char* heure, int tOpt) // // Positionne la date sous la forme // 'DD/MM/YYYY' 'HH/MM/SS', et tOpt est Date::kGMTTime // (par défaut) ou Date::kLocalTime. // // Tout ou partie de la date peut être indéterminée ('??'). //-- { nuit = 0; if (date) { if (strlen(date) >= 5) { // J(J)/M(M)/AA(AA), ?/?/?, combinaison const char* p = date; if (p[0] == '?' && (p[1] == '?' || p[1] == '/')) JJ = -1; else { if (!isdigit(p[0])) THROW(inconsistentErr); if (!isdigit(p[1]) && p[1] != '/') THROW(inconsistentErr); JJ = atoi(p); if (JJ <= 0) THROW(inconsistentErr); } if (p[1] == '/') p += 2; else if (p[2] == '/') p+= 3; else THROW(inconsistentErr); if (p[0] == '?' && (p[1] == '?' || p[1] == '/')) MM = -1; else { if (!isdigit(p[0])) THROW(inconsistentErr); if (!isdigit(p[1]) && p[1] != '/') THROW(inconsistentErr); MM = atoi(p); if (MM <= 0 || MM >12) THROW(inconsistentErr); } if (p[1] == '/') p += 2; else if (p[2] == '/') p+= 3; else THROW(inconsistentErr); if (p[0] == '?') AA = -1; else { if (!isdigit(p[0])) THROW(inconsistentErr); if (!isdigit(p[1])) THROW(inconsistentErr); AA = atoi(p); if (AA < 100 && AA >= 0 && date[6] != '0') AA += 1900; } if (AA > 0 && MM > 0 && JJ > 0 && JJ > MonthDays(MM,AA)) THROW(inconsistentErr); } else if (strlen(date)==4) { // Code EROS de date if (date[0] == '?') AA = -1; else { if (!isdigit(date[0])) THROW(inconsistentErr); AA = 1990 + date[0] - '0'; } if (date[1] == '?') MM = -1; else { if (!isalpha(date[1])) THROW(inconsistentErr); if (islower(date[1])) MM = date[1] - 'a' + 1; else MM = date[1] - 'A' + 1; if (MM<1 || MM>12) THROW(inconsistentErr); } if (date[2] == '?' && date[3] == '?') JJ = -1; else { if (!isdigit(date[2])) THROW(inconsistentErr); if (!isdigit(date[3])) THROW(inconsistentErr); JJ = atoi(date+2); } if (AA > 0 && MM > 0 && JJ > 0 && JJ > MonthDays(MM,AA)) THROW(inconsistentErr); nuit = 1; } else THROW(inconsistentErr); // Mauvaise longueur } else { // Pas de date JJ = MM = AA = -1; } if (heure) { const char* p = heure; if (p[0] == '?' && (p[1] == '?' || p[1] == ':')) hh = -1; else { if (!isdigit(p[0])) THROW(inconsistentErr); if (!isdigit(p[1]) && p[1] != ':') THROW(inconsistentErr); hh = atoi(p); } if (p[1] == ':') p += 2; else if (p[2] == ':') p+= 3; else THROW(inconsistentErr); if (p[0] == '?' && (p[1] == '?' || p[1] == ':')) mm = -1; else { if (!isdigit(p[0])) THROW(inconsistentErr); if (!isdigit(p[1]) && p[1] != '/') THROW(inconsistentErr); mm = atoi(p); } if (p[1] == ':') p += 2; else if (p[2] == ':') p+= 3; else THROW(inconsistentErr); if (p[0] == '?') ss = -1; else { if (!isdigit(p[0])) THROW(inconsistentErr); ss = atoi(p); } } else { ss = hh = mm = -1; } if (tOpt == kLocalTime) { DBASSERT(timeZone != NULL); operator -= (timeZone->gmtOffset/24.0); if (timeZone->IsDst(*this)) operator -= (timeZone->dstOffset/24.0); // A VERIFIER SANS DOUTE INEXACT } if (nuit && !UndetTime() && hh < 12) *this += 1; } //++ void Date::GetDateStr(char* s, int tOpt) const // // Récupère la date sous la forme d'une chaîne 'DD/MM/YYYY' // en tOpt = Date::kGMTTime ou Date::kLocalTime. //-- { Date dt(*this); if (tOpt == kLocalTime && !UndetTime()) { DBASSERT(timeZone != NULL); dt += timeZone->GetOffset(*this)/24.0; } if (dt.AA < 1950 || dt.AA > 1999) sprintf(s, "%02d/%02d/%04d", dt.JJ, dt.MM, dt.AA); else sprintf(s, "%02d/%02d/%02d", dt.JJ, dt.MM, dt.AA-1900); if (dt.JJ == -1) s[0] = s[1] = '?'; if (dt.MM == -1) s[3] = s[4] = '?'; if (dt.AA == -1) { s[6] = s[7] = '?'; s[8] = '\0'; } } //++ void Date::GetDateCode(char* s, int tOpt) const // // Code EROS de la date. //-- { Date dt(*this); if (tOpt == kLocalTime && !UndetTime()) { DBASSERT(timeZone != NULL); dt += timeZone->GetOffset(*this)/24.0; } if (!dt.UndetTime() && dt.hh<12) dt -= 1; if (dt.AA != -1) { if (dt.AA < 1990 || dt.AA > 2016) THROW(inconsistentErr); int i = dt.AA - 1990; if (i<10) s[0] = '0' + i; else s[0] = 'a' + i - 10; } else s[0] = '?'; if (dt.MM != -1) s[1] = 'a' + dt.MM - 1; else s[1] = '?'; if (dt.JJ != -1) sprintf(s+2, "%02d", dt.JJ); else s[2] = s[3] = '?'; s[4] = 0; } //++ void Date::GetTimeStr(char* s, int tOpt) const // // Récupère l'heure sous la forme 'HH:MM:SS' //-- { Date dt(*this); if (tOpt == kLocalTime) { DBASSERT(timeZone != NULL); dt += timeZone->GetOffset(*this)/24.0; } sprintf(s, "%02d:%02d:%02d", dt.hh, dt.mm, int(dt.ss)); if (dt.hh == -1) s[0] = s[1] = '?'; if (dt.mm == -1) s[3] = s[4] = '?'; if (dt.ss == -1) s[6] = s[7] = '?'; } //++ void Date::GetSidTStr(char* s) const // // Récupèrera un jour le temps sidéral. //-- { strcpy(s,"TOBEDONE!!"); } //++ string Date::DateStr(int tOpt) const // // Récupère la date sous la forme d'une chaîne 'DD/MM/YYYY' // en tOpt = Date::kGMTTime ou Date::kLocalTime. //-- { char s[20]; GetDateStr(s,tOpt); return s; } //++ string Date::DateCode(int tOpt) const // // Code EROS de la date. //-- { char s[20]; GetDateCode(s,tOpt); return s; } //++ string Date::TimeStr(int tOpt) const // // Récupère l'heure sous la forme 'HH:MM:SS' //-- { char s[20]; GetTimeStr(s,tOpt); return s; } //++ string Date::SidTStr() const // // Récupèrera un jour le temps sidéral. //-- { char s[20]; GetSidTStr(s); return s; } //++ int Date::DayOfWeek(int tOpt) const // // Retourne le jour dans la semaine, 0-6 // avec 0 = Date::jour_Lundi. //-- { double t = GetDays(); if (tOpt == kLocalTime && hh >=0) { DBASSERT(timeZone != NULL); t += timeZone->GetOffset(*this)/24.0; } int ndays = int(t); // nb de jours depuis 0 janvier 1901 ndays = (ndays + 1) % 7; if (ndays<0) ndays += 7; return ndays; } //++ short Date::NthMonthDay(short i, short joursem, short mois, short annee) // // Retourne la date correspondant au ieme joursem dans le mois // correspondant (exemple, 2e dimanche de mars 1960). //-- { if (i>0) { int jprem = Date(1, mois, annee, 0, 0, 0).DayOfWeek(); int d = (joursem - jprem); if (d<0) d+=7; return d + 1 + (i-1)*7; } else { int ider = MonthDays(mois, annee); int jder = Date(ider, mois, annee, 0, 0, 0).DayOfWeek(); int d = (joursem - jder); if (d>0) d-=7; return d + ider + (i+1)*7; } } //++ Date& Date::operator += (double dt) // // dt en jours //-- { int u = UndetTime(); Set(GetDays()+dt); if (u) ss = hh = mm = -1; return *this; } //++ Date& Date::operator -= (double dt) // // dt en jours //-- { int u = UndetTime(); Set(GetDays()-dt); if (u) ss = hh = mm = -1; return *this; } //++ Date operator + (Date const& d, double dt) // //-- { Date a(d); a += dt; return a; } //++ Date operator - (Date const& d, double dt) // //-- { Date a(d); a -= dt; return a; } //++ double operator - (Date const& a, Date const& b) // // Résultat en jours //-- { if (a.UndetTime() != b.UndetTime()) THROW(inconsistentErr); if (a.UndetDate() != b.UndetDate()) THROW(inconsistentErr); return a.GetDays() - b.GetDays(); } //++ bool operator == (Date const& a, Date const& b) // // On n'a pas égalite dès que des éléments déterminés sont différents //-- { if (a.nuit && a.UndetTime() && !b.nuit && !b.UndetTime() && b.hh<12) {Date d = a+1; d.nuit = 0; return d == b;} else if (b.nuit && b.UndetTime() && !a.nuit && !a.UndetTime() && a.hh<12) {Date d = b+1; d.nuit = 0; return a == d;} if (a.AA != -1 && b.AA != -1 && a.AA != b.AA) return false; if (a.MM != -1 && b.MM != -1 && a.MM != b.MM) return false; if (a.JJ != -1 && b.JJ != -1 && a.JJ != b.JJ) return false; if (a.hh != -1 && b.hh != -1 && a.hh != b.hh) return false; if (a.mm != -1 && b.mm != -1 && a.mm != b.mm) return false; if (a.ss != -1 && b.ss != -1 && a.ss != b.ss) return false; return true; } //++ bool operator != (Date const& a, Date const& b) // //-- { return !(a == b); } //++ bool operator <= (Date const& a, Date const& b) // // Inégalité large. Pas de subtilités //-- { if (a.nuit && a.UndetTime() && !b.nuit && !b.UndetTime() && b.hh<12) {Date d = a+1; d.nuit = 0; return d <= b;} else if (b.nuit && b.UndetTime() && !a.nuit && !a.UndetTime() && a.hh<12) {Date d = b+1; d.nuit = 0; return a <= d;} if (a.AA != -1 && b.AA != -1) { if (a.AA < b.AA) return true; if (a.AA > b.AA) return false; } if (a.MM != -1 && b.MM != -1) { if (a.MM < b.MM) return true; if (a.MM > b.MM) return false; } if (a.JJ != -1 && b.JJ != -1) { if (a.JJ < b.JJ) return true; if (a.JJ > b.JJ) return false; } if (a.hh != -1 && b.hh != -1) { if (a.hh < b.hh) return true; if (a.hh > b.hh) return false; } if (a.mm != -1 && b.mm != -1) { if (a.mm < b.mm) return true; if (a.mm > b.mm) return false; } if (a.ss != -1 && b.ss != -1) { if (a.ss < b.ss) return true; if (a.ss > b.ss) return false; } return true; } //++ bool operator < (Date const& a, Date const& b) // // Inégalité stricte, codée à partir de la large pour résoudre // sans cas particuliers les subtilités // 01/02/95 < ??/03/95 mais pas 01/02/95 < ??/02/95 //-- { return (a <= b) && !(a == b); } //++ bool operator >= (Date const& a, Date const& b) // //-- { return (b <= a); } //++ bool operator > (Date const& a, Date const& b) // //-- { return (b < a); } //++ ostream& operator << (ostream& s, Date const& d) // //-- { char x[20]; d.GetDateStr(x); s << x << " "; d.GetTimeStr(x); s << x; return s; }