source: trunk/FACT++/src/Time.cc@ 20086

Last change on this file since 20086 was 19430, checked in by tbretz, 6 years ago
Added the GetNext/Prev also for SunSet.
File size: 16.6 KB
Line 
1// **************************************************************************
2/** @class Time
3
4@brief Adds some functionality to boost::posix_time::ptime for our needs
5
6This is basically a wrapper around boost::posix_time::ptime which is made
7to adapt the functionality to our needs. Time can store the current
8data and time with a precision up to nanoseconds if provided by the
9undrlaying system, otherwise microsecond precision is used.
10
11It main purpose is to provide needed constructors and simplyfy the
12conversion of dates and times from and to a string/stream.
13
14Note that posix_time (as Posix times have) has a limited range. You cannot
15use it for example for very early years of the last century.
16
17@section Examples
18
19 - An example can be found in \ref time.cc
20
21@section References
22
23 - <A HREF="http://www.boost.org/doc/libs/1_45_0/doc/html/date_time.html">BOOST++ date_time (V1.45.0)</A>
24
25**/
26// **************************************************************************
27#include "Time.h"
28
29using namespace std;
30using namespace boost::posix_time;
31
32#ifdef HAVE_NOVA
33#include "externals/nova.h"
34#endif
35
36const boost::gregorian::date Time::fUnixOffset(1970, 1, 1);
37
38const Time Time::None(Time::none);
39
40// strftime
41const _time_format Time::reset = 0;
42const _time_format Time::def = "%c";
43const _time_format Time::std = "%x %X%F";
44const _time_format Time::sql = "%Y-%m-%d %H:%M:%S.%f";
45const _time_format Time::ssql = "%Y-%m-%d %H:%M:%S";
46const _time_format Time::iso = "%Y-%m-%dT%H:%M:%S%F%q";
47const _time_format Time::magic = "%Y %m %d %H %M %S %f";
48const _time_format Time::smagic = "%Y %m %d %H %M %S";
49
50// --------------------------------------------------------------------------
51//
52//! Construct a Time object with either UTC or local time, or without any
53//! particular time.
54//!
55//! @param typ
56//! enum as defined in Time::init_t
57//
58Time::Time(enum init_t typ)
59{
60 switch (typ)
61 {
62 case utc:
63 *this = microsec_clock::universal_time();
64 break;
65 case local:
66 *this = microsec_clock::local_time();
67 break;
68 case none:
69 break;
70 }
71}
72
73
74// --------------------------------------------------------------------------
75//
76//! Construct a Time object with a date_time::special_value, e.g.
77//!
78//! - neg_infin
79//! - pos_infin
80//! - not_a_date_time
81//! - max_date_time
82//! - min_date_time
83//!
84//!
85//! @param val
86//! date_time::special_value
87//
88Time::Time(const boost::date_time::special_values &val) : ptime(val)
89{
90}
91
92// --------------------------------------------------------------------------
93//
94//! Construct a Time object from seconds since 1970/1/1 and number of
95//! milliseconds, as for example returned by gettimeofday()
96//!
97//! @param tm
98//! seconds since 1970/1/1
99//!
100//! @param millisec
101//! number of milliseconds
102//
103Time::Time(const time_t &tm, const suseconds_t &usec)
104: ptime(fUnixOffset, time_duration(0, 0, tm, usec*pow(10, time_duration::num_fractional_digits()-6)))
105{
106}
107
108// --------------------------------------------------------------------------
109//
110//! Construct a Time object from a struct timeval.
111//!
112//! @param tv
113//! struct timeval
114//!
115Time::Time(const timeval &tv)
116: ptime(fUnixOffset, time_duration(0, 0, tv.tv_sec, tv.tv_usec*pow(10, time_duration::num_fractional_digits()-6)))
117{
118}
119
120// --------------------------------------------------------------------------
121//
122//! Construct a Time from a date and time.
123//!
124//! @param year, month, day, hh, mm, ss, microsec
125//! A full date and time down to microsecond precision. From the end
126//! arguments can be omitted.
127//!
128Time::Time(short year, unsigned char month, unsigned char day,
129 unsigned char hh, unsigned char mm, unsigned char ss, unsigned int microsec)
130// Last argument is fractional_seconds ( correct with num_fractional_digits() )
131: ptime(boost::gregorian::date(year, month, day),
132 time_duration(hh, mm, ss, microsec*pow(10, time_duration::num_fractional_digits()-6)))
133{
134}
135
136// --------------------------------------------------------------------------
137//
138//! Set the Time object to a given MJD. Note that this involves
139//! conversion from double. So converting forth and back many many
140//! times might results in drifts.
141//!
142//! @param mjd
143//! Modified Julian Date
144//!
145void Time::Mjd(double mjd)
146{
147 if (mjd > 2400000.5)
148 mjd -= 2400000.5;
149
150 // Convert MJD to ticks since offset
151 mjd -= 40587;
152 mjd *= 24*60*60*time_duration::ticks_per_second();
153
154 *this = ptime(fUnixOffset, time_duration(0, 0, 0, mjd));
155}
156
157// --------------------------------------------------------------------------
158//
159//! @returns the seconds of the day including the fractional seconds.
160//!
161double Time::SecondsOfDay() const
162{
163 const time_duration tod = time_of_day();
164
165 const double frac = double(tod.fractional_seconds())/time_duration::ticks_per_second();
166 const double sec = tod.total_seconds()+frac;
167
168 return sec;
169}
170
171// --------------------------------------------------------------------------
172//
173//! Get the current MJD. Note that this involves
174//! conversion to double. So converting forth and back many many
175//! times might results in drifts.
176//!
177//! @returns
178//! Modified Julian Date
179//!
180double Time::Mjd() const
181{
182 return date().modjulian_day()+SecondsOfDay()/(24*60*60);
183
184 /*
185 const time_duration mjd = *this - ptime(fUnixOffset);
186 const double sec = mjd.total_seconds()+mjd.fractional_seconds()/1e6;
187 return sec/(24*60*60)+40587;
188 */
189}
190
191// --------------------------------------------------------------------------
192//
193// @returns seconds since 1970/1/1
194//
195double Time::UnixTime() const
196{
197 return (date().modjulian_day()-40587)*24*60*60 + SecondsOfDay();
198}
199
200// --------------------------------------------------------------------------
201//
202// @returns days since 1970/1/1
203//
204double Time::UnixDate() const
205{
206 return (date().modjulian_day()-40587) + SecondsOfDay()/(24*60*60);
207}
208
209// --------------------------------------------------------------------------
210//
211// @returns seconds since 1970/1/1
212//
213time_t Time::Time_t() const
214{
215 return (date().modjulian_day()-40587)*24*60*60 + time_of_day().total_seconds();
216}
217
218// --------------------------------------------------------------------------
219//
220//! @returns the time in a format needed for root's TAxis
221//!
222double Time::RootTime() const
223{
224 return (date().modjulian_day()-49718)*24*60*60 + SecondsOfDay();
225}
226
227// --------------------------------------------------------------------------
228//
229//! Returns a string with the contents of the Time object formated
230//! as defined in format.
231//!
232//! @param format
233//! format description of the string to be returned. For details
234//! see the boost documentation or the man page of strftime
235//!
236//! @returns
237//! A string with the time formatted as requested. Note some special
238//! strings might be returned in case the time is invalid.
239//
240string Time::GetAsStr(const char *format) const
241{
242 stringstream out;
243 out << Time::fmt(format) << *this;
244 return out.str();
245}
246
247// --------------------------------------------------------------------------
248//
249//! @returns
250//! a human readable string which complies with ISO 8601, in the
251//! "CCYY-MM-DDThh:mm:ss.f"
252//
253string Time::Iso() const
254{
255 stringstream out;
256 out << Time::iso << *this;
257 return out.str();
258}
259
260// --------------------------------------------------------------------------
261//
262//! Sets the time of the Time object to a time corresponding to
263//! the one given as argument. It is evaluated according to the given
264//! format.
265//!
266//! @param str
267//! The time as a string which should be converted to the Time object
268//!
269//! @param format
270//! format description of the string to be returned. For details
271//! see the boost documentation or the man page of strftime
272//!
273void Time::SetFromStr(const string &str, const char *format)
274{
275 // FIXME: exception handline
276 stringstream stream;
277 stream << str;
278 stream >> Time::fmt(format) >> *this;
279}
280
281string Time::MinutesTo(const Time &time) const
282{
283 ostringstream str;
284 if (time>*this)
285 str << time-*this;
286 else
287 str << *this-time;
288 return str.str().substr(0, 5);
289}
290
291string Time::SecondsTo(const Time &time) const
292{
293 ostringstream str;
294 if (time>*this)
295 str << time-*this;
296 else
297 str << *this-time;
298 return str.str().substr(str.str().substr(0, 3)=="00:" ? 3 : 0, 5);
299}
300
301// --------------------------------------------------------------------------
302//
303//! @returns
304//! The time of the previous sun-rise, relative to given horizon in degree,
305//! for the observatory location given (see nova.h).
306//! If the sun is circumpolar, it simply return the intgeral fraction of
307//! the current MJD.
308//! If libnova was not compiled in, it will return the next noon.
309//!
310//! @throws
311//! A runtime_error exception is thrown if the observatory location
312//! is not defined (see nova.h)
313//
314Time Time::GetPrevSunRise(double horizon, const string &obs) const
315{
316#ifdef HAVE_NOVA
317 const Nova::LnLatPosn posn(obs);
318 if (!posn.isValid())
319 throw runtime_error("Observatory location '"+obs+"' unknown.");
320
321 ln_rst_time sun_day;
322 if (ln_get_solar_rst_horizon(JD()-0.5, const_cast<Nova::LnLatPosn*>(&posn), horizon, &sun_day)==1)
323 return Time(floor(Mjd()));
324
325 if (Time(sun_day.rise)<*this)
326 return Time(sun_day.rise);
327
328 if (ln_get_solar_rst_horizon(JD()-1.5, const_cast<Nova::LnLatPosn*>(&posn), horizon, &sun_day)==1)
329 return Time(floor(Mjd()));
330
331 return Time(sun_day.rise);
332#else
333 return Time(floor(Mjd()-0.5)+0.5);
334#endif
335}
336
337// --------------------------------------------------------------------------
338//
339//! @returns
340//! The time of the next sun-rise, relative to given horizon in degree,
341//! for the observatory location given (see nova.h).
342//! If the sun is circumpolar, it simply return the intgeral fraction of
343//! the current MJD+1.
344//! If libnova was not compiled in, it will return the next noon.
345//!
346//! @throws
347//! A runtime_error exception is thrown if the observatory location
348//! is not defined (see nova.h)
349//
350Time Time::GetNextSunRise(double horizon, const string &obs) const
351{
352#ifdef HAVE_NOVA
353 const Nova::LnLatPosn posn(obs);
354 if (!posn.isValid())
355 throw runtime_error("Observatory location '"+obs+"' unknown.");
356
357 ln_rst_time sun_day;
358 if (ln_get_solar_rst_horizon(JD()-0.5, const_cast<Nova::LnLatPosn*>(&posn), horizon, &sun_day)==1)
359 return Time(floor(Mjd())+1);
360
361 if (Time(sun_day.rise)>=*this)
362 return Time(sun_day.rise);
363
364 if (ln_get_solar_rst_horizon(JD()+0.5, const_cast<Nova::LnLatPosn*>(&posn), horizon, &sun_day)==1)
365 return Time(floor(Mjd())+1);
366
367 return Time(sun_day.rise);
368#else
369 return Time(floor(Mjd()+0.5)+0.5);
370#endif
371}
372
373// --------------------------------------------------------------------------
374//
375//! Calls GetPrevSunRise(LN_SOLAR_STANDART_HORIZON)
376//
377Time Time::GetPrevSunRise(const string &obs) const
378{
379#ifdef HAVE_NOVA
380 return GetPrevSunRise(LN_SOLAR_STANDART_HORIZON, obs);
381#else
382 return GetPrevSunRise(-0.8333, obs);
383#endif
384}
385
386// --------------------------------------------------------------------------
387//
388//! Calls GetNextSunRise(LN_SOLAR_STANDART_HORIZON)
389//
390Time Time::GetNextSunRise(const string &obs) const
391{
392#ifdef HAVE_NOVA
393 return GetNextSunRise(LN_SOLAR_STANDART_HORIZON, obs);
394#else
395 return GetNextSunRise(-0.8333, obs);
396#endif
397}
398
399// --------------------------------------------------------------------------
400//
401//! @returns
402//! The time of the previous sun-set, relative to given horizon in degree,
403//! for the observatory location given (see nova.h).
404//! If the sun is circumpolar, it simply return the intgeral fraction of
405//! the current MJD.
406//! If libnova was not compiled in, it will return the next noon.
407//!
408//! @throws
409//! A runtime_error exception is thrown if the observatory location
410//! is not defined (see nova.h)
411//
412Time Time::GetPrevSunSet(double horizon, const string &obs) const
413{
414#ifdef HAVE_NOVA
415 const Nova::LnLatPosn posn(obs);
416 if (!posn.isValid())
417 throw runtime_error("Observatory location '"+obs+"' unknown.");
418
419 ln_rst_time sun_day;
420 if (ln_get_solar_rst_horizon(JD()-0.5, const_cast<Nova::LnLatPosn*>(&posn), horizon, &sun_day)==1)
421 return Time(floor(Mjd()));
422
423 if (Time(sun_day.set)<*this)
424 return Time(sun_day.set);
425
426 if (ln_get_solar_rst_horizon(JD()-1.5, const_cast<Nova::LnLatPosn*>(&posn), horizon, &sun_day)==1)
427 return Time(floor(Mjd()));
428
429 return Time(sun_day.set);
430#else
431 return Time(floor(Mjd()-0.5)+0.5);
432#endif
433}
434
435// --------------------------------------------------------------------------
436//
437//! @returns
438//! The time of the next sun-set, relative to given horizon in degree,
439//! for the observatory location given (see nova.h).
440//! If the sun is circumpolar, it simply return the intgeral fraction of
441//! the current MJD+1.
442//! If libnova was not compiled in, it will return the next noon.
443//!
444//! @throws
445//! A runtime_error exception is thrown if the observatory location
446//! is not defined (see nova.h)
447//
448Time Time::GetNextSunSet(double horizon, const string &obs) const
449{
450#ifdef HAVE_NOVA
451 const Nova::LnLatPosn posn(obs);
452 if (!posn.isValid())
453 throw runtime_error("Observatory location '"+obs+"' unknown.");
454
455 ln_rst_time sun_day;
456 if (ln_get_solar_rst_horizon(JD()-0.5, const_cast<Nova::LnLatPosn*>(&posn), horizon, &sun_day)==1)
457 return Time(floor(Mjd())+1);
458
459 if (Time(sun_day.set)>=*this)
460 return Time(sun_day.set);
461
462 if (ln_get_solar_rst_horizon(JD()+0.5, const_cast<Nova::LnLatPosn*>(&posn), horizon, &sun_day)==1)
463 return Time(floor(Mjd())+1);
464
465 return Time(sun_day.set);
466#else
467 return Time(floor(Mjd()+0.5)+0.5);
468#endif
469}
470
471// --------------------------------------------------------------------------
472//
473//! Calls GetPrevSunSet(LN_SOLAR_STANDART_HORIZON)
474//
475Time Time::GetPrevSunSet(const string &obs) const
476{
477#ifdef HAVE_NOVA
478 return GetPrevSunSet(LN_SOLAR_STANDART_HORIZON, obs);
479#else
480 return GetPrevSunSet(-0.8333, obs);
481#endif
482}
483
484// --------------------------------------------------------------------------
485//
486//! Calls GetNextSunSet(LN_SOLAR_STANDART_HORIZON)
487//
488Time Time::GetNextSunSet(const string &obs) const
489{
490#ifdef HAVE_NOVA
491 return GetNextSunSet(LN_SOLAR_STANDART_HORIZON, obs);
492#else
493 return GetNextSunSet(-0.8333, obs);
494#endif
495}
496
497// --------------------------------------------------------------------------
498//
499//! @returns
500//! Returns an int corresponding to the current sun-cycle, that means
501//! the day of the last sun-rise w.r.t. this Time.
502//! YYYYMMDD, e.g. 20111224 for Christmas eve 2011
503//!
504//! @remark
505//! Before March 30th 2013, 12:00 noon was the reference and the
506//! returned value belonged to the day of sun-set within the
507//! 24h period between two noon's.
508//
509uint32_t Time::NightAsInt(const string &obs) const
510{
511 const Time tm = GetPrevSunRise(obs);
512 return tm.Y()*10000 + tm.M()*100 + tm.D();
513}
514
515// --------------------------------------------------------------------------
516//
517//! A stream manipulator which sets the streams Time output format
518//! as defined in the argument.
519//!
520//! @param format
521//! format description of the manipulator be returned. For details
522//! see the boost documentation or the man page of strftime
523//!
524//! @returns
525//! a stream manipulator for the given format
526//!
527const _time_format Time::fmt(const char *format)
528{
529 return format;
530}
531
532// --------------------------------------------------------------------------
533//
534//! Sets the locale discription of the stream (the way how a time is
535//! output) to the format defined by the given manipulator.
536//!
537//! Example:
538//! \code
539//! Time t();
540//! cout << Time::fmt("%Y:%m:%d %H:%M:%S.%f") << t << endl;
541//! \endcode
542//!
543//! @param out
544//! Reference to the stream
545//!
546//! @param f
547//! Time format described by a manipulator
548//!
549//! @returns
550//! A reference to the stream
551//!
552ostream &operator<<(ostream &out, const _time_format &f)
553{
554 const locale loc(locale::classic(),
555 f.ptr==0 ? 0 : new time_facet(f.ptr));
556
557 out.imbue(loc);
558
559 return out;
560}
561
562// --------------------------------------------------------------------------
563//
564//! Sets the locale discription of the stream (the way how a time is
565//! input) to the format defined by the given manipulator.
566//!
567//! Example:
568//! \code
569//! stringstream s;
570//! s << "09.09.1974 21:59";
571//!
572//! Time t;
573//! s >> Time::fmt("%d.%m.%Y %H:%M") >> t;
574//! \endcode
575//!
576//! @param in
577//! Reference to the stream
578//!
579//! @param f
580//! Time format described by a manipulator
581//!
582//! @returns
583//! A reference to the stream
584//!
585istream &operator>>(istream &in, const _time_format &f)
586{
587 const locale loc(locale::classic(),
588 f.ptr==0 ? 0 : new time_input_facet(f.ptr));
589
590 in.imbue(loc);
591
592 return in;
593}
Note: See TracBrowser for help on using the repository browser.