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

Last change on this file since 18523 was 18445, checked in by tbretz, 9 years ago
Instead of power and num_fractional_digits, ticks_per_second can directly be used.
File size: 13.5 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
29#ifdef HAVE_LIBNOVA
30#include "../externals/nova.h"
31#endif
32
33using namespace std;
34using namespace boost::posix_time;
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 coordinates of the ORM, La Palma.
306//! if libnova was not compiled in, it will return the next noon.
307//!
308//! @throws
309//! a runtime_error exception is thrown if the calculation of the sun-rise
310//! by libnova fails (this would happen if libnova thinks the sun is
311//! circumpolar which should never happen at La Palma)
312//
313Time Time::GetPrevSunRise(double horizon) const
314{
315#ifdef HAVE_LIBNOVA
316 Nova::LnLatPosn obs = Nova::ORM();
317
318 ln_rst_time sun_day;
319 if (ln_get_solar_rst_horizon(JD()-0.5, &obs, horizon, &sun_day)==1)
320 throw runtime_error("ln_get_solar_rst_horizon reported the sun to be circumpolar at the coordinates of La Palma!");
321
322 if (Time(sun_day.rise)<*this)
323 return Time(sun_day.rise);
324
325 if (ln_get_solar_rst_horizon(JD()-1.5, &obs, horizon, &sun_day)==1)
326 throw runtime_error("ln_get_solar_rst_horizon reported the sun to be circumpolar at the coordinates of La Palma!");
327
328 return Time(sun_day.rise);
329#else
330 return Time(floor(Mjd()-0.5)+0.5);
331#endif
332}
333
334// --------------------------------------------------------------------------
335//
336//! @returns
337//! The time of the next sun-rise, relative to given horizon in degree,
338//! for the coordinates of the ORM, La Palma.
339//! if libnova was not compiled in, it will return the next noon.
340//!
341//! @throws
342//! a runtime_error exception is thrown if the calculation of the sun-rise
343//! by libnova fails (this would happen if libnova thinks the sun is
344//! circumpolar which should never happen at La Palma)
345//
346Time Time::GetNextSunRise(double horizon) const
347{
348#ifdef HAVE_LIBNOVA
349 Nova::LnLatPosn obs = Nova::ORM();
350
351 ln_rst_time sun_day;
352 if (ln_get_solar_rst_horizon(JD()-0.5, &obs, horizon, &sun_day)==1)
353 throw runtime_error("ln_get_solar_rst_horizon reported the sun to be circumpolar at the coordinates of La Palma!");
354
355 if (Time(sun_day.rise)>=*this)
356 return Time(sun_day.rise);
357
358 if (ln_get_solar_rst_horizon(JD()+0.5, &obs, horizon, &sun_day)==1)
359 throw runtime_error("ln_get_solar_rst_horizon reported the sun to be circumpolar at the coordinates of La Palma!");
360
361 return Time(sun_day.rise);
362#else
363 return Time(floor(Mjd()+0.5))+0.5;
364#endif
365}
366
367// --------------------------------------------------------------------------
368//
369//! Calls GetPrevSunRise(LN_SOLAR_STANDART_HORIZON)
370//
371Time Time::GetPrevSunRise() const
372{
373 return GetPrevSunRise(LN_SOLAR_STANDART_HORIZON);
374}
375
376// --------------------------------------------------------------------------
377//
378//! Calls GetNextSunRise(LN_SOLAR_STANDART_HORIZON)
379//
380Time Time::GetNextSunRise() const
381{
382 return GetNextSunRise(LN_SOLAR_STANDART_HORIZON);
383}
384
385// --------------------------------------------------------------------------
386//
387//! @returns
388//! Returns an int corresponding to the current sun-cycle, that means
389//! the day of the last sun-rise w.r.t. this Time.
390//! YYYYMMDD, e.g. 20111224 for Christmas eve 2011
391//!
392//! @remark
393//! Before March 30th 2013, 12:00 noon was the reference and the
394//! returned value belonged to the day of sun-set within the
395//! 24h period between two noon's.
396//
397uint32_t Time::NightAsInt() const
398{
399 const Time tm = GetPrevSunRise();
400 return tm.Y()*10000 + tm.M()*100 + tm.D();
401}
402
403// --------------------------------------------------------------------------
404//
405//! A stream manipulator which sets the streams Time output format
406//! as defined in the argument.
407//!
408//! @param format
409//! format description of the manipulator be returned. For details
410//! see the boost documentation or the man page of strftime
411//!
412//! @returns
413//! a stream manipulator for the given format
414//!
415const _time_format Time::fmt(const char *format)
416{
417 return format;
418}
419
420// --------------------------------------------------------------------------
421//
422//! Sets the locale discription of the stream (the way how a time is
423//! output) to the format defined by the given manipulator.
424//!
425//! Example:
426//! \code
427//! Time t();
428//! cout << Time::fmt("%Y:%m:%d %H:%M:%S.%f") << t << endl;
429//! \endcode
430//!
431//! @param out
432//! Reference to the stream
433//!
434//! @param f
435//! Time format described by a manipulator
436//!
437//! @returns
438//! A reference to the stream
439//!
440ostream &operator<<(ostream &out, const _time_format &f)
441{
442 const locale loc(locale::classic(),
443 f.ptr==0 ? 0 : new time_facet(f.ptr));
444
445 out.imbue(loc);
446
447 return out;
448}
449
450// --------------------------------------------------------------------------
451//
452//! Sets the locale discription of the stream (the way how a time is
453//! input) to the format defined by the given manipulator.
454//!
455//! Example:
456//! \code
457//! stringstream s;
458//! s << "09.09.1974 21:59";
459//!
460//! Time t;
461//! s >> Time::fmt("%d.%m.%Y %H:%M") >> t;
462//! \endcode
463//!
464//! @param in
465//! Reference to the stream
466//!
467//! @param f
468//! Time format described by a manipulator
469//!
470//! @returns
471//! A reference to the stream
472//!
473istream &operator>>(istream &in, const _time_format &f)
474{
475 const locale loc(locale::classic(),
476 f.ptr==0 ? 0 : new time_input_facet(f.ptr));
477
478 in.imbue(loc);
479
480 return in;
481}
Note: See TracBrowser for help on using the repository browser.