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"
16 #include "time_stamp.h"
18 #include <basis/astring.h>
19 #include <basis/utf_conversion.h>
20 #include <textual/parser_bits.h>
24 #if defined(__WIN32__) || defined(__UNIX__)
25 // #include <sys/timeb.h>
30 // uncomment for noisy code.
31 //#define DEBUG_EARTH_TIME
33 using namespace basis;
34 using namespace structures;
35 using namespace textual;
40 #ifdef DEBUG_EARTH_TIME
41 #define LOG(tpr) printf("%s", (astring("earth_time::") + func + ": " + tpr + parser_bits::platform_eol_to_chars()).s())
48 const time_number days_in_month[12]
49 = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
51 const time_number leap_days_in_month[12]
52 = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
54 const time_number julian_days_in_month[12]
55 = { 31, 29, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30 };
56 //hmmm: is this right?
58 const time_number julian_leap_days_in_month[12]
59 = { 31, 30, 31, 30, 31, 30, 31, 30, 31, 30, 31, 30 };
63 void clock_time::pack(byte_array &packed_form) const
65 attach(packed_form, hour);
66 attach(packed_form, minute);
67 attach(packed_form, second);
68 attach(packed_form, millisecond);
69 attach(packed_form, microsecond);
72 bool clock_time::unpack(byte_array &packed_form)
74 if (!detach(packed_form, hour)) return false;
75 if (!detach(packed_form, minute)) return false;
76 if (!detach(packed_form, second)) return false;
77 if (!detach(packed_form, millisecond)) return false;
78 if (!detach(packed_form, microsecond)) return false;
82 #define EASY_LT(x, y) \
83 if (x < y) return true; \
84 if (x > y) return false
86 bool clock_time::operator < (const clock_time &to_compare) const
88 EASY_LT(hour, to_compare.hour);
89 EASY_LT(minute, to_compare.minute);
90 EASY_LT(second, to_compare.second);
91 EASY_LT(millisecond, to_compare.millisecond);
92 EASY_LT(microsecond, to_compare.microsecond);
96 bool clock_time::operator == (const clock_time &to_compare) const
98 return (hour == to_compare.hour)
99 && (minute == to_compare.minute)
100 && (second == to_compare.second)
101 && (millisecond == to_compare.millisecond)
102 && (microsecond == to_compare.microsecond);
105 astring clock_time::text_form(int how) const
108 text_form(to_return, how);
112 void clock_time::text_form(astring &to_return, int how) const
114 if (!how) return; // enforce use of the default.
116 to_return += a_sprintf("%02ld:%02ld", hour, minute);
118 time_number uhr = hour;
119 if (uhr > 12) uhr -= 12;
120 to_return += a_sprintf("%2ld:%02ld", uhr, minute);
122 if ( (how & SECONDS) || (how & MILLISECONDS) )
123 to_return += a_sprintf(":%02ld", second);
124 if (how & MILLISECONDS)
125 to_return += a_sprintf(":%03ld", millisecond);
126 if (how & MERIDIAN) {
127 if (hour >= 12) to_return += "PM";
128 else to_return += "AM";
132 // makes sure that "val" is not larger than "max". if it is, then max is
133 // used as a divisor and stored in "rolls".
134 #define limit_value(val, max) \
137 rolls--; /* subtract an extra one since we definitely roll before -max */ \
138 val += max * -rolls; \
139 } else if (val >= max) { \
141 val -= max * rolls; \
142 } else { rolls = 0; }
144 time_number clock_time::normalize(clock_time &to_fix)
146 time_number rolls = 0; // rollover counter.
147 limit_value(to_fix.microsecond, 1000);
148 to_fix.millisecond += rolls;
149 limit_value(to_fix.millisecond, 1000);
150 to_fix.second += rolls;
151 limit_value(to_fix.second, 60);
152 to_fix.minute += rolls;
153 limit_value(to_fix.minute, 60);
154 to_fix.hour += rolls;
155 limit_value(to_fix.hour, 24);
161 void day_in_year::pack(byte_array &packed_form) const
163 attach(packed_form, day_of_year);
164 attach(packed_form, abyte(day_of_week));
165 attach(packed_form, abyte(month));
166 attach(packed_form, day_in_month);
167 attach(packed_form, abyte(1));
168 // still packing dst chunk; must for backward compatibility.
171 bool day_in_year::unpack(byte_array &packed_form)
173 if (!detach(packed_form, day_of_year)) return false;
175 if (!detach(packed_form, temp)) return false;
176 day_of_week = days(temp);
177 if (!detach(packed_form, temp)) return false;
178 month = months(temp);
179 if (!detach(packed_form, day_in_month)) return false;
180 if (!detach(packed_form, temp)) return false; // dst chunk--backward compat.
184 bool day_in_year::operator < (const day_in_year &to_compare) const
186 EASY_LT(month, to_compare.month);
187 EASY_LT(day_in_month, to_compare.day_in_month);
191 bool day_in_year::operator == (const day_in_year &to_compare) const
193 return (month == to_compare.month)
194 && (day_in_month == to_compare.day_in_month);
197 astring day_in_year::text_form(int how) const
200 text_form(to_return, how);
204 void day_in_year::text_form(astring &to_stuff, int how) const
206 if (!how) return; // enforce use of the default.
207 if (how & INCLUDE_DAY) to_stuff += astring(day_name(day_of_week)) + " ";
208 const char *monat = short_month_name(month);
209 if (how & LONG_MONTH)
210 monat = month_name(month);
211 //hmmm: more formatting, like euro?
213 to_stuff += a_sprintf(" %02ld", day_in_month);
216 // note: this only works when adjusting across one month, not multiples.
217 time_number limit_day_of_month(time_number &day, time_number days_in_month, time_number days_in_prev_month)
219 if (day > days_in_month) {
220 day -= days_in_month;
221 return 1; // forward rollover.
222 } else if (day < 1) {
223 day += days_in_prev_month;
226 return 0; // no rolling.
229 time_number day_in_year::normalize(day_in_year &to_fix, bool leap_year)
231 time_number rolls = 0; // rollover counter.
232 time_number daysinm = leap_year?
233 leap_days_in_month[to_fix.month] : days_in_month[to_fix.month];
234 time_number prev_month = to_fix.month - 1;
235 if (prev_month < 0) prev_month = 11;
236 time_number daysinpm = leap_year?
237 leap_days_in_month[prev_month] : days_in_month[prev_month];
238 rolls = limit_day_of_month(to_fix.day_in_month, daysinm, daysinpm);
239 time_number monat = to_fix.month + rolls;
240 limit_value(monat, 12); // months are zero based.
241 to_fix.month = months(monat);
247 void time_locus::pack(byte_array &packed_form) const
249 attach(packed_form, year);
250 clock_time::pack(packed_form);
251 day_in_year::pack(packed_form);
254 bool time_locus::unpack(byte_array &packed_form)
256 if (!detach(packed_form, year)) return false;
257 if (!clock_time::unpack(packed_form)) return false;
258 if (!day_in_year::unpack(packed_form)) return false;
262 astring time_locus::text_form_long(int t, int d, int y) const
265 text_form_long(to_return, t, d, y);
269 bool time_locus::equal_to(const equalizable &s2) const {
270 const time_locus *s2_cast = dynamic_cast<const time_locus *>(&s2);
271 if (!s2_cast) throw "error: time_locus::==: unknown type";
272 return (year == s2_cast->year)
273 && ( (const day_in_year &) *this == *s2_cast)
274 && ( (const clock_time &) *this == *s2_cast);
277 bool time_locus::less_than(const orderable &s2) const {
278 const time_locus *s2_cast = dynamic_cast<const time_locus *>(&s2);
279 if (!s2_cast) throw "error: time_locus::<: unknown type";
280 EASY_LT(year, s2_cast->year);
281 if (day_in_year::operator < (*s2_cast)) return true;
282 if (!(day_in_year::operator == (*s2_cast))) return false;
283 if (clock_time::operator < (*s2_cast)) return true;
287 void time_locus::text_form_long(astring &to_stuff, int t, int d, int y) const
289 //hmmm: more formatting desired, like european.
291 text_form_long(to_stuff, t, d); // enforce use of the default.
295 day_in_year::text_form(to_stuff, d);
299 to_stuff += a_sprintf("%2ld", year % 100);
301 to_stuff += a_sprintf("%4ld", year);
304 clock_time::text_form(to_stuff, t);
307 time_number time_locus::normalize(time_locus &to_fix)
309 time_number rolls = clock_time::normalize(to_fix);
310 to_fix.day_in_month += rolls;
312 //hmmm: this little gem should be abstracted to a method.
313 bool leaping = !(to_fix.year % 4);
314 if (!(to_fix.year % 100)) leaping = false;
315 if (!(to_fix.year % 400)) leaping = true;
317 rolls = day_in_year::normalize(to_fix, leaping);
318 to_fix.year += rolls;
320 // is that always right? not for underflow.
321 //hmmm: resolve the issue of rollovers here.
326 time_locus convert(time_number seconds, time_number useconds,
327 const tm &cal_values)
332 r.millisecond = useconds / 1000;
333 r.microsecond = useconds % 1000;
335 r.hour = cal_values.tm_hour;
336 r.minute = cal_values.tm_min;
337 r.second = cal_values.tm_sec;
338 r.day_in_month = cal_values.tm_mday;
339 r.month = months(cal_values.tm_mon);
340 r.year = cal_values.tm_year + 1900;
341 r.day_of_week = days(cal_values.tm_wday);
342 r.day_of_year = cal_values.tm_yday;
344 LOG(a_sprintf("convert() returning: %s\n",
345 r.text_form_long(clock_time::MILITARY,
346 day_in_year::LONG_MONTH | day_in_year::INCLUDE_DAY,
347 time_locus::LONG_YEAR).s()));
356 int okay = gettimeofday(&currtime, NULL_POINTER);
358 LOG("failed to gettimeofday!?");
360 time_t currtime_secs = currtime.tv_sec;
362 tm *tz_ptr = localtime_r(&currtime_secs, &result);
363 if (tz_ptr != &result) {
364 LOG("failed to get time for local area with localtime_r");
366 return convert(currtime.tv_sec, currtime.tv_usec, result);
369 time_locus greenwich_now()
371 FUNCDEF("greenwich_now")
373 int okay = gettimeofday(&currtime, NULL_POINTER);
375 LOG("failed to gettimeofday!?");
377 time_t currtime_secs = currtime.tv_sec;
379 tm *tz_ptr = gmtime_r(&currtime_secs, &result);
380 if (tz_ptr != &result) {
381 LOG("failed to get time for local area with gmtime_r");
383 return convert(currtime.tv_sec, currtime.tv_usec, result);
386 clock_time time_now() { return now(); }
388 days day_now() { return now().day_of_week; }
390 months month_now() { return now().month; }
392 time_number year_now() { return now().year; }
394 day_in_year date_now() { return now(); }
396 const char *day_name(days to_name)
399 case SUNDAY: return "Sunday";
400 case MONDAY: return "Monday";
401 case TUESDAY: return "Tuesday";
402 case WEDNESDAY: return "Wednesday";
403 case THURSDAY: return "Thursday";
404 case FRIDAY: return "Friday";
405 case SATURDAY: return "Saturday";
406 default: return "Not_a_day";
410 const char *month_name(months to_name)
413 case JANUARY: return "January";
414 case FEBRUARY: return "February";
415 case MARCH: return "March";
416 case APRIL: return "April";
417 case MAY: return "May";
418 case JUNE: return "June";
419 case JULY: return "July";
420 case AUGUST: return "August";
421 case SEPTEMBER: return "September";
422 case OCTOBER: return "October";
423 case NOVEMBER: return "November";
424 case DECEMBER: return "December";
425 default: return "Not_a_month";
429 const char *short_month_name(months to_name)
432 case JANUARY: return "Jan";
433 case FEBRUARY: return "Feb";
434 case MARCH: return "Mar";
435 case APRIL: return "Apr";
436 case MAY: return "May";
437 case JUNE: return "Jun";
438 case JULY: return "Jul";
439 case AUGUST: return "Aug";
440 case SEPTEMBER: return "Sep";
441 case OCTOBER: return "Oct";
442 case NOVEMBER: return "Nov";
443 case DECEMBER: return "Dec";
444 default: return "Not";