source: XIOS/trunk/src/date.cpp @ 550

Last change on this file since 550 was 550, checked in by rlacroix, 6 years ago

Add a new user defined calendar type.

A new calendar type "user_defined" is now available. This allows the users to create a custom calendar that we can configured to be suitable for planets other than the Earth.

An user defined calendar is always defined by two mandatory attributes:

  • day_length: the duration of a day, in seconds
  • and either:
    • month_length: an array containing the duration of each month, in days (the number of elements in the array is the number of months in a year)
    • or year_length: the duration of a year, in seconds (in that case, the calendar does not have months).

If the calendar has months (i.e. month_length attribute is set) and only in that case, it is possible to define leap years in order to compensate for the duration of an astronomical year not being a multiple of the day length. The leap years are defined by two mandatory attributes:

  • leap_year_month: the month to which the extra day will be added in case of leap year, expressed as an integer number in the range [1, numberOfMonths]
  • and leap_year_drift: the fraction of a day representing the yearly drift between the calendar year and the astronomical year, expressed as a real number in the range [0, 1).

Optionally, one can define the leap_year_drift_offset attribute to set the original drift at the beginning of the time origin's year, again expressed as a real number in the range [0, 1). If leap_year_drift_offset + leap_year_drift is greater or equal to 1, then the first year will be a leap year.

For example, the following configuration creates a Gregorian-like calendar:

<calendar type="user_defined" start_date="2012-03-01 15:00:00" time_origin="2012-02-28 15:00:00 + 1d" day_length="86400" month_lengths="(1, 12) [31 28 31 30 31 30 31 31 30 31 30 31]" leap_year_month="2" leap_year_drift="0.25" leap_year_drift_offset="0.75" />

Note that dates attributes must be written differently in the configuration file when using an user defined calendar without months:

  • if the year length is greater than the day length, the input format is year-day hh:min:sec instead of year-month-day hh:min:sec
  • if the day length is greater or equal to the year length, the input format is year hh:min:sec.

In all cases, it is still possible to use the date + duration notation to build a date (with both the date and duration parts being optional).

The Fortran interface has been updated accordingly so that xios_define_calendar can accept the new attributes necessary to define custom calendars.

  • Property copyright set to
    Software name : XIOS (Xml I/O Server)
    http://forge.ipsl.jussieu.fr/ioserver
    Creation date : January 2009
    Licence : CeCCIL version2
    see license file in root directory : Licence_CeCILL_V2-en.txt
    or http://www.cecill.info/licences/Licence_CeCILL_V2-en.html
    Holder : CEA/LSCE (Laboratoire des Sciences du CLimat et de l'Environnement)
    CNRS/IPSL (Institut Pierre Simon Laplace)
    Project Manager : Yann Meurdesoif
    yann.meurdesoif@cea.fr
File size: 11.1 KB
Line 
1#include "date.hpp"
2#include "calendar.hpp"
3#include "calendar_type.hpp"
4#include "calendar_util.hpp"
5#include <boost/date_time/gregorian/gregorian.hpp>
6#include <boost/date_time/posix_time/posix_time.hpp>
7
8using namespace boost::posix_time;
9using namespace boost::gregorian;
10
11namespace xios
12{
13      /// ////////////////////// Définitions ////////////////////// ///
14      CDate::CDate(void)
15        : relCalendar(NULL)
16        , year(0), month(1),  day(1)
17        , hour(0), minute(0), second(0)
18      {}
19
20      CDate::CDate(const CCalendar& calendar)
21         : relCalendar(&calendar)
22         , year(0), month(1),  day(1)
23         , hour(0), minute(0), second(0)
24      {}
25
26      CDate::CDate(const CCalendar& calendar,
27                   int yr, int mth, int d,
28                   int hr, int min, int sec)
29         : relCalendar(&calendar)
30         , year(yr), month(mth),  day(d)
31         , hour(hr), minute(min), second(sec)
32      {
33         if (!this->checkDate())
34         {
35            DEBUG(<< "La date initialisée a été modifiée "
36                  << "car elle était incorrecte par rapport au calendrier souhaité.");
37         }
38      }
39
40      CDate::CDate(const CDate& date)
41        : relCalendar(date.relCalendar)
42        , year(date.year), month(date.month),   day(date.day)
43        , hour(date.hour), minute(date.minute), second(date.second)
44      {
45        // Delay the verification until we get a calendar we can compare the date to
46        if (relCalendar && !checkDate())
47        {
48          DEBUG(<< "La date initialisée a été modifiée "
49                << "car elle était incorrecte par rapport au calendrier souhaité.");
50        }
51      }
52
53      CDate::~CDate(void)
54      { /* Ne rien faire de plus */ }
55
56      ///---------------------------------------------------------------
57
58      CDate& CDate::operator=(const CDate& date)
59      {
60         relCalendar = date.relCalendar;
61         year = date.year; month  = date.month; day    = date.day;
62         hour = date.hour; minute = date.minute; second = date.second;
63         return (*this);
64      }
65
66      StdOStream& operator<<(StdOStream& out, const CDate& date)
67      {
68        std::streamsize s;
69        char c;
70
71        int width=4;
72        double maxSize=10000;
73        while (date.year>=maxSize)
74        {
75          maxSize*=10;
76          width++;
77        }
78        s = out.width(width); c = out.fill('0'); out << date.year << '-';
79
80        s = out.width(2); c = out.fill('0'); out << date.month << '-';
81        s = out.width(2); c = out.fill('0'); out << date.day << ' ';
82        s = out.width(2); c = out.fill('0'); out << date.hour << ':';
83        s = out.width(2); c = out.fill('0'); out << date.minute << ':';
84        s = out.width(2); c = out.fill('0'); out << date.second;
85
86        return out;
87      }
88
89      StdIStream& operator>>(StdIStream& in, CDate& date)
90      {
91        if (date.relCalendar)
92          date.relCalendar->parseDate(in, date);
93        else
94          CCalendar::parseDateDefault(in, date);
95
96        return in;
97      }
98
99      CDate::operator Time(void) const // Non vérifiée, pas optimisée ...
100      {
101        // This will check that a calendar was correctly associated to the date
102        const CCalendar& c = getRelCalendar();
103
104        // Todo : Tester si la date courante est supérieure à la date initiale.
105        Time retvalue = getSecondOfYear() - c.getTimeOrigin().getSecondOfYear();
106
107        if (c.hasLeapYear())
108        {
109          for (CDate _d(c.getTimeOrigin()); _d.getYear() < getYear(); _d.setYear(_d.getYear() + 1))
110            retvalue += c.getYearTotalLength(_d);
111        }
112        else
113          retvalue += (getYear() - c.getTimeOrigin().getYear()) * c.getYearTotalLength(*this);
114
115
116        return retvalue;
117      }
118
119      //----------------------------------------------------------------
120
121      bool CDate::checkDate(void)
122      {
123        // This will also check that a calendar was correctly associated to the date
124        return getRelCalendar().checkDate(*this);
125      }
126
127      //----------------------------------------------------------------
128
129      int CDate::getYear  (void) const { return (this->year  ); }
130      int CDate::getMonth (void) const { return (this->month ); }
131      int CDate::getDay   (void) const { return (this->day   ); }
132      int CDate::getHour  (void) const { return (this->hour  ); }
133      int CDate::getMinute(void) const { return (this->minute); }
134      int CDate::getSecond(void) const { return (this->second); }
135
136      //----------------------------------------------------------------
137
138      const CCalendar& CDate::getRelCalendar(void) const
139      {
140        if (!this->relCalendar)
141          ERROR("const CCalendar& CDate::getRelCalendar(void) const",
142                "Invalid state: The date is not associated with any calendar.");
143        return (*this->relCalendar);
144      }
145
146      bool CDate::hasRelCalendar(void) const
147      { return (this->relCalendar != NULL); }
148
149      //----------------------------------------------------------------
150
151      /*!
152        Get the number of seconds since the beginning of the year.
153        \return the number of seconds since the beginning of the year.
154      */
155      int CDate::getSecondOfYear() const
156      {
157        CDate yearStart(*this);
158        const CCalendar& c = getRelCalendar();
159        int nbDay = 0;
160
161        for (yearStart.setMonth(1); yearStart.getMonth() < getMonth(); yearStart.setMonth(yearStart.getMonth() + 1))
162          nbDay += c.getMonthLength(yearStart);
163
164        // We need to use getDayLengthInSeconds instead of getDayLength since we might
165        // have a non-integral number of hours per day for user defined calendars
166        return ((nbDay + getDay() - 1) * c.getDayLengthInSeconds()
167                  + (getHour() * c.getHourLength() + getMinute()) * c.getMinuteLength() + getSecond());
168      }
169
170      /*!
171        Get the number of days (expressed as a real number) since the beginning of the year.
172        \return the number of days (expressed as a real number) since the beginning of the year.
173      */
174      double CDate::getDayOfYear() const
175      {
176        return double(getSecondOfYear()) / getRelCalendar().getDayLengthInSeconds();
177      }
178
179      /*!
180        Get the fraction of the current year as a real number between 0 and 1.
181        \return the fraction of the current year.
182      */
183      double CDate::getFractionOfYear() const
184      {
185        return double(getSecondOfYear()) / getRelCalendar().getYearTotalLength(*this);
186      }
187
188      /*!
189        Get the number of seconds since the beginning of the day.
190        \return the number of seconds since the beginning of the day.
191      */
192      int CDate::getSecondOfDay() const
193      {
194        const CCalendar& c = getRelCalendar();
195        return ((getHour() * c.getHourLength() + getMinute()) * c.getMinuteLength() + getSecond());
196      }
197
198      /*!
199        Get the fraction of the current day as a real number between 0 and 1.
200        \return the fraction of the current day.
201      */
202      double CDate::getFractionOfDay() const
203      {
204        return double(getSecondOfDay()) / getRelCalendar().getDayLengthInSeconds();
205      }
206
207      //----------------------------------------------------------------
208
209      void CDate::setYear  (int newyear)   { this->year   = newyear; }
210      void CDate::setMonth (int newmonth)  { this->month  = newmonth; }
211      void CDate::setDay   (int newday)    { this->day    = newday; }
212      void CDate::setHour  (int newhour)   { this->hour   = newhour; }
213      void CDate::setMinute(int newminute) { this->minute = newminute; }
214      void CDate::setSecond(int newsecond) { this->second = newsecond; }
215
216      void CDate::setDate(int yr, int mth, int d, int hr, int min, int sec)
217      {
218        this->year   = yr;
219        this->month  = mth;
220        this->day    = d;
221        this->hour   = hr;
222        this->minute = min;
223        this->second = sec;
224      }
225
226      //----------------------------------------------------------------
227
228      void CDate::addMonth(int value)
229      {// Value doit être égale à 1 ou -1.
230        this->month += value;
231        if (this->month == 13) { year++; this->month = 1; }
232        if (this->month == 0)  { year--; this->month = 12; }
233      }
234
235      //----------------------------------------------------------------
236
237      bool CDate::setRelCalendar(const CCalendar& relCalendar)
238      {
239        this->relCalendar = &relCalendar;
240        return this->checkDate();
241      }
242
243      //----------------------------------------------------------------
244
245      CDate CDate::FromString(const StdString& str, const CCalendar& calendar)
246      {
247        CDate dt(calendar);
248        StdIStringStream iss(str);
249        iss >> dt;
250        return dt;
251      }
252
253      //----------------------------------------------------------------
254
255      StdString CDate::getStryyyymmdd(void) const
256      {
257        std::streamsize s;
258        char c;
259
260        ostringstream oss;
261
262        s = oss.width(4); c = oss.fill('0'); oss << year;
263        s = oss.width(2); c = oss.fill('0'); oss << month;
264        s = oss.width(2); c = oss.fill('0'); oss << day;
265
266        return oss.str();
267      }
268
269      string CDate::getStr(const string& str) const
270      {
271        ostringstream oss;
272        int level;
273
274        level=0;
275        for(string::const_iterator it=str.begin();it!=str.end();++it)
276        {
277          if (level==0)
278          {
279            if (*it=='%') level++;
280            else oss<<*it;
281          }
282          else if (level==1)
283          {
284            switch (*it)
285            {
286              case 'y' :
287                oss.width(4); oss.fill('0'); oss << year;
288                level=0;
289                break;
290              case 'm' : // month or minute
291                level++;
292                break;
293              case 'd' :
294                oss.width(2); oss.fill('0'); oss << day;
295                level=0;
296                break;
297              case 'h' :
298                oss.width(2); oss.fill('0'); oss << hour;
299                level=0;
300                break;
301              case 's' :
302                oss.width(2); oss.fill('0'); oss << second;
303                level=0;
304                break;
305              case 'S' : // seconds since time origin
306                oss.width(0); oss << Time(*this);
307                level=0;
308                break;
309              case 'D' : // days since time origin
310                oss.width(0); oss << Time(*this) / getRelCalendar().getDayLengthInSeconds();
311                level=0;
312                break;
313              default :
314                oss<<'%'<<*it;
315                level=0;
316            }
317          }
318          else if (level==2)
319          {
320            switch (*it)
321            {
322              case 'o' : // month
323                oss.width(2); oss.fill('0'); oss << month;
324                level=0;
325                break;
326              case 'i' : //minute
327                oss.width(2); oss.fill('0'); oss << minute;
328                level=0;
329                break;
330              default :
331                oss<<"%m"<<*it;
332                level=0;
333            }
334          }
335        }
336        return oss.str();
337      }
338
339      StdString CDate::toString(void) const
340      {
341        StdOStringStream oss;
342        oss << *this;
343        return oss.str();
344      }
345
346      ///---------------------------------------------------------------
347
348} // namespace xios
Note: See TracBrowser for help on using the repository browser.