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

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