Reapplying patch to disable attempts to use gtk-doc
[profile/ivi/libsoup2.4.git] / libsoup / soup-date.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-date.c: Date/time handling
4  *
5  * Copyright (C) 2005, Novell, Inc.
6  * Copyright (C) 2007, Red Hat, Inc.
7  */
8
9 #ifdef HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include <stdlib.h>
14 #include <string.h>
15 #include <glib.h>
16
17 #include "soup-date.h"
18
19 /**
20  * SoupDate:
21  * @year: the year, 1 to 9999
22  * @month: the month, 1 to 12
23  * @day: day of the month, 1 to 31
24  * @hour: hour of the day, 0 to 23
25  * @minute: minute, 0 to 59
26  * @second: second, 0 to 59 (or up to 61 in the case of leap seconds)
27  * @utc: %TRUE if the date is in UTC
28  * @offset: offset from UTC
29
30  * A date and time. The date is assumed to be in the (proleptic)
31  * Gregorian calendar. The time is in UTC if @utc is %TRUE. Otherwise,
32  * the time is a local time, and @offset gives the offset from UTC in
33  * minutes (such that adding @offset to the time would give the
34  * correct UTC time). If @utc is %FALSE and @offset is 0, then the
35  * %SoupDate represents a "floating" time with no associated timezone
36  * information.
37  **/
38
39 /* Do not internationalize */
40 static const char *const months[] = {
41         "Jan", "Feb", "Mar", "Apr", "May", "Jun",
42         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
43 };
44
45 /* Do not internationalize */
46 static const char *const days[] = {
47         "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
48 };
49
50 static const int nonleap_days_in_month[] = {
51         0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
52 };
53
54 static const int nonleap_days_before[] = {
55         0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
56 };
57
58 static inline gboolean
59 is_leap_year (int year)
60 {
61         return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
62 }
63
64 /* Computes the number of days since proleptic Gregorian 0000-12-31.
65  * (That is, 0001-01-01 is "1", and 1970-01-01 is 719163.
66  */
67 static int
68 rata_die_day (SoupDate *date)
69 {
70         int day;
71
72         day = (date->year - 1) * 365 + ((date->year - 1) / 4) -
73                 ((date->year - 1) / 100) + ((date->year - 1) / 400);
74         day += nonleap_days_before[date->month] + date->day;
75         if (is_leap_year (date->year) && date->month > 2)
76                 day++;
77         return day;
78 }
79
80 #define TIME_T_EPOCH_RATA_DIE_DAY 719163
81
82 static inline int
83 days_in_month (int month, int year)
84 {
85         if (month == 2 && is_leap_year (year))
86                 return 29;
87         else
88                 return nonleap_days_in_month[month];
89 }
90
91 GType
92 soup_date_get_type (void)
93 {
94         static volatile gsize type_volatile = 0;
95
96         if (g_once_init_enter (&type_volatile)) {
97                 GType type = g_boxed_type_register_static (
98                         g_intern_static_string ("SoupDate"),
99                         (GBoxedCopyFunc) soup_date_copy,
100                         (GBoxedFreeFunc) soup_date_free);
101                 g_once_init_leave (&type_volatile, type);
102         }
103         return type_volatile;
104 }
105
106 static void
107 soup_date_fixup (SoupDate *date)
108 {
109         /* We only correct date->second if it's negative or too high
110          * to be a leap second.
111          */
112         if (date->second < 0 || date->second > 61) {
113                 date->minute += date->second / 60;
114                 date->second %= 60;
115                 if (date->second < 0)
116                         date->second += 60;
117         }
118
119         if (date->minute < 0 || date->minute > 59) {
120                 date->hour += date->minute / 60;
121                 date->minute %= 60;
122                 if (date->minute < 0)
123                         date->minute += 60;
124         }
125
126         if (date->hour < 0 || date->hour > 23) {
127                 date->day += date->hour / 24;
128                 date->hour %= 24;
129                 if (date->hour < 0)
130                         date->hour += 24;
131         }
132
133         /* Have to make sure month is valid before we can look at the
134          * day.
135          */
136         if (date->month < 1 || date->month > 12) {
137                 date->year += ((date->month - 1) / 12) + 1;
138                 date->month = ((date->month - 1) % 12) + 1;
139                 if (date->month < 1)
140                         date->month += 12;
141         }
142
143         if (date->day < 0) {
144                 while (date->day < 0) {
145                         if (date->month == 1) {
146                                 date->month = 12;
147                                 date->year--;
148                         } else
149                                 date->month--;
150                         date->day += days_in_month (date->month, date->year);
151                 }
152         } else {
153                 while (date->day > days_in_month (date->month, date->year)) {
154                         date->day -= days_in_month (date->month, date->year);
155                         if (date->month == 12) {
156                                 date->month = 1;
157                                 date->year++;
158                         } else
159                                 date->month++;
160                 }
161         }
162 }
163
164 /**
165  * soup_date_new:
166  * @year: the year (1-9999)
167  * @month: the month (1-12)
168  * @day: the day of the month (1-31, as appropriate for @month)
169  * @hour: the hour (0-23)
170  * @minute: the minute (0-59)
171  * @second: the second (0-59, or up to 61 for leap seconds)
172  *
173  * Creates a #SoupDate representing the indicated time, UTC.
174  *
175  * Return value: a new #SoupDate
176  **/
177 SoupDate *
178 soup_date_new (int year, int month, int day, 
179                int hour, int minute, int second)
180 {
181         SoupDate *date = g_slice_new (SoupDate);
182
183         date->year   = year;
184         date->month  = month;
185         date->day    = day;
186         date->hour   = hour;
187         date->minute = minute;
188         date->second = second;
189         date->utc    = TRUE;
190         date->offset = 0;
191
192         return date;
193 }
194
195 /**
196  * soup_date_new_from_now:
197  * @offset_seconds: offset from current time
198  *
199  * Creates a #SoupDate representing a time @offset_seconds after the
200  * current time (or before it, if @offset_seconds is negative). If
201  * offset_seconds is 0, returns the current time.
202  *
203  * If @offset_seconds would indicate a time not expressible as a
204  * #time_t, the return value will be clamped into range.
205  *
206  * Return value: a new #SoupDate
207  **/
208 SoupDate *
209 soup_date_new_from_now (int offset_seconds)
210 {
211         time_t now = time (NULL);
212         time_t then = now + offset_seconds;
213
214         if (sizeof (time_t) == 4) {
215                 if (offset_seconds < 0 && then > now)
216                         return soup_date_new_from_time_t (-G_MAXINT);
217                 else if (offset_seconds > 0 && then < now)
218                         return soup_date_new_from_time_t (G_MAXINT);
219         }
220         return soup_date_new_from_time_t (then);
221 }
222
223 static gboolean
224 parse_iso8601_date (SoupDate *date, const char *date_string)
225 {
226         gulong val;
227
228         if (strlen (date_string) < 15)
229                 return FALSE;
230         if (date_string[4] == '-' &&
231             date_string[7] == '-' &&
232             date_string[10] == 'T') {
233                 /* YYYY-MM-DD */
234                 date->year  = atoi (date_string);
235                 date->month = atoi (date_string + 5);
236                 date->day   = atoi (date_string + 8);
237                 date_string += 11;
238         } else if (date_string[8] == 'T') {
239                 /* YYYYMMDD */
240                 val = atoi (date_string);
241                 date->year = val / 10000;
242                 date->month = (val % 10000) / 100;
243                 date->day = val % 100;
244                 date_string += 9;
245         } else
246                 return FALSE;
247
248         if (strlen (date_string) >= 8 &&
249             date_string[2] == ':' && date_string[5] == ':') {
250                 /* HH:MM:SS */
251                 date->hour   = atoi (date_string);
252                 date->minute = atoi (date_string + 3);
253                 date->second = atoi (date_string + 6);
254                 date_string += 8;
255         } else if (strlen (date_string) >= 6) {
256                 /* HHMMSS */
257                 val = strtoul (date_string, (char **)&date_string, 10);
258                 date->hour   = val / 10000;
259                 date->minute = (val % 10000) / 100;
260                 date->second = val % 100;
261         } else
262                 return FALSE;
263
264         if (*date_string == '.' || *date_string == ',')
265                 (void) strtoul (date_string + 1, (char **)&date_string, 10);
266
267         if (*date_string == 'Z') {
268                 date_string++;
269                 date->utc = TRUE;
270                 date->offset = 0;
271         } else if (*date_string == '+' || *date_string == '-') {
272                 int sign = (*date_string == '+') ? -1 : 1;
273                 val = strtoul (date_string + 1, (char **)&date_string, 10);
274                 if (*date_string == ':')
275                         val = 60 * val + strtoul (date_string + 1, (char **)&date_string, 10);
276                 else
277                         val = 60 * (val / 100) + (val % 100);
278                 date->offset = sign * val;
279                 date->utc = !val;
280         } else {
281                 date->offset = 0;
282                 date->utc = FALSE;
283         }
284
285         return !*date_string;
286 }
287
288 static inline gboolean
289 parse_day (SoupDate *date, const char **date_string)
290 {
291         char *end;
292
293         date->day = strtoul (*date_string, &end, 10);
294         if (end == (char *)*date_string)
295                 return FALSE;
296
297         while (*end == ' ' || *end == '-')
298                 end++;
299         *date_string = end;
300         return TRUE;
301 }
302
303 static inline gboolean
304 parse_month (SoupDate *date, const char **date_string)
305 {
306         int i;
307
308         for (i = 0; i < G_N_ELEMENTS (months); i++) {
309                 if (!g_ascii_strncasecmp (*date_string, months[i], 3)) {
310                         date->month = i + 1;
311                         *date_string += 3;
312                         while (**date_string == ' ' || **date_string == '-')
313                                 (*date_string)++;
314                         return TRUE;
315                 }
316         }
317         return FALSE;
318 }
319
320 static inline gboolean
321 parse_year (SoupDate *date, const char **date_string)
322 {
323         char *end;
324
325         date->year = strtoul (*date_string, &end, 10);
326         if (end == (char *)*date_string)
327                 return FALSE;
328
329         if (end == (char *)*date_string + 2) {
330                 if (date->year < 70)
331                         date->year += 2000;
332                 else
333                         date->year += 1900;
334         } else if (end == (char *)*date_string + 3)
335                 date->year += 1900;
336
337         while (*end == ' ' || *end == '-')
338                 end++;
339         *date_string = end;
340         return TRUE;
341 }
342
343 static inline gboolean
344 parse_time (SoupDate *date, const char **date_string)
345 {
346         char *p, *end;
347
348         date->hour = strtoul (*date_string, &end, 10);
349         if (end == (char *)*date_string || *end++ != ':')
350                 return FALSE;
351         p = end;
352         date->minute = strtoul (p, &end, 10);
353         if (end == p || *end++ != ':')
354                 return FALSE;
355         p = end;
356         date->second = strtoul (p, &end, 10);
357         if (end == p)
358                 return FALSE;
359         p = end;
360
361         while (*p == ' ')
362                 p++;
363         *date_string = p;
364         return TRUE;
365 }
366
367 static inline gboolean
368 parse_timezone (SoupDate *date, const char **date_string)
369 {
370         if (!**date_string) {
371                 date->utc = FALSE;
372                 date->offset = 0;
373         } else if (**date_string == '+' || **date_string == '-') {
374                 gulong val;
375                 int sign = (**date_string == '+') ? -1 : 1;
376                 val = strtoul (*date_string + 1, (char **)date_string, 10);
377                 if (**date_string == ':')
378                         val = 60 * val + strtoul (*date_string + 1, (char **)date_string, 10);
379                 else
380                         val =  60 * (val / 100) + (val % 100);
381                 date->offset = sign * val;
382                 date->utc = (sign == -1) && !val;
383         } else if (**date_string == 'Z') {
384                 date->offset = 0;
385                 date->utc = TRUE;
386                 (*date_string)++;
387         } else if (!strcmp (*date_string, "GMT") ||
388                    !strcmp (*date_string, "UTC")) {
389                 date->offset = 0;
390                 date->utc = TRUE;
391                 (*date_string) += 3;
392         } else if (strchr ("ECMP", **date_string) &&
393                    ((*date_string)[1] == 'D' || (*date_string)[1] == 'S') &&
394                    (*date_string)[2] == 'T') {
395                 date->offset = -60 * (5 * strcspn ("ECMP", *date_string));
396                 if ((*date_string)[1] == 'D')
397                         date->offset += 60;
398                 date->utc = FALSE;
399         } else
400                 return FALSE;
401         return TRUE;
402 }
403
404 static gboolean
405 parse_textual_date (SoupDate *date, const char *date_string)
406 {
407         /* If it starts with a word, it must be a weekday, which we skip */
408         if (g_ascii_isalpha (*date_string)) {
409                 while (g_ascii_isalpha (*date_string))
410                         date_string++;
411                 if (*date_string == ',')
412                         date_string++;
413                 while (g_ascii_isspace (*date_string))
414                         date_string++;
415         }
416
417         /* If there's now another word, this must be an asctime-date */
418         if (g_ascii_isalpha (*date_string)) {
419                 /* (Sun) Nov  6 08:49:37 1994 */
420                 if (!parse_month (date, &date_string) ||
421                     !parse_day (date, &date_string) ||
422                     !parse_time (date, &date_string) ||
423                     !parse_year (date, &date_string))
424                         return FALSE;
425
426                 /* There shouldn't be a timezone, but check anyway */
427                 parse_timezone (date, &date_string);
428         } else {
429                 /* Non-asctime date, so some variation of
430                  * (Sun,) 06 Nov 1994 08:49:37 GMT
431                  */
432                 if (!parse_day (date, &date_string) ||
433                     !parse_month (date, &date_string) ||
434                     !parse_year (date, &date_string) ||
435                     !parse_time (date, &date_string))
436                         return FALSE;
437
438                 /* This time there *should* be a timezone, but we
439                  * survive if there isn't.
440                  */
441                 parse_timezone (date, &date_string);
442         }
443         return TRUE;
444 }
445
446 /**
447  * SoupDateFormat:
448  * @SOUP_DATE_HTTP: RFC 1123 format, used by the HTTP "Date" header. Eg
449  * "Sun, 06 Nov 1994 08:49:37 GMT"
450  * @SOUP_DATE_COOKIE: The format for the "Expires" timestamp in the
451  * Netscape cookie specification. Eg, "Sun, 06-Nov-1994 08:49:37 GMT".
452  * @SOUP_DATE_RFC2822: RFC 2822 format, eg "Sun, 6 Nov 1994 09:49:37 -0100"
453  * @SOUP_DATE_ISO8601_COMPACT: ISO 8601 date/time with no optional
454  * punctuation. Eg, "19941106T094937-0100".
455  * @SOUP_DATE_ISO8601_FULL: ISO 8601 date/time with all optional
456  * punctuation. Eg, "1994-11-06T09:49:37-01:00".
457  * @SOUP_DATE_ISO8601_XMLRPC: ISO 8601 date/time as used by XML-RPC.
458  * Eg, "19941106T09:49:37".
459  * @SOUP_DATE_ISO8601: An alias for @SOUP_DATE_ISO8601_FULL.
460  *
461  * Date formats that soup_date_to_string() can use.
462  *
463  * @SOUP_DATE_HTTP and @SOUP_DATE_COOKIE always coerce the time to
464  * UTC. @SOUP_DATE_ISO8601_XMLRPC uses the time as given, ignoring the
465  * offset completely. @SOUP_DATE_RFC2822 and the other ISO 8601
466  * variants use the local time, appending the offset information if
467  * available.
468  *
469  * This enum may be extended with more values in future releases.
470  **/
471
472 /**
473  * soup_date_new_from_string:
474  * @date_string: the date in some plausible format
475  *
476  * Parses @date_string and tries to extract a date from it. This
477  * recognizes all of the "HTTP-date" formats from RFC 2616, all ISO
478  * 8601 formats containing both a time and a date, RFC 2822 dates,
479  * and reasonable approximations thereof. (Eg, it is lenient about
480  * whitespace, leading "0"s, etc.)
481  *
482  * Return value: a new #SoupDate, or %NULL if @date_string could not
483  * be parsed.
484  **/
485 SoupDate *
486 soup_date_new_from_string (const char *date_string)
487 {
488         SoupDate *date;
489         gboolean success;
490
491         g_return_val_if_fail (date_string != NULL, NULL);
492
493         date = g_slice_new (SoupDate);
494
495         while (g_ascii_isspace (*date_string))
496                 date_string++;
497
498         /* If it starts with a digit, it's either an ISO 8601 date, or
499          * an RFC2822 date without the optional weekday; in the later
500          * case, there will be a month name later on, so look for one
501          * of the month-start letters.
502          */
503         if (g_ascii_isdigit (*date_string) &&
504             !strpbrk (date_string, "JFMASOND"))
505                 success = parse_iso8601_date (date, date_string);
506         else
507                 success = parse_textual_date (date, date_string);
508
509         if (!success) {
510                 g_slice_free (SoupDate, date);
511                 return NULL;
512         }
513
514         if (date->year < 1 || date->year > 9999 ||
515             date->month < 1 || date->month > 12 ||
516             date->day < 1 ||
517             date->day > days_in_month (date->month, date->year) ||
518             date->hour < 0 || date->hour > 24 ||
519             date->minute < 0 || date->minute > 59 ||
520             date->second < 0 || date->second > 61) {
521                 soup_date_free (date);
522                 return NULL;
523         }
524         if (date->hour == 24) {
525                 /* ISO8601 allows this explicitly. We allow it for
526                  * other types as well just for simplicity.
527                  */
528                 if (date->minute == 0 && date->second == 0)
529                         soup_date_fixup (date);
530                 else {
531                         soup_date_free (date);
532                         return NULL;
533                 }
534         }
535
536         return date;
537 }
538
539 /**
540  * soup_date_new_from_time_t:
541  * @when: a #time_t
542  *
543  * Creates a #SoupDate corresponding to @when
544  *
545  * Return value: a new #SoupDate
546  **/
547 SoupDate *
548 soup_date_new_from_time_t (time_t when)
549 {
550         struct tm tm;
551
552 #ifdef HAVE_GMTIME_R
553         gmtime_r (&when, &tm);
554 #else
555         tm = *gmtime (&when);
556 #endif
557
558         return soup_date_new (tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
559                               tm.tm_hour, tm.tm_min, tm.tm_sec);
560 }
561
562 static const char *
563 soup_date_weekday (SoupDate *date)
564 {
565         /* Proleptic Gregorian 0001-01-01 was a Monday, which
566          * corresponds to 1 in the days[] array.
567          */
568         return days[rata_die_day (date) % 7];
569 }
570
571 /**
572  * soup_date_to_string:
573  * @date: a #SoupDate
574  * @format: the format to generate the date in
575  *
576  * Converts @date to a string in the format described by @format.
577  *
578  * Return value: @date as a string
579  **/
580 char *
581 soup_date_to_string (SoupDate *date, SoupDateFormat format)
582 {
583         g_return_val_if_fail (date != NULL, NULL);
584
585         if (format == SOUP_DATE_HTTP || format == SOUP_DATE_COOKIE) {
586                 /* HTTP and COOKIE formats require UTC timestamp, so coerce
587                  * @date if it's non-UTC.
588                  */
589                 SoupDate utcdate;
590
591                 if (date->offset != 0) {
592                         memcpy (&utcdate, date, sizeof (SoupDate));
593                         utcdate.minute += utcdate.offset;
594                         utcdate.offset = 0;
595                         utcdate.utc = TRUE;
596                         soup_date_fixup (&utcdate);
597                         date = &utcdate;
598                 }
599
600                 switch (format) {
601                 case SOUP_DATE_HTTP:
602                         /* "Sun, 06 Nov 1994 08:49:37 GMT" */
603                         return g_strdup_printf (
604                                 "%s, %02d %s %04d %02d:%02d:%02d GMT",
605                                 soup_date_weekday (date), date->day,
606                                 months[date->month - 1], date->year,
607                                 date->hour, date->minute, date->second);
608
609                 case SOUP_DATE_COOKIE:
610                         /* "Sun, 06-Nov-1994 08:49:37 GMT" */
611                         return g_strdup_printf (
612                                 "%s, %02d-%s-%04d %02d:%02d:%02d GMT",
613                                 soup_date_weekday (date), date->day,
614                                 months[date->month - 1], date->year,
615                                 date->hour, date->minute, date->second);
616
617                 default:
618                         g_return_val_if_reached (NULL);
619                 }
620         } else if (format == SOUP_DATE_ISO8601_XMLRPC) {
621                 /* Always "floating", ignore offset */
622                 return g_strdup_printf ("%04d%02d%02dT%02d:%02d:%02d",
623                                         date->year, date->month, date->day,
624                                         date->hour, date->minute, date->second);
625         } else {
626                 int hour_offset, minute_offset;
627                 char zone[8], sign;
628
629                 /* For other ISO8601 formats or RFC2822, use the
630                  * offset given in @date. For ISO8601 formats, use "Z"
631                  * for UTC, +-offset for non-UTC, and nothing for
632                  * floating. For RFC2822, use +-offset for UTC or
633                  * non-UTC, and -0000 for floating.
634                  */
635                 hour_offset = abs (date->offset) / 60;
636                 minute_offset = abs (date->offset) - hour_offset * 60;
637
638                 switch (format) {
639                 case SOUP_DATE_ISO8601_COMPACT:
640                         /* "19941106T084937[zone]" */
641                         if (date->utc)
642                                 strcpy (zone, "Z");
643                         else if (date->offset) {
644                                 g_snprintf (zone, sizeof (zone), "%c%02d%02d",
645                                             date->offset > 0 ? '-' : '+',
646                                             hour_offset, minute_offset);
647                         } else
648                                 *zone = '\0';                   
649
650                         return g_strdup_printf (
651                                 "%04d%02d%02dT%02d%02d%02d%s",
652                                 date->year, date->month, date->day,
653                                 date->hour, date->minute, date->second,
654                                 zone);
655
656                 case SOUP_DATE_ISO8601_FULL:
657                         /* "1994-11-06T08:49:37[zone]" */
658                         if (date->utc)
659                                 strcpy (zone, "Z");
660                         else if (date->offset) {
661                                 g_snprintf (zone, sizeof (zone), "%c%02d:%02d",
662                                             date->offset > 0 ? '-' : '+',
663                                             hour_offset, minute_offset);
664                         } else
665                                 *zone = '\0';                   
666
667                         return g_strdup_printf (
668                                 "%04d-%02d-%02dT%02d:%02d:%02d%s",
669                                 date->year, date->month, date->day,
670                                 date->hour, date->minute, date->second,
671                                 zone);
672
673                 case SOUP_DATE_RFC2822:
674                         /* "Sun, 6 Nov 1994 09:49:37 -0100" */
675                         if (date->offset)
676                                 sign = (date->offset > 0) ? '-' : '+';
677                         else
678                                 sign = date->utc ? '+' : '-';
679                         return g_strdup_printf (
680                                 "%s, %d %s %04d %02d:%02d:%02d %c%02d%02d",
681                                 soup_date_weekday (date), date->day,
682                                 months[date->month - 1], date->year,
683                                 date->hour, date->minute, date->second,
684                                 sign, hour_offset, minute_offset);
685
686                 default:
687                         return NULL;
688                 }
689         }
690 }
691
692 /**
693  * soup_date_to_time_t:
694  * @date: a #SoupDate
695  *
696  * Converts @date to a %time_t.
697  *
698  * If @date is not representable as a %time_t, it will be clamped into
699  * range. (In particular, some HTTP cookies have expiration dates
700  * after "Y2.038k" (2038-01-19T03:14:07Z).)
701  *
702  * Return value: @date as a %time_t
703  **/
704 time_t
705 soup_date_to_time_t (SoupDate *date)
706 {
707         time_t tt;
708         GTimeVal val;
709
710         g_return_val_if_fail (date != NULL, 0);
711
712         /* FIXME: offset, etc */
713
714         if (date->year < 1970)
715                 return 0;
716
717         /* If the year is later than 2038, we're guaranteed to
718          * overflow a 32-bit time_t. (If it's exactly 2038, we'll
719          * *probably* overflow, but only by a little, and it's easiest
720          * to test that at the end by seeing if the result has turned
721          * negative.)
722          */
723         if (sizeof (time_t) == 4 && date->year > 2038)
724                 return (time_t)0x7fffffff;
725
726         soup_date_to_timeval (date, &val);
727         tt = val.tv_sec;
728
729         if (sizeof (time_t) == 4 && tt < 0)
730                 return (time_t)0x7fffffff;
731         return tt;
732 }
733
734 /**
735  * soup_date_to_timeval:
736  * @date: a #SoupDate
737  * @time: (out): a #GTimeVal structure in which to store the converted time.
738  *
739  * Converts @date to a #GTimeVal.
740  *
741  * Since: 2.24
742  */
743 void
744 soup_date_to_timeval (SoupDate *date, GTimeVal *time)
745 {
746         g_return_if_fail (date != NULL);
747         g_return_if_fail (time != NULL);
748
749         /* FIXME: offset, etc */
750
751         time->tv_sec = rata_die_day (date) - TIME_T_EPOCH_RATA_DIE_DAY;
752         time->tv_sec = ((((time->tv_sec * 24) + date->hour) * 60) + date->minute) * 60 + date->second;
753         time->tv_usec = 0;
754 }
755
756 /**
757  * soup_date_is_past:
758  * @date: a #SoupDate
759  *
760  * Determines if @date is in the past.
761  *
762  * Return value: %TRUE if @date is in the past
763  *
764  * Since: 2.24
765  **/
766 gboolean
767 soup_date_is_past (SoupDate *date)
768 {
769         g_return_val_if_fail (date != NULL, TRUE);
770
771         /* optimization */
772         if (date->year < 2010)
773                 return TRUE;
774
775         return soup_date_to_time_t (date) < time (NULL);
776 }
777
778 /**
779  * soup_date_get_year:
780  * @date: a #SoupDate
781  *
782  * Gets @date's year.
783  *
784  * Return value: @date's year
785  *
786  * Since: 2.32
787  **/
788 int
789 soup_date_get_year (SoupDate *date)
790 {
791         return date->year;
792 }
793
794 /**
795  * soup_date_get_month:
796  * @date: a #SoupDate
797  *
798  * Gets @date's month.
799  *
800  * Return value: @date's month
801  *
802  * Since: 2.32
803  **/
804 int
805 soup_date_get_month (SoupDate *date)
806 {
807         return date->month;
808 }
809
810 /**
811  * soup_date_get_day:
812  * @date: a #SoupDate
813  *
814  * Gets @date's day.
815  *
816  * Return value: @date's day
817  *
818  * Since: 2.32
819  **/
820 int
821 soup_date_get_day (SoupDate *date)
822 {
823         return date->day;
824 }
825
826 /**
827  * soup_date_get_hour:
828  * @date: a #SoupDate
829  *
830  * Gets @date's hour.
831  *
832  * Return value: @date's hour
833  *
834  * Since: 2.32
835  **/
836 int
837 soup_date_get_hour (SoupDate *date)
838 {
839         return date->hour;
840 }
841
842 /**
843  * soup_date_get_minute:
844  * @date: a #SoupDate
845  *
846  * Gets @date's minute.
847  *
848  * Return value: @date's minute
849  *
850  * Since: 2.32
851  **/
852 int
853 soup_date_get_minute (SoupDate *date)
854 {
855         return date->minute;
856 }
857
858 /**
859  * soup_date_get_second:
860  * @date: a #SoupDate
861  *
862  * Gets @date's second.
863  *
864  * Return value: @date's second
865  *
866  * Since: 2.32
867  **/
868 int
869 soup_date_get_second (SoupDate *date)
870 {
871         return date->second;
872 }
873
874 /**
875  * soup_date_get_utc:
876  * @date: a #SoupDate
877  *
878  * Gets @date's UTC flag
879  *
880  * Return value: %TRUE if @date is UTC.
881  *
882  * Since: 2.32
883  **/
884 gboolean
885 soup_date_get_utc (SoupDate *date)
886 {
887         return date->utc;
888 }
889
890 /**
891  * soup_date_get_offset:
892  * @date: a #SoupDate
893  *
894  * Gets @date's offset from UTC.
895  *
896  * Return value: @date's offset from UTC. If soup_date_get_utc()
897  * returns %FALSE but soup_date_get_offset() returns 0, that means the
898  * date is a "floating" time with no associated offset information.
899  *
900  * Since: 2.32
901  **/
902 int
903 soup_date_get_offset (SoupDate *date)
904 {
905         return date->offset;
906 }
907
908 /**
909  * soup_date_copy:
910  * @date: a #SoupDate
911  *
912  * Copies @date.
913  **/
914 SoupDate *
915 soup_date_copy (SoupDate *date)
916 {
917         SoupDate *copy;
918
919         g_return_val_if_fail (date != NULL, NULL);
920
921         copy = g_slice_new (SoupDate);
922         memcpy (copy, date, sizeof (SoupDate));
923         return copy;
924 }
925
926 /**
927  * soup_date_free:
928  * @date: a #SoupDate
929  *
930  * Frees @date.
931  **/
932 void
933 soup_date_free (SoupDate *date)
934 {
935         g_return_if_fail (date != NULL);
936
937         g_slice_free (SoupDate, date);
938 }