/*************************************************************************** * Copyright (C) 2005 by Nicolas PASCAL * * nicolas.pascal@icare.univ-lille1.fr * * * * 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 2 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, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "date.h" vector < Epoch2Leap > Date::utc_2_tai_leap_sec (0); void Date::init_leap_sec_table() { if ( Date::utc_2_tai_leap_sec.empty() ) { char format [] = "%Y-%m-%dT%H:%M:%S"; // list of dates (included) of leap seconds changes const char *v_date_change [] = { "1972-01-01T00:00:00", "1972-07-01T00:00:00", "1973-01-01T00:00:00", "1974-01-01T00:00:00", "1975-01-01T00:00:00", "1976-01-01T00:00:00", "1977-01-01T00:00:00", "1978-01-01T00:00:00", "1979-01-01T00:00:00", "1980-01-01T00:00:00", "1981-07-01T00:00:00", "1982-07-01T00:00:00", "1983-07-01T00:00:00", "1985-07-01T00:00:00", "1988-01-01T00:00:00", "1990-01-01T00:00:00", "1991-01-01T00:00:00", "1992-07-01T00:00:00", "1993-07-01T00:00:00", "1994-07-01T00:00:00", "1996-01-01T00:00:00", "1997-07-01T00:00:00", "1999-01-01T00:00:00", "2006-01-01T00:00:00", "2009-01-01T00:00:00", }; int sz = sizeof (v_date_change) / sizeof (char *); Date::utc_2_tai_leap_sec.reserve ( sz ); int leap_sec ( 9 ); double epoch; for ( int i = 0 ; i < sz ; ++i ) { epoch = Date::get_epoch_time ( v_date_change [i] , format); Date::utc_2_tai_leap_sec.push_back ( Epoch2Leap ( epoch, ++leap_sec ) ); // Date::utc_2_tai_leap_sec.rbegin()->print(); } } } Date::Date( int _year, int _month, int _day, int _hour, int _min, int _sec ) { set_date( _year, _month, _day, _hour, _min, _sec); } Date::Date( const double & _epoch_time ) { epoch_time = static_cast(_epoch_time); } Date::Date( const time_t &_epoch_time ) { epoch_time = _epoch_time; } Date & Date::operator =( const Date & d ) { if (&d == this) return *this; // protection against self assignation if ( &d != NULL ) { epoch_time=d.get_epoch_time(); } return *this; } bool Date::operator ==( const Date & d ) const { if (&d == NULL) return false; else return (epoch_time==d.get_epoch_time()); } bool Date::operator !=( const Date & d ) const { if (&d == NULL) return false; else return (epoch_time!=d.get_epoch_time()); } bool Date::operator<= (const Date &d) const { if (&d == NULL) return false; else return (epoch_time<=d.get_epoch_time()); } bool Date::operator>= (const Date &d) const { if (&d == NULL) return false; else return (epoch_time>=d.get_epoch_time()); } bool Date::operator< (const Date &d) const { if (&d == NULL) return false; else return (epoch_time (const Date &d) const { if (&d == NULL) return false; else return (epoch_time>d.get_epoch_time()); } void Date::set_epoch_time(const time_t& _x) { epoch_time = _x; } const int Date::get_sec( ) const { return gmtime(&epoch_time)->tm_sec; } const int Date::get_min( ) const { return gmtime(&epoch_time)->tm_min; } const int Date::get_hour( ) const { return gmtime(&epoch_time)->tm_hour; } const int Date::get_day( ) const { return gmtime(&epoch_time)->tm_mday; } const int Date::get_yday( ) const { return gmtime(&epoch_time)->tm_yday; } const int Date::get_month( ) const { return gmtime(&epoch_time)->tm_mon+1; } const int Date::get_year( ) const { return gmtime(&epoch_time)->tm_year+1900; } void Date::set_julian_time( const double & julian_time ) { // convert to epoch time using year unit double epoch_day = julian_time-2440587.5; // 2440587.5 is the 01/01/1970 in julian days epoch_time=static_cast(epoch_day*86400.); // year into secs } const double Date::get_julian_time() { double epoch_day = epoch_time/86400.; // epoch time in days return epoch_day+2440587.5; // 2440587.5 is the 01/01/1970 in julian days } void Date::set_date( const int & _year, const int & _month, const int & _day, const int & _hour, const int & _min, const int & _sec ) { struct tm gmt_tm; gmt_tm.tm_year=_year-1900; gmt_tm.tm_mon=_month-1; // from 0 to 11 with time.h gmt_tm.tm_mday = _day; gmt_tm.tm_hour=_hour; gmt_tm.tm_min=_min; gmt_tm.tm_sec=_sec; epoch_time = timegm(&gmt_tm); } Date Date::operator +(const double & time ) { Date d=*this; d.epoch_time+=(time_t)time; return d; } void Date::set_ECMWF_time( const double & time ) { double time_in_sec=time*3600.; /* ECMWF time starts at 1900:01:01T00:00:00 and epoch one at 1970:01:01T00:00:00 -> difference of 70 years that represents 70 years of 365 days + 18 bissextile years = 70.*365.*24.+(17.*24.) = 613 608. hours from 1900/01/01T00:00:00 to 1970/01/01T00:00:00 = 2 208 988 800 secs */ epoch_time=(time_t)(time_in_sec-2208988800.); } const double Date::get_ECMWF_time( ) const { // 2208988800 = nb of hour between 1900:01:01T00:00:00 and 1973:01:01T00:00:00 return (epoch_time/3600.)+2208988800.; } void Date::set_TAI93_time( const double & time ) { // DAO time starts at 1993:01:01T00:00:00, and epoch one at 1970:01:01T00:00:00 -> difference of 23 years + 6 days for bissextile years ; 23.*365.*24.*3600.+(6.*24.*3600.)=725 846 400 sec. from 1970/01/01T00:00:00 to 1993/01/01T00:00:00 epoch_time=(time_t)(time+725846400.); } const double Date::get_TAI93_time() const { // 725 846 400 sec. from 1970/01/01T00:00:00 (epoch time start) to 1993/01/01T00:00:00 (TAI time start) return epoch_time-725846400.; } const bool Date::is_the_same_day( const Date & d ) { return (get_year()==d.get_year() && get_month()==d.get_month() && get_day()==d.get_day() ); } void Date::set_date_str( const string & s, const char * format ) { struct tm gmt_tm; char * status = strptime(s.c_str(),format,&gmt_tm); if (status!=NULL) epoch_time = timegm(&gmt_tm); else { parse_date_error e(s,format); throw e; } } const string Date::get_date_str( const char * format ) { char s_date[MAX_FORMATTED_STRING_SIZE]; // format it size_t nb_char_parsed=strftime(s_date,MAX_FORMATTED_STRING_SIZE,format,gmtime(&epoch_time)); if (nb_char_parsed==0) { parse_date_error e(ctime(&epoch_time),format); throw e; } return string(s_date); } const string Date::get_month_string( const int month ) { string res(""); if (month<1 || month>12) { bad_date e; throw e; } if ( month<10 ) res+="0"; res+=MyTools::to_string(month); return res; } void Date::get_day_range( const int &year, const int & month, int & first, int & last ) { // format month string string s_month = Date::get_month_string(month); string s_date = MyTools::to_string(year)+"-"+s_month+"-"; // read the number of the first day of the month struct tm gmt_tm; strptime((s_date+"01").c_str(),"%F",&gmt_tm); first = gmt_tm.tm_yday+1; // read the number of the last day of the month. bool bissex = ( (year%4)==0 ); string s_last_day_of_month; if (month==1 || month==3 || month==5 || month==7 || month==8 || month==10 || month==12 ) s_last_day_of_month="31"; else if (month==4 || month==6 || month==9 || month==11) s_last_day_of_month="30"; else if (bissex) // month is february (2), during a bissextile year s_last_day_of_month="29"; else s_last_day_of_month="28"; struct tm gmt_tm2; strptime((s_date+s_last_day_of_month).c_str(),"%F",&gmt_tm2); last = gmt_tm2.tm_yday+1; } const vector< Date > Date::get_days( const Date & start, const Date & end ) { vector days; Date d(start); while (d.get_epoch_time()<=end.get_epoch_time()) { days.push_back(d); d.set_epoch_time(d.get_epoch_time()+(time_t)(86400.)); // 86400 is the number of seconds in one day } return days; } const int Date::get_nb_day( const int & month, const int & year ) { if (month >=1 && month<=12) { int my_month=month; int my_year=year; if (my_month==12) { my_month=0; my_year+=1; } // last day of the month = next month's first day minus one day Date d(year,month+1); d.set_TAI93_time(d.get_TAI93_time()-86400.); return d.get_day(); } else { cerr<<"Date::get_nb_day : in "<<__FILE__<<" at "<<__LINE__<<" Bad Month Number "<=1 && month<=12) { Date d(year,month-1,day); return d.get_yday(); } else { cerr<<"Date::get_day_number : in "<<__FILE__<<" at "<<__LINE__<<" Bad Month Number "<::iterator it_leap = Date::utc_2_tai_leap_sec.begin(); vector < Epoch2Leap >::iterator it_start = Date::utc_2_tai_leap_sec.begin(); vector < Epoch2Leap >::iterator it_end = Date::utc_2_tai_leap_sec.end(); if ( epoch < it_start->get_epoch_time () ) { // before first date set Date d_min ( Date::utc_2_tai_leap_sec [0].get_epoch_time () ); cerr << "In " << __FILE__ << " at " << __LINE__ << " : dates before " << d_min << " are not supported. You asked for " << d << endl; exit(-1); return -1; } while ( ( ( it_leap + 1 ) != it_end ) and ( epoch >= ( it_leap + 1 )->get_epoch_time () ) ) ++it_leap; return it_leap->get_leap_sec(); } double Date::get_tai93_leap_sec(const Date & d) { double tai_leap_sec = Date::get_tai_leap_sec ( d ); return tai_leap_sec - Date::nsec_tai93_to_utc_start; }