source: branches/testFACT++branch/src/Time.cc@ 18302

Last change on this file since 18302 was 16978, checked in by tbretz, 11 years ago
When compiling without libnova, GetMjd must read Mjd
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 seconds since offset
151 mjd -= 40587;
152 mjd *= 24*60*60;
153
154 const double frac = fmod(mjd, 1)*pow(10, time_duration::num_fractional_digits());
155
156 *this = ptime(fUnixOffset, time_duration(0, 0, mjd, frac));
157}
158
159// --------------------------------------------------------------------------
160//
161//! @returns the seconds of the day including the fractional seconds.
162//!
163double Time::SecondsOfDay() const
164{
165 const time_duration tod = time_of_day();
166
167 const double frac = tod.fractional_seconds()/pow(10, time_duration::num_fractional_digits());
168 const double sec = tod.total_seconds()+frac;
169
170 return sec;
171}
172
173// --------------------------------------------------------------------------
174//
175//! Get the current MJD. Note that this involves
176//! conversion to double. So converting forth and back many many
177//! times might results in drifts.
178//!
179//! @returns
180//! Modified Julian Date
181//!
182double Time::Mjd() const
183{
184 return date().modjulian_day()+SecondsOfDay()/(24*60*60);
185
186 /*
187 const time_duration mjd = *this - ptime(fUnixOffset);
188 const double sec = mjd.total_seconds()+mjd.fractional_seconds()/1e6;
189 return sec/(24*60*60)+40587;
190 */
191}
192
193// --------------------------------------------------------------------------
194//
195// @returns seconds since 1970/1/1
196//
197double Time::UnixTime() const
198{
199 return (date().modjulian_day()-40587)*24*60*60 + SecondsOfDay();
200}
201
202// --------------------------------------------------------------------------
203//
204// @returns days since 1970/1/1
205//
206double Time::UnixDate() const
207{
208 return (date().modjulian_day()-40587) + SecondsOfDay()/(24*60*60);
209}
210
211// --------------------------------------------------------------------------
212//
213// @returns seconds since 1970/1/1
214//
215time_t Time::Time_t() const
216{
217 return (date().modjulian_day()-40587)*24*60*60 + time_of_day().total_seconds();
218}
219
220// --------------------------------------------------------------------------
221//
222//! @returns the time in a format needed for root's TAxis
223//!
224double Time::RootTime() const
225{
226 return (date().modjulian_day()-49718)*24*60*60 + SecondsOfDay();
227}
228
229// --------------------------------------------------------------------------
230//
231//! Returns a string with the contents of the Time object formated
232//! as defined in format.
233//!
234//! @param format
235//! format description of the string to be returned. For details
236//! see the boost documentation or the man page of strftime
237//!
238//! @returns
239//! A string with the time formatted as requested. Note some special
240//! strings might be returned in case the time is invalid.
241//
242string Time::GetAsStr(const char *format) const
243{
244 stringstream out;
245 out << Time::fmt(format) << *this;
246 return out.str();
247}
248
249// --------------------------------------------------------------------------
250//
251//! @returns
252//! a human readable string which complies with ISO 8601, in the
253//! "CCYY-MM-DDThh:mm:ss.f"
254//
255string Time::Iso() const
256{
257 stringstream out;
258 out << Time::iso << *this;
259 return out.str();
260}
261
262// --------------------------------------------------------------------------
263//
264//! Sets the time of the Time object to a time corresponding to
265//! the one given as argument. It is evaluated according to the given
266//! format.
267//!
268//! @param str
269//! The time as a string which should be converted to the Time object
270//!
271//! @param format
272//! format description of the string to be returned. For details
273//! see the boost documentation or the man page of strftime
274//!
275void Time::SetFromStr(const string &str, const char *format)
276{
277 // FIXME: exception handline
278 stringstream stream;
279 stream << str;
280 stream >> Time::fmt(format) >> *this;
281}
282
283string Time::MinutesTo(const Time &time) const
284{
285 ostringstream str;
286 if (time>*this)
287 str << time-*this;
288 else
289 str << *this-time;
290 return str.str().substr(0, 5);
291}
292
293string Time::SecondsTo(const Time &time) const
294{
295 ostringstream str;
296 if (time>*this)
297 str << time-*this;
298 else
299 str << *this-time;
300 return str.str().substr(str.str().substr(0, 3)=="00:" ? 3 : 0, 5);
301}
302
303// --------------------------------------------------------------------------
304//
305//! @returns
306//! The time of the previous sun-rise, relative to given horizon in degree,
307//! for the coordinates of the ORM, La Palma.
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 calculation of the sun-rise
312//! by libnova fails (this would happen if libnova thinks the sun is
313//! circumpolar which should never happen at La Palma)
314//
315Time Time::GetPrevSunRise(double horizon) const
316{
317#ifdef HAVE_LIBNOVA
318 Nova::LnLatPosn obs = Nova::ORM();
319
320 ln_rst_time sun_day;
321 if (ln_get_solar_rst_horizon(JD()-0.5, &obs, horizon, &sun_day)==1)
322 throw runtime_error("ln_get_solar_rst_horizon reported the sun to be circumpolar at the coordinates of La Palma!");
323
324 if (Time(sun_day.rise)<*this)
325 return Time(sun_day.rise);
326
327 if (ln_get_solar_rst_horizon(JD()-1.5, &obs, horizon, &sun_day)==1)
328 throw runtime_error("ln_get_solar_rst_horizon reported the sun to be circumpolar at the coordinates of La Palma!");
329
330 return Time(sun_day.rise);
331#else
332 return Time(floor(Mjd()-0.5)+0.5);
333#endif
334}
335
336// --------------------------------------------------------------------------
337//
338//! @returns
339//! The time of the next sun-rise, relative to given horizon in degree,
340//! for the coordinates of the ORM, La Palma.
341//! if libnova was not compiled in, it will return the next noon.
342//!
343//! @throws
344//! a runtime_error exception is thrown if the calculation of the sun-rise
345//! by libnova fails (this would happen if libnova thinks the sun is
346//! circumpolar which should never happen at La Palma)
347//
348Time Time::GetNextSunRise(double horizon) const
349{
350#ifdef HAVE_LIBNOVA
351 Nova::LnLatPosn obs = Nova::ORM();
352
353 ln_rst_time sun_day;
354 if (ln_get_solar_rst_horizon(JD()-0.5, &obs, horizon, &sun_day)==1)
355 throw runtime_error("ln_get_solar_rst_horizon reported the sun to be circumpolar at the coordinates of La Palma!");
356
357 if (Time(sun_day.rise)>=*this)
358 return Time(sun_day.rise);
359
360 if (ln_get_solar_rst_horizon(JD()+0.5, &obs, horizon, &sun_day)==1)
361 throw runtime_error("ln_get_solar_rst_horizon reported the sun to be circumpolar at the coordinates of La Palma!");
362
363 return Time(sun_day.rise);
364#else
365 return Time(floor(Mjd()+0.5))+0.5;
366#endif
367}
368
369// --------------------------------------------------------------------------
370//
371//! Calls GetPrevSunRise(LN_SOLAR_STANDART_HORIZON)
372//
373Time Time::GetPrevSunRise() const
374{
375 return GetPrevSunRise(LN_SOLAR_STANDART_HORIZON);
376}
377
378// --------------------------------------------------------------------------
379//
380//! Calls GetNextSunRise(LN_SOLAR_STANDART_HORIZON)
381//
382Time Time::GetNextSunRise() const
383{
384 return GetNextSunRise(LN_SOLAR_STANDART_HORIZON);
385}
386
387// --------------------------------------------------------------------------
388//
389//! @returns
390//! Returns an int corresponding to the current sun-cycle, that means
391//! the day of the last sun-rise w.r.t. this Time.
392//! YYYYMMDD, e.g. 20111224 for Christmas eve 2011
393//!
394//! @remark
395//! Before March 30th 2013, 12:00 noon was the reference and the
396//! returned value belonged to the day of sun-set within the
397//! 24h period between two noon's.
398//
399uint32_t Time::NightAsInt() const
400{
401 const Time tm = GetPrevSunRise();
402 return tm.Y()*10000 + tm.M()*100 + tm.D();
403}
404
405// --------------------------------------------------------------------------
406//
407//! A stream manipulator which sets the streams Time output format
408//! as defined in the argument.
409//!
410//! @param format
411//! format description of the manipulator be returned. For details
412//! see the boost documentation or the man page of strftime
413//!
414//! @returns
415//! a stream manipulator for the given format
416//!
417const _time_format Time::fmt(const char *format)
418{
419 return format;
420}
421
422// --------------------------------------------------------------------------
423//
424//! Sets the locale discription of the stream (the way how a time is
425//! output) to the format defined by the given manipulator.
426//!
427//! Example:
428//! \code
429//! Time t();
430//! cout << Time::fmt("%Y:%m:%d %H:%M:%S.%f") << t << endl;
431//! \endcode
432//!
433//! @param out
434//! Reference to the stream
435//!
436//! @param f
437//! Time format described by a manipulator
438//!
439//! @returns
440//! A reference to the stream
441//!
442ostream &operator<<(ostream &out, const _time_format &f)
443{
444 const locale loc(locale::classic(),
445 f.ptr==0 ? 0 : new time_facet(f.ptr));
446
447 out.imbue(loc);
448
449 return out;
450}
451
452// --------------------------------------------------------------------------
453//
454//! Sets the locale discription of the stream (the way how a time is
455//! input) to the format defined by the given manipulator.
456//!
457//! Example:
458//! \code
459//! stringstream s;
460//! s << "09.09.1974 21:59";
461//!
462//! Time t;
463//! s >> Time::fmt("%d.%m.%Y %H:%M") >> t;
464//! \endcode
465//!
466//! @param in
467//! Reference to the stream
468//!
469//! @param f
470//! Time format described by a manipulator
471//!
472//! @returns
473//! A reference to the stream
474//!
475istream &operator>>(istream &in, const _time_format &f)
476{
477 const locale loc(locale::classic(),
478 f.ptr==0 ? 0 : new time_input_facet(f.ptr));
479
480 in.imbue(loc);
481
482 return in;
483}
Note: See TracBrowser for help on using the repository browser.