]> git.tdb.fi Git - libs/core.git/blob - source/time/datetime.cpp
Added DateTime
[libs/core.git] / source / time / datetime.cpp
1 /* $Id$ */
2 #include <sstream>
3 #include <iomanip>
4 #include <msp/error.h>
5 #include "datetime.h"
6 #include "timestamp.h"
7
8 using namespace std;
9
10 namespace {
11
12 inline bool is_leap_year(int32_t y)
13 { return y%4==0 && (y%100 || y%400==0); }
14
15 inline uint8_t month_days(int32_t y, uint8_t m)
16 {
17         switch(m)
18         {
19         case 4:
20         case 6:
21         case 9:
22         case 11:
23                 return 30;
24         case 2:
25                 return is_leap_year(y)?29:28;
26         default:
27                 return 31;
28         }
29 }
30
31 template<typename T>
32 inline int cmp_(T a, T b)
33 {
34         if(a<b)
35                 return -1;
36         if(a>b)
37                 return 1;
38         return 0;
39 }
40
41 }
42
43 #include <iostream>
44 using namespace std;
45
46 namespace Msp {
47 namespace Time {
48
49 DateTime::DateTime(const TimeStamp &ts):
50         year(1970),
51         month(1),
52         mday(1),
53         hour(0),
54         minute(0),
55         second(0),
56         usec(0)
57 {
58         add_raw(ts.raw());
59 }
60
61 DateTime::DateTime(int32_t y, uint8_t m, uint8_t d):
62         year(y),
63         month(m),
64         mday(d),
65         hour(0),
66         minute(0),
67         second(0),
68         usec(0)
69 { }
70
71 void DateTime::add_days(int32_t days)
72 {
73         unsigned new_year=year;
74
75         /* Leap years have a 400 year cycle, so any 400 consecutive years have a
76         constant number of days */
77         new_year+=days/146097*400;
78         days%=146097;
79
80         if(days<0)
81         {
82                 new_year-=400;
83                 days+=146097;
84         }
85
86         // Fudge factor for leap day
87         int fudge=(month<=2)?1:0;
88
89         // (Almost) every 4 year cycle has 1 leap year and 3 normal years
90         unsigned cycles=days/1461;
91         days%=1461;
92
93         new_year+=cycles*4;
94
95         // See how many non-leap-years we counted as leap years and reclaim the lost days
96         unsigned missed_leap_days=((year-fudge)%100+cycles*4)/100;
97         if((year-fudge)%400+cycles*4>=400)
98                 --missed_leap_days;
99
100         days+=missed_leap_days;
101
102         // Count single years from the 4 year cycle
103         cycles=days/365;
104         days%=365;
105
106         new_year+=cycles;
107
108         if((year-fudge)%4+cycles>=4)
109         {
110                 // We passed a leap year - decrement days
111                 if(days==0)
112                 {
113                         --new_year;
114                         days=is_leap_year(new_year)?365:364;
115                 }
116                 else
117                         --days;
118         }
119
120         year=new_year;
121
122         // Step months
123         while(mday+days>month_days(year, month))
124         {
125                 days-=month_days(year, month);
126                 ++month;
127                 if(month>12)
128                 {
129                         ++year;
130                         month=1;
131                 }
132         }
133
134         mday+=days;
135 }
136
137 DateTime DateTime::operator+(const TimeDelta &td) const
138 {
139         DateTime dt(*this);
140         dt.add_raw(td.raw());
141         return dt;
142 }
143
144 DateTime &DateTime::operator+=(const TimeDelta &td)
145 {
146         add_raw(td.raw());
147         return *this;
148 }
149
150 int DateTime::cmp(const DateTime &dt) const
151 {
152         if(int c=cmp_(year, dt.year))
153                 return c;
154         if(int c=cmp_(month, dt.month))
155                 return c;
156         if(int c=cmp_(mday, dt.mday))
157                 return c;
158         if(int c=cmp_(hour, dt.hour))
159                 return c;
160         if(int c=cmp_(minute, dt.minute))
161                 return c;
162         if(int c=cmp_(second, dt.second))
163                 return c;
164         if(int c=cmp_(usec, dt.usec))
165                 return c;
166         return 0;
167 }
168
169 TimeStamp DateTime::get_timestamp() const
170 {
171         if(year<-289701 || year>293641)
172                 throw Exception("DateTime is not representable as a TimeStamp");
173
174         int64_t raw=(((hour*60LL)+minute)*60+second)*1000000+usec;
175         int days=(year-1970)*365;
176         days+=(year-1)/4-(year-1)/100+(year-1)/400-477;
177         for(unsigned i=1; i<month; ++i)
178                 days+=month_days(year, i);
179         days+=mday-1;
180
181         raw+=days*86400000000LL;
182
183         return TimeStamp(raw);
184 }
185
186 string DateTime::format(const string &fmt) const
187 {
188         ostringstream ss;
189         ss.fill('0');
190         for(string::const_iterator i=fmt.begin(); i!=fmt.end(); ++i)
191         {
192                 if(*i=='%')
193                 {
194                         ++i;
195                         if(i==fmt.end())
196                                 break;
197                         else if(*i=='d')
198                                 ss<<setw(2)<<int(mday);
199                         else if(*i=='H')
200                                 ss<<setw(2)<<int(hour);
201                         else if(*i=='I')
202                                 ss<<setw(2)<<hour%12;
203                         else if(*i=='m')
204                                 ss<<setw(2)<<int(month);
205                         else if(*i=='M')
206                                 ss<<setw(2)<<int(minute);
207                         else if(*i=='p')
208                                 ss<<((hour>=12) ? "PM" : "AM");
209                         else if(*i=='S')
210                                 ss<<setw(2)<<int(second);
211                         else if(*i=='y')
212                                 ss<<setw(2)<<year%100;
213                         else if(*i=='Y')
214                                 ss<<year;
215                         else if(*i=='%')
216                                 ss<<'%';
217                 }
218                 else
219                         ss<<*i;
220         }
221
222         return ss.str();
223 }
224
225 void DateTime::add_raw(int64_t raw)
226 {
227         int32_t days=raw/86400000000LL;
228         raw%=86400000000LL;
229         if(raw<0)
230         {
231                 ++days;
232                 raw+=86400000000LL;
233         }
234
235         usec+=raw%1000000; raw/=1000000;
236         second+=raw%60;    raw/=60;
237         minute+=raw%60;    raw/=60;
238         hour+=raw%24;      raw/=24;
239
240         add_days(days);
241         normalize();
242 }
243
244 void DateTime::normalize()
245 {
246         second+=usec/1000000;
247         usec%=1000000;
248         minute+=second/60;
249         second%=60;
250         hour+=minute/60;
251         minute%=60;
252         mday+=hour/24;
253         hour%=24;
254         while(mday>month_days(year, month))
255         {
256                 mday-=month_days(year, month);
257                 ++month;
258                 if(month>12)
259                 {
260                         ++year;
261                         month=1;
262                 }
263         }
264 }
265
266 } // namespace Time
267 } // namespace Msp