source: XIOS/dev/branch_openmp/src/date/user_defined.cpp @ 1501

Last change on this file since 1501 was 591, checked in by rlacroix, 9 years ago

Remove leftovers from the XMLIO age.

File size: 9.7 KB
Line 
1#include "user_defined.hpp"
2#include "calendar_util.hpp"
3
4namespace xios
5{
6  /// ////////////////////// Définitions ////////////////////// ///
7
8  CUserDefinedCalendar::CUserDefinedCalendar(int dayLength, const CArray<int,1>& monthLengths)
9    : CCalendar("user_defined")
10    , dayLength(dayLength)
11    , monthLengths(monthLengths)
12    , yearLength(sum(monthLengths) * dayLength)
13    , leapYearMonth(0)
14    , leapYearDrift(0.0)
15    , leapYearDriftOffset(0.0)
16  {
17    if (dayLength <= 0)
18      ERROR("CUserDefinedCalendar::CUserDefinedCalendar(int dayLength, const CArray<int,1>& monthLengths)",
19            << "The day length must be strictly positive.");
20    if (monthLengths.numElements() == 0)
21      ERROR("CUserDefinedCalendar::CUserDefinedCalendar(int dayLength, const CArray<int,1>& monthLengths)",
22            << "The month lengths must be specified.");
23    if (min(monthLengths) <= 0)
24      ERROR("CUserDefinedCalendar::CUserDefinedCalendar(int dayLength, const CArray<int,1>& monthLengths)",
25            << "All month lengths must be strictly positive.");
26    // Ensure that the month lengths array is always 0-indexed
27    this->monthLengths.reindexSelf(TinyVector<int, 1>(0));
28  }
29
30  CUserDefinedCalendar::CUserDefinedCalendar(int dayLength, int yearLength)
31    : CCalendar("user_defined")
32    , dayLength(dayLength)
33    , yearLength(yearLength)
34    , leapYearMonth(0)
35    , leapYearDrift(0.0)
36    , leapYearDriftOffset(0.0)
37  {
38    if (dayLength <= 0)
39      ERROR("CUserDefinedCalendar::CUserDefinedCalendar(int dayLength, int yearLength)",
40            << "The day length must be strictly positive.");
41    if (yearLength <= 0)
42      ERROR("CUserDefinedCalendar::CUserDefinedCalendar(int dayLength, int yearLength)",
43            << "The year length must be strictly positive.");
44  }
45
46  CUserDefinedCalendar::~CUserDefinedCalendar(void)
47  { /* Nothing to do here */ }
48
49  ///--------------------------------------------------------------
50
51  void CUserDefinedCalendar::configureLeapYear(int leapYearMonth, double leapYearDrift, double leapYearDriftOffset /*= 0.0*/)
52  {
53    if (monthLengths.numElements() == 0)
54      ERROR("void CUserDefinedCalendar::configureLeapYear(int leapYearMonth, double leapYearDrift, double leapYearDriftOffset /*= 0.0*/)",
55            << "Impossible to define leap years on a calendar without months.");
56    if (leapYearMonth < 1 || leapYearMonth > monthLengths.numElements())
57      ERROR("void CUserDefinedCalendar::configureLeapYear(int leapYearMonth, double leapYearDrift, double leapYearDriftOffset /*= 0.0*/)",
58            << "The month chosen for the additional day must be in the range [1, " << monthLengths.numElements() << "].");
59    if (leapYearDrift < 0.0 || leapYearDrift >= 1.0)
60      ERROR("void CUserDefinedCalendar::configureLeapYear(int leapYearMonth, double leapYearDrift, double leapYearDriftOffset /*= 0.0*/)",
61            << "The year drift must be in the range [0.0, 1.0).");
62    if (leapYearDriftOffset < 0.0 || leapYearDriftOffset >= 1.0)
63      ERROR("void CUserDefinedCalendar::configureLeapYear(int leapYearMonth, double leapYearDrift, double leapYearDriftOffset /*= 0.0*/)",
64            << "The year drift offset must be in the range [0.0, 1.0).");
65
66    this->leapYearMonth = leapYearMonth;
67    this->leapYearDrift = leapYearDrift;
68    this->leapYearDriftOffset = leapYearDriftOffset;
69  }
70
71  ///--------------------------------------------------------------
72
73  StdString CUserDefinedCalendar::getType(void) const { return StdString("user_defined"); }
74
75  /*! Returns the duration of the date's month in days. */
76  int CUserDefinedCalendar::getMonthLength(const CDate& date) const
77  {
78    int monthLength = 0;
79    if (monthLengths.numElements() > 0)
80    {
81      monthLength = monthLengths(date.getMonth() - 1);
82      if (date.getMonth() == leapYearMonth && isLeapYear(date.getYear()))
83        monthLength++;
84    }
85    return monthLength;
86  }
87
88  /*! Returns the duration of the date's year in seconds. */
89  int CUserDefinedCalendar::getYearTotalLength(const CDate& date) const
90  {
91    return isLeapYear(date.getYear()) ? yearLength + dayLength : yearLength;
92  }
93
94  /*! Returns the duration of a day in hours. Note that this value is
95      not necessarily exact since it can be rounded but it is always
96      the smallest integer number of hours that can hold a day. */
97  int CUserDefinedCalendar::getDayLength(void) const { return int(ceil(double(dayLength) / (getHourLength() * getMinuteLength()))); }
98
99  /*! Returns the duration of a year in months. */
100  int CUserDefinedCalendar::getYearLength(void) const { return monthLengths.numElements(); }
101
102  /*! Returns the day length expressed in seconds. */
103  int CUserDefinedCalendar::getDayLengthInSeconds(void) const { return dayLength; }
104
105  /*! Test if the calendar can have leap year. */
106  bool CUserDefinedCalendar::hasLeapYear() const{ return (leapYearDrift != 0.0); }
107
108  /*!
109    Test if the specified year is a leap year.
110    \param year the year to be tested
111    \return true if and only the specified year is a leap year
112  */
113  bool CUserDefinedCalendar::isLeapYear(int year) const
114  {
115    double intPart; // dummy variable
116    return (hasLeapYear() &&
117            (abs(1.0 - (modf(leapYearDriftOffset + (year - getTimeOrigin().getYear()) * leapYearDrift, &intPart) + leapYearDrift)) < 1e-14));
118  }
119
120  CDuration& CUserDefinedCalendar::resolve(CDuration& dur, bool noNegativeTime /*= false*/) const
121  {
122    if (monthLengths.numElements() > 0) // normal case, we can rely on the generic function
123      return CCalendar::resolve(dur, noNegativeTime);
124
125    if (dur.month)
126      ERROR("CDuration& CUserDefinedCalendar::resolve(CDuration& dur, bool noNegativeTime /*= false*/) const",
127            << "month = " << dur.month << " but the user defined calendar has no month.");
128
129    const int hourLengthInSeconds = getHourLength() * getMinuteLength();
130
131    // Convert everything in seconds
132    Time t = Time(dur.year * yearLength + dur.day * dayLength
133                    + (dur.hour * getHourLength() + dur.minute) * getMinuteLength() + dur.second);
134
135    // Then convert back to years
136    dur.year = int(t / yearLength);
137    t %= yearLength;
138
139    // days
140    dur.day = int(t / dayLength);
141    t %= dayLength;
142
143    // Do we allow hour, minute, second to be negative?
144    if (noNegativeTime)
145    {
146      // If we don't, we remove some days or years until the time is positive
147      double& val = (dayLength < yearLength) ? dur.day : dur.year;
148      int length  = (dayLength < yearLength) ? dayLength : yearLength;
149      while (t < 0)
150      {
151        t += length;
152        val -= 1.0;
153      }
154    }
155
156    // hours
157    dur.hour = int(t / hourLengthInSeconds);
158    t %= hourLengthInSeconds;
159    // minutes
160    dur.minute = int(t / getMinuteLength());
161    // secondes
162    dur.second = int(t % getMinuteLength());
163
164    return dur;
165  }
166
167  /*! Parse a date using the calendar's parser. */
168  void CUserDefinedCalendar::parseDate(StdIStream& in, CDate& date) const
169  {
170    if (monthLengths.numElements() > 0) // normal case, we can rely on the generic function
171    {
172      CCalendar::parseDate(in, date);
173      return;
174    }
175
176    char sep = '-'; // Le caractÚre c est utilisé pour "recueillir" les séparateurs "/" et ":".
177    char c;
178
179    // Default initialize the date
180    int year = 00, month  = 01, day    = 01;
181    int hour = 00, minute = 00, second = 00;
182
183    // Read the year
184    in >> year;
185    // Read the day only if the day length is smaller than the year length
186    c = in.get();
187    if (c == sep && dayLength < yearLength)
188    {
189      in >> day;
190      c = in.get();
191    }
192    // Read the time
193    sep = ' ';
194    if (c == sep)
195    {
196      in >> hour >> c;
197      sep = ':';
198      if (c == sep)
199      {
200        in >> minute >> c;
201        if (c == sep)
202        {
203          in >> second;
204          in >> c;
205        }
206      }
207    }
208
209    date.setDate(year, month, day, hour, minute, second);
210
211    // Delay the verification until we get a calendar we can compare the date to
212    if (!checkDate(date))
213      ERROR("void CUserDefinedCalendar::parseDate(StdIStream& in, CDate& date) const",
214            << "Bad date format or not conform to calendar");
215
216    if (c == '+') // We will be adding a duration to the date
217    {
218      CDuration dur;
219      in >> dur;
220      date = date + dur;
221    }
222    else if (!in.eof())
223      ERROR("void CUserDefinedCalendar::parseDate(StdIStream& in, CDate& date) const",
224            << "Invalid date format: unexpected trailing character(s)");
225  }
226
227  /*! Test if a date is valid with regard to the current calendar. */
228  bool CUserDefinedCalendar::checkDate(CDate& date) const
229  {
230    if (monthLengths.numElements() > 0) // normal case, we can rely on the generic function
231      return CCalendar::checkDate(date);
232
233    const int maxDay = (yearLength + dayLength - 1) / dayLength;
234
235    bool isValid = true;
236
237    // Vérification de la valeur du mois.
238    if (date.getMonth() != 1)
239    { isValid = false; date.setMonth(1); }
240
241    // Vérification de la valeur du jour.
242    if (date.getDay() < 1)
243    { isValid = false; date.setDay(1); }
244    else if (date.getDay() > maxDay)
245    { isValid = false; date.setDay(maxDay); }
246
247    // Vérification de la valeur de l'heure.
248    if (date.getHour() < 0)
249    { isValid = false; date.setHour(0); }
250    else if (date.getHour() >= getDayLength())
251    { isValid = false; date.setHour(getDayLength() - 1); }
252
253    // Vérification de la valeur des minutes.
254    if (date.getMinute() < 0)
255    { isValid = false; date.setMinute(0); }
256    else if (date.getMinute() >= getHourLength())
257    { isValid = false; date.setMinute(getHourLength() - 1); }
258
259    // Vérification de la valeur des secondes.
260    if (date.getSecond() < 0)
261    { isValid = false; date.setSecond(0); }
262    else if (date.getSecond() >= getMinuteLength())
263    { isValid = false; date.setSecond(getMinuteLength() - 1); }
264
265    return isValid;
266  }
267} // namespace xios
268
Note: See TracBrowser for help on using the repository browser.