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

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