--- /dev/null
+/* $Id$ */
+#include <sstream>
+#include <iomanip>
+#include <msp/error.h>
+#include "datetime.h"
+#include "timestamp.h"
+
+using namespace std;
+
+namespace {
+
+inline bool is_leap_year(int32_t y)
+{ return y%4==0 && (y%100 || y%400==0); }
+
+inline uint8_t month_days(int32_t y, uint8_t m)
+{
+ switch(m)
+ {
+ case 4:
+ case 6:
+ case 9:
+ case 11:
+ return 30;
+ case 2:
+ return is_leap_year(y)?29:28;
+ default:
+ return 31;
+ }
+}
+
+template<typename T>
+inline int cmp_(T a, T b)
+{
+ if(a<b)
+ return -1;
+ if(a>b)
+ return 1;
+ return 0;
+}
+
+}
+
+#include <iostream>
+using namespace std;
+
+namespace Msp {
+namespace Time {
+
+DateTime::DateTime(const TimeStamp &ts):
+ year(1970),
+ month(1),
+ mday(1),
+ hour(0),
+ minute(0),
+ second(0),
+ usec(0)
+{
+ add_raw(ts.raw());
+}
+
+DateTime::DateTime(int32_t y, uint8_t m, uint8_t d):
+ year(y),
+ month(m),
+ mday(d),
+ hour(0),
+ minute(0),
+ second(0),
+ usec(0)
+{ }
+
+void DateTime::add_days(int32_t days)
+{
+ unsigned new_year=year;
+
+ /* Leap years have a 400 year cycle, so any 400 consecutive years have a
+ constant number of days */
+ new_year+=days/146097*400;
+ days%=146097;
+
+ if(days<0)
+ {
+ new_year-=400;
+ days+=146097;
+ }
+
+ // Fudge factor for leap day
+ int fudge=(month<=2)?1:0;
+
+ // (Almost) every 4 year cycle has 1 leap year and 3 normal years
+ unsigned cycles=days/1461;
+ days%=1461;
+
+ new_year+=cycles*4;
+
+ // See how many non-leap-years we counted as leap years and reclaim the lost days
+ unsigned missed_leap_days=((year-fudge)%100+cycles*4)/100;
+ if((year-fudge)%400+cycles*4>=400)
+ --missed_leap_days;
+
+ days+=missed_leap_days;
+
+ // Count single years from the 4 year cycle
+ cycles=days/365;
+ days%=365;
+
+ new_year+=cycles;
+
+ if((year-fudge)%4+cycles>=4)
+ {
+ // We passed a leap year - decrement days
+ if(days==0)
+ {
+ --new_year;
+ days=is_leap_year(new_year)?365:364;
+ }
+ else
+ --days;
+ }
+
+ year=new_year;
+
+ // Step months
+ while(mday+days>month_days(year, month))
+ {
+ days-=month_days(year, month);
+ ++month;
+ if(month>12)
+ {
+ ++year;
+ month=1;
+ }
+ }
+
+ mday+=days;
+}
+
+DateTime DateTime::operator+(const TimeDelta &td) const
+{
+ DateTime dt(*this);
+ dt.add_raw(td.raw());
+ return dt;
+}
+
+DateTime &DateTime::operator+=(const TimeDelta &td)
+{
+ add_raw(td.raw());
+ return *this;
+}
+
+int DateTime::cmp(const DateTime &dt) const
+{
+ if(int c=cmp_(year, dt.year))
+ return c;
+ if(int c=cmp_(month, dt.month))
+ return c;
+ if(int c=cmp_(mday, dt.mday))
+ return c;
+ if(int c=cmp_(hour, dt.hour))
+ return c;
+ if(int c=cmp_(minute, dt.minute))
+ return c;
+ if(int c=cmp_(second, dt.second))
+ return c;
+ if(int c=cmp_(usec, dt.usec))
+ return c;
+ return 0;
+}
+
+TimeStamp DateTime::get_timestamp() const
+{
+ if(year<-289701 || year>293641)
+ throw Exception("DateTime is not representable as a TimeStamp");
+
+ int64_t raw=(((hour*60LL)+minute)*60+second)*1000000+usec;
+ int days=(year-1970)*365;
+ days+=(year-1)/4-(year-1)/100+(year-1)/400-477;
+ for(unsigned i=1; i<month; ++i)
+ days+=month_days(year, i);
+ days+=mday-1;
+
+ raw+=days*86400000000LL;
+
+ return TimeStamp(raw);
+}
+
+string DateTime::format(const string &fmt) const
+{
+ ostringstream ss;
+ ss.fill('0');
+ for(string::const_iterator i=fmt.begin(); i!=fmt.end(); ++i)
+ {
+ if(*i=='%')
+ {
+ ++i;
+ if(i==fmt.end())
+ break;
+ else if(*i=='d')
+ ss<<setw(2)<<int(mday);
+ else if(*i=='H')
+ ss<<setw(2)<<int(hour);
+ else if(*i=='I')
+ ss<<setw(2)<<hour%12;
+ else if(*i=='m')
+ ss<<setw(2)<<int(month);
+ else if(*i=='M')
+ ss<<setw(2)<<int(minute);
+ else if(*i=='p')
+ ss<<((hour>=12) ? "PM" : "AM");
+ else if(*i=='S')
+ ss<<setw(2)<<int(second);
+ else if(*i=='y')
+ ss<<setw(2)<<year%100;
+ else if(*i=='Y')
+ ss<<year;
+ else if(*i=='%')
+ ss<<'%';
+ }
+ else
+ ss<<*i;
+ }
+
+ return ss.str();
+}
+
+void DateTime::add_raw(int64_t raw)
+{
+ int32_t days=raw/86400000000LL;
+ raw%=86400000000LL;
+ if(raw<0)
+ {
+ ++days;
+ raw+=86400000000LL;
+ }
+
+ usec+=raw%1000000; raw/=1000000;
+ second+=raw%60; raw/=60;
+ minute+=raw%60; raw/=60;
+ hour+=raw%24; raw/=24;
+
+ add_days(days);
+ normalize();
+}
+
+void DateTime::normalize()
+{
+ second+=usec/1000000;
+ usec%=1000000;
+ minute+=second/60;
+ second%=60;
+ hour+=minute/60;
+ minute%=60;
+ mday+=hour/24;
+ hour%=24;
+ while(mday>month_days(year, month))
+ {
+ mday-=month_days(year, month);
+ ++month;
+ if(month>12)
+ {
+ ++year;
+ month=1;
+ }
+ }
+}
+
+} // namespace Time
+} // namespace Msp
--- /dev/null
+/* $Id$ */
+#ifndef MSP_TIME_DATETIME_H_
+#define MSP_TIME_DATETIME_H_
+
+#include <stdint.h>
+#include <string>
+
+namespace Msp {
+namespace Time {
+
+class TimeDelta;
+class TimeStamp;
+
+/**
+Provides handling of arbitary dates and times. Can represent a moment of time
+in the range of about ±2.1×10⁹ years. It can also be formatted into a string
+for presentation to the user.
+
+Due to the complex internal representation, arithmetic operations on a DateTime
+are relatively slow. For purposes of internal scheduling in a program, a
+TimeStamp is a better choice.
+*/
+class DateTime
+{
+public:
+ DateTime(const TimeStamp &);
+ DateTime(int32_t, uint8_t, uint8_t);
+ DateTime(int32_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t);
+ DateTime(int32_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint32_t);
+
+ int32_t get_year() const { return year; }
+ uint8_t get_month() const { return month; }
+ uint8_t get_mday() const { return mday; }
+ uint8_t get_hour() const { return hour; }
+ uint8_t get_minute() const { return minute; }
+ uint8_t get_second() const { return second; }
+ uint32_t get_usec() const { return usec; }
+
+ void add_days(int32_t);
+
+ DateTime operator+(const TimeDelta &) const;
+ DateTime &operator+=(const TimeDelta &);
+
+ bool operator==(const DateTime &d) const { return cmp(d)==0; }
+ bool operator!=(const DateTime &d) const { return cmp(d)!=0; }
+ bool operator<(const DateTime &d) const { return cmp(d)<0; }
+ bool operator<=(const DateTime &d) const { return cmp(d)<=0; }
+ bool operator>(const DateTime &d) const { return cmp(d)>0; }
+ bool operator>=(const DateTime &d) const { return cmp(d)>=0; }
+
+ int cmp(const DateTime &) const;
+ TimeStamp get_timestamp() const;
+ std::string format(const std::string &) const;
+private:
+ int32_t year;
+ uint8_t month;
+ uint8_t mday;
+ uint8_t hour;
+ uint8_t minute;
+ uint8_t second;
+ uint32_t usec;
+
+ void add_raw(int64_t);
+ void normalize();
+};
+
+} // namespace Time
+} // namespace Msp
+
+#endif
Distributed under the LGPL
*/
#include <sstream>
+#include <iomanip>
#include "timedelta.h"
#include "units.h"
using namespace std;
-namespace Msp {
-namespace Time {
+namespace {
void print_part(ostream &out, int64_t &value, int64_t unit, char sep, bool &first)
{
- if(value>=unit || !first)
- {
- if(first)
- out<<value/unit;
- else
- {
- out.width(2);
- out<<value/unit;
- }
- if(sep) out<<sep;
- value%=unit;
- first=false;
- }
+ if(!value || (value<unit && first))
+ return;
+
+ if(!first)
+ out<<sep<<setw(2);
+
+ out<<value/unit;
+ value%=unit;
+ first=false;
}
+}
+
+namespace Msp {
+namespace Time {
+
ostream &operator<<(ostream &out, const TimeDelta &td)
{
ostringstream ss;
ss.fill('0');
- if(td.usec<1000)
- ss<<td.usec<<"µs";
- else if(td.usec<1000000)
+
+ int64_t value=td.raw();
+
+ if(value<0)
{
- ss<<td.usec/1000;
- if(td.usec%1000)
- {
- ss<<'.';
- ss.width(3);
- ss<<td.usec%1000;
- }
- ss<<"ms";
+ ss<<'-';
+ value=-value;
}
- else if(td.usec<60000000)
+
+ if(value==0)
+ ss<<'0';
+ else if(value<1000)
+ ss<<value<<"µs";
+ else if(value<1000000)
{
- ss<<td.usec/1000000;
- if(td.usec%1000000)
- {
- ss<<'.';
- if(td.usec%1000)
- {
- ss.width(6);
- ss<<td.usec%1000000;
- }
- else
- {
- ss.width(3);
- ss<<td.usec/1000%1000;
- }
- }
- ss<<"s";
+ ss<<value/1000;
+ value%=1000;
+ if(value)
+ ss<<'.'<<setw(3)<<value;
+ ss<<"ms";
}
else
{
- int64_t temp=td.usec;
- bool first=true;
- print_part(ss, temp, day.raw(), '-', first);
- print_part(ss, temp, hour.raw(), ':', first);
- print_part(ss, temp, min.raw(), ':', first);
- print_part(ss, temp, sec.raw(), 0, first);
- if(temp)
+ bool first=true;
+ print_part(ss, value, 86400000000LL, 0, first);
+ print_part(ss, value, 3600000000LL, '-', first);
+ print_part(ss, value, 60000000LL, ':', first);
+ print_part(ss, value, 1000000LL, ':', first);
+
+ if(value)
{
ss<<'.';
- if(temp%1000)
- {
- ss.width(6);
- ss<<temp;
- }
+ if(value%1000)
+ ss<<setw(6)<<value;
else
- {
- ss.width(3);
- ss<<temp/1000;
- }
+ ss<<setw(3)<<value/1000;
}
ss<<"s";
}
bool operator!=(const TimeDelta &t) const { return usec!=t.usec; }
operator bool() const { return usec; }
-
- friend std::ostream &operator<<(std::ostream &, const TimeDelta &);
private:
int64_t usec;
};
template<typename T>
inline TimeDelta operator*(T a, const TimeDelta &t) { return t*a; }
+extern std::ostream &operator<<(std::ostream &, const TimeDelta &);
+
} // namespace Time
} // namespace Msp
namespace Time {
/**
-Represents a moment in time, such as the last tick. This class is NOT intended
-to be used for storing arbitary user-defined times, I'll add a DateTime class
-if the need arises.
+Represents a moment in time. The main source of TimeStamps is the now()
+function.
+
+For representing user-specified times, use the DateTime class.
*/
class TimeStamp
{
explicit TimeStamp(int64_t u): usec(u) { }
/**
- Returns the raw number stored inside the TimeStamp. This should only be used
- for serialization and the result should not be interpreted in any way.
+ Returns the raw number stored inside the TimeStamp. This value should be
+ considered opaque and only be used for serialization.
*/
int64_t raw() const { return usec; }
#include <sys/resource.h>
#include <sys/time.h>
#endif
+#include "datetime.h"
#include "timedelta.h"
#include "timestamp.h"
#include "units.h"
#include "utils.h"
+using namespace std;
+
namespace Msp {
namespace Time {
#endif
}
+string format_now(const string &fmt)
+{
+ return DateTime(now()).format(fmt);
+}
+
/**
Returns the CPU time used by the program so far.
*/
class TimeStamp;
extern TimeStamp now();
+extern std::string format_now(const std::string &);
extern TimeDelta get_cpu_time();
extern int sleep(const TimeDelta &);