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

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