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

Last change on this file since 19340 was 19215, checked in by tbretz, 6 years ago
The precompiler directive to check for libnova should be identical everywhere.
File size: 13.7 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//! Returns an int corresponding to the current sun-cycle, that means
403//! the day of the last sun-rise w.r.t. this Time.
404//! YYYYMMDD, e.g. 20111224 for Christmas eve 2011
405//!
406//! @remark
407//! Before March 30th 2013, 12:00 noon was the reference and the
408//! returned value belonged to the day of sun-set within the
409//! 24h period between two noon's.
410//
411uint32_t Time::NightAsInt(const string &obs) const
412{
413 const Time tm = GetPrevSunRise(obs);
414 return tm.Y()*10000 + tm.M()*100 + tm.D();
415}
416
417// --------------------------------------------------------------------------
418//
419//! A stream manipulator which sets the streams Time output format
420//! as defined in the argument.
421//!
422//! @param format
423//! format description of the manipulator be returned. For details
424//! see the boost documentation or the man page of strftime
425//!
426//! @returns
427//! a stream manipulator for the given format
428//!
429const _time_format Time::fmt(const char *format)
430{
431 return format;
432}
433
434// --------------------------------------------------------------------------
435//
436//! Sets the locale discription of the stream (the way how a time is
437//! output) to the format defined by the given manipulator.
438//!
439//! Example:
440//! \code
441//! Time t();
442//! cout << Time::fmt("%Y:%m:%d %H:%M:%S.%f") << t << endl;
443//! \endcode
444//!
445//! @param out
446//! Reference to the stream
447//!
448//! @param f
449//! Time format described by a manipulator
450//!
451//! @returns
452//! A reference to the stream
453//!
454ostream &operator<<(ostream &out, const _time_format &f)
455{
456 const locale loc(locale::classic(),
457 f.ptr==0 ? 0 : new time_facet(f.ptr));
458
459 out.imbue(loc);
460
461 return out;
462}
463
464// --------------------------------------------------------------------------
465//
466//! Sets the locale discription of the stream (the way how a time is
467//! input) to the format defined by the given manipulator.
468//!
469//! Example:
470//! \code
471//! stringstream s;
472//! s << "09.09.1974 21:59";
473//!
474//! Time t;
475//! s >> Time::fmt("%d.%m.%Y %H:%M") >> t;
476//! \endcode
477//!
478//! @param in
479//! Reference to the stream
480//!
481//! @param f
482//! Time format described by a manipulator
483//!
484//! @returns
485//! A reference to the stream
486//!
487istream &operator>>(istream &in, const _time_format &f)
488{
489 const locale loc(locale::classic(),
490 f.ptr==0 ? 0 : new time_input_facet(f.ptr));
491
492 in.imbue(loc);
493
494 return in;
495}
Note: See TracBrowser for help on using the repository browser.