Diffstat (limited to 'libical/src/libical/icaltime.c') (more/less context) (ignore whitespace changes)
-rw-r--r-- | libical/src/libical/icaltime.c | 1116 |
1 files changed, 777 insertions, 339 deletions
diff --git a/libical/src/libical/icaltime.c b/libical/src/libical/icaltime.c index a04ca04..3aac74e 100644 --- a/libical/src/libical/icaltime.c +++ b/libical/src/libical/icaltime.c | |||
@@ -13,565 +13,1003 @@ | |||
13 | 13 | ||
14 | The LGPL as published by the Free Software Foundation, version | 14 | The LGPL as published by the Free Software Foundation, version |
15 | 2.1, available at: http://www.fsf.org/copyleft/lesser.html | 15 | 2.1, available at: http://www.fsf.org/copyleft/lesser.html |
16 | 16 | ||
17 | Or: | 17 | Or: |
18 | 18 | ||
19 | The Mozilla Public License Version 1.0. You may obtain a copy of | 19 | The Mozilla Public License Version 1.0. You may obtain a copy of |
20 | the License at http://www.mozilla.org/MPL/ | 20 | the License at http://www.mozilla.org/MPL/ |
21 | 21 | ||
22 | The Original Code is eric. The Initial Developer of the Original | 22 | The Original Code is eric. The Initial Developer of the Original |
23 | Code is Eric Busboom | 23 | Code is Eric Busboom |
24 | 24 | ||
25 | 25 | ||
26 | ======================================================================*/ | 26 | ======================================================================*/ |
27 | 27 | ||
28 | #ifdef HAVE_CONFIG_H | 28 | #ifdef HAVE_CONFIG_H |
29 | #include <config.h> | 29 | #include "config.h" |
30 | #endif | 30 | #endif |
31 | 31 | ||
32 | #include "icaltime.h" | 32 | #include "icaltime.h" |
33 | #include <assert.h> | 33 | #include <assert.h> |
34 | #include <string.h> | 34 | #include <string.h> |
35 | #include <stdlib.h> | 35 | #include <stdlib.h> |
36 | #include <stdio.h> | 36 | #include <stdio.h> |
37 | #include <time.h> | ||
37 | 38 | ||
38 | int snprintf(char *str, size_t n, char const *fmt, ...); | 39 | #include "astime.h" /* Julian data handling routines */ |
39 | 40 | ||
40 | #ifdef ICAL_NO_LIBICAL | ||
41 | #define icalerror_set_errno(x) | ||
42 | #define icalerror_check_arg_rv(x,y) | ||
43 | #define icalerror_check_arg_re(x,y,z) | ||
44 | #else | ||
45 | #include "icalerror.h" | 41 | #include "icalerror.h" |
46 | #include "icalmemory.h" | 42 | #include "icalmemory.h" |
43 | |||
44 | #include "icaltimezone.h" | ||
45 | #include "icalvalue.h" | ||
46 | |||
47 | #ifdef WIN32 | ||
48 | #include <Windows.h> | ||
49 | #define ERROR error | ||
50 | #define snprintf _snprintf | ||
51 | #define strcasecmp stricmp | ||
47 | #endif | 52 | #endif |
48 | 53 | ||
54 | /* | ||
55 | * Function to convert a struct tm time specification | ||
56 | * to an ANSI time_t using the specified time zone. | ||
57 | * This is different from the standard mktime() function | ||
58 | * in that we dont want the automatic adjustments for | ||
59 | * local daylight savings time applied to the result. | ||
60 | * This function expects well-formed input. | ||
61 | */ | ||
62 | static time_t make_time(struct tm *tm, int tzm) | ||
63 | { | ||
64 | time_t tim; | ||
49 | 65 | ||
66 | static int days[] = { -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364 }; | ||
50 | 67 | ||
68 | /* check that year specification within range */ | ||
51 | 69 | ||
52 | struct icaltimetype | 70 | if (tm->tm_year < 70 || tm->tm_year > 138) |
53 | icaltime_from_timet(time_t tm, int is_date) | 71 | return((time_t) -1); |
54 | { | ||
55 | struct icaltimetype tt = icaltime_null_time(); | ||
56 | struct tm t; | ||
57 | 72 | ||
58 | t = *(gmtime(&tm)); | 73 | /* check that month specification within range */ |
59 | |||
60 | if(is_date == 0){ | ||
61 | tt.second = t.tm_sec; | ||
62 | tt.minute = t.tm_min; | ||
63 | tt.hour = t.tm_hour; | ||
64 | } else { | ||
65 | tt.second = tt.minute =tt.hour = 0 ; | ||
66 | } | ||
67 | 74 | ||
68 | tt.day = t.tm_mday; | 75 | if (tm->tm_mon < 0 || tm->tm_mon > 11) |
69 | tt.month = t.tm_mon + 1; | 76 | return((time_t) -1); |
70 | tt.year = t.tm_year+ 1900; | ||
71 | |||
72 | tt.is_utc = 1; | ||
73 | tt.is_date = is_date; | ||
74 | 77 | ||
75 | return tt; | 78 | /* check for upper bound of Jan 17, 2038 (to avoid possibility of |
76 | } | 79 | 32-bit arithmetic overflow) */ |
80 | |||
81 | if (tm->tm_year == 138) { | ||
82 | if (tm->tm_mon > 0) | ||
83 | return((time_t) -1); | ||
84 | else if (tm->tm_mday > 17) | ||
85 | return((time_t) -1); | ||
86 | } | ||
77 | 87 | ||
78 | /* Structure used by set_tz to hold an old value of TZ, and the new | 88 | /* |
79 | value, which is in memory we will have to free in unset_tz */ | 89 | * calculate elapsed days since start of the epoch (midnight Jan |
80 | struct set_tz_save {char* orig_tzid; char* new_env_str;}; | 90 | * 1st, 1970 UTC) 17 = number of leap years between 1900 and 1970 |
91 | * (number of leap days to subtract) | ||
92 | */ | ||
81 | 93 | ||
82 | /* Temporarily change the TZ environmental variable. */ | 94 | tim = (tm->tm_year - 70) * 365 + ((tm->tm_year - 1) / 4) - 17; |
83 | struct set_tz_save set_tz(const char* tzid) | ||
84 | { | ||
85 | 95 | ||
86 | char *orig_tzid = 0; | 96 | /* add number of days elapsed in the current year */ |
87 | char *new_env_str; | ||
88 | struct set_tz_save savetz; | ||
89 | size_t tmp_sz; | ||
90 | 97 | ||
91 | savetz.orig_tzid = 0; | 98 | tim += days[tm->tm_mon]; |
92 | savetz.new_env_str = 0; | ||
93 | 99 | ||
94 | if(getenv("TZ") != 0){ | 100 | /* check and adjust for leap years (the leap year check only valid |
95 | orig_tzid = (char*)icalmemory_strdup(getenv("TZ")); | 101 | during the 32-bit era */ |
96 | 102 | ||
97 | if(orig_tzid == 0){ | 103 | if ((tm->tm_year & 3) == 0 && tm->tm_mon > 1) |
98 | icalerror_set_errno(ICAL_NEWFAILED_ERROR); | 104 | tim += 1; |
99 | return savetz; | ||
100 | } | ||
101 | } | ||
102 | 105 | ||
103 | tmp_sz =strlen(tzid)+4; | 106 | /* elapsed days to current date */ |
104 | new_env_str = (char*)malloc(tmp_sz); | ||
105 | 107 | ||
106 | if(new_env_str == 0){ | 108 | tim += tm->tm_mday; |
107 | icalerror_set_errno(ICAL_NEWFAILED_ERROR); | ||
108 | return savetz; | ||
109 | } | ||
110 | |||
111 | /* Copy the TZid into a string with the form that putenv expects. */ | ||
112 | strcpy(new_env_str,"TZ="); | ||
113 | strcpy(new_env_str+3,tzid); | ||
114 | 109 | ||
115 | putenv(new_env_str); | ||
116 | 110 | ||
117 | /* Old value of TZ and the string we will have to free later */ | 111 | /* calculate elapsed hours since start of the epoch */ |
118 | savetz.orig_tzid = orig_tzid; | 112 | |
119 | savetz.new_env_str = new_env_str; | 113 | tim = tim * 24 + tm->tm_hour; |
120 | return savetz; | 114 | |
115 | /* calculate elapsed minutes since start of the epoch */ | ||
116 | |||
117 | tim = tim * 60 + tm->tm_min; | ||
118 | |||
119 | /* adjust per time zone specification */ | ||
120 | |||
121 | tim -= tzm; | ||
122 | |||
123 | /* calculate elapsed seconds since start of the epoch */ | ||
124 | |||
125 | tim = tim * 60 + tm->tm_sec; | ||
126 | |||
127 | /* return number of seconds since start of the epoch */ | ||
128 | |||
129 | return(tim); | ||
121 | } | 130 | } |
122 | 131 | ||
123 | void unset_tz(struct set_tz_save savetz) | 132 | /**@brief Constructor (deprecated). |
133 | * | ||
134 | * Convert seconds past UNIX epoch to a timetype. | ||
135 | * | ||
136 | * @deprecated This constructor is deprecated and shouldn't be used in | ||
137 | * new software. Use icaltime_from_timet_with_zone(time_t, int, | ||
138 | * icaltimezone *) instead. In the meantime, calls to this method | ||
139 | * return a floating time, which can always be converted to a local | ||
140 | * time with an appropriate call to icaltime_convert_to_zone(). | ||
141 | */ | ||
142 | |||
143 | struct icaltimetype | ||
144 | icaltime_from_timet(const time_t tm, const int is_date) | ||
124 | { | 145 | { |
125 | /* restore the original TZ environment */ | 146 | #ifndef NO_WARN_DEPRECATED |
147 | icalerror_warn("icaltime_from_timet() is DEPRECATED, use icaltime_from_timet_with_zone() instead"); | ||
148 | #endif | ||
126 | 149 | ||
127 | char* orig_tzid = savetz.orig_tzid; | 150 | return icaltime_from_timet_with_zone(tm, is_date, 0); |
151 | } | ||
128 | 152 | ||
129 | if(orig_tzid!=0){ | ||
130 | size_t tmp_sz =strlen(orig_tzid)+4; | ||
131 | char* orig_env_str = (char*)icalmemory_tmp_buffer(tmp_sz); | ||
132 | 153 | ||
133 | if(orig_env_str == 0){ | 154 | /**@brief Constructor. |
134 | icalerror_set_errno(ICAL_NEWFAILED_ERROR); | 155 | * |
135 | return; | 156 | *@param tm The time |
136 | } | 157 | *@param is_date Boolean: 1 means we should treat tm as a DATE |
137 | 158 | *@param zone The timezone tm is in, NULL means to treat tm as a | |
138 | strcpy(orig_env_str,"TZ="); | 159 | * floating time |
139 | strcpy(orig_env_str+3,orig_tzid); | 160 | * |
140 | #ifndef _WIN32 | 161 | *Return a new icaltime instance, initialized to the given time |
141 | setenv("TZ", savetz.orig_tzid,1 ); | 162 | *expressed as seconds past UNIX epoch, optionally using the given |
142 | #else | 163 | *timezone. |
143 | putenv("TZ=MEZ");//, savetz.orig_tzid ); | 164 | * |
144 | #endif | 165 | *If the caller specifies the is_date param as TRUE, the returned |
145 | free(orig_tzid); | 166 | *object is of DATE type, otherwise the input is meant to be of |
146 | } else { | 167 | *DATE-TIME type. |
147 | #ifdef __FreeBSD__ | 168 | *If the zone is not specified (NULL zone param) the time is taken |
148 | unsetenv("TZ"); | 169 | *to be floating, that is, valid in any timezone. Note that, in |
170 | *addition to the uses specified in [RFC2445], this can be used | ||
171 | *when doing simple math on couples of times. | ||
172 | *If the zone is specified (UTC or otherwise), it's stored in the | ||
173 | *object and it's used as the native timezone for this object. | ||
174 | *This means that the caller can convert this time to a different | ||
175 | *target timezone with no need to store the source timezone. | ||
176 | * | ||
177 | */ | ||
178 | struct icaltimetype | ||
179 | icaltime_from_timet_with_zone(const time_t tm, const int is_date, | ||
180 | const icaltimezone *zone) | ||
181 | { | ||
182 | struct icaltimetype tt = icaltime_null_time(); | ||
183 | struct tm t; | ||
184 | icaltimezone *utc_zone; | ||
185 | |||
186 | /* Convert the time_t to a struct tm. We can trust gmtime for this. */ | ||
187 | #ifdef HAVE_GMTIME_R | ||
188 | gmtime_r(&tm, &t); | ||
149 | #else | 189 | #else |
150 | putenv("TZ"); /* Delete from environment */ | 190 | { |
191 | struct tm *t_ptr = gmtime(&tm); | ||
192 | t = *t_ptr; | ||
193 | } | ||
151 | #endif | 194 | #endif |
152 | } | ||
153 | 195 | ||
154 | if(savetz.new_env_str != 0){ | 196 | tt.year = t.tm_year + 1900; |
155 | free(savetz.new_env_str); | 197 | tt.month = t.tm_mon + 1; |
198 | tt.day = t.tm_mday; | ||
199 | |||
200 | if (is_date) { | ||
201 | tt.is_date = 1; | ||
202 | return tt; | ||
156 | } | 203 | } |
204 | |||
205 | tt.hour = t.tm_hour; | ||
206 | tt.minute = t.tm_min; | ||
207 | tt.second = t.tm_sec; | ||
208 | |||
209 | /* If it's a floating time, we don't do any conversion. */ | ||
210 | if (zone == NULL) { | ||
211 | return tt; | ||
212 | } | ||
213 | |||
214 | utc_zone = icaltimezone_get_utc_timezone (); | ||
215 | tt.is_utc = (zone == utc_zone) ? 1 : 0; | ||
216 | tt.zone = zone; | ||
217 | |||
218 | return tt; | ||
157 | } | 219 | } |
158 | 220 | ||
221 | /**@brief Convenience constructor. | ||
222 | * | ||
223 | * Returns the current time in the given timezone, as an icaltimetype. | ||
224 | */ | ||
225 | struct icaltimetype icaltime_current_time_with_zone(const icaltimezone *zone) | ||
226 | { | ||
227 | return icaltime_from_timet_with_zone (time (NULL), 0, zone); | ||
228 | } | ||
159 | 229 | ||
160 | time_t icaltime_as_timet(struct icaltimetype tt) | 230 | /**@brief Convenience constructor. |
231 | * | ||
232 | * Returns the current day as an icaltimetype, with is_date set. | ||
233 | */ | ||
234 | struct icaltimetype icaltime_today(void) | ||
235 | { | ||
236 | return icaltime_from_timet_with_zone (time (NULL), 1, NULL); | ||
237 | } | ||
238 | |||
239 | /** @briefReturn the time as seconds past the UNIX epoch | ||
240 | */ | ||
241 | time_t icaltime_as_timet(const struct icaltimetype tt) | ||
161 | { | 242 | { |
162 | struct tm stm; | 243 | struct tm stm; |
163 | time_t t; | 244 | time_t t; |
164 | 245 | ||
165 | memset(&stm,0,sizeof( struct tm)); | 246 | /* If the time is the special null time, return 0. */ |
166 | 247 | if (icaltime_is_null_time(tt)) { | |
167 | if(icaltime_is_null_time(tt)) { | ||
168 | return 0; | 248 | return 0; |
169 | } | 249 | } |
170 | 250 | ||
251 | /* Copy the icaltimetype to a struct tm. */ | ||
252 | memset (&stm, 0, sizeof (struct tm)); | ||
253 | |||
171 | stm.tm_sec = tt.second; | 254 | stm.tm_sec = tt.second; |
172 | stm.tm_min = tt.minute; | 255 | stm.tm_min = tt.minute; |
173 | stm.tm_hour = tt.hour; | 256 | stm.tm_hour = tt.hour; |
174 | stm.tm_mday = tt.day; | 257 | stm.tm_mday = tt.day; |
175 | stm.tm_mon = tt.month-1; | 258 | stm.tm_mon = tt.month-1; |
176 | stm.tm_year = tt.year-1900; | 259 | stm.tm_year = tt.year-1900; |
177 | stm.tm_isdst = -1; | 260 | stm.tm_isdst = -1; |
178 | 261 | ||
179 | if(tt.is_utc == 1 || tt.is_date == 1){ | 262 | t = make_time(&stm, 0); |
180 | struct set_tz_save old_tz = set_tz("UTC"); | ||
181 | t = mktime(&stm); | ||
182 | unset_tz(old_tz); | ||
183 | } else { | ||
184 | t = mktime(&stm); | ||
185 | } | ||
186 | 263 | ||
187 | return t; | 264 | return t; |
188 | 265 | ||
189 | } | 266 | } |
190 | 267 | ||
191 | char* icaltime_as_ical_string(struct icaltimetype tt) | 268 | /** @briefReturn the time as seconds past the UNIX epoch, using the |
269 | *given timezone. | ||
270 | * | ||
271 | *This convenience method combines a call to icaltime_convert() with | ||
272 | *a call to icaltime_as_timet(). | ||
273 | *If the input timezone is null, no conversion is done; that is, the | ||
274 | *time is simply returned as time_t in its native timezone. | ||
275 | */ | ||
276 | time_t icaltime_as_timet_with_zone(const struct icaltimetype _tt, | ||
277 | const icaltimezone *zone) | ||
278 | { | ||
279 | struct icaltimetype tt = _tt; | ||
280 | struct tm stm; | ||
281 | time_t t; | ||
282 | |||
283 | /* If the time is the special null time, return 0. */ | ||
284 | if (icaltime_is_null_time(tt)) { | ||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | if (zone != NULL) { | ||
289 | tt = icaltime_convert_to_zone(_tt, zone); | ||
290 | } | ||
291 | |||
292 | /* Copy the icaltimetype to a struct tm. */ | ||
293 | memset (&stm, 0, sizeof (struct tm)); | ||
294 | |||
295 | stm.tm_sec = tt.second; | ||
296 | stm.tm_min = tt.minute; | ||
297 | stm.tm_hour = tt.hour; | ||
298 | stm.tm_mday = tt.day; | ||
299 | stm.tm_mon = tt.month-1; | ||
300 | stm.tm_year = tt.year-1900; | ||
301 | stm.tm_isdst = -1; | ||
302 | |||
303 | t = make_time(&stm, 0); | ||
304 | |||
305 | return t; | ||
306 | } | ||
307 | |||
308 | /** | ||
309 | * Return a string represention of the time, in RFC2445 format. The | ||
310 | * string is owned by libical | ||
311 | */ | ||
312 | const char* icaltime_as_ical_string(const struct icaltimetype tt) | ||
192 | { | 313 | { |
193 | size_t size = 17; | 314 | size_t size = 17; |
194 | char* buf = icalmemory_new_buffer(size); | 315 | char* buf = icalmemory_new_buffer(size); |
195 | 316 | ||
196 | if(tt.is_date){ | 317 | if(tt.is_date){ |
197 | snprintf(buf, size,"%04d%02d%02d",tt.year,tt.month,tt.day); | 318 | snprintf(buf, size,"%04d%02d%02d",tt.year,tt.month,tt.day); |
198 | } else { | 319 | } else { |
199 | char* fmt; | 320 | char* fmt; |
200 | if(tt.is_utc){ | 321 | if(tt.is_utc){ |
201 | fmt = "%04d%02d%02dT%02d%02d%02dZ"; | 322 | fmt = "%04d%02d%02dT%02d%02d%02dZ"; |
202 | } else { | 323 | } else { |
203 | fmt = "%04d%02d%02dT%02d%02d%02d"; | 324 | fmt = "%04d%02d%02dT%02d%02d%02d"; |
204 | } | 325 | } |
205 | snprintf(buf, size,fmt,tt.year,tt.month,tt.day, | 326 | snprintf(buf, size,fmt,tt.year,tt.month,tt.day, |
206 | tt.hour,tt.minute,tt.second); | 327 | tt.hour,tt.minute,tt.second); |
207 | } | 328 | } |
208 | 329 | ||
209 | icalmemory_add_tmp_buffer(buf); | 330 | icalmemory_add_tmp_buffer(buf); |
210 | 331 | ||
211 | return buf; | 332 | return buf; |
212 | 333 | ||
213 | } | 334 | } |
214 | 335 | ||
215 | 336 | /** | |
216 | /* convert tt, of timezone tzid, into a utc time */ | 337 | *Reset all of the time components to be in their normal ranges. For |
217 | struct icaltimetype icaltime_as_utc(struct icaltimetype tt,const char* tzid) | 338 | *instance, given a time with minutes=70, the minutes will be reduces |
218 | { | 339 | *to 10, and the hour incremented. This allows the caller to do |
219 | int tzid_offset; | 340 | *arithmetic on times without worrying about overflow or |
220 | 341 | *underflow. | |
221 | if(tt.is_utc == 1 || tt.is_date == 1){ | 342 | * |
222 | return tt; | 343 | *Implementation note: we call icaltime_adjust() with no adjustment. |
223 | } | 344 | */ |
224 | 345 | struct icaltimetype icaltime_normalize(const struct icaltimetype tt) | |
225 | tzid_offset = icaltime_utc_offset(tt,tzid); | ||
226 | |||
227 | tt.second -= tzid_offset; | ||
228 | |||
229 | tt.is_utc = 1; | ||
230 | |||
231 | return icaltime_normalize(tt); | ||
232 | } | ||
233 | |||
234 | /* convert tt, a time in UTC, into a time in timezone tzid */ | ||
235 | struct icaltimetype icaltime_as_zone(struct icaltimetype tt,const char* tzid) | ||
236 | { | 346 | { |
237 | int tzid_offset; | 347 | struct icaltimetype ret = tt; |
238 | 348 | icaltime_adjust(&ret, 0, 0, 0, 0); | |
239 | tzid_offset = icaltime_utc_offset(tt,tzid); | 349 | return ret; |
240 | |||
241 | tt.second += tzid_offset; | ||
242 | |||
243 | tt.is_utc = 0; | ||
244 | |||
245 | return icaltime_normalize(tt); | ||
246 | |||
247 | } | ||
248 | |||
249 | |||
250 | /* Return the offset of the named zone as seconds. tt is a time | ||
251 | indicating the date for which you want the offset */ | ||
252 | int icaltime_utc_offset(struct icaltimetype ictt, const char* tzid) | ||
253 | { | ||
254 | |||
255 | time_t tt = icaltime_as_timet(ictt); | ||
256 | time_t offset_tt; | ||
257 | struct tm gtm; | ||
258 | struct set_tz_save old_tz; | ||
259 | |||
260 | if(tzid != 0){ | ||
261 | old_tz = set_tz(tzid); | ||
262 | } | ||
263 | |||
264 | /* Mis-interpret a UTC broken out time as local time */ | ||
265 | gtm = *(gmtime(&tt)); | ||
266 | gtm.tm_isdst = localtime(&tt)->tm_isdst; | ||
267 | offset_tt = mktime(>m); | ||
268 | |||
269 | if(tzid != 0){ | ||
270 | unset_tz(old_tz); | ||
271 | } | ||
272 | |||
273 | return tt-offset_tt; | ||
274 | } | ||
275 | |||
276 | |||
277 | |||
278 | /* Normalize by converting from localtime to utc and back to local | ||
279 | time. This uses localtime because localtime and mktime are inverses | ||
280 | of each other */ | ||
281 | |||
282 | struct icaltimetype icaltime_normalize(struct icaltimetype tt) | ||
283 | { | ||
284 | struct tm stm; | ||
285 | time_t tut; | ||
286 | |||
287 | memset(&stm,0,sizeof( struct tm)); | ||
288 | |||
289 | stm.tm_sec = tt.second; | ||
290 | stm.tm_min = tt.minute; | ||
291 | stm.tm_hour = tt.hour; | ||
292 | stm.tm_mday = tt.day; | ||
293 | stm.tm_mon = tt.month-1; | ||
294 | stm.tm_year = tt.year-1900; | ||
295 | stm.tm_isdst = -1; /* prevents mktime from changing hour based on | ||
296 | daylight savings */ | ||
297 | |||
298 | tut = mktime(&stm); | ||
299 | |||
300 | stm = *(localtime(&tut)); | ||
301 | |||
302 | tt.second = stm.tm_sec; | ||
303 | tt.minute = stm.tm_min; | ||
304 | tt.hour = stm.tm_hour; | ||
305 | tt.day = stm.tm_mday; | ||
306 | tt.month = stm.tm_mon +1; | ||
307 | tt.year = stm.tm_year+1900; | ||
308 | |||
309 | return tt; | ||
310 | } | 350 | } |
311 | 351 | ||
312 | 352 | ||
313 | #ifndef ICAL_NO_LIBICAL | ||
314 | #include "icalvalue.h" | ||
315 | 353 | ||
354 | /**@brief Contructor. | ||
355 | * | ||
356 | * Create a time from an ISO format string. | ||
357 | * | ||
358 | * @todo If the given string specifies a DATE-TIME not in UTC, there | ||
359 | * is no way to know if this is a floating time or really refers to a | ||
360 | * timezone. We should probably add a new constructor: | ||
361 | * icaltime_from_string_with_zone() | ||
362 | */ | ||
316 | struct icaltimetype icaltime_from_string(const char* str) | 363 | struct icaltimetype icaltime_from_string(const char* str) |
317 | { | 364 | { |
318 | struct icaltimetype tt = icaltime_null_time(); | 365 | struct icaltimetype tt = icaltime_null_time(); |
319 | int size; | 366 | int size; |
320 | 367 | ||
321 | icalerror_check_arg_re(str!=0,"str",icaltime_null_time()); | 368 | icalerror_check_arg_re(str!=0,"str",icaltime_null_time()); |
322 | 369 | ||
323 | size = strlen(str); | 370 | size = strlen(str); |
324 | 371 | ||
325 | if(size == 15) { /* floating time */ | 372 | if(size == 15) { /* floating time */ |
326 | tt.is_utc = 0; | 373 | tt.is_utc = 0; |
327 | tt.is_date = 0; | 374 | tt.is_date = 0; |
328 | } else if (size == 16) { /* UTC time, ends in 'Z'*/ | 375 | } else if (size == 16) { /* UTC time, ends in 'Z'*/ |
376 | if(str[15] != 'Z') | ||
377 | goto ERROR; | ||
378 | |||
329 | tt.is_utc = 1; | 379 | tt.is_utc = 1; |
380 | tt.zone = icaltimezone_get_utc_timezone(); | ||
330 | tt.is_date = 0; | 381 | tt.is_date = 0; |
331 | |||
332 | if(str[15] != 'Z'){ | ||
333 | icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); | ||
334 | return icaltime_null_time(); | ||
335 | } | ||
336 | |||
337 | } else if (size == 8) { /* A DATE */ | 382 | } else if (size == 8) { /* A DATE */ |
338 | tt.is_utc = 1; | 383 | tt.is_utc = 1; |
339 | tt.is_date = 1; | 384 | tt.is_date = 1; |
340 | } else { /* error */ | 385 | } else { /* error */ |
341 | icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); | 386 | goto ERROR; |
342 | return icaltime_null_time(); | ||
343 | } | 387 | } |
344 | 388 | ||
345 | if(tt.is_date == 1){ | 389 | if(tt.is_date == 1){ |
346 | sscanf(str,"%04d%02d%02d",&tt.year,&tt.month,&tt.day); | 390 | if (sscanf(str,"%04d%02d%02d",&tt.year,&tt.month,&tt.day) < 3) |
391 | goto ERROR; | ||
347 | } else { | 392 | } else { |
348 | char tsep; | 393 | char tsep; |
349 | sscanf(str,"%04d%02d%02d%c%02d%02d%02d",&tt.year,&tt.month,&tt.day, | 394 | if (sscanf(str,"%04d%02d%02d%c%02d%02d%02d",&tt.year,&tt.month,&tt.day, |
350 | &tsep,&tt.hour,&tt.minute,&tt.second); | 395 | &tsep,&tt.hour,&tt.minute,&tt.second) < 7) |
351 | 396 | goto ERROR; | |
352 | if(tsep != 'T'){ | ||
353 | icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); | ||
354 | return icaltime_null_time(); | ||
355 | } | ||
356 | 397 | ||
398 | if(tsep != 'T') | ||
399 | goto ERROR; | ||
357 | } | 400 | } |
358 | 401 | ||
359 | return tt; | 402 | return tt; |
403 | |||
404 | ERROR: | ||
405 | icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); | ||
406 | return icaltime_null_time(); | ||
360 | } | 407 | } |
361 | #endif | ||
362 | 408 | ||
363 | char ctime_str[20]; | ||
364 | char* icaltime_as_ctime(struct icaltimetype t) | ||
365 | { | ||
366 | time_t tt; | ||
367 | |||
368 | tt = icaltime_as_timet(t); | ||
369 | sprintf(ctime_str,"%s",ctime(&tt)); | ||
370 | 409 | ||
371 | ctime_str[strlen(ctime_str)-1] = 0; | 410 | /* Returns whether the specified year is a leap year. Year is the normal year, |
411 | e.g. 2001. */ | ||
412 | int | ||
413 | icaltime_is_leap_year (const int year) | ||
414 | { | ||
372 | 415 | ||
373 | return ctime_str; | 416 | if (year <= 1752) |
417 | return (year % 4 == 0); | ||
418 | else | ||
419 | return ( (year % 4==0) && (year % 100 !=0 )) || (year % 400 == 0); | ||
374 | } | 420 | } |
375 | 421 | ||
422 | static int _days_in_month[] = {0,31,28,31,30,31,30,31,31,30,31,30,31}; | ||
376 | 423 | ||
377 | short days_in_month[] = {0,31,28,31,30,31,30,31,31,30,31,30,31}; | 424 | int icaltime_days_in_month(const int month, const int year) |
378 | |||
379 | short icaltime_days_in_month(short month,short year) | ||
380 | { | 425 | { |
381 | 426 | ||
382 | int is_leap =0; | 427 | int days = _days_in_month[month]; |
383 | int days = days_in_month[month]; | ||
384 | 428 | ||
385 | assert(month > 0); | 429 | assert(month > 0); |
386 | assert(month <= 12); | 430 | assert(month <= 12); |
387 | 431 | ||
388 | if( (year % 4 == 0 && year % 100 != 0) || | ||
389 | year % 400 == 0){ | ||
390 | is_leap =1; | ||
391 | } | ||
392 | |||
393 | if( month == 2){ | 432 | if( month == 2){ |
394 | days += is_leap; | 433 | days += icaltime_is_leap_year(year); |
395 | } | 434 | } |
396 | 435 | ||
397 | return days; | 436 | return days; |
398 | } | 437 | } |
399 | 438 | ||
400 | /* 1-> Sunday, 7->Saturday */ | 439 | /* 1-> Sunday, 7->Saturday */ |
401 | short icaltime_day_of_week(struct icaltimetype t){ | 440 | int icaltime_day_of_week(const struct icaltimetype t){ |
441 | UTinstant jt; | ||
402 | 442 | ||
403 | time_t tt = icaltime_as_timet(t); | 443 | memset(&jt,0,sizeof(UTinstant)); |
404 | struct tm *tm; | ||
405 | 444 | ||
406 | if(t.is_utc == 1 || t.is_date == 1){ | 445 | jt.year = t.year; |
407 | tm = gmtime(&tt); | 446 | jt.month = t.month; |
408 | } else { | 447 | jt.day = t.day; |
409 | tm = localtime(&tt); | 448 | jt.i_hour = 0; |
410 | } | 449 | jt.i_minute = 0; |
450 | jt.i_second = 0; | ||
451 | |||
452 | juldat(&jt); | ||
411 | 453 | ||
412 | return tm->tm_wday+1; | 454 | return jt.weekday + 1; |
413 | } | 455 | } |
414 | 456 | ||
415 | /* Day of the year that the first day of the week (Sunday) is on */ | 457 | /** Day of the year that the first day of the week (Sunday) is on. |
416 | short icaltime_start_doy_of_week(struct icaltimetype t){ | 458 | * |
417 | time_t tt = icaltime_as_timet(t); | 459 | * @todo Doesn't take into account different week start days. |
418 | time_t start_tt; | 460 | */ |
419 | struct tm *stm; | 461 | int icaltime_start_doy_of_week(const struct icaltimetype t){ |
420 | int syear; | 462 | UTinstant jt; |
421 | 463 | ||
422 | stm = gmtime(&tt); | 464 | memset(&jt,0,sizeof(UTinstant)); |
423 | syear = stm->tm_year; | ||
424 | 465 | ||
425 | start_tt = tt - stm->tm_wday*(60*60*24); | 466 | jt.year = t.year; |
467 | jt.month = t.month; | ||
468 | jt.day = t.day; | ||
469 | jt.i_hour = 0; | ||
470 | jt.i_minute = 0; | ||
471 | jt.i_second = 0; | ||
426 | 472 | ||
427 | stm = gmtime(&start_tt); | 473 | juldat(&jt); |
428 | 474 | caldat(&jt); | |
429 | if(syear == stm->tm_year){ | ||
430 | return stm->tm_yday+1; | ||
431 | } else { | ||
432 | /* return negative to indicate that start of week is in | ||
433 | previous year. */ | ||
434 | int is_leap = 0; | ||
435 | int year = stm->tm_year; | ||
436 | |||
437 | if( (year % 4 == 0 && year % 100 != 0) || | ||
438 | year % 400 == 0){ | ||
439 | is_leap =1; | ||
440 | } | ||
441 | 475 | ||
442 | return (stm->tm_yday+1)-(365+is_leap); | 476 | return jt.day_of_year - jt.weekday; |
443 | } | ||
444 | |||
445 | } | 477 | } |
446 | 478 | ||
447 | short icaltime_week_number(struct icaltimetype ictt) | 479 | /** |
480 | * @todo Doesn't take into account the start day of the | ||
481 | * week. strftime assumes that weeks start on Monday. | ||
482 | */ | ||
483 | int icaltime_week_number(const struct icaltimetype ictt) | ||
448 | { | 484 | { |
449 | char str[5]; | 485 | UTinstant jt; |
450 | time_t t = icaltime_as_timet(ictt); | ||
451 | int week_no; | ||
452 | 486 | ||
453 | strftime(str,5,"%V", gmtime(&t)); | 487 | memset(&jt,0,sizeof(UTinstant)); |
454 | 488 | ||
455 | week_no = atoi(str); | 489 | jt.year = ictt.year; |
490 | jt.month = ictt.month; | ||
491 | jt.day = ictt.day; | ||
492 | jt.i_hour = 0; | ||
493 | jt.i_minute = 0; | ||
494 | jt.i_second = 0; | ||
456 | 495 | ||
457 | return week_no; | 496 | juldat(&jt); |
497 | caldat(&jt); | ||
458 | 498 | ||
499 | return (jt.day_of_year - jt.weekday) / 7; | ||
459 | } | 500 | } |
460 | 501 | ||
502 | /* The first array is for non-leap years, the second for leap years*/ | ||
503 | static const int days_in_year[2][13] = | ||
504 | { /* jan feb mar apr may jun jul aug sep oct nov dec */ | ||
505 | { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, | ||
506 | { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } | ||
507 | }; | ||
461 | 508 | ||
462 | short icaltime_day_of_year(struct icaltimetype t){ | 509 | /** |
463 | time_t tt; | 510 | *Returns the day of the year, counting from 1 (Jan 1st). |
464 | struct tm *stm; | 511 | */ |
465 | struct set_tz_save old_tz; | 512 | int icaltime_day_of_year(const struct icaltimetype t){ |
466 | 513 | int is_leap = icaltime_is_leap_year (t.year); | |
467 | tt = icaltime_as_timet(t); | ||
468 | 514 | ||
469 | old_tz = set_tz("UTC"); | 515 | return days_in_year[is_leap][t.month - 1] + t.day; |
470 | stm = localtime(&tt); | ||
471 | unset_tz(old_tz); | ||
472 | |||
473 | return stm->tm_yday+1; | ||
474 | |||
475 | } | 516 | } |
476 | 517 | ||
518 | /**@brief Contructor. | ||
519 | * | ||
520 | *Create a new time, given a day of year and a year. | ||
521 | */ | ||
477 | /* Jan 1 is day #1, not 0 */ | 522 | /* Jan 1 is day #1, not 0 */ |
478 | struct icaltimetype icaltime_from_day_of_year(short doy, short year) | 523 | struct icaltimetype icaltime_from_day_of_year(const int _doy, const int _year) |
479 | { | 524 | { |
480 | struct tm stm; | 525 | struct icaltimetype tt = icaltime_null_date(); |
481 | time_t tt; | 526 | int is_leap; |
482 | struct set_tz_save old_tz; | 527 | int month; |
483 | 528 | int doy = _doy; | |
484 | /* Get the time of january 1 of this year*/ | 529 | int year = _year; |
485 | memset(&stm,0,sizeof(struct tm)); | 530 | |
486 | stm.tm_year = year-1900; | 531 | is_leap = icaltime_is_leap_year(year); |
487 | stm.tm_mday = 1; | 532 | |
533 | /* Zero and neg numbers represent days of the previous year */ | ||
534 | if(doy <1){ | ||
535 | year--; | ||
536 | is_leap = icaltime_is_leap_year(year); | ||
537 | doy += days_in_year[is_leap][12]; | ||
538 | } else if(doy > days_in_year[is_leap][12]){ | ||
539 | /* Move on to the next year*/ | ||
540 | is_leap = icaltime_is_leap_year(year); | ||
541 | doy -= days_in_year[is_leap][12]; | ||
542 | year++; | ||
543 | } | ||
488 | 544 | ||
489 | old_tz = set_tz("UTC"); | 545 | tt.year = year; |
490 | tt = mktime(&stm); | ||
491 | unset_tz(old_tz); | ||
492 | 546 | ||
547 | for (month = 11; month >= 0; month--) { | ||
548 | if (doy > days_in_year[is_leap][month]) { | ||
549 | tt.month = month + 1; | ||
550 | tt.day = doy - days_in_year[is_leap][month]; | ||
551 | break; | ||
552 | } | ||
553 | } | ||
493 | 554 | ||
494 | /* Now add in the days */ | 555 | return tt; |
556 | } | ||
495 | 557 | ||
496 | doy--; | 558 | /**@brief Constructor. |
497 | tt += doy *60*60*24; | 559 | * |
560 | *Return a null time, which indicates no time has been set. | ||
561 | *This time represents the beginning of the epoch. | ||
562 | */ | ||
563 | struct icaltimetype icaltime_null_time(void) | ||
564 | { | ||
565 | struct icaltimetype t; | ||
566 | memset(&t,0,sizeof(struct icaltimetype)); | ||
498 | 567 | ||
499 | return icaltime_from_timet(tt, 1); | 568 | return t; |
500 | } | 569 | } |
501 | 570 | ||
502 | struct icaltimetype icaltime_null_time() | 571 | /**@brief Constructor. |
572 | * | ||
573 | *Return a null date, which indicates no time has been set. | ||
574 | */ | ||
575 | struct icaltimetype icaltime_null_date(void) | ||
503 | { | 576 | { |
504 | struct icaltimetype t; | 577 | struct icaltimetype t; |
505 | memset(&t,0,sizeof(struct icaltimetype)); | 578 | memset(&t,0,sizeof(struct icaltimetype)); |
506 | 579 | ||
580 | t.is_date = 1; | ||
581 | |||
582 | /* | ||
583 | * Init to -1 to match what icalyacc.y used to do. | ||
584 | * Does anything depend on this? | ||
585 | */ | ||
586 | t.hour = -1; | ||
587 | t.minute = -1; | ||
588 | t.second = -1; | ||
589 | |||
507 | return t; | 590 | return t; |
508 | } | 591 | } |
509 | 592 | ||
510 | 593 | ||
511 | int icaltime_is_valid_time(struct icaltimetype t){ | 594 | /** |
595 | *Returns false if the time is clearly invalid, but is not null. This | ||
596 | *is usually the result of creating a new time type buy not clearing | ||
597 | *it, or setting one of the flags to an illegal value. | ||
598 | */ | ||
599 | int icaltime_is_valid_time(const struct icaltimetype t){ | ||
512 | if(t.is_utc > 1 || t.is_utc < 0 || | 600 | if(t.is_utc > 1 || t.is_utc < 0 || |
513 | t.year < 0 || t.year > 3000 || | 601 | t.year < 0 || t.year > 3000 || |
514 | t.is_date > 1 || t.is_date < 0){ | 602 | t.is_date > 1 || t.is_date < 0){ |
515 | return 0; | 603 | return 0; |
516 | } else { | 604 | } else { |
517 | return 1; | 605 | return 1; |
518 | } | 606 | } |
519 | 607 | ||
520 | } | 608 | } |
521 | 609 | ||
522 | int icaltime_is_null_time(struct icaltimetype t) | 610 | /**@brief Returns true if time is a DATE |
611 | */ | ||
612 | int icaltime_is_date(const struct icaltimetype t) { | ||
613 | |||
614 | return t.is_date; | ||
615 | } | ||
616 | |||
617 | /**@brief Returns true if time is relative to UTC zone | ||
618 | * | ||
619 | *@todo We should only check the zone | ||
620 | */ | ||
621 | int icaltime_is_utc(const struct icaltimetype t) { | ||
622 | |||
623 | return t.is_utc; | ||
624 | } | ||
625 | |||
626 | /** | ||
627 | *Return true if the time is null. | ||
628 | */ | ||
629 | int icaltime_is_null_time(const struct icaltimetype t) | ||
523 | { | 630 | { |
524 | if (t.second +t.minute+t.hour+t.day+t.month+t.year == 0){ | 631 | if (t.second +t.minute+t.hour+t.day+t.month+t.year == 0){ |
525 | return 1; | 632 | return 1; |
526 | } | 633 | } |
527 | 634 | ||
528 | return 0; | 635 | return 0; |
529 | 636 | ||
530 | } | 637 | } |
531 | 638 | ||
532 | int icaltime_compare(struct icaltimetype a,struct icaltimetype b) | 639 | /** |
533 | { | 640 | *Return -1, 0, or 1 to indicate that a<b, a==b, or a>b. |
534 | time_t t1 = icaltime_as_timet(a); | 641 | * This calls icaltime_compare function after converting them to the utc |
535 | time_t t2 = icaltime_as_timet(b); | 642 | * timezone. |
536 | 643 | */ | |
537 | if (t1 > t2) { | ||
538 | return 1; | ||
539 | } else if (t1 < t2) { | ||
540 | return -1; | ||
541 | } else { | ||
542 | return 0; | ||
543 | } | ||
544 | 644 | ||
645 | int icaltime_compare(const struct icaltimetype a_in, const struct icaltimetype b_in) | ||
646 | { | ||
647 | int retval = 0; | ||
648 | struct icaltimetype a, b; | ||
649 | |||
650 | a = icaltime_convert_to_zone(a_in, icaltimezone_get_utc_timezone()); | ||
651 | b = icaltime_convert_to_zone(b_in, icaltimezone_get_utc_timezone()); | ||
652 | |||
653 | if (a.year > b.year) | ||
654 | retval = 1; | ||
655 | else if (a.year < b.year) | ||
656 | retval = -1; | ||
657 | |||
658 | else if (a.month > b.month) | ||
659 | retval = 1; | ||
660 | else if (a.month < b.month) | ||
661 | retval = -1; | ||
662 | |||
663 | else if (a.day > b.day) | ||
664 | retval = 1; | ||
665 | else if (a.day < b.day) | ||
666 | retval = -1; | ||
667 | |||
668 | /* if both are dates, we are done */ | ||
669 | if (a.is_date && b.is_date) | ||
670 | return retval; | ||
671 | |||
672 | /* else, if we already found a difference, we are done */ | ||
673 | else if (retval != 0) | ||
674 | return retval; | ||
675 | |||
676 | /* else, if only one is a date (and we already know the date part is equal), | ||
677 | then the other is greater */ | ||
678 | else if (b.is_date) | ||
679 | retval = 1; | ||
680 | else if (a.is_date) | ||
681 | retval = -1; | ||
682 | |||
683 | else if (a.hour > b.hour) | ||
684 | retval = 1; | ||
685 | else if (a.hour < b.hour) | ||
686 | retval = -1; | ||
687 | |||
688 | else if (a.minute > b.minute) | ||
689 | retval = 1; | ||
690 | else if (a.minute < b.minute) | ||
691 | retval = -1; | ||
692 | |||
693 | else if (a.second > b.second) | ||
694 | retval = 1; | ||
695 | else if (a.second < b.second) | ||
696 | retval = -1; | ||
697 | |||
698 | return retval; | ||
545 | } | 699 | } |
546 | 700 | ||
701 | /** | ||
702 | *like icaltime_compare, but only use the date parts. | ||
703 | */ | ||
704 | |||
547 | int | 705 | int |
548 | icaltime_compare_date_only (struct icaltimetype a, struct icaltimetype b) | 706 | icaltime_compare_date_only(const struct icaltimetype a_in, const struct icaltimetype b_in) |
549 | { | 707 | { |
550 | time_t t1; | 708 | int retval; |
551 | time_t t2; | 709 | struct icaltimetype a, b; |
552 | 710 | ||
553 | if (a.year == b.year && a.month == b.month && a.day == b.day) | 711 | a = icaltime_convert_to_zone(a_in, icaltimezone_get_utc_timezone()); |
554 | return 0; | 712 | b = icaltime_convert_to_zone(b_in, icaltimezone_get_utc_timezone()); |
555 | 713 | ||
556 | t1 = icaltime_as_timet (a); | 714 | if (a.year > b.year) |
557 | t2 = icaltime_as_timet (b); | 715 | retval = 1; |
558 | 716 | else if (a.year < b.year) | |
559 | if (t1 > t2) | 717 | retval = -1; |
560 | return 1; | 718 | |
561 | else if (t1 < t2) | 719 | else if (a.month > b.month) |
562 | return -1; | 720 | retval = 1; |
563 | else { | 721 | else if (a.month < b.month) |
564 | /* not reached */ | 722 | retval = -1; |
565 | assert (0); | 723 | |
566 | return 0; | 724 | else if (a.day > b.day) |
567 | } | 725 | retval = 1; |
568 | } | 726 | else if (a.day < b.day) |
727 | retval = -1; | ||
569 | 728 | ||
729 | else | ||
730 | retval = 0; | ||
731 | |||
732 | return retval; | ||
733 | } | ||
570 | 734 | ||
571 | /* These are defined in icalduration.c: | 735 | /* These are defined in icalduration.c: |
572 | struct icaltimetype icaltime_add(struct icaltimetype t, | 736 | struct icaltimetype icaltime_add(struct icaltimetype t, |
573 | struct icaldurationtype d) | 737 | struct icaldurationtype d) |
574 | struct icaldurationtype icaltime_subtract(struct icaltimetype t1, | 738 | struct icaldurationtype icaltime_subtract(struct icaltimetype t1, |
575 | struct icaltimetype t2) | 739 | struct icaltimetype t2) |
576 | */ | 740 | */ |
577 | 741 | ||
742 | |||
743 | |||
744 | /**@brief Internal, shouldn't be part of the public API | ||
745 | * | ||
746 | *Adds (or subtracts) a time from a icaltimetype. | ||
747 | *NOTE: This function is exactly the same as icaltimezone_adjust_change() | ||
748 | *except for the type of the first parameter. | ||
749 | */ | ||
750 | void | ||
751 | icaltime_adjust(struct icaltimetype *tt, const int days, const int hours, | ||
752 | const int minutes, const int seconds) { | ||
753 | |||
754 | int second, minute, hour, day; | ||
755 | int minutes_overflow, hours_overflow, days_overflow, years_overflow; | ||
756 | int days_in_month; | ||
757 | |||
758 | /* Add on the seconds. */ | ||
759 | second = tt->second + seconds; | ||
760 | tt->second = second % 60; | ||
761 | minutes_overflow = second / 60; | ||
762 | if (tt->second < 0) { | ||
763 | tt->second += 60; | ||
764 | minutes_overflow--; | ||
765 | } | ||
766 | |||
767 | /* Add on the minutes. */ | ||
768 | minute = tt->minute + minutes + minutes_overflow; | ||
769 | tt->minute = minute % 60; | ||
770 | hours_overflow = minute / 60; | ||
771 | if (tt->minute < 0) { | ||
772 | tt->minute += 60; | ||
773 | hours_overflow--; | ||
774 | } | ||
775 | |||
776 | /* Add on the hours. */ | ||
777 | hour = tt->hour + hours + hours_overflow; | ||
778 | tt->hour = hour % 24; | ||
779 | days_overflow = hour / 24; | ||
780 | if (tt->hour < 0) { | ||
781 | tt->hour += 24; | ||
782 | days_overflow--; | ||
783 | } | ||
784 | |||
785 | /* Normalize the month. We do this before handling the day since we may | ||
786 | need to know what month it is to get the number of days in it. | ||
787 | Note that months are 1 to 12, so we have to be a bit careful. */ | ||
788 | if (tt->month >= 13) { | ||
789 | years_overflow = (tt->month - 1) / 12; | ||
790 | tt->year += years_overflow; | ||
791 | tt->month -= years_overflow * 12; | ||
792 | } else if (tt->month <= 0) { | ||
793 | /* 0 to -11 is -1 year out, -12 to -23 is -2 years. */ | ||
794 | years_overflow = (tt->month / 12) - 1; | ||
795 | tt->year += years_overflow; | ||
796 | tt->month -= years_overflow * 12; | ||
797 | } | ||
798 | |||
799 | /* Add on the days. */ | ||
800 | day = tt->day + days + days_overflow; | ||
801 | if (day > 0) { | ||
802 | for (;;) { | ||
803 | days_in_month = icaltime_days_in_month (tt->month, tt->year); | ||
804 | if (day <= days_in_month) | ||
805 | break; | ||
806 | |||
807 | tt->month++; | ||
808 | if (tt->month >= 13) { | ||
809 | tt->year++; | ||
810 | tt->month = 1; | ||
811 | } | ||
812 | |||
813 | day -= days_in_month; | ||
814 | } | ||
815 | } else { | ||
816 | while (day <= 0) { | ||
817 | if (tt->month == 1) { | ||
818 | tt->year--; | ||
819 | tt->month = 12; | ||
820 | } else { | ||
821 | tt->month--; | ||
822 | } | ||
823 | |||
824 | day += icaltime_days_in_month (tt->month, tt->year); | ||
825 | } | ||
826 | } | ||
827 | tt->day = day; | ||
828 | } | ||
829 | |||
830 | /**@brief Convert time to a given timezone | ||
831 | * | ||
832 | *Convert a time from its native timezone to a given timezone. | ||
833 | * | ||
834 | *If tt is a date, the returned time is an exact | ||
835 | *copy of the input. If it's a floating time, the returned object | ||
836 | *represents the same time translated to the given timezone. | ||
837 | *Otherwise the time will be converted to the new | ||
838 | *time zone, and its native timezone set to the right timezone. | ||
839 | */ | ||
840 | struct icaltimetype icaltime_convert_to_zone(const struct icaltimetype tt, | ||
841 | icaltimezone *zone) { | ||
842 | |||
843 | struct icaltimetype ret = tt; | ||
844 | |||
845 | /* If it's a date do nothing */ | ||
846 | if (tt.is_date) { | ||
847 | return ret; | ||
848 | } | ||
849 | |||
850 | if (tt.zone == zone) { | ||
851 | return ret; | ||
852 | } | ||
853 | |||
854 | /* If it's a floating time we don't want to adjust the time */ | ||
855 | if (tt.zone != NULL) { | ||
856 | icaltimezone_convert_time(&ret, tt.zone, zone); | ||
857 | } | ||
858 | |||
859 | ret.zone = zone; | ||
860 | if (zone == icaltimezone_get_utc_timezone()) { | ||
861 | ret.is_utc = 1; | ||
862 | } else { | ||
863 | ret.is_utc = 0; | ||
864 | } | ||
865 | |||
866 | return ret; | ||
867 | } | ||
868 | |||
869 | const icaltimezone * | ||
870 | icaltime_get_timezone(const struct icaltimetype t) { | ||
871 | |||
872 | return t.zone; | ||
873 | } | ||
874 | |||
875 | char * | ||
876 | icaltime_get_tzid(const struct icaltimetype t) { | ||
877 | |||
878 | if (t.zone != NULL) { | ||
879 | return icaltimezone_get_tzid(t.zone); | ||
880 | } else { | ||
881 | return NULL; | ||
882 | } | ||
883 | } | ||
884 | |||
885 | /**@brief Set the timezone | ||
886 | * | ||
887 | *Force the icaltime to be interpreted relative to another timezone. | ||
888 | *If you need to do timezone conversion, applying offset adjustments, | ||
889 | *then you should use icaltime_convert_to_timezone instead. | ||
890 | */ | ||
891 | struct icaltimetype | ||
892 | icaltime_set_timezone(struct icaltimetype *t, const icaltimezone *zone) { | ||
893 | |||
894 | /* If it's a date do nothing */ | ||
895 | if (t->is_date) { | ||
896 | return *t; | ||
897 | } | ||
898 | |||
899 | if (t->zone == zone) { | ||
900 | return *t; | ||
901 | } | ||
902 | |||
903 | t->zone = zone; | ||
904 | if (zone == icaltimezone_get_utc_timezone()) { | ||
905 | t->is_utc = 1; | ||
906 | } else { | ||
907 | t->is_utc = 0; | ||
908 | } | ||
909 | |||
910 | return *t; | ||
911 | } | ||
912 | |||
913 | |||
914 | /** | ||
915 | * @brief builds an icaltimespan given a start time, end time and busy value. | ||
916 | * | ||
917 | * @param dtstart The beginning time of the span, can be a date-time | ||
918 | * or just a date. | ||
919 | * @param dtend The end time of the span. | ||
920 | * @param is_busy A boolean value, 0/1. | ||
921 | * @return A span using the supplied values. | ||
922 | * | ||
923 | * returned span contains times specified in UTC. | ||
924 | */ | ||
925 | |||
926 | icaltime_span icaltime_span_new(struct icaltimetype dtstart, | ||
927 | struct icaltimetype dtend, | ||
928 | int is_busy) | ||
929 | { | ||
930 | icaltime_span span; | ||
931 | |||
932 | span.is_busy = is_busy; | ||
933 | |||
934 | span.start = icaltime_as_timet_with_zone(dtstart, | ||
935 | icaltimezone_get_utc_timezone()); | ||
936 | |||
937 | if (icaltime_is_null_time(dtend)) { | ||
938 | if (!icaltime_is_date(dtstart)) { | ||
939 | /* If dtstart is a DATE-TIME and there is no DTEND nor DURATION | ||
940 | it takes no time */ | ||
941 | span.end = span.start; | ||
942 | return span; | ||
943 | } else { | ||
944 | dtend = dtstart; | ||
945 | } | ||
946 | } | ||
947 | |||
948 | span.end = icaltime_as_timet_with_zone(dtend, icaltimezone_get_utc_timezone()); | ||
949 | |||
950 | if (icaltime_is_date(dtstart)) { | ||
951 | /* no time specified, go until the end of the day..*/ | ||
952 | span.end += 60*60*24 - 1; | ||
953 | } | ||
954 | return span; | ||
955 | } | ||
956 | |||
957 | |||
958 | /** @brief Returns true if the two spans overlap | ||
959 | * | ||
960 | * @param s1 1st span to test | ||
961 | * @param s2 2nd span to test | ||
962 | * @return boolean value | ||
963 | * | ||
964 | * The result is calculated by testing if the start time of s1 is contained | ||
965 | * by the s2 span, or if the end time of s1 is contained by the s2 span. | ||
966 | * | ||
967 | * Also returns true if the spans are equal. | ||
968 | * | ||
969 | * Note, this will return false if the spans are adjacent. | ||
970 | */ | ||
971 | |||
972 | int icaltime_span_overlaps(icaltime_span *s1, | ||
973 | icaltime_span *s2) | ||
974 | { | ||
975 | /* s1->start in s2 */ | ||
976 | if (s1->start > s2->start && s1->start < s2->end) | ||
977 | return 1; | ||
978 | |||
979 | /* s1->end in s2 */ | ||
980 | if (s1->end > s2->start && s1->end < s2->end) | ||
981 | return 1; | ||
982 | |||
983 | /* s2->start in s1 */ | ||
984 | if (s2->start > s1->start && s2->start < s1->end) | ||
985 | return 1; | ||
986 | |||
987 | /* s2->end in s1 */ | ||
988 | if (s2->end > s1->start && s2->end < s1->end) | ||
989 | return 1; | ||
990 | |||
991 | if (s1->start == s2->start && s1->end == s2->end) | ||
992 | return 1; | ||
993 | |||
994 | return 0; | ||
995 | } | ||
996 | |||
997 | /** @brief Returns true if the span is totally within the containing | ||
998 | * span | ||
999 | * | ||
1000 | * @param s The span to test for. | ||
1001 | * @param container The span to test against. | ||
1002 | * @return boolean value. | ||
1003 | * | ||
1004 | */ | ||
1005 | |||
1006 | int icaltime_span_contains(icaltime_span *s, | ||
1007 | icaltime_span *container) | ||
1008 | { | ||
1009 | |||
1010 | if ((s->start >= container->start && s->start < container->end) && | ||
1011 | (s->end <= container->end && s->end > container->start)) | ||
1012 | return 1; | ||
1013 | |||
1014 | return 0; | ||
1015 | } | ||