1 /*****************************************************************************\
4 * Author : Chris Koeritz *
6 *******************************************************************************
7 * Copyright (c) 1999-$now By Author. This program is free software; you can *
8 * redistribute it and/or modify it under the terms of the GNU General Public *
9 * License as published by the Free Software Foundation; either version 2 of *
10 * the License or (at your option) any later version. This is online at: *
11 * http://www.fsf.org/copyleft/gpl.html *
12 * Please send any updates to: fred@gruntose.com *
13 \*****************************************************************************/
15 #include "earth_time.h"
17 #include <basis/astring.h>
18 #include <basis/utf_conversion.h>
21 #if defined(__WIN32__) || defined(__UNIX__)
22 #include <sys/timeb.h>
25 using namespace basis;
26 using namespace structures;
30 const int days_in_month[12]
31 = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
33 const int leap_days_in_month[12]
34 = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
36 const int julian_days_in_month[12]
37 = { 31, 29, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30 };
38 //hmmm: is this right?
40 const int julian_leap_days_in_month[12]
41 = { 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30 };
45 void clock_time::pack(byte_array &packed_form) const
47 attach(packed_form, hour);
48 attach(packed_form, minute);
49 attach(packed_form, second);
50 attach(packed_form, millisecond);
51 attach(packed_form, microsecond);
54 bool clock_time::unpack(byte_array &packed_form)
56 if (!detach(packed_form, hour)) return false;
57 if (!detach(packed_form, minute)) return false;
58 if (!detach(packed_form, second)) return false;
59 if (!detach(packed_form, millisecond)) return false;
60 if (!detach(packed_form, microsecond)) return false;
64 #define EASY_LT(x, y) \
65 if (x < y) return true; \
66 if (x > y) return false
68 bool clock_time::operator < (const clock_time &to_compare) const
70 EASY_LT(hour, to_compare.hour);
71 EASY_LT(minute, to_compare.minute);
72 EASY_LT(second, to_compare.second);
73 EASY_LT(millisecond, to_compare.millisecond);
74 EASY_LT(microsecond, to_compare.microsecond);
78 bool clock_time::operator == (const clock_time &to_compare) const
80 return (hour == to_compare.hour)
81 && (minute == to_compare.minute)
82 && (second == to_compare.second)
83 && (millisecond == to_compare.millisecond)
84 && (microsecond == to_compare.microsecond);
87 astring clock_time::text_form(int how) const
90 text_form(to_return, how);
94 void clock_time::text_form(astring &to_return, int how) const
96 if (!how) return; // enforce use of the default.
98 to_return += a_sprintf("%02d:%02d", hour, minute);
101 if (uhr > 12) uhr -= 12;
102 to_return += a_sprintf("%2d:%02d", uhr, minute);
104 if ( (how & SECONDS) || (how & MILLISECONDS) )
105 to_return += a_sprintf(":%02d", second);
106 if (how & MILLISECONDS)
107 to_return += a_sprintf(":%03d", millisecond);
108 if (how & MERIDIAN) {
109 if (hour >= 12) to_return += "PM";
110 else to_return += "AM";
114 // makes sure that "val" is not larger than "max". if it is, then max is
115 // used as a divisor and stored in "rolls".
116 #define limit_value(val, max) \
119 rolls--; /* subtract an extra one since we definitely roll before -max */ \
120 val += max * -rolls; \
121 } else if (val >= max) { \
123 val -= max * rolls; \
124 } else { rolls = 0; }
126 int clock_time::normalize(clock_time &to_fix)
128 int rolls = 0; // rollover counter.
129 limit_value(to_fix.microsecond, 1000);
130 to_fix.millisecond += rolls;
131 limit_value(to_fix.millisecond, 1000);
132 to_fix.second += rolls;
133 limit_value(to_fix.second, 60);
134 to_fix.minute += rolls;
135 limit_value(to_fix.minute, 60);
136 to_fix.hour += rolls;
137 limit_value(to_fix.hour, 24);
143 void day_in_year::pack(byte_array &packed_form) const
145 attach(packed_form, day_of_year);
146 attach(packed_form, abyte(day_of_week));
147 attach(packed_form, abyte(month));
148 attach(packed_form, day_in_month);
149 attach(packed_form, abyte(1));
150 // still packing dst chunk; must for backward compatibility.
153 bool day_in_year::unpack(byte_array &packed_form)
155 if (!detach(packed_form, day_of_year)) return false;
157 if (!detach(packed_form, temp)) return false;
158 day_of_week = days(temp);
159 if (!detach(packed_form, temp)) return false;
160 month = months(temp);
161 if (!detach(packed_form, day_in_month)) return false;
162 if (!detach(packed_form, temp)) return false; // dst chunk--backward compat.
166 bool day_in_year::operator < (const day_in_year &to_compare) const
168 EASY_LT(month, to_compare.month);
169 EASY_LT(day_in_month, to_compare.day_in_month);
173 bool day_in_year::operator == (const day_in_year &to_compare) const
175 return (month == to_compare.month)
176 && (day_in_month == to_compare.day_in_month);
179 astring day_in_year::text_form(int how) const
182 text_form(to_return, how);
186 void day_in_year::text_form(astring &to_stuff, int how) const
188 if (!how) return; // enforce use of the default.
189 if (how & INCLUDE_DAY) to_stuff += astring(day_name(day_of_week)) + " ";
190 const char *monat = short_month_name(month);
191 if (how & LONG_MONTH)
192 monat = month_name(month);
193 //hmmm: more formatting, like euro?
195 to_stuff += a_sprintf(" %02d", day_in_month);
198 // note: this only works when adjusting across one month, not multiples.
199 int limit_day_of_month(int &day, int days_in_month, int days_in_prev_month)
201 if (day > days_in_month) {
202 day -= days_in_month;
203 return 1; // forward rollover.
204 } else if (day < 1) {
205 day += days_in_prev_month;
208 return 0; // no rolling.
211 int day_in_year::normalize(day_in_year &to_fix, bool leap_year)
213 int rolls = 0; // rollover counter.
214 int daysinm = leap_year?
215 leap_days_in_month[to_fix.month] : days_in_month[to_fix.month];
216 int prev_month = to_fix.month - 1;
217 if (prev_month < 0) prev_month = 11;
218 int daysinpm = leap_year?
219 leap_days_in_month[prev_month] : days_in_month[prev_month];
220 rolls = limit_day_of_month(to_fix.day_in_month, daysinm, daysinpm);
221 int monat = to_fix.month + rolls;
222 limit_value(monat, 12); // months are zero based.
223 to_fix.month = months(monat);
229 void time_locus::pack(byte_array &packed_form) const
231 attach(packed_form, year);
232 clock_time::pack(packed_form);
233 day_in_year::pack(packed_form);
236 bool time_locus::unpack(byte_array &packed_form)
238 if (!detach(packed_form, year)) return false;
239 if (!clock_time::unpack(packed_form)) return false;
240 if (!day_in_year::unpack(packed_form)) return false;
244 astring time_locus::text_form_long(int t, int d, int y) const
247 text_form_long(to_return, t, d, y);
251 bool time_locus::equal_to(const equalizable &s2) const {
252 const time_locus *s2_cast = dynamic_cast<const time_locus *>(&s2);
253 if (!s2_cast) throw "error: time_locus::==: unknown type";
254 return (year == s2_cast->year)
255 && ( (const day_in_year &) *this == *s2_cast)
256 && ( (const clock_time &) *this == *s2_cast);
259 bool time_locus::less_than(const orderable &s2) const {
260 const time_locus *s2_cast = dynamic_cast<const time_locus *>(&s2);
261 if (!s2_cast) throw "error: time_locus::<: unknown type";
262 EASY_LT(year, s2_cast->year);
263 if (day_in_year::operator < (*s2_cast)) return true;
264 if (!(day_in_year::operator == (*s2_cast))) return false;
265 if (clock_time::operator < (*s2_cast)) return true;
269 void time_locus::text_form_long(astring &to_stuff, int t, int d, int y) const
271 //hmmm: more formatting desired, like european.
273 text_form_long(to_stuff, t, d); // enforce use of the default.
277 day_in_year::text_form(to_stuff, d);
281 to_stuff += a_sprintf("%2d", year % 100);
283 to_stuff += a_sprintf("%4d", year);
286 clock_time::text_form(to_stuff, t);
289 int time_locus::normalize(time_locus &to_fix)
291 int rolls = clock_time::normalize(to_fix);
292 to_fix.day_in_month += rolls;
294 //hmmm: this little gem should be abstracted to a method.
295 bool leaping = !(to_fix.year % 4);
296 if (!(to_fix.year % 100)) leaping = false;
297 if (!(to_fix.year % 400)) leaping = true;
299 rolls = day_in_year::normalize(to_fix, leaping);
300 to_fix.year += rolls;
302 // is that always right? not for underflow.
303 //hmmm: resolve the issue of rollovers here.
308 time_locus convert(const tm &to_convert, int ms)
312 // we lack the resolution for this, currently.
315 r.second = to_convert.tm_sec;
316 r.minute = to_convert.tm_min;
317 r.hour = to_convert.tm_hour;
318 r.day_in_month = to_convert.tm_mday;
319 r.month = months(to_convert.tm_mon);
320 r.year = to_convert.tm_year + 1900;
321 r.day_of_week = days(to_convert.tm_wday);
322 r.day_of_year = to_convert.tm_yday;
331 tm split_time(*localtime(¤t.time));
332 return convert(split_time, current.millitm);
335 time_locus greenwich_now()
339 tm split_time(*gmtime(¤t.time));
340 return convert(split_time, current.millitm);
343 clock_time time_now() { return now(); }
345 days day_now() { return now().day_of_week; }
347 months month_now() { return now().month; }
349 int year_now() { return now().year; }
351 day_in_year date_now() { return now(); }
353 const char *day_name(days to_name)
356 case SUNDAY: return "Sunday";
357 case MONDAY: return "Monday";
358 case TUESDAY: return "Tuesday";
359 case WEDNESDAY: return "Wednesday";
360 case THURSDAY: return "Thursday";
361 case FRIDAY: return "Friday";
362 case SATURDAY: return "Saturday";
363 default: return "Not_a_day";
367 const char *month_name(months to_name)
370 case JANUARY: return "January";
371 case FEBRUARY: return "February";
372 case MARCH: return "March";
373 case APRIL: return "April";
374 case MAY: return "May";
375 case JUNE: return "June";
376 case JULY: return "July";
377 case AUGUST: return "August";
378 case SEPTEMBER: return "September";
379 case OCTOBER: return "October";
380 case NOVEMBER: return "November";
381 case DECEMBER: return "December";
382 default: return "Not_a_month";
386 const char *short_month_name(months to_name)
389 case JANUARY: return "Jan";
390 case FEBRUARY: return "Feb";
391 case MARCH: return "Mar";
392 case APRIL: return "Apr";
393 case MAY: return "May";
394 case JUNE: return "Jun";
395 case JULY: return "Jul";
396 case AUGUST: return "Aug";
397 case SEPTEMBER: return "Sep";
398 case OCTOBER: return "Oct";
399 case NOVEMBER: return "Nov";
400 case DECEMBER: return "Dec";
401 default: return "Not";