source: XIOS/trunk/src/calendar.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.2 KB
Line 
1#include "calendar.hpp"
2#include "duration.hpp"
3#include "date.hpp"
4#include "calendar_util.hpp"
5
6namespace xios
7{
8      /// ////////////////////// Définitions ////////////////////// ///
9      CCalendar::CCalendar(void)
10         : CObject()
11         , initDate(*this)
12         , timeOrigin(*this)
13         , currentDate(*this)
14      {}
15
16      CCalendar::CCalendar(const StdString& id)
17               : CObject(id)
18               , initDate(*this)
19               , timeOrigin(*this)
20               , currentDate(*this)
21      {}
22
23      CCalendar::CCalendar(const StdString& id,
24                           int yr, int mth, int d,
25                           int hr /*= 0*/, int min /*= 0*/, int sec /*= 0*/)
26               : CObject(id)
27               , initDate(*this)
28               , timeOrigin(*this)
29               , currentDate(*this)
30      {
31        initializeDate(yr, mth, d, hr, min, sec);
32      }
33
34      CCalendar::CCalendar(const StdString& id, const CDate& startDate)
35               : CObject(id)
36               , initDate(startDate)
37               , timeOrigin(startDate)
38               , currentDate(startDate)
39      {
40        // Initialize the dates only in the derivated classes
41        // since we want to use the overloaded virtual functions
42      }
43
44      CCalendar::CCalendar(const StdString& id, const CDate& startDate, const CDate& timeOrigin)
45               : CObject(id)
46               , initDate(startDate)
47               , timeOrigin(timeOrigin)
48               , currentDate(startDate)
49      {
50        // Initialize the dates only in the derivated classes
51        // since we want to use the overloaded virtual functions
52      }
53
54      void CCalendar::initializeDate()
55      {
56        if (!initDate.setRelCalendar(*this))
57          ERROR("CCalendar::initializeDate()",
58                "initDate: Bad format or date not conform to the calendar");
59        if (!timeOrigin.setRelCalendar(*this))
60          ERROR("CCalendar::initializeDate()",
61                "timeOrigin: Bad format or date not conform to the calendar");
62        if (!currentDate.setRelCalendar(*this))
63          ERROR("CCalendar::initializeDate()",
64                "currentDate: Bad format or date not conform to the calendar");
65      }
66
67      void CCalendar::initializeDate(int yr, int mth, int d,
68                                     int hr /*= 0*/, int min /*= 0*/, int sec /*= 0*/)
69      {
70        initDate = CDate(*this, yr, mth, d, hr, min, sec);
71        timeOrigin = initDate;
72        currentDate = initDate;
73      }
74
75      void CCalendar::initializeDate(const StdString& dateStr)
76      {
77        initDate = CDate::FromString(dateStr, *this);
78        timeOrigin = initDate;
79        currentDate = initDate;
80      }
81
82      void CCalendar::initializeDate(const StdString& dateStr, const StdString& timeOriginStr)
83      {
84        initDate = CDate::FromString(dateStr, *this);
85        timeOrigin = CDate::FromString(timeOriginStr, *this);
86        currentDate = initDate;
87      }
88
89      CCalendar::~CCalendar(void)
90      { /* Ne rien faire de plus */ }
91
92      ///---------------------------------------------------------------
93
94      StdString CCalendar::toString(void) const
95      {
96         StdOStringStream oss;
97         oss <<   "[type: "   << this->getId()
98             << ", start: "   << this->initDate
99             << ", current: " << this->currentDate << "]";
100         return (oss.str());
101      }
102
103      void CCalendar::fromString(const StdString& str)
104      { ERROR("CCalendar::fromString(str)",
105               << "[ str = " << str << "] Not implemented yet !"); }
106
107      //-----------------------------------------------------------------
108
109      void CCalendar::setTimeStep(const CDuration& timestep)
110      {
111        if (timestep.timestep)
112          ERROR("CCalendar::setTimeStep(const CDuration& timestep)",
113                << "Circular definition of the timestep: the timestep cannot refer to itself.");
114        this->timestep = timestep;
115      }
116
117      CDate& CCalendar::update(int step)
118      {
119        info(20) << "update step : " << step << " timestep " << this->timestep << std::endl;
120        return (this->getCurrentDate() = this->getInitDate() + step * this->timestep);
121      }
122
123      //-----------------------------------------------------------------
124
125      void CCalendar::setInitDate(const CDate& initDate)
126      {
127        if (&initDate.getRelCalendar() != this)
128          ERROR("CCalendar::setInitDate(const CDate& initDate)",
129                << "The init date cannot be attached to another calendar.");
130        this->initDate = initDate;
131      }
132
133      void CCalendar::setTimeOrigin(const CDate& timeOrigin)
134      {
135        if (&timeOrigin.getRelCalendar() != this)
136          ERROR("CCalendar::setInitDate(const CDate& timeOrigin)",
137                << "The time origin cannot be attached to another calendar.");
138        this->timeOrigin = timeOrigin;
139      }
140
141      //-----------------------------------------------------------------
142
143      const CDuration& CCalendar::getTimeStep(void) const { return this->timestep; }
144      const CDate& CCalendar::getInitDate(void) const     { return this->initDate; }
145      const CDate& CCalendar::getTimeOrigin(void) const   { return this->timeOrigin; }
146      CDate& CCalendar::getCurrentDate(void)              { return this->currentDate; }
147
148      //-----------------------------------------------------------------
149
150      int CCalendar::getMonthLength(const CDate& date) const
151      { // Retourne la durée du mois en jour.
152        static const int NoLeapMonthLength[] =
153          { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
154        return NoLeapMonthLength[date.getMonth() - 1];
155      }
156
157      StdString CCalendar::getType(void) const { return StdString(this->getId()); }
158
159      int CCalendar::getYearTotalLength(const CDate& date) const { return (365 * 86400); }
160
161      int CCalendar::getYearLength  (void) const { return 12; }
162      int CCalendar::getDayLength   (void) const { return 24; }
163      int CCalendar::getHourLength  (void) const { return 60; }
164      int CCalendar::getMinuteLength(void) const { return 60; }
165      int CCalendar::getDayLengthInSeconds(void) const { return getDayLength() * getHourLength() * getMinuteLength(); }
166
167      bool CCalendar::hasLeapYear() const { return false; }
168
169      StdString CCalendar::getMonthName(int monthId) const
170      {
171        static const StdString MonthNames[] =
172          { "january", "february", "march",     "april" ,  "may",      "june",
173            "july",    "august",   "september", "october", "november", "december" };
174        return MonthNames[monthId - 1];
175      }
176
177      const StdString CCalendar::getMonthShortName(int monthId) const
178      { StdString value = this->getMonthName(monthId); value.resize(3); return value; }
179
180      CDuration& CCalendar::resolve(CDuration& dur, bool noNegativeTime /*= false*/) const
181      {
182        const int hourLengthInSeconds = getHourLength() * getMinuteLength();
183
184        // Simplify the days, hours, minutes and seconds.
185        // First convert them to seconds
186        Time t = Time(modf(dur.day, &dur.day) * getDayLengthInSeconds()
187                        + (dur.hour * getHourLength() + dur.minute) * getMinuteLength() + dur.second);
188        // Then convert back to days
189        dur.day += int(t / getDayLengthInSeconds());
190        t %= getDayLengthInSeconds();
191
192        // Do we allow hour, minute, second to be negative?
193        if (noNegativeTime)
194        {
195          // If we don't, we remove some days until the time is positive
196          while (t < 0)
197          {
198            t += getDayLengthInSeconds();
199            dur.day -= 1.0;
200          }
201        }
202
203        // hours
204        dur.hour = int(t / hourLengthInSeconds);
205        t %= hourLengthInSeconds;
206        // minutes
207        dur.minute = int(t / getMinuteLength());
208        // secondes
209        dur.second = int(t % getMinuteLength());
210
211        // Nothing to do for the months yet since this depends on an actual date
212
213        // Simplify the years
214        dur.month  += modf(dur.year, &dur.year) * getYearLength();
215        dur.year   += int(dur.month) / getYearLength(); dur.month = int(dur.month) % getYearLength();
216
217        return dur;
218      }
219
220      /*! Parse a date using a generic parser. */
221      void CCalendar::parseDateDefault(StdIStream& in, CDate& date)
222      {
223        char sep = '-'; // Le caractÚre c est utilisé pour "recueillir" les séparateurs "/" et ":".
224        char c;
225
226        // Default initialize the date
227        int year = 00, month  = 01, day    = 01;
228        int hour = 00, minute = 00, second = 00;
229
230        in >> year >> c;
231        if (c == sep)
232        {
233          in >> month >> c;
234          if (c == sep)
235          {
236            in >> day;
237            c = in.get();
238            sep = ' ';
239            if (c == sep)
240            {
241              in >> hour >> c;
242              sep = ':';
243              if (c == sep)
244              {
245                in >> minute >> c;
246                if (c == sep)
247                {
248                  in >> second;
249                  in >> c;
250                }
251              }
252            }
253          }
254        }
255
256        date.setDate(year, month, day, hour, minute, second);
257
258        // Delay the verification until we get a calendar we can compare the date to
259        if (date.hasRelCalendar() && !date.checkDate())
260          ERROR("void parseDateDefault(StdIStream& in, CDate& date)",
261                << "Bad date format or not conform to calendar");
262
263        if (c == '+') // We will be adding a duration to the date
264        {
265          CDuration dur;
266          in >> dur;
267          date = date + dur;
268        }
269        else if (!in.eof())
270          ERROR("void parseDateDefault(StdIStream& in, CDate& date)",
271                << "Invalid date format: unexpected trailing character(s)");
272      }
273
274      /*! Parse a date using the calendar's parser. */
275      void CCalendar::parseDate(StdIStream& in, CDate& date) const
276      {
277        parseDateDefault(in, date);
278      }
279
280      /*! Test if a date is valid with regard to the current calendar. */
281      bool CCalendar::checkDate(CDate& date) const
282      {
283        bool isValid = true;
284
285        // Vérification de la valeur du mois.
286        if (date.getMonth() < 1)
287        { isValid = false; date.setMonth(1); }
288        else if (date.getMonth() > getYearLength())
289        { isValid = false; date.setMonth(getYearLength()); }
290
291        // Vérification de la valeur du jour.
292        if (date.getDay() < 1)
293        { isValid = false; date.setDay(1); }
294        else if (date.getDay() > getMonthLength(*this))
295        { isValid = false; date.setDay(getMonthLength(*this)); }
296
297        // Vérification de la valeur de l'heure.
298        if (date.getHour() < 0)
299        { isValid = false; date.setHour(0); }
300        else if (date.getHour() >= getDayLength())
301        { isValid = false; date.setHour(getDayLength() - 1); }
302
303        // Vérification de la valeur des minutes.
304        if (date.getMinute() < 0)
305        { isValid = false; date.setMinute(0); }
306        else if (date.getMinute() >= getHourLength())
307        { isValid = false; date.setMinute(getHourLength() - 1); }
308
309        // Vérification de la valeur des secondes.
310        if (date.getSecond() < 0)
311        { isValid = false; date.setSecond(0); }
312        else if (date.getSecond() >= getMinuteLength())
313        { isValid = false; date.setSecond(getMinuteLength() - 1); }
314
315        return isValid;
316      }
317} // namespace xios
Note: See TracBrowser for help on using the repository browser.