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

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

Revised calendar functionalities:

  • the calendar is now configured from a specific calendar child node of the context in the XML configuration file. Example: <calendar type="Gregorian" start_date="2012-03-01 15:00:00" time_origin="2012-02-29 15:00:00" timestep="1h" />
  • the calendar type should now be configured when defining the start time and/or the time origin.
  • the start time and the time origin are now optional, 0000-01-01 00:00:00 will be used by default. It is also possible to define them partially. For example, 2015 and 2014-12 are valid dates corresponding respectively to 2015-01-01 00:00:00 and 2014-12-01 00:00:00.
  • an optional duration offset can be added to the start date and time origin. For example, it's possible to define the date 2015-01-12 12:00:00 as 2015-01-11 + 36h or 2015-01-11 12:00:00 + 1d. The duration format is the same as the time step. Being that the date is optional, it is possible to only use a duration (for example + 42s is the same as 0000-01-01 00:00:00 + 42s). An error will be raised if a duration based on the time step is used before the time step was configured. For example, the following would cause an error: <calendar type="Gregorian" start_date="+ 1ts" /> but <calendar type="Gregorian" start_date="+ 1ts" timestep="0.5h" /> would not.
  • new Fortran interface to define the calendar:
    • xios_define_calendar(type[, timestep, start_date, time_origin]) will create a calendar when none had previously been defined. Only the type argument is mandatory, the rest is optional. Calendar operations on dates and durations are possible as soon as the calendar is created (either using this procedure or directly from the XML configuration file).
    • the following getter and setter procedures are available: xios_set_timestep, xios_set_start_date, xios_set_time_origin, xios_get_calendar_type, xios_get_timestep, xios_get_start_date, xios_get_time_origin.
  • new Fortran interface to interact with the calendar: xios_update_calendar, xios_get_current_date, xios_get_year_length_in_seconds, xios_get_day_length_in_seconds.
  • new Fortran interface for date conversion: xios_date_get_second_of_year, xios_date_get_day_of_year, xios_date_get_fraction_of_year, xios_date_get_second_of_day, xios_date_get_fraction_of_day.
  • two new placeholders are available to format the file name when splitting the output (split_freq_format attribute):
    • %S the number of seconds since the time origin
    • %D the integral number of days since the time origin
  • 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: 13.4 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) // Non testée.
90      {
91        char sep = '-'; // Le caractÚre c est utilisé pour "recueillir" les séparateurs "/" et ":".
92        char c;
93
94        // Default initialize the date
95        date.year = 0;
96        date.month = 1;
97        date.day = 1;
98        date.hour = 0;
99        date.minute = 0;
100        date.second = 0;
101
102        in >> date.year >> c;
103        if (c == sep)
104        {
105          in >> date.month >> c;
106          if (c == sep)
107          {
108            in >> date.day;
109            c = in.get();
110            sep = ' ';
111            if (c == sep)
112            {
113              in >> date.hour >> c;
114              sep = ':';
115              if (c == sep)
116              {
117                in>>date.minute >> c;
118                if (c == sep)
119                {
120                  in >> date.second;
121                  in >> c;
122                }
123              }
124            }
125          }
126        }
127
128        // Delay the verification until we get a calendar we can compare the date to
129        if (date.relCalendar && !date.checkDate())
130          ERROR("StdIStream& operator >> (StdIStream& in, CDate& date)",
131                << "Bad date format or not conform to calendar");
132
133        if (c == '+') // We will be adding a duration to the date
134        {
135          CDuration dur;
136          in >> dur;
137          date = date + dur;
138        }
139        else if (!in.eof())
140          ERROR("StdIStream& operator >> (StdIStream& in, CDate& date)",
141                << "Invalid date format: unexpected trailing character(s)");
142
143        return in;
144      }
145
146      CDate::operator Time(void) const // Non vérifiée, pas optimisée ...
147      {
148        // This will check that a calendar was correctly associated to the date
149        const CCalendar& c = getRelCalendar();
150
151        // Todo : Tester si la date courante est supérieure à la date initiale.
152        Time retvalue = getSecondOfYear() - c.getTimeOrigin().getSecondOfYear();
153
154        if ((c.getId().compare("D360")    == 0) ||
155            (c.getId().compare("AllLeap") == 0) ||
156            (c.getId().compare("NoLeap")  == 0))
157        return (retvalue + (getYear() - c.getTimeOrigin().getYear())
158                                      * c.getYearTotalLength(*this));
159
160        for (CDate _d(c.getTimeOrigin()); _d.getYear() < getYear(); _d.setYear(_d.getYear() + 1))
161           retvalue += c.getYearTotalLength(_d);
162
163        return retvalue;
164      }
165
166      //----------------------------------------------------------------
167
168      bool CDate::checkDate(void)
169      {
170        bool retValue = true;
171
172        // This will check that a calendar was correctly associated to the date
173        const CCalendar& c = getRelCalendar();
174
175        // Vérification de la valeur du mois.
176        if (month  < 1) { retValue = false; month  = 1; }
177        if (month  > c.getYearLength())
178        { retValue = false; month = c.getYearLength(); }
179
180        // Vérification de la valeur du jour.
181        if (day    < 1) { retValue = false; day  = 1; }
182        if (day    > c.getMonthLength(*this))
183        { retValue = false; day = c.getMonthLength(*this); }
184
185        // Vérification de la valeur de l'heure.
186        if (hour   < 0) { retValue = false; hour  = 0; }
187        if (hour   >= c.getDayLength())
188        { retValue = false; hour = c.getDayLength() - 1; }
189
190        // Vérification de la valeur des minutes.
191        if (minute < 0) { retValue = false; minute = 0; }
192        if (minute >= c.getHourLength())
193        { retValue = false; minute = c.getHourLength() - 1; }
194
195        // Vérification de la valeur des secondes.
196        if (second < 0) { retValue = false; second = 0; }
197        if (second >= c.getMinuteLength())
198        { retValue = false; second = c.getMinuteLength() - 1; }
199
200        return retValue;
201      }
202
203      //----------------------------------------------------------------
204
205      int CDate::getYear  (void) const { return (this->year  ); }
206      int CDate::getMonth (void) const { return (this->month ); }
207      int CDate::getDay   (void) const { return (this->day   ); }
208      int CDate::getHour  (void) const { return (this->hour  ); }
209      int CDate::getMinute(void) const { return (this->minute); }
210      int CDate::getSecond(void) const { return (this->second); }
211
212      //----------------------------------------------------------------
213
214      const CCalendar& CDate::getRelCalendar(void) const
215      {
216        if (!this->relCalendar)
217          ERROR("const CCalendar& CDate::getRelCalendar(void) const",
218                "Invalid state: The date is not associated with any calendar.");
219        return (*this->relCalendar);
220      }
221
222      bool CDate::hasRelCalendar(void) const
223      { return (this->relCalendar != NULL); }
224
225      //----------------------------------------------------------------
226
227      /*!
228        Get the number of seconds since the beginning of the year.
229        \return the number of seconds since the beginning of the year.
230      */
231      int CDate::getSecondOfYear() const
232      {
233        CDate yearStart(*this);
234        const CCalendar& c = getRelCalendar();
235        int nbDay = 0;
236
237        for (yearStart.setMonth(1); yearStart.getMonth() < getMonth(); yearStart.setMonth(yearStart.getMonth() + 1))
238          nbDay += c.getMonthLength(yearStart);
239
240        return ((((nbDay + getDay() - 1) * c.getDayLength() + getHour()) * c.getHourLength()
241                   + getMinute()) * c.getMinuteLength() + getSecond());
242      }
243
244      /*!
245        Get the number of days (expressed as a real number) since the beginning of the year.
246        \return the number of days (expressed as a real number) since the beginning of the year.
247      */
248      double CDate::getDayOfYear() const
249      {
250        return double(getSecondOfYear()) / getRelCalendar().getDayLengthInSeconds();
251      }
252
253      /*!
254        Get the fraction of the current year as a real number between 0 and 1.
255        \return the fraction of the current year.
256      */
257      double CDate::getFractionOfYear() const
258      {
259        return double(getSecondOfYear()) / getRelCalendar().getYearTotalLength(*this);
260      }
261
262      /*!
263        Get the number of seconds since the beginning of the day.
264        \return the number of seconds since the beginning of the day.
265      */
266      int CDate::getSecondOfDay() const
267      {
268        const CCalendar& c = getRelCalendar();
269        return ((getHour() * c.getHourLength() + getMinute()) * c.getMinuteLength() + getSecond());
270      }
271
272      /*!
273        Get the fraction of the current day as a real number between 0 and 1.
274        \return the fraction of the current day.
275      */
276      double CDate::getFractionOfDay() const
277      {
278        return double(getSecondOfDay()) / getRelCalendar().getDayLengthInSeconds();
279      }
280
281      //----------------------------------------------------------------
282
283      void CDate::setYear  (int newyear)   { this->year   = newyear; }
284      void CDate::setMonth (int newmonth)  { this->month  = newmonth; }
285      void CDate::setDay   (int newday)    { this->day    = newday; }
286      void CDate::setHour  (int newhour)   { this->hour   = newhour; }
287      void CDate::setMinute(int newminute) { this->minute = newminute; }
288      void CDate::setSecond(int newsecond) { this->second = newsecond; }
289
290      void CDate::setDate(int yr, int mth, int d, int hr, int min, int sec)
291      {
292        this->year   = yr;
293        this->month  = mth;
294        this->day    = d;
295        this->hour   = hr;
296        this->minute = min;
297        this->second = sec;
298      }
299
300      //----------------------------------------------------------------
301
302      void CDate::addMonth(int value)
303      {// Value doit être égale à 1 ou -1.
304        this->month += value;
305        if (this->month == 13) { year++; this->month = 1; }
306        if (this->month == 0)  { year--; this->month = 12; }
307      }
308
309      //----------------------------------------------------------------
310
311      bool CDate::setRelCalendar(const CCalendar& relCalendar)
312      {
313        this->relCalendar = &relCalendar;
314        return this->checkDate();
315      }
316
317      //----------------------------------------------------------------
318
319      CDate CDate::FromString(const StdString& str, const CCalendar& calendar)
320      {
321        CDate dt(calendar);
322        StdIStringStream iss(str);
323        iss >> dt;
324        return dt;
325      }
326
327      //----------------------------------------------------------------
328
329      StdString CDate::getStryyyymmdd(void) const
330      {
331        std::streamsize s;
332        char c;
333
334        ostringstream oss;
335
336        s = oss.width(4); c = oss.fill('0'); oss << year;
337        s = oss.width(2); c = oss.fill('0'); oss << month;
338        s = oss.width(2); c = oss.fill('0'); oss << day;
339
340        return oss.str();
341      }
342
343      string CDate::getStr(const string& str) const
344      {
345        ostringstream oss;
346        int level;
347
348        level=0;
349        for(string::const_iterator it=str.begin();it!=str.end();++it)
350        {
351          if (level==0)
352          {
353            if (*it=='%') level++;
354            else oss<<*it;
355          }
356          else if (level==1)
357          {
358            switch (*it)
359            {
360              case 'y' :
361                oss.width(4); oss.fill('0'); oss << year;
362                level=0;
363                break;
364              case 'm' : // month or minute
365                level++;
366                break;
367              case 'd' :
368                oss.width(2); oss.fill('0'); oss << day;
369                level=0;
370                break;
371              case 'h' :
372                oss.width(2); oss.fill('0'); oss << hour;
373                level=0;
374                break;
375              case 's' :
376                oss.width(2); oss.fill('0'); oss << second;
377                level=0;
378                break;
379              case 'S' : // seconds since time origin
380                oss.width(0); oss << Time(*this);
381                level=0;
382                break;
383              case 'D' : // days since time origin
384                oss.width(0); oss << Time(*this) / getRelCalendar().getDayLengthInSeconds();
385                level=0;
386                break;
387              default :
388                oss<<'%'<<*it;
389                level=0;
390            }
391          }
392          else if (level==2)
393          {
394            switch (*it)
395            {
396              case 'o' : // month
397                oss.width(2); oss.fill('0'); oss << month;
398                level=0;
399                break;
400              case 'i' : //minute
401                oss.width(2); oss.fill('0'); oss << minute;
402                level=0;
403                break;
404              default :
405                oss<<"%m"<<*it;
406                level=0;
407            }
408          }
409        }
410        return oss.str();
411      }
412
413      StdString CDate::toString(void) const
414      {
415        StdOStringStream oss;
416        oss << *this;
417        return oss.str();
418      }
419
420      ///---------------------------------------------------------------
421
422} // namespace xios
Note: See TracBrowser for help on using the repository browser.