1#include "TimeUtils.h"
2#include <stdio.h>
3#include <cutils/tztime.h>
4
5namespace android {
6
7static void
8dump(const Time& t)
9{
10    #ifdef HAVE_TM_GMTOFF
11        long tm_gmtoff = t.t.tm_gmtoff;
12    #else
13        long tm_gmtoff = 0;
14    #endif
15    printf("%04d-%02d-%02d %02d:%02d:%02d (%d,%ld,%d,%d)\n",
16            t.t.tm_year+1900, t.t.tm_mon+1, t.t.tm_mday,
17            t.t.tm_hour, t.t.tm_min, t.t.tm_sec,
18            t.t.tm_isdst, tm_gmtoff, t.t.tm_wday, t.t.tm_yday);
19}
20
21Time::Time()
22{
23    t.tm_sec = 0;
24    t.tm_min = 0;
25    t.tm_hour = 0;
26    t.tm_mday = 0;
27    t.tm_mon = 0;
28    t.tm_year = 0;
29    t.tm_wday = 0;
30    t.tm_yday = 0;
31    t.tm_isdst = -1; // we don't know, so let the C library determine
32    #ifdef HAVE_TM_GMTOFF
33        t.tm_gmtoff = 0;
34    #endif
35}
36
37
38#define COMPARE_FIELD(field) do { \
39        int diff = a.t.field - b.t.field; \
40        if (diff != 0) return diff; \
41    } while(0)
42
43int
44Time::compare(Time& a, Time& b)
45{
46    if (0 == strcmp(a.timezone, b.timezone)) {
47        // if the timezones are the same, we can easily compare the two
48        // times.  Otherwise, convert to milliseconds and compare that.
49        // This requires that object be normalized.
50        COMPARE_FIELD(tm_year);
51        COMPARE_FIELD(tm_mon);
52        COMPARE_FIELD(tm_mday);
53        COMPARE_FIELD(tm_hour);
54        COMPARE_FIELD(tm_min);
55        COMPARE_FIELD(tm_sec);
56        return 0;
57    } else {
58        int64_t am = a.toMillis(false /* use isDst */);
59        int64_t bm = b.toMillis(false /* use isDst */);
60        int64_t diff = am-bm;
61        return (diff < 0) ? -1 : ((diff > 0) ? 1 : 0);
62    }
63}
64
65static const int DAYS_PER_MONTH[] = {
66                        31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
67                    };
68
69static inline int days_this_month(int year, int month)
70{
71    int n = DAYS_PER_MONTH[month];
72    if (n != 28) {
73        return n;
74    } else {
75        int y = year;
76        return ((y%4)==0&&((y%100)!=0||(y%400)==0)) ? 29 : 28;
77    }
78}
79
80void
81Time::switchTimezone(const char* timezone)
82{
83    time_t seconds = mktime_tz(&(this->t), this->timezone);
84    localtime_tz(&seconds, &(this->t), timezone);
85}
86
87String8
88Time::format(const char *format, const struct strftime_locale *locale) const
89{
90    char buf[257];
91    int n = strftime_tz(buf, 257, format, &(this->t), locale);
92    if (n > 0) {
93        return String8(buf);
94    } else {
95        return String8();
96    }
97}
98
99static inline short
100tochar(int n)
101{
102    return (n >= 0 && n <= 9) ? ('0'+n) : ' ';
103}
104
105static inline short
106next_char(int *m, int k)
107{
108    int n = *m / k;
109    *m = *m % k;
110    return tochar(n);
111}
112
113void
114Time::format2445(short* buf, bool hasTime) const
115{
116    int n;
117
118    n = t.tm_year+1900;
119    buf[0] = next_char(&n, 1000);
120    buf[1] = next_char(&n, 100);
121    buf[2] = next_char(&n, 10);
122    buf[3] = tochar(n);
123
124    n = t.tm_mon+1;
125    buf[4] = next_char(&n, 10);
126    buf[5] = tochar(n);
127
128    n = t.tm_mday;
129    buf[6] = next_char(&n, 10);
130    buf[7] = tochar(n);
131
132    if (hasTime) {
133      buf[8] = 'T';
134
135      n = t.tm_hour;
136      buf[9] = next_char(&n, 10);
137      buf[10] = tochar(n);
138
139      n = t.tm_min;
140      buf[11] = next_char(&n, 10);
141      buf[12] = tochar(n);
142
143      n = t.tm_sec;
144      buf[13] = next_char(&n, 10);
145      buf[14] = tochar(n);
146      bool inUtc = strcmp("UTC", timezone) == 0;
147      if (inUtc) {
148          buf[15] = 'Z';
149      }
150    }
151}
152
153String8
154Time::toString() const
155{
156    String8 str;
157    char* s = str.lockBuffer(150);
158    #ifdef HAVE_TM_GMTOFF
159        long tm_gmtoff = t.tm_gmtoff;
160    #else
161        long tm_gmtoff = 0;
162    #endif
163    sprintf(s, "%04d%02d%02dT%02d%02d%02d%s(%d,%d,%ld,%d,%d)",
164            t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min,
165            t.tm_sec, timezone, t.tm_wday, t.tm_yday, tm_gmtoff, t.tm_isdst,
166            (int)(((Time*)this)->toMillis(false /* use isDst */)/1000));
167    str.unlockBuffer();
168    return str;
169}
170
171void
172Time::setToNow()
173{
174    time_t seconds;
175    time(&seconds);
176    localtime_tz(&seconds, &(this->t), this->timezone);
177}
178
179int64_t
180Time::toMillis(bool ignoreDst)
181{
182    if (ignoreDst) {
183        this->t.tm_isdst = -1;
184    }
185    int64_t r = mktime_tz(&(this->t), this->timezone);
186    if (r == -1)
187        return -1;
188    return r * 1000;
189}
190
191void
192Time::set(int64_t millis)
193{
194    time_t seconds = millis / 1000;
195    localtime_tz(&seconds, &(this->t), this->timezone);
196}
197
198}; // namespace android
199
200