Back to index

lightning-sunbird  0.9+nobinonly
icaltimezone.c
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
00002 /*======================================================================
00003  FILE: icaltimezone.c
00004  CREATOR: Damon Chaplin 15 March 2001
00005 
00006  $Id: icaltimezone.c,v 1.1.1.1 2005/01/05 20:09:17 mvl%exedo.nl Exp $
00007  $Locker:  $
00008 
00009  (C) COPYRIGHT 2001, Damon Chaplin
00010 
00011  This program is free software; you can redistribute it and/or modify
00012  it under the terms of either: 
00013 
00014     The LGPL as published by the Free Software Foundation, version
00015     2.1, available at: http://www.fsf.org/copyleft/lesser.html
00016 
00017   Or:
00018 
00019     The Mozilla Public License Version 1.0. You may obtain a copy of
00020     the License at http://www.mozilla.org/MPL/
00021 
00022 
00023 ======================================================================*/
00024 
00029 #ifdef HAVE_CONFIG_H
00030 #include "config.h"
00031 #endif
00032 
00033 #include <stdio.h>
00034 #include <stdlib.h>
00035 #include <string.h>
00036 #include "icalproperty.h"
00037 #include "icalarray.h"
00038 #include "icalerror.h"
00039 #include "icalparser.h"
00040 #include "icaltimezone.h"
00041 
00042 #ifdef WIN32
00043 #define snprintf _snprintf
00044 #define PACKAGE_DATA_DIR "/Projects/libical"
00045 #else
00046 #ifdef XP_MAC
00047 #define PACKAGE_DATA_DIR "/Projects/libical"
00048 #else
00049 #ifndef PACKAGE_DATA_DIR
00050 #define PACKAGE_DATA_DIR "/usr/share/libical"
00051 #endif
00052 #endif
00053 #endif
00054 
00056 #define ZONEINFO_DIRECTORY  PACKAGE_DATA_DIR "/zoneinfo"
00057 
00059 #define TZID_PREFIX         "/softwarestudio.org/"
00060 #define TZID_PREFIX_LEN            20
00061 
00064 #define ZONES_TAB_FILENAME  "zones.tab"
00065 
00068 #define ICALTIMEZONE_EXTRA_COVERAGE       5
00069 
00072 #define ICALTIMEZONE_MAX_YEAR             2035
00073 
00074 struct _icaltimezone {
00075     char             *tzid;
00081     char             *location;
00088     char             *tznames;
00096     double            latitude;
00097     double            longitude;
00100     icalcomponent    *component;
00104     icaltimezone     *builtin_timezone;
00111     int                      end_year;
00116     icalarray        *changes;
00120 };
00121 
00122 typedef struct _icaltimezonechange icaltimezonechange;
00123 
00124 struct _icaltimezonechange {
00125     int               utc_offset;
00128     int               prev_utc_offset;
00131     int               year;        
00132     int               month;              
00133     int               day;
00134     int               hour;
00135     int               minute;
00136     int               second;
00141     int               is_daylight;
00143 };
00144 
00145 
00147 static icalarray *builtin_timezones = NULL;
00148 
00150 static icaltimezone utc_timezone = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
00151 
00152 static char* zone_files_directory = NULL;
00153 
00154 static void  icaltimezone_reset                  (icaltimezone   *zone);
00155 static char* icaltimezone_get_location_from_vtimezone (icalcomponent *component);
00156 static char* icaltimezone_get_tznames_from_vtimezone (icalcomponent *component);
00157 static void  icaltimezone_expand_changes  (icaltimezone *zone,
00158                                            int           end_year);
00159 static void  icaltimezone_expand_vtimezone       (icalcomponent       *comp,
00160                                            int           end_year,
00161                                            icalarray    *changes);
00162 static int   icaltimezone_compare_change_fn      (const void   *elem1,
00163                                            const void   *elem2);
00164 
00165 static int   icaltimezone_find_nearby_change     (icaltimezone *zone,
00166                                            icaltimezonechange *change);
00167 
00168 static void  icaltimezone_adjust_change          (icaltimezonechange *tt,
00169                                            int           days,
00170                                            int           hours,
00171                                            int           minutes,
00172                                            int           seconds);
00173 
00174 static void  icaltimezone_init                   (icaltimezone *zone);
00175 
00179 static int   icaltimezone_get_vtimezone_properties (icaltimezone  *zone,
00180                                               icalcomponent *component);
00181 
00182 
00183 static void  icaltimezone_load_builtin_timezone  (icaltimezone *zone);
00184 
00185 static void  icaltimezone_ensure_coverage (icaltimezone *zone,
00186                                            int           end_year);
00187 
00188 
00189 static void  icaltimezone_init_builtin_timezones(void);
00190 
00191 static void  icaltimezone_parse_zone_tab  (void);
00192 
00193 static char* icaltimezone_load_get_line_fn       (char         *s,
00194                                            size_t               size,
00195                                            void         *data);
00196 
00197 static void  format_utc_offset                   (int           utc_offset,
00198                                            char         *buffer);
00199 
00200 static char* get_zone_directory(void);
00201 
00202 
00204 icaltimezone*
00205 icaltimezone_new                   (void)
00206 {
00207     icaltimezone *zone;
00208 
00209     zone = (icaltimezone*) malloc (sizeof (icaltimezone));
00210     if (!zone) {
00211        icalerror_set_errno (ICAL_NEWFAILED_ERROR);
00212        return NULL;
00213     }
00214 
00215     icaltimezone_init (zone);
00216 
00217     return zone;
00218 }
00219 
00220 
00222 void
00223 icaltimezone_free                  (icaltimezone *zone,
00224                                     int          free_struct)
00225 {
00226     icaltimezone_reset (zone);
00227     if (free_struct)
00228        free (zone);
00229 }
00230 
00231 
00233 static void
00234 icaltimezone_reset                 (icaltimezone *zone)
00235 {
00236     if (zone->tzid)
00237               free (zone->tzid);
00238     if (zone->location)
00239               free (zone->location);
00240     if (zone->tznames)
00241               free (zone->tznames);
00242     if (zone->component)
00243               icalcomponent_free (zone->component);
00244     if (zone->changes)
00245               icalarray_free (zone->changes);
00246        
00247     icaltimezone_init (zone);
00248 }
00249 
00250 
00252 static void
00253 icaltimezone_init                  (icaltimezone *zone)
00254 {
00255     zone->tzid = NULL;
00256     zone->location = NULL;
00257     zone->tznames = NULL;
00258     zone->latitude = 0.0;
00259     zone->longitude = 0.0;
00260     zone->component = NULL;
00261     zone->builtin_timezone = NULL;
00262     zone->end_year = 0;
00263     zone->changes = NULL;
00264 }
00265 
00266 
00272 static int
00273 icaltimezone_get_vtimezone_properties     (icaltimezone *zone,
00274                                     icalcomponent       *component)
00275 {
00276     icalproperty *prop;
00277     const char *tzid;
00278  
00279     prop = icalcomponent_get_first_property (component, ICAL_TZID_PROPERTY);
00280     if (!prop)
00281        return 0;
00282 
00283     /* A VTIMEZONE MUST have a TZID, or a lot of our code won't work. */
00284     tzid = icalproperty_get_tzid (prop);
00285     if (!tzid)
00286        return 0;
00287 
00288     zone->tzid = strdup (tzid);
00289     zone->component = component;
00290        if ( zone->location != 0 ) free ( zone->location );
00291     zone->location = icaltimezone_get_location_from_vtimezone (component);
00292     zone->tznames = icaltimezone_get_tznames_from_vtimezone (component);
00293 
00294     return 1;
00295 }
00296 
00298 static char*
00299 icaltimezone_get_location_from_vtimezone (icalcomponent *component)
00300 {
00301     icalproperty *prop;
00302     const char *location;
00303     const char *name;
00304 
00305     prop = icalcomponent_get_first_property (component,
00306                                         ICAL_LOCATION_PROPERTY);
00307     if (prop) {
00308        location = icalproperty_get_location (prop);
00309        if (location)
00310            return strdup (location);
00311     }
00312 
00313     prop = icalcomponent_get_first_property (component, ICAL_X_PROPERTY);
00314     while (prop) {
00315        name = icalproperty_get_x_name (prop);
00316        if (name && !strcmp (name, "X-LIC-LOCATION")) {
00317            location = icalproperty_get_x (prop);
00318            if (location)
00319               return strdup (location);
00320        }
00321        prop = icalcomponent_get_next_property (component,
00322                                           ICAL_X_PROPERTY);
00323     }
00324 
00325     return NULL;
00326 }
00327 
00328 
00334 static char*
00335 icaltimezone_get_tznames_from_vtimezone (icalcomponent *component)
00336 {
00337     icalcomponent *comp;
00338     icalcomponent_kind type;
00339     icalproperty *prop;
00340     struct icaltimetype dtstart;
00341     struct icaldatetimeperiodtype rdate;
00342     const char *current_tzname;
00343     const char *standard_tzname = NULL, *daylight_tzname = NULL;
00344     struct icaltimetype standard_max_date, daylight_max_date;
00345     struct icaltimetype current_max_date;
00346 
00347     standard_max_date = icaltime_null_time();
00348     daylight_max_date = icaltime_null_time();
00349 
00350     /* Step through the STANDARD & DAYLIGHT subcomponents. */
00351     comp = icalcomponent_get_first_component (component, ICAL_ANY_COMPONENT);
00352     while (comp) {
00353        type = icalcomponent_isa (comp);
00354        if (type == ICAL_XSTANDARD_COMPONENT
00355            || type == ICAL_XDAYLIGHT_COMPONENT) {
00356            current_max_date = icaltime_null_time ();
00357            current_tzname = NULL;
00358 
00359            /* Step through the properties. We want to find the TZNAME, and
00360               the largest DTSTART or RDATE. */
00361            prop = icalcomponent_get_first_property (comp, ICAL_ANY_PROPERTY);
00362            while (prop) {
00363               switch (icalproperty_isa (prop)) {
00364               case ICAL_TZNAME_PROPERTY:
00365                   current_tzname = icalproperty_get_tzname (prop);
00366                   break;
00367 
00368               case ICAL_DTSTART_PROPERTY:
00369                   dtstart = icalproperty_get_dtstart (prop);
00370                   if (icaltime_compare (dtstart, current_max_date) > 0)
00371                      current_max_date = dtstart;
00372 
00373                   break;
00374 
00375               case ICAL_RDATE_PROPERTY:
00376                   rdate = icalproperty_get_rdate (prop);
00377                   if (icaltime_compare (rdate.time, current_max_date) > 0)
00378                      current_max_date = rdate.time;
00379 
00380                   break;
00381 
00382               default:
00383                   break;
00384               }
00385 
00386               prop = icalcomponent_get_next_property (comp,
00387                                                  ICAL_ANY_PROPERTY);
00388            }
00389 
00390            if (current_tzname) {
00391               if (type == ICAL_XSTANDARD_COMPONENT) {
00392                   if (!standard_tzname
00393                      || icaltime_compare (current_max_date,
00394                                         standard_max_date) > 0) {
00395                      standard_max_date = current_max_date;
00396                      standard_tzname = current_tzname;
00397                   }
00398               } else {
00399                   if (!daylight_tzname
00400                      || icaltime_compare (current_max_date,
00401                                         daylight_max_date) > 0) {
00402                      daylight_max_date = current_max_date;
00403                      daylight_tzname = current_tzname;
00404                   }
00405               }
00406            }
00407        }
00408 
00409         comp = icalcomponent_get_next_component (component,
00410                                            ICAL_ANY_COMPONENT);
00411     }
00412 
00413     /* Outlook (2000) places "Standard Time" and "Daylight Time" in the TZNAME
00414        strings, which is totally useless. So we return NULL in that case. */
00415     if (standard_tzname && !strcmp (standard_tzname, "Standard Time"))
00416        return NULL;
00417 
00418     /* If both standard and daylight TZNAMEs were found, if they are the same
00419        we return just one, else we format them like "EST/EDT". */
00420     if (standard_tzname && daylight_tzname) {
00421        unsigned int standard_len, daylight_len;
00422        char *tznames;
00423 
00424        if (!strcmp (standard_tzname, daylight_tzname))
00425            return strdup (standard_tzname);
00426 
00427        standard_len = strlen (standard_tzname);
00428        daylight_len = strlen (daylight_tzname);
00429        tznames = malloc (standard_len + daylight_len + 2);
00430        strcpy (tznames, standard_tzname);
00431        tznames[standard_len] = '/';
00432        strcpy (tznames + standard_len + 1, daylight_tzname);
00433        return tznames;
00434     } else {
00435        const char *tznames;
00436 
00437        /* If either of the TZNAMEs was found just return that, else NULL. */
00438        tznames = standard_tzname ? standard_tzname : daylight_tzname;
00439        return tznames ? strdup (tznames) : NULL;
00440     }
00441 }
00442 
00443 
00444 static void
00445 icaltimezone_ensure_coverage              (icaltimezone *zone,
00446                                     int           end_year)
00447 {
00448     /* When we expand timezone changes we always expand at least up to this
00449        year, plus ICALTIMEZONE_EXTRA_COVERAGE. */
00450     static int icaltimezone_minimum_expansion_year = -1;
00451 
00452     int changes_end_year;
00453 
00454     if (!zone->component)
00455        icaltimezone_load_builtin_timezone (zone);
00456 
00457     if (icaltimezone_minimum_expansion_year == -1) {
00458        struct icaltimetype today = icaltime_today();
00459        icaltimezone_minimum_expansion_year = today.year;
00460     }
00461 
00462     changes_end_year = end_year;
00463     if (changes_end_year < icaltimezone_minimum_expansion_year)
00464        changes_end_year = icaltimezone_minimum_expansion_year;
00465 
00466     changes_end_year += ICALTIMEZONE_EXTRA_COVERAGE;
00467 
00468     if (changes_end_year > ICALTIMEZONE_MAX_YEAR)
00469        changes_end_year = ICALTIMEZONE_MAX_YEAR;
00470 
00471     if (!zone->changes || zone->end_year < end_year)
00472        icaltimezone_expand_changes (zone, changes_end_year);
00473 }
00474 
00475 
00476 static void
00477 icaltimezone_expand_changes        (icaltimezone *zone,
00478                                     int           end_year)
00479 {
00480     icalarray *changes;
00481     icalcomponent *comp;
00482 
00483 #if 0
00484     printf ("\nExpanding changes for: %s to year: %i\n", zone->tzid, end_year);
00485 #endif
00486 
00487     changes = icalarray_new (sizeof (icaltimezonechange), 32);
00488     if (!changes)
00489        return;
00490 
00491     /* Scan the STANDARD and DAYLIGHT subcomponents. */
00492     comp = icalcomponent_get_first_component (zone->component,
00493                                          ICAL_ANY_COMPONENT);
00494     while (comp) {
00495        icaltimezone_expand_vtimezone (comp, end_year, changes);
00496        comp = icalcomponent_get_next_component (zone->component,
00497                                            ICAL_ANY_COMPONENT);
00498     }
00499 
00500     /* Sort the changes. We may have duplicates but I don't think it will
00501        matter. */
00502     icalarray_sort (changes, icaltimezone_compare_change_fn);
00503 
00504     if (zone->changes)
00505        icalarray_free (zone->changes);
00506 
00507     zone->changes = changes;
00508     zone->end_year = end_year;
00509 }
00510 
00511 
00512 static void
00513 icaltimezone_expand_vtimezone             (icalcomponent       *comp,
00514                                     int           end_year,
00515                                     icalarray    *changes)
00516 {
00517     icaltimezonechange change;
00518     icalproperty *prop;
00519     struct icaltimetype dtstart, occ;
00520     struct icalrecurrencetype rrule;
00521     icalrecur_iterator* rrule_iterator;
00522     struct icaldatetimeperiodtype rdate;
00523     int found_dtstart = 0, found_tzoffsetto = 0, found_tzoffsetfrom = 0;
00524     int has_recurrence = 0;
00525 
00526     /* First we check if it is a STANDARD or DAYLIGHT component, and
00527        just return if it isn't. */
00528     if (icalcomponent_isa (comp) == ICAL_XSTANDARD_COMPONENT)
00529        change.is_daylight = 0;
00530     else if (icalcomponent_isa (comp) == ICAL_XDAYLIGHT_COMPONENT)
00531        change.is_daylight = 1;
00532     else 
00533        return;
00534 
00535     /* Step through each of the properties to find the DTSTART,
00536        TZOFFSETFROM and TZOFFSETTO. We can't expand recurrences here
00537        since we need these properties before we can do that. */
00538     prop = icalcomponent_get_first_property (comp, ICAL_ANY_PROPERTY);
00539     while (prop) {
00540        switch (icalproperty_isa (prop)) {
00541        case ICAL_DTSTART_PROPERTY:
00542            dtstart = icalproperty_get_dtstart (prop);
00543            found_dtstart = 1;
00544            break;
00545        case ICAL_TZOFFSETTO_PROPERTY:
00546            change.utc_offset = icalproperty_get_tzoffsetto (prop);
00547            /*printf ("Found TZOFFSETTO: %i\n", change.utc_offset);*/
00548            found_tzoffsetto = 1;
00549            break;
00550        case ICAL_TZOFFSETFROM_PROPERTY:
00551            change.prev_utc_offset = icalproperty_get_tzoffsetfrom (prop);
00552            /*printf ("Found TZOFFSETFROM: %i\n", change.prev_utc_offset);*/
00553            found_tzoffsetfrom = 1;
00554            break;
00555        case ICAL_RDATE_PROPERTY:
00556        case ICAL_RRULE_PROPERTY:
00557            has_recurrence = 1;
00558            break;
00559        default:
00560            /* Just ignore any other properties. */
00561            break;
00562        }
00563 
00564        prop = icalcomponent_get_next_property (comp, ICAL_ANY_PROPERTY);
00565     }
00566 
00567     /* If we didn't find a DTSTART, TZOFFSETTO and TZOFFSETFROM we have to
00568        ignore the component. FIXME: Add an error property? */
00569     if (!found_dtstart || !found_tzoffsetto || !found_tzoffsetfrom)
00570        return;
00571 
00572 #if 0
00573     printf ("\n Expanding component DTSTART (Y/M/D): %i/%i/%i %i:%02i:%02i\n",
00574            dtstart.year, dtstart.month, dtstart.day,
00575            dtstart.hour, dtstart.minute, dtstart.second);
00576 #endif
00577 
00578     /* If the STANDARD/DAYLIGHT component has no recurrence data, we just add
00579        a single change for the DTSTART. */
00580     if (!has_recurrence) {
00581        change.year   = dtstart.year;
00582        change.month  = dtstart.month;
00583        change.day    = dtstart.day;
00584        change.hour   = dtstart.hour;
00585        change.minute = dtstart.minute;
00586        change.second = dtstart.second;
00587 
00588        /* Convert to UTC. */
00589        icaltimezone_adjust_change (&change, 0, 0, 0, -change.prev_utc_offset);
00590 
00591 #if 0
00592        printf ("  Appending single DTSTART (Y/M/D): %i/%02i/%02i %i:%02i:%02i\n",
00593               change.year, change.month, change.day,
00594               change.hour, change.minute, change.second);
00595 #endif
00596 
00597        /* Add the change to the array. */
00598        icalarray_append (changes, &change);
00599        return;
00600     }
00601 
00602     /* The component has recurrence data, so we expand that now. */
00603     prop = icalcomponent_get_first_property (comp, ICAL_ANY_PROPERTY);
00604     while (prop) {
00605 #if 0
00606        printf ("Expanding property...\n");
00607 #endif
00608        switch (icalproperty_isa (prop)) {
00609        case ICAL_RDATE_PROPERTY:
00610            rdate = icalproperty_get_rdate (prop);
00611            change.year   = rdate.time.year;
00612            change.month  = rdate.time.month;
00613            change.day    = rdate.time.day;
00614            /* RDATEs with a DATE value inherit the time from
00615               the DTSTART. */
00616            if (icaltime_is_date(rdate.time)) {
00617               change.hour   = dtstart.hour;
00618               change.minute = dtstart.minute;
00619               change.second = dtstart.second;
00620            } else {
00621               change.hour   = rdate.time.hour;
00622               change.minute = rdate.time.minute;
00623               change.second = rdate.time.second;
00624 
00625               /* The spec was a bit vague about whether RDATEs were in local
00626                  time or UTC so we support both to be safe. So if it is in
00627                  UTC we have to add the UTC offset to get a local time. */
00628               if (!icaltime_is_utc(rdate.time))
00629                   icaltimezone_adjust_change (&change, 0, 0, 0,
00630                                           -change.prev_utc_offset);
00631            }
00632 
00633 #if 0
00634            printf ("  Appending RDATE element (Y/M/D): %i/%02i/%02i %i:%02i:%02i\n",
00635                   change.year, change.month, change.day,
00636                   change.hour, change.minute, change.second);
00637 #endif
00638 
00639            icalarray_append (changes, &change);
00640            break;
00641        case ICAL_RRULE_PROPERTY:
00642            rrule = icalproperty_get_rrule (prop);
00643 
00644            /* If the rrule UNTIL value is set and is in UTC, we convert it to
00645               a local time, since the recurrence code has no way to convert
00646               it itself. */
00647            if (!icaltime_is_null_time (rrule.until) && rrule.until.is_utc) {
00648 #if 0
00649               printf ("  Found RRULE UNTIL in UTC.\n");
00650 #endif
00651 
00652               /* To convert from UTC to a local time, we use the TZOFFSETFROM
00653                  since that is the offset from UTC that will be in effect
00654                  when each of the RRULE occurrences happens. */
00655               icaltime_adjust (&rrule.until, 0, 0, 0,
00656                              change.prev_utc_offset);
00657               rrule.until.is_utc = 0;
00658            }
00659 
00660            rrule_iterator = icalrecur_iterator_new (rrule, dtstart);
00661            for (;;) {
00662               occ = icalrecur_iterator_next (rrule_iterator);
00663               if (occ.year > end_year || icaltime_is_null_time (occ))
00664                   break;
00665 
00666               change.year   = occ.year;
00667               change.month  = occ.month;
00668               change.day    = occ.day;
00669               change.hour   = occ.hour;
00670               change.minute = occ.minute;
00671               change.second = occ.second;
00672 
00673 #if 0
00674               printf ("  Appending RRULE element (Y/M/D): %i/%02i/%02i %i:%02i:%02i\n",
00675                      change.year, change.month, change.day,
00676                      change.hour, change.minute, change.second);
00677 #endif
00678 
00679               icaltimezone_adjust_change (&change, 0, 0, 0,
00680                                        -change.prev_utc_offset);
00681 
00682               icalarray_append (changes, &change);
00683            }
00684 
00685            icalrecur_iterator_free (rrule_iterator);
00686            break;
00687        default:
00688            break;
00689        }
00690 
00691        prop = icalcomponent_get_next_property (comp, ICAL_ANY_PROPERTY);
00692     }
00693 }
00694 
00695 
00697 static int
00698 icaltimezone_compare_change_fn            (const void   *elem1,
00699                                     const void   *elem2)
00700 {
00701     const icaltimezonechange *change1, *change2;
00702     int retval;
00703 
00704     change1 = elem1;
00705     change2 = elem2;
00706 
00707     if (change1->year < change2->year)
00708        retval = -1;
00709     else if (change1->year > change2->year)
00710        retval = 1;
00711 
00712     else if (change1->month < change2->month)
00713        retval = -1;
00714     else if (change1->month > change2->month)
00715        retval = 1;
00716 
00717     else if (change1->day < change2->day)
00718        retval = -1;
00719     else if (change1->day > change2->day)
00720        retval = 1;
00721 
00722     else if (change1->hour < change2->hour)
00723        retval = -1;
00724     else if (change1->hour > change2->hour)
00725        retval = 1;
00726 
00727     else if (change1->minute < change2->minute)
00728        retval = -1;
00729     else if (change1->minute > change2->minute)
00730        retval = 1;
00731 
00732     else if (change1->second < change2->second)
00733        retval = -1;
00734     else if (change1->second > change2->second)
00735        retval = 1;
00736 
00737     else
00738        retval = 0;
00739 
00740     return retval;
00741 }
00742 
00743 
00744 
00745 void
00746 icaltimezone_convert_time          (struct icaltimetype *tt,
00747                                     icaltimezone *from_zone,
00748                                     icaltimezone *to_zone)
00749 {
00750     int utc_offset, is_daylight;
00751 
00752     /* If the time is a DATE value or both timezones are the same, or we are
00753        converting a floating time, we don't need to do anything. */
00754     if (icaltime_is_date(*tt) || from_zone == to_zone || from_zone == NULL)
00755        return;
00756 
00757     /* Convert the time to UTC by getting the UTC offset and subtracting it. */
00758     utc_offset = icaltimezone_get_utc_offset (from_zone, tt, NULL);
00759     icaltime_adjust (tt, 0, 0, 0, -utc_offset);
00760 
00761     /* Now we convert the time to the new timezone by getting the UTC offset
00762        of our UTC time and adding it. */       
00763     utc_offset = icaltimezone_get_utc_offset_of_utc_time (to_zone, tt,
00764                                                    &is_daylight);
00765     tt->is_daylight = is_daylight;
00766     icaltime_adjust (tt, 0, 0, 0, utc_offset);
00767 }
00768 
00769 
00770 
00771 
00774 /* Calculates the UTC offset of a given local time in the given
00775    timezone.  It is the number of seconds to add to UTC to get local
00776    time.  The is_daylight flag is set to 1 if the time is in
00777    daylight-savings time. */
00778 int
00779 icaltimezone_get_utc_offset        (icaltimezone *zone,
00780                                     struct icaltimetype *tt,
00781                                     int          *is_daylight)
00782 {
00783     icaltimezonechange *zone_change, *prev_zone_change, tt_change, tmp_change;
00784     int change_num, step, utc_offset_change, cmp;
00785     int change_num_to_use;
00786     int want_daylight;
00787 
00788     if (tt == NULL)
00789        return 0;
00790 
00791     if (is_daylight)
00792        *is_daylight = 0;
00793 
00794     /* For local times and UTC return 0. */
00795     if (zone == NULL || zone == &utc_timezone)
00796        return 0;
00797 
00798     /* Use the builtin icaltimezone if possible. */
00799     if (zone->builtin_timezone)
00800        zone = zone->builtin_timezone;
00801 
00802     /* Make sure the changes array is expanded up to the given time. */
00803     icaltimezone_ensure_coverage (zone, tt->year);
00804 
00805     if (!zone->changes || zone->changes->num_elements == 0)
00806        return 0;
00807 
00808     /* Copy the time parts of the icaltimetype to an icaltimezonechange so we
00809        can use our comparison function on it. */
00810     tt_change.year   = tt->year;
00811     tt_change.month  = tt->month;
00812     tt_change.day    = tt->day;
00813     tt_change.hour   = tt->hour;
00814     tt_change.minute = tt->minute;
00815     tt_change.second = tt->second;
00816 
00817     /* This should find a change close to the time, either the change before
00818        it or the change after it. */
00819     change_num = icaltimezone_find_nearby_change (zone, &tt_change);
00820 
00821     /* Sanity check. */
00822     icalerror_assert (change_num >= 0,
00823                     "Negative timezone change index");
00824     icalerror_assert (change_num < zone->changes->num_elements,
00825                     "Timezone change index out of bounds");
00826 
00827     /* Now move backwards or forwards to find the timezone change that applies
00828        to tt. It should only have to do 1 or 2 steps. */
00829     zone_change = icalarray_element_at (zone->changes, change_num);
00830     step = 1;
00831     change_num_to_use = -1;
00832     for (;;) {
00833        /* Copy the change, so we can adjust it. */
00834        tmp_change = *zone_change;
00835 
00836        /* If the clock is going backward, check if it is in the region of time
00837           that is used twice. If it is, use the change with the daylight
00838           setting which matches tt, or use standard if we don't know. */
00839        if (tmp_change.utc_offset < tmp_change.prev_utc_offset) {
00840            /* If the time change is at 2:00AM local time and the clock is
00841               going back to 1:00AM we adjust the change to 1:00AM. We may
00842               have the wrong change but we'll figure that out later. */
00843            icaltimezone_adjust_change (&tmp_change, 0, 0, 0,
00844                                    tmp_change.utc_offset);
00845        } else {
00846            icaltimezone_adjust_change (&tmp_change, 0, 0, 0,
00847                                    tmp_change.prev_utc_offset);
00848        }
00849 
00850        cmp = icaltimezone_compare_change_fn (&tt_change, &tmp_change);
00851 
00852        /* If the given time is on or after this change, then this change may
00853           apply, but we continue as a later change may be the right one.
00854           If the given time is before this change, then if we have already
00855           found a change which applies we can use that, else we need to step
00856           backwards. */
00857        if (cmp >= 0)
00858            change_num_to_use = change_num;
00859        else
00860            step = -1;
00861 
00862        /* If we are stepping backwards through the changes and we have found
00863           a change that applies, then we know this is the change to use so
00864           we exit the loop. */
00865        if (step == -1 && change_num_to_use != -1)
00866            break;
00867 
00868        change_num += step;
00869 
00870        /* If we go past the start of the changes array, then we have no data
00871           for this time so we return a UTC offset of 0. */
00872        if (change_num < 0)
00873            return 0;
00874 
00875        if (change_num >= zone->changes->num_elements)
00876            break;
00877 
00878        zone_change = icalarray_element_at (zone->changes, change_num);
00879     }
00880 
00881     /* If we didn't find a change to use, then we have a bug! */
00882     icalerror_assert (change_num_to_use != -1,
00883                     "No applicable timezone change found");
00884 
00885     /* Now we just need to check if the time is in the overlapped region of
00886        time when clocks go back. */
00887     zone_change = icalarray_element_at (zone->changes, change_num_to_use);
00888 
00889     utc_offset_change = zone_change->utc_offset - zone_change->prev_utc_offset;
00890     if (utc_offset_change < 0 && change_num_to_use > 0) {
00891        tmp_change = *zone_change;
00892        icaltimezone_adjust_change (&tmp_change, 0, 0, 0,
00893                                 tmp_change.prev_utc_offset);
00894 
00895        if (icaltimezone_compare_change_fn (&tt_change, &tmp_change) < 0) {
00896            /* The time is in the overlapped region, so we may need to use
00897               either the current zone_change or the previous one. If the
00898               time has the is_daylight field set we use the matching change,
00899               else we use the change with standard time. */
00900            prev_zone_change = icalarray_element_at (zone->changes,
00901                                                change_num_to_use - 1);
00902 
00903            /* I was going to add an is_daylight flag to struct icaltimetype,
00904               but iCalendar doesn't let us distinguish between standard and
00905               daylight time anyway, so there's no point. So we just use the
00906               standard time instead. */
00907            want_daylight = (tt->is_daylight == 1) ? 1 : 0;
00908 
00909 #if 0
00910            if (zone_change->is_daylight == prev_zone_change->is_daylight)
00911               printf (" **** Same is_daylight setting\n");
00912 #endif
00913 
00914            if (zone_change->is_daylight != want_daylight
00915               && prev_zone_change->is_daylight == want_daylight)
00916               zone_change = prev_zone_change;
00917        }
00918     }
00919 
00920     /* Now we know exactly which timezone change applies to the time, so
00921        we can return the UTC offset and whether it is a daylight time. */
00922     if (is_daylight)
00923        *is_daylight = zone_change->is_daylight;
00924     return zone_change->utc_offset;
00925 }
00926 
00927 
00934 int
00935 icaltimezone_get_utc_offset_of_utc_time   (icaltimezone *zone,
00936                                     struct icaltimetype *tt,
00937                                     int          *is_daylight)
00938 {
00939     icaltimezonechange *zone_change, tt_change, tmp_change;
00940     int change_num, step, change_num_to_use;
00941 
00942     if (is_daylight)
00943        *is_daylight = 0;
00944 
00945     /* For local times and UTC return 0. */
00946     if (zone == NULL || zone == &utc_timezone)
00947        return 0;
00948 
00949     /* Use the builtin icaltimezone if possible. */
00950     if (zone->builtin_timezone)
00951        zone = zone->builtin_timezone;
00952 
00953     /* Make sure the changes array is expanded up to the given time. */
00954     icaltimezone_ensure_coverage (zone, tt->year);
00955 
00956     if (!zone->changes || zone->changes->num_elements == 0)
00957        return 0;
00958 
00959     /* Copy the time parts of the icaltimetype to an icaltimezonechange so we
00960        can use our comparison function on it. */
00961     tt_change.year   = tt->year;
00962     tt_change.month  = tt->month;
00963     tt_change.day    = tt->day;
00964     tt_change.hour   = tt->hour;
00965     tt_change.minute = tt->minute;
00966     tt_change.second = tt->second;
00967 
00968     /* This should find a change close to the time, either the change before
00969        it or the change after it. */
00970     change_num = icaltimezone_find_nearby_change (zone, &tt_change);
00971 
00972     /* Sanity check. */
00973     icalerror_assert (change_num >= 0,
00974                     "Negative timezone change index");
00975     icalerror_assert (change_num < zone->changes->num_elements,
00976                     "Timezone change index out of bounds");
00977 
00978     /* Now move backwards or forwards to find the timezone change that applies
00979        to tt. It should only have to do 1 or 2 steps. */
00980     zone_change = icalarray_element_at (zone->changes, change_num);
00981     step = 1;
00982     change_num_to_use = -1;
00983     for (;;) {
00984        /* Copy the change and adjust it to UTC. */
00985        tmp_change = *zone_change;
00986 
00987        /* If the given time is on or after this change, then this change may
00988           apply, but we continue as a later change may be the right one.
00989           If the given time is before this change, then if we have already
00990           found a change which applies we can use that, else we need to step
00991           backwards. */
00992        if (icaltimezone_compare_change_fn (&tt_change, &tmp_change) >= 0)
00993            change_num_to_use = change_num;
00994        else
00995            step = -1;
00996 
00997        /* If we are stepping backwards through the changes and we have found
00998           a change that applies, then we know this is the change to use so
00999           we exit the loop. */
01000        if (step == -1 && change_num_to_use != -1)
01001            break;
01002 
01003        change_num += step;
01004 
01005        /* If we go past the start of the changes array, then we have no data
01006           for this time so we return a UTC offset of 0. */
01007        if (change_num < 0)
01008            return 0;
01009 
01010        if (change_num >= zone->changes->num_elements)
01011            break;
01012 
01013        zone_change = icalarray_element_at (zone->changes, change_num);
01014     }
01015 
01016     /* If we didn't find a change to use, then we have a bug! */
01017     icalerror_assert (change_num_to_use != -1,
01018                     "No applicable timezone change found");
01019 
01020     /* Now we know exactly which timezone change applies to the time, so
01021        we can return the UTC offset and whether it is a daylight time. */
01022     zone_change = icalarray_element_at (zone->changes, change_num_to_use);
01023     if (is_daylight)
01024        *is_daylight = zone_change->is_daylight;
01025 
01026     return zone_change->utc_offset;
01027 }
01028 
01029 
01032 static int
01033 icaltimezone_find_nearby_change           (icaltimezone        *zone,
01034                                     icaltimezonechange  *change)
01035 {
01036     icaltimezonechange *zone_change;
01037     int lower, upper, middle, cmp;
01038                                     
01039     /* Do a simple binary search. */
01040     lower = middle = 0;
01041     upper = zone->changes->num_elements;
01042 
01043     while (lower < upper) {
01044        middle = (lower + upper) / 2;
01045        zone_change = icalarray_element_at (zone->changes, middle);
01046        cmp = icaltimezone_compare_change_fn (change, zone_change);
01047        if (cmp == 0)
01048            break;
01049        else if (cmp < 0)
01050            upper = middle;
01051        else
01052            lower = middle + 1;
01053     }
01054 
01055     return middle;
01056 }
01057 
01058 
01059 
01060 
01064 static void
01065 icaltimezone_adjust_change         (icaltimezonechange *tt,
01066                                     int           days,
01067                                     int           hours,
01068                                     int           minutes,
01069                                     int           seconds)
01070 {
01071     int second, minute, hour, day;
01072     int minutes_overflow, hours_overflow, days_overflow;
01073     int days_in_month;
01074 
01075     /* Add on the seconds. */
01076     second = tt->second + seconds;
01077     tt->second = second % 60;
01078     minutes_overflow = second / 60;
01079     if (tt->second < 0) {
01080        tt->second += 60;
01081        minutes_overflow--;
01082     }
01083 
01084     /* Add on the minutes. */
01085     minute = tt->minute + minutes + minutes_overflow;
01086     tt->minute = minute % 60;
01087     hours_overflow = minute / 60;
01088     if (tt->minute < 0) {
01089        tt->minute += 60;
01090        hours_overflow--;
01091     }
01092 
01093     /* Add on the hours. */
01094     hour = tt->hour + hours + hours_overflow;
01095     tt->hour = hour % 24;
01096     days_overflow = hour / 24;
01097     if (tt->hour < 0) {
01098        tt->hour += 24;
01099        days_overflow--;
01100     }
01101 
01102     /* Add on the days. */
01103     day = tt->day + days + days_overflow;
01104     if (day > 0) {
01105        for (;;) {
01106            days_in_month = icaltime_days_in_month (tt->month, tt->year);
01107            if (day <= days_in_month)
01108               break;
01109 
01110            tt->month++;
01111            if (tt->month >= 13) {
01112               tt->year++;
01113               tt->month = 1;
01114            }
01115 
01116            day -= days_in_month;
01117        }
01118     } else {
01119        while (day <= 0) {
01120            if (tt->month == 1) {
01121               tt->year--;
01122               tt->month = 12;
01123            } else {
01124               tt->month--;
01125            }
01126 
01127            day += icaltime_days_in_month (tt->month, tt->year);
01128        }
01129     }
01130     tt->day = day;
01131 }
01132 
01133 
01134 char*
01135 icaltimezone_get_tzid                     (icaltimezone *zone)
01136 {
01137     /* If this is a floating time, without a timezone, return NULL. */
01138     if (!zone)
01139        return NULL;
01140 
01141     if (!zone->tzid)
01142        icaltimezone_load_builtin_timezone (zone);
01143 
01144     return zone->tzid;
01145 }
01146 
01147 
01148 char*
01149 icaltimezone_get_location          (icaltimezone *zone)
01150 {
01151     /* If this is a floating time, without a timezone, return NULL. */
01152     if (!zone)
01153        return NULL;
01154 
01155     /* Note that for builtin timezones this comes from zones.tab so we don't
01156        need to check the timezone is loaded here. */
01157     return zone->location;
01158 }
01159 
01160 
01161 char*
01162 icaltimezone_get_tznames           (icaltimezone *zone)
01163 {
01164     /* If this is a floating time, without a timezone, return NULL. */
01165     if (!zone)
01166        return NULL;
01167 
01168     if (!zone->component)
01169        icaltimezone_load_builtin_timezone (zone);
01170 
01171     return zone->tznames;
01172 }
01173 
01174 
01176 double
01177 icaltimezone_get_latitude          (icaltimezone *zone)
01178 {
01179     /* If this is a floating time, without a timezone, return 0. */
01180     if (!zone)
01181        return 0.0;
01182 
01183     /* Note that for builtin timezones this comes from zones.tab so we don't
01184        need to check the timezone is loaded here. */
01185     return zone->latitude;
01186 }
01187 
01188 
01190 double
01191 icaltimezone_get_longitude         (icaltimezone *zone)
01192 {
01193     /* If this is a floating time, without a timezone, return 0. */
01194     if (!zone)
01195        return 0.0;
01196 
01197     /* Note that for builtin timezones this comes from zones.tab so we don't
01198        need to check the timezone is loaded here. */
01199     return zone->longitude;
01200 }
01201 
01202 
01204 icalcomponent*
01205 icaltimezone_get_component         (icaltimezone *zone)
01206 {
01207     /* If this is a floating time, without a timezone, return NULL. */
01208     if (!zone)
01209        return NULL;
01210 
01211     if (!zone->component)
01212        icaltimezone_load_builtin_timezone (zone);
01213 
01214     return zone->component;
01215 }
01216 
01217 
01221 int
01222 icaltimezone_set_component         (icaltimezone *zone,
01223                                     icalcomponent       *comp)
01224 {
01225     icaltimezone_reset (zone);
01226     return icaltimezone_get_vtimezone_properties (zone, comp);
01227 }
01228 
01229 
01230 icalarray*
01231 icaltimezone_array_new                    (void)
01232 {
01233     return icalarray_new (sizeof (icaltimezone), 16);
01234 }
01235 
01236 
01237 void
01238 icaltimezone_array_append_from_vtimezone (icalarray     *timezones,
01239                                      icalcomponent      *child)
01240 {
01241     icaltimezone zone;
01242 
01243     icaltimezone_init (&zone);
01244     if (icaltimezone_get_vtimezone_properties (&zone, child))
01245        icalarray_append (timezones, &zone);
01246 }
01247 
01248 
01249 void
01250 icaltimezone_array_free                   (icalarray    *timezones)
01251 {
01252     icaltimezone *zone;
01253     int i;
01254 
01255        if ( timezones )
01256        {
01257               for (i = 0; i < timezones->num_elements; i++) {
01258               zone = icalarray_element_at (timezones, i);
01259               icaltimezone_free (zone, 0);
01260               }
01261 
01262               icalarray_free (timezones);
01263        }
01264 }
01265 
01266 
01267 /*
01268  * BUILTIN TIMEZONE HANDLING
01269  */
01270 
01271 
01276 icalarray*
01277 icaltimezone_get_builtin_timezones (void)
01278 {
01279     if (!builtin_timezones)
01280        icaltimezone_init_builtin_timezones ();
01281 
01282     return builtin_timezones;
01283 }
01284 
01286 void
01287 icaltimezone_free_builtin_timezones(void)
01288 {
01289        icaltimezone_array_free(builtin_timezones);
01290 }
01291 
01292 
01294 icaltimezone*
01295 icaltimezone_get_builtin_timezone  (const char *location)
01296 {
01297     icaltimezone *zone;
01298     int lower, upper, middle, cmp;
01299     char *zone_location;
01300 
01301     if (!location || !location[0])
01302        return NULL;
01303 
01304     if (!strcmp (location, "UTC"))
01305        return &utc_timezone;
01306 
01307     if (!builtin_timezones)
01308        icaltimezone_init_builtin_timezones ();
01309 
01310     /* Do a simple binary search. */
01311     lower = middle = 0;
01312     upper = builtin_timezones->num_elements;
01313 
01314     while (lower < upper) {
01315        middle = (lower + upper) / 2;
01316        zone = icalarray_element_at (builtin_timezones, middle);
01317        zone_location = icaltimezone_get_location (zone);
01318        cmp = strcmp (location, zone_location);
01319        if (cmp == 0)
01320            return zone;
01321        else if (cmp < 0)
01322            upper = middle;
01323        else
01324            lower = middle + 1;
01325     }
01326 
01327     return NULL;
01328 }
01329 
01330 
01332 icaltimezone*
01333 icaltimezone_get_builtin_timezone_from_tzid (const char *tzid)
01334 {
01335     int num_slashes = 0;
01336     const char *p, *zone_tzid;
01337     icaltimezone *zone;
01338 
01339     if (!tzid || !tzid[0])
01340        return NULL;
01341 
01342     /* Check that the TZID starts with our unique prefix. */
01343     if (strncmp (tzid, TZID_PREFIX, TZID_PREFIX_LEN))
01344        return NULL;
01345 
01346     /* Get the location, which is after the 3rd '/' character. */
01347     p = tzid;
01348     for (p = tzid; *p; p++) {
01349        if (*p == '/') {
01350            num_slashes++;
01351            if (num_slashes == 3)
01352               break;
01353        }
01354     }
01355 
01356     if (num_slashes != 3)
01357        return NULL;
01358 
01359     p++;
01360 
01361     /* Now we can use the function to get the builtin timezone from the
01362        location string. */
01363     zone = icaltimezone_get_builtin_timezone (p);
01364     if (!zone)
01365        return NULL;
01366 
01367     /* Check that the builtin TZID matches exactly. We don't want to return
01368        a different version of the VTIMEZONE. */
01369     zone_tzid = icaltimezone_get_tzid (zone);
01370     if (!strcmp (zone_tzid, tzid))
01371        return zone;
01372     else
01373        return NULL;
01374 }
01375 
01376 
01378 icaltimezone*
01379 icaltimezone_get_utc_timezone             (void)
01380 {
01381     if (!builtin_timezones)
01382        icaltimezone_init_builtin_timezones ();
01383 
01384     return &utc_timezone;
01385 }
01386 
01387 
01388 
01392 static void
01393 icaltimezone_init_builtin_timezones       (void)
01394 {
01395     /* Initialize the special UTC timezone. */
01396     utc_timezone.tzid = "UTC";
01397 
01398     icaltimezone_parse_zone_tab ();
01399 }
01400 
01401 
01409 static void
01410 icaltimezone_parse_zone_tab        (void)
01411 {
01412     char *filename;
01413     FILE *fp;
01414     char buf[1024];  /* Used to store each line of zones.tab as it is read. */
01415     char location[1024]; /* Stores the city name when parsing buf. */
01416     unsigned int filename_len;
01417     int latitude_degrees, latitude_minutes, latitude_seconds;
01418     int longitude_degrees, longitude_minutes, longitude_seconds;
01419     icaltimezone zone;
01420 
01421     icalerror_assert (builtin_timezones == NULL,
01422                     "Parsing zones.tab file multiple times");
01423 
01424     builtin_timezones = icalarray_new (sizeof (icaltimezone), 32);
01425 
01426     filename_len = strlen (get_zone_directory()) + strlen (ZONES_TAB_FILENAME)
01427        + 2;
01428 
01429     filename = (char*) malloc (filename_len);
01430     if (!filename) {
01431        icalerror_set_errno(ICAL_NEWFAILED_ERROR);
01432        return;
01433     }
01434 
01435     snprintf (filename, filename_len, "%s/%s", get_zone_directory(),
01436              ZONES_TAB_FILENAME);
01437 
01438     fp = fopen (filename, "r");
01439     free (filename);
01440     if (!fp) {
01441        icalerror_set_errno(ICAL_FILE_ERROR);
01442        return;
01443     }
01444 
01445     while (fgets (buf, sizeof(buf), fp)) {
01446        if (*buf == '#') continue;
01447 
01448        /* The format of each line is: "latitude longitude location". */
01449        if (sscanf (buf, "%4d%2d%2d %4d%2d%2d %s",
01450                   &latitude_degrees, &latitude_minutes,
01451                   &latitude_seconds,
01452                   &longitude_degrees, &longitude_minutes,
01453                   &longitude_seconds,
01454                   location) != 7) {
01455            fprintf (stderr, "Invalid timezone description line: %s\n", buf);
01456            continue;
01457        }
01458 
01459        icaltimezone_init (&zone);
01460        zone.location = strdup (location);
01461 
01462        if (latitude_degrees >= 0)
01463            zone.latitude = (double) latitude_degrees
01464               + (double) latitude_minutes / 60
01465               + (double) latitude_seconds / 3600;
01466        else
01467            zone.latitude = (double) latitude_degrees
01468               - (double) latitude_minutes / 60
01469               - (double) latitude_seconds / 3600;
01470 
01471        if (longitude_degrees >= 0)
01472            zone.longitude = (double) longitude_degrees
01473               + (double) longitude_minutes / 60
01474               + (double) longitude_seconds / 3600;
01475        else
01476            zone.longitude = (double) longitude_degrees
01477               - (double) longitude_minutes / 60
01478               - (double) longitude_seconds / 3600;
01479 
01480        icalarray_append (builtin_timezones, &zone);
01481 
01482 #if 0
01483        printf ("Found zone: %s %f %f\n",
01484               location, zone.latitude, zone.longitude);
01485 #endif
01486     }
01487 
01488     fclose (fp);
01489 }
01490 
01491 
01493 static void
01494 icaltimezone_load_builtin_timezone (icaltimezone *zone)
01495 {
01496     char *filename;
01497     unsigned int filename_len;
01498     FILE *fp;
01499     icalparser *parser;
01500     icalcomponent *comp, *subcomp;
01501 
01502            /* If the location isn't set, it isn't a builtin timezone. */
01503     if (!zone->location || !zone->location[0])
01504        return;
01505 
01506     filename_len = strlen (get_zone_directory()) + strlen (zone->location) + 6;
01507 
01508     filename = (char*) malloc (filename_len);
01509     if (!filename) {
01510        icalerror_set_errno(ICAL_NEWFAILED_ERROR);
01511        return;
01512     }
01513 
01514     snprintf (filename, filename_len, "%s/%s.ics", get_zone_directory(),
01515              zone->location);
01516 
01517     fp = fopen (filename, "r");
01518     free (filename);
01519     if (!fp) {
01520        icalerror_set_errno(ICAL_FILE_ERROR);
01521        return;
01522     }
01523 
01524        
01525        /* ##### B.# Sun, 11 Nov 2001 04:04:29 +1100 
01526        this is where the MALFORMEDDATA error is being set, after the call to 'icalparser_parse'
01527        fprintf(stderr, "** WARNING ** %s: %d %s\n", __FILE__, __LINE__, icalerror_strerror(icalerrno));
01528        */
01529 
01530     parser = icalparser_new ();
01531        icalparser_set_gen_data (parser, fp);
01532        comp = icalparser_parse (parser, icaltimezone_load_get_line_fn);
01533     icalparser_free (parser);
01534        fclose (fp);
01535 
01536        
01537        
01538     /* Find the VTIMEZONE component inside the VCALENDAR. There should be 1. */
01539     subcomp = icalcomponent_get_first_component (comp,
01540                                            ICAL_VTIMEZONE_COMPONENT);
01541     if (!subcomp) {
01542        icalerror_set_errno(ICAL_PARSE_ERROR);
01543        return;
01544     }
01545 
01546     icaltimezone_get_vtimezone_properties (zone, subcomp);
01547 
01548        icalcomponent_remove_component(comp,subcomp);
01549 
01550        icalcomponent_free(comp);
01551 
01552 }
01553 
01554 
01556 static char *
01557 icaltimezone_load_get_line_fn             (char         *s,
01558                                     size_t               size,
01559                                     void         *data)
01560 {
01561     return fgets (s, size, (FILE*) data);
01562 }
01563 
01564 
01565 
01566 
01567 /*
01568  * DEBUGGING
01569  */
01570 
01586 int
01587 icaltimezone_dump_changes          (icaltimezone *zone,
01588                                     int           max_year,
01589                                     FILE         *fp)
01590 {
01591     static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
01592                            "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
01593     icaltimezonechange *zone_change;
01594     int change_num;
01595     char buffer[8];
01596 
01597     /* Make sure the changes array is expanded up to the given time. */
01598     icaltimezone_ensure_coverage (zone, max_year);
01599 
01600 #if 0
01601     printf ("Num changes: %i\n", zone->changes->num_elements);
01602 #endif
01603 
01604     change_num = 0;
01605     for (change_num = 0; change_num < zone->changes->num_elements; change_num++) {
01606        zone_change = icalarray_element_at (zone->changes, change_num);
01607 
01608        if (zone_change->year > max_year)
01609            break;
01610 
01611        fprintf (fp, "%s\t%2i %s %04i\t%2i:%02i:%02i",
01612               zone->location,
01613               zone_change->day, months[zone_change->month - 1],
01614               zone_change->year,
01615               zone_change->hour, zone_change->minute, zone_change->second);
01616 
01617        /* Wall Clock Time offset from UTC. */
01618        format_utc_offset (zone_change->utc_offset, buffer);
01619        fprintf (fp, "\t%s", buffer);
01620 
01621        fprintf (fp, "\n");
01622     }
01623        return 1;
01624 }
01625 
01626 
01629 static void
01630 format_utc_offset                  (int           utc_offset,
01631                                     char         *buffer)
01632 {
01633   char *sign = "+";
01634   int hours, minutes, seconds;
01635 
01636   if (utc_offset < 0) {
01637     utc_offset = -utc_offset;
01638     sign = "-";
01639   }
01640 
01641   hours = utc_offset / 3600;
01642   minutes = (utc_offset % 3600) / 60;
01643   seconds = utc_offset % 60;
01644 
01645   /* Sanity check. Standard timezone offsets shouldn't be much more than 12
01646      hours, and daylight saving shouldn't change it by more than a few hours.
01647      (The maximum offset is 15 hours 56 minutes at present.) */
01648   if (hours < 0 || hours >= 24 || minutes < 0 || minutes >= 60
01649       || seconds < 0 || seconds >= 60) {
01650     fprintf (stderr, "Warning: Strange timezone offset: H:%i M:%i S:%i\n",
01651             hours, minutes, seconds);
01652   }
01653 
01654   if (seconds == 0)
01655     sprintf (buffer, "%s%02i%02i", sign, hours, minutes);
01656   else
01657     sprintf (buffer, "%s%02i%02i%02i", sign, hours, minutes, seconds);
01658 }
01659 
01660 static char* get_zone_directory(void)
01661 {
01662        return zone_files_directory == NULL ? ZONEINFO_DIRECTORY : zone_files_directory;
01663 }
01664 
01665 void set_zone_directory(char *path)
01666 {
01667        zone_files_directory = malloc(strlen(path)+1);
01668        if ( zone_files_directory != NULL )
01669        {
01670               strcpy(zone_files_directory,path);
01671        }
01672 }
01673 
01674 void free_zone_directory(void)
01675 {
01676        if ( zone_files_directory != NULL )
01677        {
01678               free(zone_files_directory);
01679        }
01680 }