Exemple #1
0
void
icalarray_sort			(icalarray	*array,
				 int	       (*compare) (const void *,
							   const void *))
{
    if (array->num_elements == 0) {
        return;
    }
    
    if (array->num_elements <= array->increment_size) {
      qsort(array->chunks[0], array->num_elements, array->element_size, compare);
    } else {
      int pos;
      void *tmp = malloc (array->num_elements * array->element_size);
      if (!tmp)
          return;
      for (pos = 0; pos < array->num_elements; pos++)
          memcpy((char *) tmp + array->element_size * pos, icalarray_element_at(array, pos), array->element_size);
    
      qsort (tmp, array->num_elements, array->element_size, compare);

      for (pos = 0; pos < array->num_elements; pos++)
          memcpy(icalarray_element_at(array, pos), (char *) tmp + array->element_size * pos, array->element_size);
      free (tmp);
    }
}
/** Returns a single builtin timezone, given its Olson city name. */
icaltimezone*
icaltimezone_get_builtin_timezone	(const char *location)
{
    icaltimezone *zone;
    int lower, upper, middle, cmp;
    char *zone_location;

    if (!location || !location[0])
	return NULL;

    if (!builtin_timezones)
	icaltimezone_init_builtin_timezones ();

    if (!strcmp (location, "UTC"))
	return &utc_timezone;

    /* Do a simple binary search. */
    lower = middle = 0;
    upper = builtin_timezones->num_elements;

    while (lower < upper) {
	middle = (lower + upper) / 2;
	zone = icalarray_element_at (builtin_timezones, middle);
	zone_location = icaltimezone_get_location (zone);
	cmp = strcmp (location, zone_location);
	if (cmp == 0)
	    return zone;
	else if (cmp < 0)
	    upper = middle;
	else
	    lower = middle + 1;
    }

    return NULL;
}
/** Returns the index of a timezone change which is close to the time
   given in change. */
static int
icaltimezone_find_nearby_change		(icaltimezone		*zone,
					 icaltimezonechange	*change)
{
    icaltimezonechange *zone_change;
    int lower, upper, middle, cmp;
					 
    /* Do a simple binary search. */
    lower = middle = 0;
    upper = zone->changes->num_elements;

    while (lower < upper) {
	middle = (lower + upper) / 2;
	zone_change = icalarray_element_at (zone->changes, middle);
	cmp = icaltimezone_compare_change_fn (change, zone_change);
	if (cmp == 0)
	    break;
	else if (cmp < 0)
	    upper = middle;
	else
	    lower = middle + 1;
    }

    return middle;
}
Exemple #4
0
void
icalarray_remove_element_at	(icalarray	*array,
				 int		 position)
{
    void *dest;
    int elements_to_move;

    assert (position >= 0);
    assert ((unsigned int)position < array->num_elements);

    while (position < array->num_elements - 1) {
      memmove(icalarray_element_at(array, position),
              icalarray_element_at(array, position + 1),
              array->element_size);
      position++;
    }

    array->num_elements--;
}
Exemple #5
0
void
icalarray_append		(icalarray	*array,
				 const void		*element)
{
    int pos;
    if (array->num_elements >= array->space_allocated)
	icalarray_expand (array, 1);

    pos = array->num_elements++;
    memcpy (icalarray_element_at(array, pos), element, array->element_size);
}
void
icaltimezone_array_free			(icalarray	*timezones)
{
    icaltimezone *zone;
    int i;

	if ( timezones )
	{
		for (i = 0; i < timezones->num_elements; i++) {
		zone = icalarray_element_at (timezones, i);
		icaltimezone_free (zone, 0);
		}

		icalarray_free (timezones);
	}
}
/**
 * This outputs a list of timezone changes for the given timezone to the
 * given file, up to the maximum year given. We compare this output with the
 * output from 'vzic --dump-changes' to make sure that we are consistent.
 * (vzic is the Olson timezone database to VTIMEZONE converter.)
 * 
 * The output format is:
 *
 *	Zone-Name [tab] Date [tab] Time [tab] UTC-Offset
 *
 * The Date and Time fields specify the time change in UTC.
 *
 * The UTC Offset is for local (wall-clock) time. It is the amount of time
 * to add to UTC to get local time.
 */
int
icaltimezone_dump_changes		(icaltimezone	*zone,
					 int		 max_year,
					 FILE		*fp)
{
    static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
			      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
    icaltimezonechange *zone_change;
    int change_num;
    char buffer[8];

    /* Make sure the changes array is expanded up to the given time. */
    icaltimezone_ensure_coverage (zone, max_year);

#if 0
    printf ("Num changes: %i\n", zone->changes->num_elements);
#endif

    change_num = 0;
    for (change_num = 0; change_num < zone->changes->num_elements; change_num++) {
	zone_change = icalarray_element_at (zone->changes, change_num);

	if (zone_change->year > max_year)
	    break;

	fprintf (fp, "%s\t%2i %s %04i\t%2i:%02i:%02i",
		zone->location,
		zone_change->day, months[zone_change->month - 1],
		zone_change->year,
		zone_change->hour, zone_change->minute, zone_change->second);

	/* Wall Clock Time offset from UTC. */
	format_utc_offset (zone_change->utc_offset, buffer);
	fprintf (fp, "\t%s", buffer);

	fprintf (fp, "\n");
    }
	return 1;
}
Exemple #8
0
static void
test_libical_timezones_compatibility (gconstpointer user_data)
{
	gboolean retval = TRUE;
	gint i;

	for (i = 0; i < builtin_timezones->num_elements; i++) {
		icaltimezone *zone;
		const gchar *zone_location;

		zone = icalarray_element_at (builtin_timezones, i);
		zone_location = icaltimezone_get_location (zone);

		if (ical_to_msdn_equivalent (zone_location) == NULL) {
			if (!is_a_known_unknown_timezone (zone_location)) {
				retval = FALSE;
				g_printerr ("\nMissing ical_tz_location: %s\n", zone_location);
			}
		}
	}

	g_assert (retval == TRUE);
}
int main()
{
    icalarray *builtin_timezones;
    icaltimetype tt;
    int dd, hh, zz, tried = 0;
    long zz2 = -1;

    set_zone_directory("../../zoneinfo");
    icaltimezone_set_tzid_prefix("/softwarestudio.org/");

    tt = icaltime_current_time_with_zone(icaltimezone_get_builtin_timezone("America/New_York"));

    tt.year = 2038;
    (void)icaltime_as_timet_with_zone(tt, icaltimezone_get_builtin_timezone("PST"));
    tried++;

    tt.year = 2050;
    (void)icaltime_as_timet_with_zone(tt, icaltimezone_get_builtin_timezone("PST"));
    tried++;

    tt.year = 1958;
    (void)icaltime_as_timet_with_zone(tt, icaltimezone_get_builtin_timezone("PST"));
    tried++;

    builtin_timezones = icaltimezone_get_builtin_timezones();
    printf("got %lu zones\n", (unsigned long)builtin_timezones->num_elements);
    if (builtin_timezones->num_elements == 0) {
      printf("YIKES. Try running from the build/bin directory\n");
      return(1);
    }

    for (zz = -1; zz < (int)builtin_timezones->num_elements; zz++) {
        icaltimezone *zone;

        if (zz < 0) {
            zone = icaltimezone_get_utc_timezone();
        } else {
            zone = icalarray_element_at(builtin_timezones, (size_t)zz);
        }

        tt = icaltime_current_time_with_zone(zone);

        for (dd = 0; dd < 370; dd += 17) {
            for (hh = 0; hh < 60 * 60 * 24; hh += 567) {
                int zz2cnt;

                icaltime_adjust(&tt, 0, 0, 0, 1);

                for (zz2cnt = 0; zz2cnt < 15; zz2cnt++) {
                    icaltimezone *zone2;

                    if (zz2 < 0) {
                        zone2 = icaltimezone_get_utc_timezone();
                    } else {
                        zone2 = icalarray_element_at(builtin_timezones, (size_t)zz2);
                    }

                    (void)icaltime_as_timet_with_zone(tt, zone2);
                    tried++;

                    zz2++;
                    if (zz2 >= (long)builtin_timezones->num_elements)
                        zz2 = -1;
                }
            }
        }

        printf("\r%lu %% done",
               (zz >= 0 ? zz : 0) * 100 / (unsigned long)builtin_timezones->num_elements);
        fflush(stdout);
    }

    printf("\ntried %d times\n", tried);
    return 0;
}
Exemple #10
0
static void
test_time_zones_sync (gconstpointer user_data)
{
	gboolean retval = FALSE;
	gint i;
	GError *error = NULL;
	UhmServer *local_server;
	EwsTestData *etd = (gpointer) user_data;
	EwsCalendarConvertData convert_data;
	EwsFolderId *calendar_fid = NULL;
	gboolean includes_last_folder = FALSE;
	gchar *old_sync_state = NULL;
	gchar **tokens;
	GSList *zone_location_errors = NULL;

	local_server = ews_test_get_mock_server ();

	ews_test_server_set_trace_directory (local_server, etd->version, "calendar/timezones");
	ews_test_server_start_trace (local_server, etd, "get_server_time_zones_sync", &error);
	if (error != NULL) {
		g_printerr ("\n%s\n", error->message);
		goto exit;
	}

	while (!includes_last_folder) {
		GSList *folders_created = NULL;
		GSList *folders_updated = NULL;
		GSList *folders_deleted = NULL;
		GSList *l;
		gchar *new_sync_state = NULL;
		gboolean found = FALSE;

		old_sync_state = new_sync_state;

		e_ews_connection_sync_folder_hierarchy_sync (
			etd->connection,
			EWS_PRIORITY_MEDIUM,
			old_sync_state,
			&new_sync_state,
			&includes_last_folder,
			&folders_created,
			&folders_updated,
			&folders_deleted,
			NULL,
			&error);

		if (error != NULL) {
			g_free (old_sync_state);
			g_printerr ("\n%s\n", error->message);
			goto exit;
		}

		for (l = folders_created; l != NULL; l = l->next) {
			EEwsFolder *folder = l->data;

			if (e_ews_folder_get_folder_type (folder) == E_EWS_FOLDER_TYPE_CALENDAR) {
				const EwsFolderId *fid;

				fid = e_ews_folder_get_id (folder);

				calendar_fid = g_new0 (EwsFolderId, 1);
				calendar_fid->id = g_strdup (fid->id);
				calendar_fid->change_key = g_strdup (fid->change_key);

				found = TRUE;
				break;
			}
		}

		g_slist_free_full (folders_created, g_object_unref);
		g_slist_free_full (folders_updated, g_object_unref);
		g_slist_free_full (folders_deleted, g_free);

		g_free (old_sync_state);
		old_sync_state = NULL;

		if (found) {
			g_free (new_sync_state);
			break;
		}
	}

	if (!calendar_fid) {
		g_printerr ("No calendar folder found\n");
		goto exit;
	}

	convert_data.connection = etd->connection;
	convert_data.default_zone = icaltimezone_get_utc_timezone ();

	tokens = g_strsplit (str_comp, "ICAL_TIMEZONE", 0);

	for (i = 0; i < builtin_timezones->num_elements; i++) {
		GSList *ll;
		GSList *ids = NULL;
		icaltimezone *zone;
		ECalComponent *comp;
		const gchar *zone_location;
		gchar *str;

		zone = icalarray_element_at (builtin_timezones, i);
		zone_location = icaltimezone_get_location (zone);

		if (is_a_known_unknown_timezone (zone_location))
			continue;

		str = g_strdup_printf ("%s%s%s%s%s", tokens[0], zone_location, tokens[1], zone_location, tokens[2]);
		comp = e_cal_component_new_from_string (str);
		g_free (str);

		convert_data.icalcomp = e_cal_component_get_icalcomponent (comp);

		e_ews_connection_create_items_sync (
			etd->connection,
			EWS_PRIORITY_MEDIUM,
			"SaveOnly",
			"SendToNone",
			calendar_fid,
			convert_calcomp_to_xml,
			&convert_data,
			&ids,
			NULL,
			&error);

		g_object_unref (comp);

		if (error != NULL) {
			g_printerr ("\n%s\n", error->message);
			g_clear_error (&error);

			zone_location_errors = g_slist_append (zone_location_errors, g_strdup (zone_location));
			continue;
		}

		for (ll = ids; ll != NULL; ll = ll->next) {
			EEwsItem *item = ll->data;

			if (e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) {
				const GError *item_error = e_ews_item_get_error (item);
				g_printerr ("\n%s\n", item_error->message);
				g_clear_error (&error);

				zone_location_errors = g_slist_append (zone_location_errors, g_strdup (zone_location));
				continue;
			}
		}

		g_slist_free_full (ids, g_object_unref);
	}

	retval = zone_location_errors == NULL;

 exit:
	if (zone_location_errors != NULL) {
		GSList *l;

		g_printerr ("Errors found in: \n");
		for (l = zone_location_errors; l != NULL; l = l->next)
			g_printerr (" - %s\n", (gchar *) l->data);

		g_slist_free_full (zone_location_errors, g_free);
	}

	uhm_server_end_trace (local_server);
	g_clear_error (&error);

	g_assert (retval == TRUE);
}
Exemple #11
0
int main()
{
    icalarray *timezones;
    icaltimezone *zone, *utc_zone;
    char *zone_location;
    size_t i;
    int ret = 0;
    unsigned int total_failed = 0;
    unsigned int total_okay = 0;
    unsigned int percent_failed = 0;
    int verbose = 0;

    int day;
    time_t start_time;
    struct tm start_tm;
    time_t curr_time;
    struct tm curr_tm;
    struct icaltimetype curr_tt;
    int failed = 0;
    int curr_failed;
    int zonedef_printed = 0;
#if !defined(HAVE_SETENV)
    static char new_tz[256];
#endif

    set_zone_directory("../../zoneinfo");
    icaltimezone_set_tzid_prefix("/softwarestudio.org/");

    timezones = icaltimezone_get_builtin_timezones();
    utc_zone = icaltimezone_get_utc_timezone();

    /* for all known time zones... */
    for (i = 0; i < timezones->num_elements; i++) {
        zone = (icaltimezone *)icalarray_element_at(timezones, i);
        zone_location = (char *)icaltimezone_get_location(zone);
        if (!zone_location)
            continue;

        /*
         * select this location for glibc: needs support for TZ=<location>
         * which is not POSIX
         */
#if defined(HAVE_SETENV)
        setenv("TZ", zone_location, 1);
#else
        new_tz[0] = '\0';
        strncat(new_tz, "TZ=", 255);
        strncat(new_tz, zone_location, 255 - strlen(new_tz));
        putenv(new_tz);
#endif
        tzset();
        /*
         * determine current local time and date: always use midday in
         * the current zone and first day of first month in the year
         */
        start_time = time(NULL);
        localtime_r(&start_time, &start_tm);
        start_tm.tm_hour = 12;
        start_tm.tm_min = 0;
        start_tm.tm_sec = 0;
        start_tm.tm_mday = 1;
        start_tm.tm_mon = 0;
        start_time = mktime(&start_tm);

        /* check time conversion for the next 365 days */
        for (day = 0, curr_time = start_time; day < 365; day++, curr_time += 24 * 60 * 60) {
            /* determine date/time with glibc */
            localtime_r(&curr_time, &curr_tm);
            /* determine date/time with libical */
            curr_tt = icaltime_from_timet_with_zone(curr_time, 0, utc_zone);
            curr_tt.zone = utc_zone;    /* workaround: icaltime_from_timet_with_zone()
                                           should do this, but doesn't! */
            curr_tt = icaltime_convert_to_zone(curr_tt, zone);

            /* compare... */
            curr_failed =
                curr_tm.tm_year + 1900 != curr_tt.year ||
                curr_tm.tm_mon + 1 != curr_tt.month ||
                curr_tm.tm_mday != curr_tt.day ||
                curr_tm.tm_hour != curr_tt.hour ||
                curr_tm.tm_min != curr_tt.minute ||
                curr_tm.tm_sec != curr_tt.second;

            /* only print first failed day and first day which is okay again */
            if (verbose || curr_failed != failed) {
                struct tm utc_tm;

                if (!gmtime_r(&curr_time, &utc_tm))
                    memset(&utc_tm, 0, sizeof(utc_tm));

                printf(
                    "%s: day %03d: %s: %04d-%02d-%02d %02d:%02d:%02d UTC = "
                    "libc %04d-%02d-%02d %02d:%02d:%02d dst %d",
                    zone_location, day,
                    verbose ? (curr_failed ? "failed" : "okay") : (curr_failed ? "first failed" :
                                                                   "okay again"),
                    utc_tm.tm_year + 1900, utc_tm.tm_mon + 1, utc_tm.tm_mday, utc_tm.tm_hour,
                    utc_tm.tm_min, utc_tm.tm_sec, curr_tm.tm_year + 1900, curr_tm.tm_mon + 1,
                    curr_tm.tm_mday, curr_tm.tm_hour, curr_tm.tm_min, curr_tm.tm_sec,
                    curr_tm.tm_isdst);
                if (curr_failed) {
                    printf(" != libical %04d-%02d-%02d %02d:%02d:%02d dst %d",
                        curr_tt.year,
                        curr_tt.month,
                        curr_tt.day,
                        curr_tt.hour,
                        curr_tt.minute,
                        curr_tt.second,
                        curr_tt.is_daylight);
                    ret = 1;
                }
                printf("\n");
                failed = curr_failed;

                if (!zonedef_printed) {
                    icalcomponent *comp = icaltimezone_get_component(zone);

                    if (comp) {
                        printf("%s\n", icalcomponent_as_ical_string(comp));
                    }
                    zonedef_printed = 1;
                }
            }

            if (curr_failed) {
                total_failed++;
            } else {
                total_okay++;
            }
        }
    }

    if (total_failed || total_okay) {
        percent_failed = total_failed * 100 / (total_failed + total_okay);
        printf(" *** Summary: %lu zones tested, %u days failed, %u okay => %u%% failed ***\n",
               (unsigned long)timezones->num_elements, total_failed, total_okay, percent_failed);

        if (!icaltzutil_get_exact_vtimezones_support()) {
            if (!percent_failed) {
                ret = 0;
                printf(" *** Expect some small error rate with inter-operable vtimezones *** \n");
            }
       }
    }

    icaltimezone_free_builtin_timezones();
    return ret;
}
static gchar *
system_timezone_find (void)
{
	GHashTable *ical_zones;
	icalarray *builtin_timezones;
	gint ii;
	gchar *tz, *config_tz = NULL;

	/* return only timezones known to libical */
	ical_zones = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
	g_hash_table_insert (ical_zones, g_strdup ("UTC"), GINT_TO_POINTER (1));

	builtin_timezones = icaltimezone_get_builtin_timezones ();
	for (ii = 0; ii < builtin_timezones->num_elements; ii++) {
		icaltimezone *zone;
		const gchar *location;

		zone = icalarray_element_at (builtin_timezones, ii);
		if (!zone)
			continue;

		location = icaltimezone_get_location (zone);
		if (location)
			g_hash_table_insert (ical_zones, g_strdup (location), GINT_TO_POINTER (1));
	}

	/* softlink is the best option, it points to the correct file */
	tz = system_timezone_read_etc_localtime_softlink (ical_zones);
	if (system_timezone_is_valid (tz, ical_zones)) {
		g_hash_table_destroy (ical_zones);
		return tz;
	}

	g_free (tz);

	config_tz = NULL;

	/* read correct timezone name from config file; checking
	 * on /etc/localtime content can pick wrong timezone name,
	 * even the file is same
	 */
	for (ii = 0; get_system_timezone_methods_config[ii] != NULL; ii++) {
		config_tz = get_system_timezone_methods_config[ii] (ical_zones);

		if (system_timezone_is_valid (config_tz, ical_zones)) {
			break;
		}

		g_free (config_tz);
		config_tz = NULL;
	}

	if (config_tz) {
		struct stat stat_localtime;
		gchar *localtime_content = NULL;
		gsize localtime_content_len = -1;

		if (g_stat (ETC_LOCALTIME, &stat_localtime) == 0 &&
		    S_ISREG (stat_localtime.st_mode) &&
		    g_file_get_contents (ETC_LOCALTIME,
					 &localtime_content,
					 &localtime_content_len,
					 NULL)) {
			struct stat stat_config_tz;
			gchar *filename = g_build_filename (SYSTEM_ZONEINFODIR, config_tz, NULL);

			if (filename &&
			    g_stat (filename, &stat_config_tz) == 0 &&
			    files_are_identical_content (&stat_localtime,
				&stat_config_tz,
				localtime_content,
				localtime_content_len,
				filename)) {
				g_free (filename);
				g_free (localtime_content);
				g_hash_table_destroy (ical_zones);

				/* corresponding file name to config_tz matches /etc/localtime,
				 * thus that's the correct one - return it as system timezone;
				 * bonus is that it might match configured system timezone name too
				*/
				return config_tz;
			}
			g_free (filename);
		}

		g_free (localtime_content);
	}

	for (ii = 0; get_system_timezone_methods_slow[ii] != NULL; ii++) {
		tz = get_system_timezone_methods_slow[ii] (ical_zones);

		if (system_timezone_is_valid (tz, ical_zones)) {
			g_hash_table_destroy (ical_zones);
			g_free (config_tz);
			return tz;
		}

		g_free (tz);
	}

	g_hash_table_destroy (ical_zones);

	return config_tz;
}
Exemple #13
0
int main(int argc, char* argv[])
{
  icalarray *zones;
  icaltimezone *zone;
  char *zone_directory, *zone_subdirectory, *zone_filename, *location;
  char output_directory[PATHNAME_BUFFER_SIZE];
  char filename[PATHNAME_BUFFER_SIZE];
  FILE *fp;
  int i;
  int skipping = TRUE;

  /*
   * Command-Line Option Parsing.
   */
  for (i = 1; i < argc; i++) {
    /* --dump-changes: Dumps a list of times when each timezone changed,
       and the new local time offset from UTC. */
    if (!strcmp (argv[i], "--dump-changes"))
      VzicDumpChanges = TRUE;

    else
      usage ();
  }


  zones = icaltimezone_get_builtin_timezones ();

  ensure_directory_exists (directory);

  for (i = 0; i < zones->num_elements; i++) {
    zone = icalarray_element_at (zones, i);

    location = icaltimezone_get_location (zone);

#if 0
    /* Use this to start at a certain zone. */
    if (skipping && strcmp (location, "America/Boise"))
      continue;
#endif

    skipping = FALSE;

    /* Use this to only output data for certain timezones. */
#if 0
    if (strcmp (location, "America/Cancun")
	&& strcmp (location, "Asia/Baku")
	&& strcmp (location, "Asia/Nicosia")
	&& strcmp (location, "Asia/Novosibirsk")
	&& strcmp (location, "Asia/Samarkand")
	&& strcmp (location, "Asia/Tashkent")
	&& strcmp (location, "Asia/Tbilisi")
	&& strcmp (location, "Asia/Yerevan")
	&& strcmp (location, "Australia/Broken_Hill")
	&& strcmp (location, "Europe/Simferopol")
	&& strcmp (location, "Europe/Tallinn")
	&& strcmp (location, "Europe/Zaporozhye")
	)
      continue;
#endif

#if 0
    printf ("%s\n", location);
#endif

    parse_zone_name (location, &zone_directory, &zone_subdirectory,
		     &zone_filename);

    sprintf (output_directory, "%s/%s", directory, zone_directory);
    ensure_directory_exists (output_directory);
    sprintf (filename, "%s/%s", output_directory, zone_filename);

    if (zone_subdirectory) {
      sprintf (output_directory, "%s/%s/%s", directory, zone_directory,
	       zone_subdirectory);
      ensure_directory_exists (output_directory);
      sprintf (filename, "%s/%s", output_directory, zone_filename);
    }

    fp = fopen (filename, "w");
    if (!fp) {
      fprintf (stderr, "Couldn't create file: %s\n", filename);
      exit (1);
    }

    /* We can run 2 different tests - output all changes for each zone, or
       test against mktime()/localtime(). Should have a command-line option
       or something. */
    if (VzicDumpChanges)
      icaltimezone_dump_changes (zone, CHANGES_MAX_YEAR, fp);
    else
      dump_local_times (zone, fp);

    if (ferror (fp)) {
      fprintf (stderr, "Error writing file: %s\n", filename);
      exit (1);
    }

    fclose (fp);
  }

  return 0;
}
/** Calculates the UTC offset of a given UTC time in the given
   timezone.  It is the number of seconds to add to UTC to get local
   time.  The is_daylight flag is set to 1 if the time is in
   daylight-savings time. */
int
icaltimezone_get_utc_offset_of_utc_time	(icaltimezone	*zone,
					 struct icaltimetype	*tt,
					 int		*is_daylight)
{
    icaltimezonechange *zone_change, tt_change, tmp_change;
    int change_num, step, change_num_to_use;

    if (is_daylight)
	*is_daylight = 0;

    /* For local times and UTC return 0. */
    if (zone == NULL || zone == &utc_timezone)
	return 0;

    /* Use the builtin icaltimezone if possible. */
    if (zone->builtin_timezone)
	zone = zone->builtin_timezone;

    /* Make sure the changes array is expanded up to the given time. */
    icaltimezone_ensure_coverage (zone, tt->year);

    if (!zone->changes || zone->changes->num_elements == 0)
	return 0;

    /* Copy the time parts of the icaltimetype to an icaltimezonechange so we
       can use our comparison function on it. */
    tt_change.year   = tt->year;
    tt_change.month  = tt->month;
    tt_change.day    = tt->day;
    tt_change.hour   = tt->hour;
    tt_change.minute = tt->minute;
    tt_change.second = tt->second;

    /* This should find a change close to the time, either the change before
       it or the change after it. */
    change_num = icaltimezone_find_nearby_change (zone, &tt_change);

    /* Sanity check. */
    icalerror_assert (change_num >= 0,
		      "Negative timezone change index");
    icalerror_assert (change_num < zone->changes->num_elements,
		      "Timezone change index out of bounds");

    /* Now move backwards or forwards to find the timezone change that applies
       to tt. It should only have to do 1 or 2 steps. */
    zone_change = icalarray_element_at (zone->changes, change_num);
    step = 1;
    change_num_to_use = -1;
    for (;;) {
	/* Copy the change and adjust it to UTC. */
	tmp_change = *zone_change;

	/* If the given time is on or after this change, then this change may
	   apply, but we continue as a later change may be the right one.
	   If the given time is before this change, then if we have already
	   found a change which applies we can use that, else we need to step
	   backwards. */
	if (icaltimezone_compare_change_fn (&tt_change, &tmp_change) >= 0)
	    change_num_to_use = change_num;
	else
	    step = -1;

	/* If we are stepping backwards through the changes and we have found
	   a change that applies, then we know this is the change to use so
	   we exit the loop. */
	if (step == -1 && change_num_to_use != -1)
	    break;

	change_num += step;

	/* If we go past the start of the changes array, then we have no data
	   for this time so we return a UTC offset of 0. */
	if (change_num < 0)
	    return 0;

	if (change_num >= zone->changes->num_elements)
	    break;

	zone_change = icalarray_element_at (zone->changes, change_num);
    }

    /* If we didn't find a change to use, then we have a bug! */
    icalerror_assert (change_num_to_use != -1,
		      "No applicable timezone change found");

    /* Now we know exactly which timezone change applies to the time, so
       we can return the UTC offset and whether it is a daylight time. */
    zone_change = icalarray_element_at (zone->changes, change_num_to_use);
    if (is_daylight)
	*is_daylight = zone_change->is_daylight;

    return zone_change->utc_offset;
}
/* Calculates the UTC offset of a given local time in the given
   timezone.  It is the number of seconds to add to UTC to get local
   time.  The is_daylight flag is set to 1 if the time is in
   daylight-savings time. */
int
icaltimezone_get_utc_offset		(icaltimezone	*zone,
					 struct icaltimetype	*tt,
					 int		*is_daylight)
{
    icaltimezonechange *zone_change, *prev_zone_change, tt_change, tmp_change;
    int change_num, step, utc_offset_change, cmp;
    int change_num_to_use;
    int want_daylight;

    if (tt == NULL)
	return 0;

    if (is_daylight)
	*is_daylight = 0;

    /* For local times and UTC return 0. */
    if (zone == NULL || zone == &utc_timezone)
	return 0;

    /* Use the builtin icaltimezone if possible. */
    if (zone->builtin_timezone)
	zone = zone->builtin_timezone;

    /* Make sure the changes array is expanded up to the given time. */
    icaltimezone_ensure_coverage (zone, tt->year);

    if (!zone->changes || zone->changes->num_elements == 0)
	return 0;

    /* Copy the time parts of the icaltimetype to an icaltimezonechange so we
       can use our comparison function on it. */
    tt_change.year   = tt->year;
    tt_change.month  = tt->month;
    tt_change.day    = tt->day;
    tt_change.hour   = tt->hour;
    tt_change.minute = tt->minute;
    tt_change.second = tt->second;

    /* This should find a change close to the time, either the change before
       it or the change after it. */
    change_num = icaltimezone_find_nearby_change (zone, &tt_change);

    /* Sanity check. */
    icalerror_assert (change_num >= 0,
		      "Negative timezone change index");
    icalerror_assert (change_num < zone->changes->num_elements,
		      "Timezone change index out of bounds");

    /* Now move backwards or forwards to find the timezone change that applies
       to tt. It should only have to do 1 or 2 steps. */
    zone_change = icalarray_element_at (zone->changes, change_num);
    step = 1;
    change_num_to_use = -1;
    for (;;) {
	/* Copy the change, so we can adjust it. */
	tmp_change = *zone_change;

	/* If the clock is going backward, check if it is in the region of time
	   that is used twice. If it is, use the change with the daylight
	   setting which matches tt, or use standard if we don't know. */
	if (tmp_change.utc_offset < tmp_change.prev_utc_offset) {
	    /* If the time change is at 2:00AM local time and the clock is
	       going back to 1:00AM we adjust the change to 1:00AM. We may
	       have the wrong change but we'll figure that out later. */
	    icaltimezone_adjust_change (&tmp_change, 0, 0, 0,
					tmp_change.utc_offset);
	} else {
	    icaltimezone_adjust_change (&tmp_change, 0, 0, 0,
					tmp_change.prev_utc_offset);
	}

	cmp = icaltimezone_compare_change_fn (&tt_change, &tmp_change);

	/* If the given time is on or after this change, then this change may
	   apply, but we continue as a later change may be the right one.
	   If the given time is before this change, then if we have already
	   found a change which applies we can use that, else we need to step
	   backwards. */
	if (cmp >= 0)
	    change_num_to_use = change_num;
	else
	    step = -1;

	/* If we are stepping backwards through the changes and we have found
	   a change that applies, then we know this is the change to use so
	   we exit the loop. */
	if (step == -1 && change_num_to_use != -1)
	    break;

	change_num += step;

	/* If we go past the start of the changes array, then we have no data
	   for this time so we return a UTC offset of 0. */
	if (change_num < 0)
	    return 0;

	if (change_num >= zone->changes->num_elements)
	    break;

	zone_change = icalarray_element_at (zone->changes, change_num);
    }

    /* If we didn't find a change to use, then we have a bug! */
    icalerror_assert (change_num_to_use != -1,
		      "No applicable timezone change found");

    /* Now we just need to check if the time is in the overlapped region of
       time when clocks go back. */
    zone_change = icalarray_element_at (zone->changes, change_num_to_use);

    utc_offset_change = zone_change->utc_offset - zone_change->prev_utc_offset;
    if (utc_offset_change < 0 && change_num_to_use > 0) {
	tmp_change = *zone_change;
	icaltimezone_adjust_change (&tmp_change, 0, 0, 0,
				    tmp_change.prev_utc_offset);

	if (icaltimezone_compare_change_fn (&tt_change, &tmp_change) < 0) {
	    /* The time is in the overlapped region, so we may need to use
	       either the current zone_change or the previous one. If the
	       time has the is_daylight field set we use the matching change,
	       else we use the change with standard time. */
	    prev_zone_change = icalarray_element_at (zone->changes,
						     change_num_to_use - 1);

	    /* I was going to add an is_daylight flag to struct icaltimetype,
	       but iCalendar doesn't let us distinguish between standard and
	       daylight time anyway, so there's no point. So we just use the
	       standard time instead. */
	    want_daylight = (tt->is_daylight == 1) ? 1 : 0;

#if 0
	    if (zone_change->is_daylight == prev_zone_change->is_daylight)
		printf (" **** Same is_daylight setting\n");
#endif

	    if (zone_change->is_daylight != want_daylight
		&& prev_zone_change->is_daylight == want_daylight)
		zone_change = prev_zone_change;
	}
    }

    /* Now we know exactly which timezone change applies to the time, so
       we can return the UTC offset and whether it is a daylight time. */
    if (is_daylight)
	*is_daylight = zone_change->is_daylight;
    return zone_change->utc_offset;
}