1 /*
2 MDAL - Mesh Data Abstraction Library (MIT License)
3 Copyright (C) 2019 Vincent Cloarec (vcloarec at gmail dot com)
4 */
5 #include "mdal_datetime.hpp"
6 #include "mdal_utils.hpp"
7
8
9 constexpr double MILLISECONDS_IN_SECOND = 1000;
10 constexpr double MILLISECONDS_IN_MINUTE = 1000 * 60;
11 constexpr double MILLISECONDS_IN_HOUR = 1000 * 60 * 60;
12 constexpr double MILLISECONDS_IN_DAY = 1000 * 60 * 60 * 24;
13 constexpr double MILLISECONDS_IN_WEEK = 1000 * 60 * 60 * 24 * 7;
14
15 //https://www.unidata.ucar.edu/software/netcdf-java/current/CDM/CalendarDateTime.html
16 constexpr double MILLISECONDS_IN_EXACT_YEAR = 3.15569259747e10; //CF Compliant
17 constexpr double MILLISECONDS_IN_MONTH_CF = MILLISECONDS_IN_EXACT_YEAR / 12.0; //CF Compliant
18
19 MDAL::DateTime::DateTime() = default;
20
DateTime(int year,int month,int day,int hours,int minutes,double seconds,MDAL::DateTime::Calendar calendar)21 MDAL::DateTime::DateTime( int year, int month, int day, int hours, int minutes, double seconds, MDAL::DateTime::Calendar calendar )
22 {
23 DateTimeValues value{year, month, day, hours, minutes, seconds};
24
25 switch ( calendar )
26 {
27 case MDAL::DateTime::Gregorian:
28 setWithGregorianJulianCalendarDate( value );
29 break;
30 case MDAL::DateTime::ProlepticGregorian:
31 setWithGregorianCalendarDate( value );
32 break;
33 case MDAL::DateTime::Julian:
34 setWithJulianCalendarDate( value );
35 break;
36 }
37 }
38
DateTime(double value,Epoch epoch)39 MDAL::DateTime::DateTime( double value, Epoch epoch ): mValid( true )
40 {
41 switch ( epoch )
42 {
43 case MDAL::DateTime::Unix:
44 mJulianTime = ( DateTime( 1970, 01, 01, 0, 0, 0, Gregorian ) + RelativeTimestamp( value, RelativeTimestamp::seconds ) ).mJulianTime;
45 break;
46 case MDAL::DateTime::JulianDay:
47 mJulianTime = int64_t( value * MILLISECONDS_IN_DAY + 0.5 );
48 break;
49 }
50 }
51
DateTime(const std::string & fromISO8601)52 MDAL::DateTime::DateTime( const std::string &fromISO8601 )
53 {
54 std::vector<std::string> splitedDateTime = split( fromISO8601, 'T' );
55
56 if ( splitedDateTime.size() != 2 )
57 return;
58 //parse date
59 std::vector<std::string> splitedDate = split( splitedDateTime.at( 0 ), '-' );
60 if ( splitedDate.size() != 3 )
61 return;
62
63 //parse time
64 splitedDateTime[1] = replace( splitedDateTime.at( 1 ), "Z", "", ContainsBehaviour::CaseInsensitive );
65 std::vector<std::string> splitedTime = split( splitedDateTime.at( 1 ), ':' );
66 if ( splitedTime.size() < 2 || splitedTime.size() > 3 )
67 return;
68
69 DateTimeValues dateTimeValues;
70 dateTimeValues.year = toInt( splitedDate[0] );
71 dateTimeValues.month = toInt( splitedDate[1] );
72 dateTimeValues.day = toInt( splitedDate[2] );
73 dateTimeValues.hours = toInt( splitedTime[0] );
74 dateTimeValues.minutes = toInt( splitedTime[1] );
75 if ( splitedTime.size() == 3 )
76 dateTimeValues.seconds = toDouble( splitedTime[2] );
77 else
78 dateTimeValues.seconds = 0.0;
79
80 setWithGregorianCalendarDate( dateTimeValues );
81 }
82
toStandardCalendarISO8601() const83 std::string MDAL::DateTime::toStandardCalendarISO8601() const
84 {
85 if ( mValid )
86 {
87 DateTimeValues value = dateTimeGregorianProleptic();
88 if ( value.year > 0 )
89 return toString( value );
90 }
91
92 return "";
93 }
94
toJulianDay() const95 double MDAL::DateTime::toJulianDay() const
96 {
97 return mJulianTime / MILLISECONDS_IN_DAY;
98 }
99
toJulianDayString() const100 std::string MDAL::DateTime::toJulianDayString() const
101 {
102 return std::to_string( toJulianDay() );
103 }
104
expandToCalendarArray() const105 std::vector<int> MDAL::DateTime::expandToCalendarArray() const
106 {
107 std::vector<int> dateTimeArray( 6, 0 );
108 if ( mValid )
109 {
110 DateTimeValues value = dateTimeGregorianProleptic();
111 dateTimeArray[0] = value.year;
112 dateTimeArray[1] = value.month;
113 dateTimeArray[2] = value.day;
114 dateTimeArray[3] = value.hours;
115 dateTimeArray[4] = value.minutes;
116 dateTimeArray[5] = int( value.seconds + 0.5 );
117 }
118
119 return dateTimeArray;
120 }
121
122
operator +(const MDAL::RelativeTimestamp & duration) const123 MDAL::DateTime MDAL::DateTime::operator+( const MDAL::RelativeTimestamp &duration ) const
124 {
125 if ( !mValid )
126 return DateTime();
127 return DateTime( mJulianTime + duration.mDuration );
128 }
129
130
operator -(const MDAL::RelativeTimestamp & duration) const131 MDAL::DateTime MDAL::DateTime::operator-( const MDAL::RelativeTimestamp &duration ) const
132 {
133 if ( !mValid )
134 return DateTime();
135 return DateTime( mJulianTime - duration.mDuration );
136 }
137
operator ==(const MDAL::DateTime & other) const138 bool MDAL::DateTime::operator==( const MDAL::DateTime &other ) const
139 {
140 if ( !mValid && !other.mValid )
141 return true;
142
143 return ( mValid && other.mValid ) && ( mJulianTime == other.mJulianTime );
144 }
145
operator <(const MDAL::DateTime & other) const146 bool MDAL::DateTime::operator<( const MDAL::DateTime &other ) const
147 {
148 if ( !mValid && !other.mValid )
149 return false;
150 return ( mValid && other.mValid ) && ( mJulianTime < other.mJulianTime );
151 }
152
isValid() const153 bool MDAL::DateTime::isValid() const { return mValid; }
154
DateTime(int64_t julianTime)155 MDAL::DateTime::DateTime( int64_t julianTime ): mJulianTime( julianTime ), mValid( true )
156 {}
157
158 /*
159 MDAL::DateTime::DateTimeValues MDAL::DateTime::dateTimeGregorianJulianCalendar() const
160 {
161 // https://fr.wikipedia.org/wiki/Jour_julien
162 DateTimeValues values;
163 int Z = int( mJulianTime / MILLISECONDS_IN_DAY + 0.5 ); // integer part of julian days count
164 double F = ( mJulianTime - MILLISECONDS_IN_DAY * ( Z - 0.5 ) ) / MILLISECONDS_IN_DAY; // fractional part of julian days count;
165 int S;
166
167 if ( Z < 2299161 )
168 S = Z;
169 else
170 {
171 int alpha = int( ( Z - 1867216.25 ) / 36524.25 );
172 S = Z + 1 + alpha - int( alpha / 4 );
173 }
174
175 int B = S + 1524;
176 int C = int( ( B - 122.1 ) / 365.25 );
177 int D = int( 365.25 * C );
178 int E = int( ( B - D ) / 30.6001 );
179
180 values.day = B - D - int( 30.6001 * E );
181 if ( E < 14 )
182 values.month = E - 1;
183 else
184 values.month = E - 13;
185
186 if ( values.month > 2 )
187 values.year = C - 4716;
188 else
189 values.year = C - 4715;
190
191 values.hours = int( F / MILLISECONDS_IN_HOUR );
192 F = int( F - values.hours * MILLISECONDS_IN_HOUR );
193 values.minutes = int( F / MILLISECONDS_IN_MINUTE );
194 F = int( F - values.minutes * MILLISECONDS_IN_MINUTE );
195 values.seconds = int( F / MILLISECONDS_IN_SECOND );
196
197 return values;
198 }
199 */
200
dateTimeGregorianProleptic() const201 MDAL::DateTime::DateTimeValues MDAL::DateTime::dateTimeGregorianProleptic() const
202 {
203 // https://fr.wikipedia.org/wiki/Jour_julien
204 DateTimeValues values;
205 int Z = int( mJulianTime / MILLISECONDS_IN_DAY + 0.5 ); // integer part of julian days count
206 int F = int( mJulianTime - MILLISECONDS_IN_DAY * ( Z - 0.5 ) ) ; // fractional part of julian days count in ms;
207
208 int alpha = int( ( Z - 1867216.25 ) / 36524.25 );
209 int S = Z + 1 + alpha - int( alpha / 4 );
210
211 int B = S + 1524;
212 int C = int( ( B - 122.1 ) / 365.25 );
213 int D = int( 365.25 * C );
214 int E = int( ( B - D ) / 30.6001 );
215
216 values.day = B - D - int( 30.6001 * E );
217 if ( E < 14 )
218 values.month = E - 1;
219 else
220 values.month = E - 13;
221
222 if ( values.month > 2 )
223 values.year = C - 4716;
224 else
225 values.year = C - 4715;
226
227 values.hours = int( F / MILLISECONDS_IN_HOUR );
228 F = int( F - values.hours * MILLISECONDS_IN_HOUR );
229 values.minutes = int( F / MILLISECONDS_IN_MINUTE );
230 F = int( F - values.minutes * MILLISECONDS_IN_MINUTE );
231 values.seconds = int( F / MILLISECONDS_IN_SECOND );
232
233 return values;
234 }
235
236
setWithGregorianCalendarDate(MDAL::DateTime::DateTimeValues values)237 void MDAL::DateTime::setWithGregorianCalendarDate( MDAL::DateTime::DateTimeValues values )
238 {
239 // https://quasar.as.utexas.edu/BillInfo/JulianDatesG.html
240 if ( values.month <= 2 )
241 {
242 values.year--;
243 values.month += 12;
244 }
245
246 int A = values.year / 100;
247 int B = A / 4;
248 int C = 2 - A + B;
249 int E = int( 365.25 * ( values.year + 4716 ) );
250 int F = int( 30.6001 * ( values.month + 1 ) );
251 double julianDay = C + values.day + E + F - 1524.5;
252
253 mValid = true;
254 mJulianTime = int64_t( julianDay * MILLISECONDS_IN_DAY +
255 ( values.hours ) * MILLISECONDS_IN_HOUR +
256 values.minutes * MILLISECONDS_IN_MINUTE +
257 values.seconds * MILLISECONDS_IN_SECOND );
258 }
259
setWithJulianCalendarDate(MDAL::DateTime::DateTimeValues values)260 void MDAL::DateTime::setWithJulianCalendarDate( MDAL::DateTime::DateTimeValues values )
261 {
262 // https://quasar.as.utexas.edu/BillInfo/JulianDatesG.html
263 if ( values.month <= 2 )
264 {
265 values.year--;
266 values.month += 12;
267 }
268
269 int E = int( 365.25 * ( values.year + 4716 ) );
270 int F = int( 30.6001 * ( values.month + 1 ) );
271 double julianDay = values.day + E + F - 1524.5;
272
273 mValid = true;
274 mJulianTime = int64_t( julianDay * MILLISECONDS_IN_DAY +
275 ( values.hours ) * MILLISECONDS_IN_HOUR +
276 values.minutes * MILLISECONDS_IN_MINUTE +
277 values.seconds * MILLISECONDS_IN_SECOND );
278 }
279
setWithGregorianJulianCalendarDate(MDAL::DateTime::DateTimeValues values)280 void MDAL::DateTime::setWithGregorianJulianCalendarDate( MDAL::DateTime::DateTimeValues values )
281 {
282 // https://quasar.as.utexas.edu/BillInfo/JulianDatesG.html
283
284 mValid = true;
285
286 if ( values.year > 1582 ||
287 ( values.year == 1582 && ( values.month > 10 || ( values.month == 10 && values.day >= 15 ) ) ) ) // gregorian calendar
288 {
289 setWithGregorianCalendarDate( values );
290 }
291 else
292 setWithJulianCalendarDate( values );
293 }
294
toString(MDAL::DateTime::DateTimeValues values) const295 std::string MDAL::DateTime::toString( MDAL::DateTime::DateTimeValues values ) const
296 {
297 int milliseconds = int( ( values.seconds - int( values.seconds ) ) * 1000 + 0.5 );
298 std::string msStr;
299 if ( milliseconds > 0 )
300 {
301 if ( milliseconds < 10 )
302 msStr = prependZero( std::to_string( milliseconds ), 3 );
303 else if ( milliseconds < 100 )
304 msStr = prependZero( std::to_string( milliseconds ), 2 );
305 else if ( milliseconds < 1000 )
306 msStr = std::to_string( milliseconds );
307
308 msStr = std::string( "," ).append( msStr );
309 }
310
311 std::string strDateTime = prependZero( std::to_string( values.year ), 4 ) + "-" +
312 prependZero( std::to_string( values.month ), 2 ) + "-" +
313 prependZero( std::to_string( values.day ), 2 ) + "T" +
314 prependZero( std::to_string( values.hours ), 2 ) + ":" +
315 prependZero( std::to_string( values.minutes ), 2 ) + ":" +
316 prependZero( std::to_string( int( values.seconds ) ), 2 ) +
317 msStr;
318
319 return strDateTime;
320 }
321
operator -(const MDAL::DateTime & other) const322 MDAL::RelativeTimestamp MDAL::DateTime::operator-( const MDAL::DateTime &other ) const
323 {
324 if ( !mValid || !other.mValid )
325 return RelativeTimestamp();
326 return RelativeTimestamp( mJulianTime - other.mJulianTime );
327 }
328
329
330 MDAL::RelativeTimestamp::RelativeTimestamp() = default;
331
RelativeTimestamp(double duration,MDAL::RelativeTimestamp::Unit unit)332 MDAL::RelativeTimestamp::RelativeTimestamp( double duration, MDAL::RelativeTimestamp::Unit unit )
333 {
334 switch ( unit )
335 {
336 case MDAL::RelativeTimestamp::milliseconds:
337 mDuration = int64_t( duration );
338 break;
339 case MDAL::RelativeTimestamp::seconds:
340 mDuration = int64_t( duration * MILLISECONDS_IN_SECOND + 0.5 );
341 break;
342 case MDAL::RelativeTimestamp::minutes:
343 mDuration = int64_t( duration * MILLISECONDS_IN_MINUTE + 0.5 );
344 break;
345 case MDAL::RelativeTimestamp::hours:
346 mDuration = int64_t( duration * MILLISECONDS_IN_HOUR + 0.5 );
347 break;
348 case MDAL::RelativeTimestamp::days:
349 mDuration = int64_t( duration * MILLISECONDS_IN_DAY + 0.5 );
350 break;
351 case MDAL::RelativeTimestamp::weeks:
352 mDuration = int64_t( duration * MILLISECONDS_IN_WEEK + 0.5 );
353 break;
354 case MDAL::RelativeTimestamp::months_CF:
355 mDuration = int64_t( duration * MILLISECONDS_IN_MONTH_CF + 0.5 );
356 break;
357 case MDAL::RelativeTimestamp::exact_years:
358 mDuration = int64_t( duration * MILLISECONDS_IN_EXACT_YEAR + 0.5 );
359 break;
360 }
361 }
362
value(MDAL::RelativeTimestamp::Unit unit) const363 double MDAL::RelativeTimestamp::value( MDAL::RelativeTimestamp::Unit unit ) const
364 {
365 switch ( unit )
366 {
367 case MDAL::RelativeTimestamp::milliseconds:
368 return double( mDuration );
369 case MDAL::RelativeTimestamp::seconds:
370 return mDuration / MILLISECONDS_IN_SECOND;
371 case MDAL::RelativeTimestamp::minutes:
372 return mDuration / MILLISECONDS_IN_MINUTE;
373 case MDAL::RelativeTimestamp::hours:
374 return mDuration / MILLISECONDS_IN_HOUR;
375 case MDAL::RelativeTimestamp::days:
376 return double( mDuration ) / MILLISECONDS_IN_DAY;
377 case MDAL::RelativeTimestamp::weeks:
378 return double( mDuration ) / MILLISECONDS_IN_WEEK;
379 case MDAL::RelativeTimestamp::months_CF:
380 return double( mDuration ) / MILLISECONDS_IN_MONTH_CF;
381 case MDAL::RelativeTimestamp::exact_years:
382 return double( mDuration ) / MILLISECONDS_IN_EXACT_YEAR;
383 }
384
385 return 0;
386 }
387
operator ==(const MDAL::RelativeTimestamp & other) const388 bool MDAL::RelativeTimestamp::operator==( const MDAL::RelativeTimestamp &other ) const
389 {
390 return mDuration == other.mDuration;
391 }
392
operator <(const MDAL::RelativeTimestamp & other) const393 bool MDAL::RelativeTimestamp::operator<( const MDAL::RelativeTimestamp &other ) const
394 {
395 return mDuration < other.mDuration;
396 }
397
RelativeTimestamp(int64_t ms)398 MDAL::RelativeTimestamp::RelativeTimestamp( int64_t ms ): mDuration( ms )
399 {}
400