20#include "icaltimezone_p.h"
21#include "icaltimezoneimpl.h"
23#include "icalerror_p.h"
37#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
39#if defined(PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP)
42static pthread_mutex_t builtin_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
44static pthread_mutex_t builtin_mutex = PTHREAD_MUTEX_INITIALIZER;
47static pthread_mutex_t changes_mutex = PTHREAD_MUTEX_INITIALIZER;
51#if !defined(_WIN32_WCE)
58#define ZONEINFO_DIRECTORY PACKAGE_DATA_DIR "/zoneinfo"
63#define BUILTIN_TZID_PREFIX_LEN 256
71#define BUILTIN_TZID_PREFIX "/freeassociation.sourceforge.net/"
74static const struct _compat_tzids {
77} glob_compat_tzids[] = {
78 {
"/freeassociation.sourceforge.net/Tzfile/", 3},
79 {
"/freeassociation.sourceforge.net/", 2},
84static ICAL_GLOBAL_VAR
char s_zoneinfopath[MAXPATHLEN] = {0};
87static const char *s_zoneinfo_search_paths[] = {
88 "/usr/share/zoneinfo",
91 "/usr/share/lib/zoneinfo"};
100#define ZONES_TAB_FILENAME "zones.tab"
106#define ICALTIMEZONE_EXTRA_COVERAGE 5
108#if (SIZEOF_ICALTIME_T > 4)
111#define ICALTIMEZONE_MAX_YEAR 2582
115#define ICALTIMEZONE_MAX_YEAR 2037
118typedef struct _icaltimezonechange icaltimezonechange;
120struct _icaltimezonechange {
142static ICAL_GLOBAL_VAR icalarray *builtin_timezones = NULL;
145static ICAL_GLOBAL_VAR
icaltimezone utc_timezone = {0, 0, 0, 0, 0, 0, 0, 0, 0};
147static ICAL_GLOBAL_VAR
char *zone_files_directory = NULL;
149#if defined(USE_BUILTIN_TZDATA)
150static ICAL_GLOBAL_VAR
int use_builtin_tzdata =
true;
152static ICAL_GLOBAL_VAR
int use_builtin_tzdata =
false;
156static void icaltimezone_expand_changes(
icaltimezone *zone,
int end_year);
157static int icaltimezone_compare_change_fn(
const void *elem1,
const void *elem2);
159static size_t icaltimezone_find_nearby_change(
icaltimezone *zone,
const icaltimezonechange *change);
161static void icaltimezone_adjust_change(icaltimezonechange *tt,
162 int days,
int hours,
int minutes,
int seconds);
172static bool icaltimezone_get_vtimezone_properties(
icaltimezone *zone, icalcomponent *component)
173#if defined(THREAD_SANITIZER)
174 __attribute__((no_sanitize(
"thread")))
178static void icaltimezone_load_builtin_timezone(
icaltimezone *zone)
179#if defined(THREAD_SANITIZER)
180 __attribute__((no_sanitize(
"thread")))
184static void icaltimezone_ensure_coverage(
icaltimezone *zone,
int end_year);
186static void icaltimezone_init_builtin_timezones(
void);
188static void icaltimezone_parse_zone_tab(
void);
190static char *icaltimezone_load_get_line_fn(
char *s,
size_t size,
void *data);
192static void format_utc_offset(
int utc_offset,
char *buffer,
size_t buffer_size);
193static const char *get_zone_directory_builtin(
void);
195static void icaltimezone_builtin_lock(
void)
197#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
198 pthread_mutex_lock(&builtin_mutex);
202static void icaltimezone_builtin_unlock(
void)
204#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
205 pthread_mutex_unlock(&builtin_mutex);
209static void icaltimezone_changes_lock(
void)
211#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
212 pthread_mutex_lock(&changes_mutex);
216static void icaltimezone_changes_unlock(
void)
218#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
219 pthread_mutex_unlock(&changes_mutex);
225 return s_ical_tzid_prefix;
238 icaltimezone_init(zone);
254 if (zone->tzid != NULL) {
257 if (zone->location != NULL) {
260 if (zone->tznames != NULL) {
264 icaltimezone_changes_lock();
265 if (zone->changes != NULL) {
268 icaltimezone_changes_unlock();
272 zone->component = NULL;
279 icaltimezone_reset(zone);
293 if (zone->component) {
300 zone->changes = NULL;
304 icaltimezone_init(zone);
313 zone->location = NULL;
314 zone->tznames = NULL;
315 zone->latitude = 0.0;
316 zone->longitude = 0.0;
317 zone->component = NULL;
318 zone->builtin_timezone = NULL;
320 zone->changes = NULL;
332static bool icaltimezone_get_vtimezone_properties(
icaltimezone *zone, icalcomponent *component)
343 tzid = icalproperty_get_tzid(prop);
351 if (zone->component) {
354 zone->component = component;
368 const char *location;
372 location = icalproperty_get_location(prop);
381 if (name && !strcasecmp(name,
"X-LIC-LOCATION")) {
382 location = icalproperty_get_x(prop);
398 const char *current_tzname;
399 const char *standard_tzname = NULL, *daylight_tzname = NULL;
400 struct icaltimetype standard_max_date, daylight_max_date;
411 current_tzname = NULL;
418 case ICAL_TZNAME_PROPERTY:
419 current_tzname = icalproperty_get_tzname(prop);
422 case ICAL_DTSTART_PROPERTY:
423 dtstart = icalproperty_get_dtstart(prop);
425 current_max_date = dtstart;
430 case ICAL_RDATE_PROPERTY:
431 rdate = icalproperty_get_rdate(prop);
433 current_max_date = rdate.time;
445 if (current_tzname) {
447 if (!standard_tzname ||
449 standard_max_date = current_max_date;
450 standard_tzname = current_tzname;
453 if (!daylight_tzname ||
455 daylight_max_date = current_max_date;
456 daylight_tzname = current_tzname;
467 if (standard_tzname && !strcmp(standard_tzname,
"Standard Time")) {
473 if (standard_tzname && daylight_tzname) {
474 size_t standard_len, daylight_len;
477 if (!strcmp(standard_tzname, daylight_tzname)) {
481 standard_len = strlen(standard_tzname);
482 daylight_len = strlen(daylight_tzname);
483 const size_t len_tznames = standard_len + daylight_len + 2;
485 strncpy(tznames, standard_tzname, len_tznames);
486 tznames[standard_len] =
'/';
487 strncpy(tznames + standard_len + 1, daylight_tzname, len_tznames - standard_len - 1);
488 tznames[len_tznames - 1] =
'\0';
494 tznames = standard_tzname ? standard_tzname : daylight_tzname;
503 static ICAL_GLOBAL_VAR
int icaltimezone_minimum_expansion_year = -1;
509 icaltimezone_load_builtin_timezone(
zone);
511 if (icaltimezone_minimum_expansion_year == -1) {
514 icaltimezone_minimum_expansion_year = today.
year;
517 int changes_end_year = end_year;
518 if (changes_end_year < icaltimezone_minimum_expansion_year) {
519 changes_end_year = icaltimezone_minimum_expansion_year;
528 if (!
zone->changes ||
zone->end_year < end_year) {
529 icaltimezone_expand_changes(
zone, changes_end_year);
542#ifdef ICALTIMEZONE_DEBUG_PRINT
543 printf(
"\nExpanding changes for: %s to year: %i\n",
zone->tzid, end_year);
566 zone->changes = changes;
567 zone->end_year = end_year;
572 icaltimezonechange change;
576 icalrecur_iterator *rrule_iterator;
578 int found_dtstart = 0, found_tzoffsetto = 0, found_tzoffsetfrom = 0;
579 int has_rdate = 0, has_rrule = 0;
584 change.is_daylight = 0;
586 change.is_daylight = 1;
597 case ICAL_DTSTART_PROPERTY:
598 dtstart = icalproperty_get_dtstart(prop);
601 case ICAL_TZOFFSETTO_PROPERTY:
602 change.utc_offset = icalproperty_get_tzoffsetto(prop);
604 found_tzoffsetto = 1;
606 case ICAL_TZOFFSETFROM_PROPERTY:
607 change.prev_utc_offset = icalproperty_get_tzoffsetfrom(prop);
609 found_tzoffsetfrom = 1;
611 case ICAL_RDATE_PROPERTY:
614 case ICAL_RRULE_PROPERTY:
628 if (found_tzoffsetto && !found_tzoffsetfrom) {
629 change.prev_utc_offset = change.utc_offset;
630 found_tzoffsetfrom = 1;
635 if (!found_dtstart || !found_tzoffsetto || !found_tzoffsetfrom) {
639#ifdef ICALTIMEZONE_DEBUG_PRINT
640 printf(
"\n Expanding component DTSTART (Y/M/D): %i/%i/%i %i:%02i:%02i\n",
647 change.year = dtstart.
year;
648 change.month = dtstart.
month;
649 change.day = dtstart.
day;
650 change.hour = dtstart.
hour;
651 change.minute = dtstart.
minute;
652 change.second = dtstart.
second;
655 icaltimezone_adjust_change(&change, 0, 0, 0, -change.prev_utc_offset);
657#ifdef ICALTIMEZONE_DEBUG_PRINT
658 printf(
" Appending single DTSTART (Y/M/D): %i/%02i/%02i %i:%02i:%02i\n",
659 change.year, change.month, change.day, change.hour, change.minute, change.second);
670 while (prop && (has_rdate || has_rrule)) {
671#ifdef ICALTIMEZONE_DEBUG_PRINT
672 printf(
"Expanding property...\n");
675 case ICAL_RDATE_PROPERTY:
676 rdate = icalproperty_get_rdate(prop);
677 change.year = rdate.time.year;
678 change.month = rdate.time.month;
679 change.day = rdate.time.day;
683 change.hour = dtstart.
hour;
684 change.minute = dtstart.
minute;
685 change.second = dtstart.
second;
687 change.hour = rdate.time.hour;
688 change.minute = rdate.time.minute;
689 change.second = rdate.time.second;
695 icaltimezone_adjust_change(&change, 0, 0, 0, -change.prev_utc_offset);
699#ifdef ICALTIMEZONE_DEBUG_PRINT
700 printf(
" Appending RDATE element (Y/M/D): %i/%02i/%02i %i:%02i:%02i\n",
701 change.year, change.month, change.day,
702 change.hour, change.minute, change.second);
707 case ICAL_RRULE_PROPERTY:
708 rrule = icalproperty_get_rrule(prop);
718#ifdef ICALTIMEZONE_DEBUG_PRINT
719 printf(
" Found RRULE UNTIL in UTC.\n");
731 change.year = dtstart.
year;
732 change.month = dtstart.
month;
733 change.day = dtstart.
day;
734 change.hour = dtstart.
hour;
735 change.minute = dtstart.
minute;
736 change.second = dtstart.
second;
738#ifdef ICALTIMEZONE_DEBUG_PRINT
739 printf(
" Appending RRULE element (Y/M/D): %i/%02i/%02i %i:%02i:%02i\n",
740 change.year, change.month, change.day,
741 change.hour, change.minute, change.second);
744 icaltimezone_adjust_change(&change, 0, 0, 0, -change.prev_utc_offset);
749 for (
size_t rrule_iterator_count = 0; rrule_iterator && rrule_iterator_count < max_rrule_search; rrule_iterator_count++) {
758 change.year = occ.
year;
759 change.month = occ.
month;
760 change.day = occ.
day;
761 change.hour = occ.
hour;
762 change.minute = occ.
minute;
763 change.second = occ.
second;
765#ifdef ICALTIMEZONE_DEBUG_PRINT
766 printf(
" Appending RRULE element (Y/M/D): %i/%02i/%02i %i:%02i:%02i\n",
767 change.year, change.month, change.day,
768 change.hour, change.minute, change.second);
771 icaltimezone_adjust_change(&change, 0, 0, 0, -change.prev_utc_offset);
791static int icaltimezone_compare_change_fn(
const void *elem1,
const void *elem2)
793 const icaltimezonechange *change1, *change2;
796 change1 = (
const icaltimezonechange *)elem1;
797 change2 = (
const icaltimezonechange *)elem2;
799 if (change1->year < change2->year) {
801 }
else if (change1->year > change2->year) {
803 }
else if (change1->month < change2->month) {
805 }
else if (change1->month > change2->month) {
807 }
else if (change1->day < change2->day) {
809 }
else if (change1->day > change2->day) {
811 }
else if (change1->hour < change2->hour) {
813 }
else if (change1->hour > change2->hour) {
815 }
else if (change1->minute < change2->minute) {
817 }
else if (change1->minute > change2->minute) {
819 }
else if (change1->second < change2->second) {
821 }
else if (change1->second > change2->second) {
833 int utc_offset, is_daylight;
855 const icaltimezonechange *zone_change;
856 const icaltimezonechange *prev_zone_change;
857 icaltimezonechange tt_change = {0}, tmp_change;
858 size_t change_num, change_num_to_use;
860 int step, utc_offset_change, cmp;
872 if (zone == NULL || zone == &utc_timezone) {
877 if (zone->builtin_timezone) {
878 zone = zone->builtin_timezone;
881 icaltimezone_changes_lock();
884 icaltimezone_ensure_coverage(zone, tt->
year);
886 if (!zone->changes || zone->changes->num_elements == 0) {
887 icaltimezone_changes_unlock();
893 tt_change.year = tt->
year;
894 tt_change.month = tt->
month;
895 tt_change.day = tt->
day;
896 tt_change.hour = tt->
hour;
897 tt_change.minute = tt->
minute;
898 tt_change.second = tt->
second;
902 change_num = icaltimezone_find_nearby_change(zone, &tt_change);
909 change_num_to_use = (size_t)-1;
912 tmp_change = *zone_change;
917 if (tmp_change.utc_offset < tmp_change.prev_utc_offset) {
921 icaltimezone_adjust_change(&tmp_change, 0, 0, 0, tmp_change.utc_offset);
923 icaltimezone_adjust_change(&tmp_change, 0, 0, 0, tmp_change.prev_utc_offset);
926 cmp = icaltimezone_compare_change_fn(&tt_change, &tmp_change);
935 change_num_to_use = change_num;
943 if (step == -1 && found_change == 1) {
949 if (change_num == 0 && step < 0) {
951 *is_daylight = !tmp_change.is_daylight;
954 icaltimezone_changes_unlock();
956 return tmp_change.prev_utc_offset;
959 change_num += (size_t)step;
961 if (change_num >= zone->changes->num_elements) {
969 icalerror_assert(found_change == 1,
"No applicable timezone change found");
975 utc_offset_change = zone_change->utc_offset - zone_change->prev_utc_offset;
976 if (utc_offset_change < 0 && change_num_to_use > 0) {
977 tmp_change = *zone_change;
978 icaltimezone_adjust_change(&tmp_change, 0, 0, 0, tmp_change.prev_utc_offset);
980 if (icaltimezone_compare_change_fn(&tt_change, &tmp_change) < 0) {
993#ifdef ICALTIMEZONE_DEBUG_PRINT
994 if (zone_change->is_daylight == prev_zone_change->is_daylight) {
995 printf(
" **** Same is_daylight setting\n");
999 if (zone_change->is_daylight != want_daylight &&
1000 prev_zone_change->is_daylight == want_daylight) {
1001 zone_change = prev_zone_change;
1009 *is_daylight = zone_change->is_daylight;
1011 utc_offset_change = zone_change->utc_offset;
1013 icaltimezone_changes_unlock();
1015 return utc_offset_change;
1021 const icaltimezonechange *zone_change;
1022 icaltimezonechange tt_change, tmp_change;
1023 size_t change_num, change_num_to_use;
1024 int found_change = 1;
1025 int step, utc_offset;
1032 if (zone == NULL || zone == &utc_timezone) {
1037 if (zone->builtin_timezone) {
1038 zone = zone->builtin_timezone;
1041 icaltimezone_changes_lock();
1044 icaltimezone_ensure_coverage(zone, tt->
year);
1046 if (!zone->changes || zone->changes->num_elements == 0) {
1047 icaltimezone_changes_unlock();
1053 tt_change.year = tt->
year;
1054 tt_change.month = tt->
month;
1055 tt_change.day = tt->
day;
1056 tt_change.hour = tt->
hour;
1057 tt_change.minute = tt->
minute;
1058 tt_change.second = tt->
second;
1062 change_num = icaltimezone_find_nearby_change(zone, &tt_change);
1069 change_num_to_use = (size_t)-1;
1072 tmp_change = *zone_change;
1079 if (icaltimezone_compare_change_fn(&tt_change, &tmp_change) >= 0) {
1081 change_num_to_use = change_num;
1089 if (step == -1 && found_change == 1) {
1095 if (change_num == 0 && step < 0) {
1097 *is_daylight = !tmp_change.is_daylight;
1100 icaltimezone_changes_unlock();
1102 return tmp_change.prev_utc_offset;
1105 change_num += (size_t)step;
1107 if (change_num >= zone->changes->num_elements) {
1115 icalerror_assert(found_change == 1,
"No applicable timezone change found");
1121 *is_daylight = zone_change->is_daylight;
1123 utc_offset = zone_change->utc_offset;
1125 icaltimezone_changes_unlock();
1136static size_t icaltimezone_find_nearby_change(
icaltimezone *zone,
const icaltimezonechange *change)
1138 size_t lower, middle, upper;
1142 upper = zone->changes->num_elements;
1144 while (lower < upper) {
1145 middle = (lower + upper) / 2;
1147 int cmp = icaltimezone_compare_change_fn(change, zone_change);
1150 }
else if (cmp < 0) {
1166static void icaltimezone_adjust_change(icaltimezonechange *tt,
1167 int days,
int hours,
int minutes,
int seconds)
1169 int second, minute, hour, day;
1170 int minutes_overflow, hours_overflow, days_overflow;
1173 second = tt->second + seconds;
1174 tt->second = second % 60;
1175 minutes_overflow = second / 60;
1176 if (tt->second < 0) {
1182 minute = tt->minute + minutes + minutes_overflow;
1183 tt->minute = minute % 60;
1184 hours_overflow = minute / 60;
1185 if (tt->minute < 0) {
1191 hour = tt->hour + hours + hours_overflow;
1192 tt->hour = hour % 24;
1193 days_overflow = hour / 24;
1200 day = tt->day + days + days_overflow;
1204 if (day <= days_in_month) {
1209 if (tt->month >= 13) {
1214 day -= days_in_month;
1218 if (tt->month == 1) {
1238 icaltimezone_load_builtin_timezone(zone);
1252 return zone->location;
1262 icaltimezone_load_builtin_timezone(zone);
1264 return zone->tznames;
1276 return zone->latitude;
1288 return zone->longitude;
1298 icaltimezone_load_builtin_timezone(zone);
1300 return zone->component;
1305 icaltimezone_reset(zone);
1306 return icaltimezone_get_vtimezone_properties(zone, comp);
1309static const char *skip_slashes(
const char *text,
int n_slashes)
1312 int num_slashes = 0;
1318 for (pp = text; *pp; pp++) {
1321 if (num_slashes == n_slashes) {
1336 if (!display_name) {
1339 if (!display_name) {
1346 if (!strncmp(display_name, tzid_prefix, strlen(tzid_prefix))) {
1348 display_name += strlen(tzid_prefix);
1353 return display_name;
1357icalarray *icaltimezone_array_new(
void)
1362void icaltimezone_array_append_from_vtimezone(icalarray *timezones, icalcomponent *child)
1366 icaltimezone_init(&zone);
1367 if (icaltimezone_get_vtimezone_properties(&zone, child)) {
1372void icaltimezone_array_free(icalarray *timezones)
1375 for (
size_t i = 0; i < timezones->num_elements; i++) {
1391 if (!builtin_timezones) {
1392 icaltimezone_init_builtin_timezones();
1395 return builtin_timezones;
1400 icaltimezone_builtin_lock();
1401 icaltimezone_array_free(builtin_timezones);
1402 builtin_timezones = 0;
1403 icaltimezone_builtin_unlock();
1408 icalcomponent *comp;
1412 if (!location || !location[0]) {
1416 if (!builtin_timezones) {
1417 icaltimezone_init_builtin_timezones();
1420 if (strcmp(location,
"UTC") == 0 || strcmp(location,
"GMT") == 0) {
1421 return &utc_timezone;
1426 for (lower = 0; lower < builtin_timezones->num_elements; lower++) {
1429 if (zone_location && strcmp(location, zone_location) == 0) {
1436 comp = icaltimezone_fetch_timezone(location);
1440 icaltimezone_init(&tz);
1452static struct icaltimetype tm_to_icaltimetype(const struct tm *tm)
1458 itt.second = tm->tm_sec;
1459 itt.minute = tm->tm_min;
1460 itt.hour = tm->tm_hour;
1462 itt.day = tm->tm_mday;
1463 itt.month = tm->tm_mon + 1;
1464 itt.year = tm->tm_year + 1900;
1476 const icaltime_t now = icaltime(NULL);
1478 memset(&local, 0,
sizeof(
struct tm));
1479 if (!icalgmtime_r(&now, &local)) {
1483 tt = tm_to_icaltimetype(&local);
1491 if (!builtin_timezones) {
1492 icaltimezone_init_builtin_timezones();
1496 return &utc_timezone;
1503 size_t count = builtin_timezones->num_elements;
1505 for (
size_t i = 0; i < count; i++) {
1508 icaltimezone_load_builtin_timezone(
zone);
1509 int z_offset = get_offset(
zone);
1510 if (z_offset == offset &&
zone->tznames && !strcmp(tzname,
zone->tznames)) {
1521 const char *p, *zone_tzid, *tzid_prefix;
1525 if (!tzid || !tzid[0]) {
1529 if (strcmp(tzid,
"UTC") == 0 || strcmp(tzid,
"GMT") == 0) {
1535 if (strncmp(tzid, tzid_prefix, strlen(tzid_prefix)) != 0) {
1538 for (ii = 0; glob_compat_tzids[ii].tzid; ii++) {
1539 if (strncmp(tzid, glob_compat_tzids[ii].tzid, strlen(glob_compat_tzids[ii].tzid)) == 0) {
1540 p = skip_slashes(tzid, glob_compat_tzids[ii].slashes);
1555 p = tzid + strlen(tzid_prefix);
1560 strncmp(p,
"Tzfile/", 7) == 0) {
1568 if (!
zone || compat) {
1572#if defined(USE_BUILTIN_TZDATA)
1573 if (use_builtin_tzdata) {
1581 if (!strcmp(zone_tzid, tzid)) {
1590 if (!builtin_timezones) {
1591 icaltimezone_init_builtin_timezones();
1594 return &utc_timezone;
1603static void icaltimezone_init_builtin_timezones(
void)
1606 utc_timezone.tzid = (
char *)
"UTC";
1608 icaltimezone_builtin_lock();
1609 if (!builtin_timezones) {
1610 icaltimezone_parse_zone_tab();
1612 icaltimezone_builtin_unlock();
1615static bool parse_coord(
const char *coord,
int len,
int *degrees,
int *minutes,
int *seconds)
1618 sscanf(coord + 1,
"%2d%2d", degrees, minutes);
1619 }
else if (len == 6) {
1620 sscanf(coord + 1,
"%3d%2d", degrees, minutes);
1621 }
else if (len == 7) {
1622 sscanf(coord + 1,
"%2d%2d%2d", degrees, minutes, seconds);
1623 }
else if (len == 8) {
1624 sscanf(coord + 1,
"%3d%2d%2d", degrees, minutes, seconds);
1626 icalerrprintf(
"Invalid coordinate: %s\n", coord);
1630 if (coord[0] ==
'-') {
1631 *degrees = -*degrees;
1637static bool fetch_lat_long_from_string(
const char *str,
1638 int *latitude_degrees,
int *latitude_minutes,
1639 int *latitude_seconds,
1640 int *longitude_degrees,
int *longitude_minutes,
1641 int *longitude_seconds,
1642 char *location,
size_t len_location)
1645 const char *loc, *temp;
1649 if (!location || !len_location) {
1654 const size_t len_str = strlen(str);
1657 while ((*sptr !=
'\t') && (*sptr !=
'\0')) {
1665 while (*sptr !=
'\t' && *sptr !=
'\0') {
1668 len = (size_t)(ptrdiff_t)(sptr - temp);
1676 memset(lat,
'\0', len + 1);
1677 strncpy(lat, temp, len + 1);
1679 while ((*sptr !=
'\t') && (*sptr !=
'\0')) {
1683 while (!isspace((
int)(*sptr)) && (*sptr !=
'\0')) {
1686 len = (size_t)(ptrdiff_t)(sptr - loc);
1687 if (len >= len_location) {
1688 len = len_location - 1;
1690 strncpy(location, loc, len);
1691 location[len] =
'\0';
1694 while (*lon !=
'\0' && *lon !=
'+' && *lon !=
'-') {
1698 if (parse_coord(lat, (
int)(lon - lat),
1701 latitude_seconds) == 1 ||
1702 parse_coord(lon, (
int)strlen(lon),
1703 longitude_degrees, longitude_minutes, longitude_seconds) == 1) {
1723static void icaltimezone_parse_zone_tab(
void)
1725 const char *zonedir, *zonetab;
1728 size_t filename_len;
1730 icalerror_assert(builtin_timezones == NULL,
"Parsing zones.tab file multiple times");
1734 if (!use_builtin_tzdata) {
1736 zonetab = ZONES_TAB_SYSTEM_FILENAME;
1738 zonedir = get_zone_directory_builtin();
1744 filename_len = strlen(zonedir);
1747 icalerror_assert(filename_len > 0,
"Unable to locate a zoneinfo dir");
1748 if (filename_len == 0) {
1750 builtin_timezones = timezones;
1756 filename_len += strlen(zonetab);
1762 builtin_timezones = timezones;
1767 snprintf(filename, filename_len,
"%s/%s", zonedir, zonetab);
1769 fp = fopen(filename,
"r");
1770 icalerror_assert(fp,
"Cannot open the zonetab file for reading");
1773 builtin_timezones = timezones;
1780#if !defined(__clang_analyzer__)
1782 while (!feof(fp) && !ferror(fp) && fgets(buf, (
int)
sizeof(buf), fp)) {
1783 char location[1024] = {0};
1784 int longitude_degrees, longitude_minutes, longitude_seconds;
1785 int latitude_degrees, latitude_minutes, latitude_seconds;
1787 if (buf[0] ==
'\0') {
1794 if (use_builtin_tzdata) {
1796 if (buf[0] !=
'+' && buf[0] !=
'-') {
1797 latitude_degrees = longitude_degrees = 360;
1798 latitude_minutes = longitude_minutes = 0;
1799 latitude_seconds = longitude_seconds = 0;
1800 if (sscanf(buf,
"%1000s", location) != 1) {
1803 icalerrprintf(
"%s: Invalid timezone description line: %s\n", filename, buf);
1806 }
else if (sscanf(buf,
"%4d%2d%2d %4d%2d%2d %1000s",
1809 &latitude_degrees, &latitude_minutes,
1811 &longitude_degrees, &longitude_minutes,
1812 &longitude_seconds, location) != 7) {
1813 icalerrprintf(
"%s: Invalid timezone description line: %s\n", filename, buf);
1817 if (fetch_lat_long_from_string(buf, &latitude_degrees, &latitude_minutes,
1819 &longitude_degrees, &longitude_minutes,
1820 &longitude_seconds, location,
sizeof(location))) {
1821 icalerrprintf(
"%s: Invalid timezone description line: %s\n", filename, buf);
1824 if (location[0] ==
'\0') {
1825 icalerrprintf(
"%s: Invalid timezone location: %s\n", filename, buf);
1830 icaltimezone_init(&
zone);
1834 if (latitude_degrees >= 0) {
1836 (double)latitude_degrees +
1837 (
double)latitude_minutes / 60 +
1838 (double)latitude_seconds / 3600;
1841 (double)latitude_degrees -
1842 (
double)latitude_minutes / 60 -
1843 (double)latitude_seconds / 3600;
1846 if (longitude_degrees >= 0) {
1848 (double)longitude_degrees +
1849 (
double)longitude_minutes / 60 +
1850 (double)longitude_seconds / 3600;
1853 (double)longitude_degrees -
1854 (
double)longitude_minutes / 60 -
1855 (double)longitude_seconds / 3600;
1860#ifdef ICALTIMEZONE_DEBUG_PRINT
1861 printf(
"Found zone: %s %f %f\n", location,
zone.latitude,
zone.longitude);
1866 builtin_timezones = timezones;
1877 icalcomponent *comp = 0, *subcomp;
1880 if (
zone->component) {
1884 icaltimezone_builtin_lock();
1887 if (
zone->component) {
1888 icaltimezone_builtin_unlock();
1893 if (!
zone->location || !
zone->location[0]) {
1894 icaltimezone_builtin_unlock();
1898 if (use_builtin_tzdata) {
1900 size_t filename_len;
1904 filename_len = strlen(get_zone_directory_builtin()) + strlen(
zone->location) + 6;
1912 snprintf(filename, filename_len,
"%s/%s.ics", get_zone_directory_builtin(),
zone->location);
1914 fp = fopen(filename,
"r");
1943 size_t new_tzid_len;
1946 new_tzid_len = strlen(tzid_prefix) + strlen(
zone->location) + 1;
1949 snprintf(new_tzid, new_tzid_len,
"%s%s", tzid_prefix,
zone->location);
1950 icalproperty_set_tzid(prop, new_tzid);
1963 icalproperty_set_location(prop,
zone->location);
1972 if (name && !strcasecmp(name,
"X-LIC-LOCATION")) {
1973 icalproperty_set_x(prop,
zone->location);
1979 subcomp = icaltimezone_fetch_timezone(
zone->location);
1987 icaltimezone_get_vtimezone_properties(
zone, subcomp);
1989 if (use_builtin_tzdata) {
1995 icaltimezone_builtin_unlock();
2001static char *icaltimezone_load_get_line_fn(
char *s,
size_t size,
void *data)
2003 return fgets(s, (
int)size, (FILE *)data);
2012 static const char months[][4] = {
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
2013 "Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"};
2014 const icaltimezonechange *zone_change;
2019 icaltimezone_ensure_coverage(
zone, max_year);
2021#ifdef ICALTIMEZONE_DEBUG_PRINT
2022 printf(
"Num changes: %zu\n",
zone->changes->num_elements);
2025 icaltimezone_changes_lock();
2027 for (change_num = 0; change_num <
zone->changes->num_elements; change_num++) {
2030 if (zone_change->year > max_year) {
2034 fprintf(fp,
"%s\t%2i %s %04i\t%2i:%02i:%02i",
2036 zone_change->day, months[zone_change->month - 1],
2037 zone_change->year, zone_change->hour, zone_change->minute, zone_change->second);
2040 format_utc_offset(zone_change->utc_offset, buffer,
sizeof(buffer));
2041 fprintf(fp,
"\t%s", buffer);
2046 icaltimezone_changes_unlock();
2056static void format_utc_offset(
int utc_offset,
char *buffer,
size_t buffer_size)
2058 const char *sign =
"+";
2059 int hours, minutes, seconds;
2061 if (utc_offset < 0) {
2062 utc_offset = -utc_offset;
2066 hours = utc_offset / 3600;
2067 minutes = (utc_offset % 3600) / 60;
2068 seconds = utc_offset % 60;
2073 if (hours < 0 || hours >= 24 || minutes < 0 || minutes >= 60 || seconds < 0) {
2074 icalerrprintf(
"Warning: Strange timezone offset: H:%i M:%i S:%i\n",
2075 hours, minutes, seconds);
2077#if defined(__GNUC__) && !defined(__clang__)
2078#pragma GCC diagnostic push
2079#pragma GCC diagnostic ignored "-Wformat-truncation"
2082 snprintf(buffer, buffer_size,
"%s%02i%02i", sign, hours, minutes);
2084 snprintf(buffer, buffer_size,
"%s%02i%02i%02i", sign, hours, minutes, seconds);
2087#if defined(__GNUC__) && !defined(__clang__)
2088#pragma GCC diagnostic pop
2091static const char *get_zone_directory_builtin(
void)
2096 wchar_t wbuffer[1000];
2098#if !defined(_WIN32_WCE)
2099 char buffer[1000], zoneinfodir[1000], dirname[1000];
2102 wchar_t zoneinfodir[1000], dirname[1000];
2104 static ICAL_GLOBAL_VAR
char *cache = NULL;
2106#if !defined(_WIN32_WCE)
2107 unsigned char *dirslash, *zislash;
2108 const char *zislashp1;
2110 wchar_t *dirslash, *zislash;
2114 if (zone_files_directory) {
2115 return zone_files_directory;
2124 if (!GetModuleFileNameW(NULL, wbuffer,
sizeof(wbuffer) /
sizeof(wbuffer[0]))) {
2129#if !defined(_WIN32_WCE)
2131 if (!WideCharToMultiByte(CP_ACP, 0, wbuffer, -1, buffer,
sizeof(buffer),
2132 NULL, &used_default) ||
2135 if (!GetShortPathNameW(wbuffer, wbuffer,
2136 sizeof(wbuffer) /
sizeof(wbuffer[0])) ||
2137 !WideCharToMultiByte(CP_ACP, 0, wbuffer, -1, buffer,
sizeof(buffer),
2138 NULL, &used_default) ||
2179#if !defined(_WIN32_WCE)
2180 dirslash = _mbsrchr((
unsigned char *)buffer,
'\\');
2182 dirslash = wcsrchr(wbuffer, L
'\\');
2185#if !defined(_WIN32_WCE)
2192#if defined(_WIN32_WCE)
2193 while ((dirslash = wcsrchr(wbuffer,
'\\'))) {
2199 while ((zislash = wcschr(zoneinfodir, L
'/'))) {
2201 wcscpy(dirname, wbuffer);
2202 wcscat(dirname,
"/");
2203 wcscat(dirname, zislash + 1);
2204 if (stat(dirname, &st) == 0 && S_ISDIR(st.st_mode)) {
2205 cache = wce_wctomb(dirname);
2211 while ((dirslash = _mbsrchr((
unsigned char *)buffer,
'\\'))) {
2216 while ((zislash = _mbschr((
unsigned char *)zoneinfodir,
'/'))) {
2218 strcpy(dirname, buffer);
2219 strcat(dirname,
"/");
2220 zislashp1 = (
const char *)(zislash + 1);
2221 strcat(dirname, zislashp1);
2222 if (stat(dirname, &st) == 0 && S_ISDIR(st.st_mode)) {
2235 if ((zonepath == NULL) || (zonepath[0] ==
'\0')) {
2236 memset(s_zoneinfopath, 0, MAXPATHLEN);
2238 strncpy(s_zoneinfopath, zonepath, MAXPATHLEN - 1);
2242static void set_zoneinfopath(
void)
2244 char file_path[MAXPATHLEN] = {0};
2245 const char *fname = ZONES_TAB_SYSTEM_FILENAME;
2246 size_t i, num_zi_search_paths;
2249 const char *env_tzdir = getenv(
"TZDIR");
2250 if (env_tzdir != NULL) {
2251 snprintf(file_path, MAXPATHLEN,
"%s/%s", env_tzdir, fname);
2252 if (!access(file_path, F_OK | R_OK)) {
2253 strncpy(s_zoneinfopath, env_tzdir, MAXPATHLEN - 1);
2259 num_zi_search_paths =
sizeof(s_zoneinfo_search_paths) /
sizeof(s_zoneinfo_search_paths[0]);
2260 for (i = 0; i < num_zi_search_paths; i++) {
2261 snprintf(file_path, MAXPATHLEN,
"%s/%s", s_zoneinfo_search_paths[i], fname);
2262 if (!access(file_path, F_OK | R_OK)) {
2263 strncpy(s_zoneinfopath, s_zoneinfo_search_paths[i], MAXPATHLEN - 1);
2270 if (s_zoneinfopath[0] ==
'\0') {
2274 return s_zoneinfopath;
2279 if (use_builtin_tzdata) {
2280 return get_zone_directory_builtin();
2288 if (zone_files_directory) {
2292 const size_t len_path = strlen(path) + 1;
2295 if (zone_files_directory != NULL) {
2296 strncpy(zone_files_directory, path, len_path);
2297 zone_files_directory[len_path - 1] =
'\0';
2303 if (zone_files_directory != NULL) {
2305 zone_files_directory = NULL;
2318 use_builtin_tzdata = set;
2323 return use_builtin_tzdata;
2333static void check_tombstone(
struct observance *tombstone,
2334 struct observance *obs)
2339 tombstone->offset_from = tombstone->offset_to = obs->offset_to;
2340 tombstone->onset = obs->onset;
2346 struct icaldatetimeperiodtype date;
2349static int rdate_compare(
const void *rdate1,
const void *rdate2)
2352 ((
struct rdate *)rdate2)->date.time);
2359 icalcomponent *comp, *nextc, *tomb_std = NULL, *tomb_day = NULL;
2360 icalproperty *prop, *proleptic_prop = NULL;
2361 struct observance tombstone;
2365 if (!need_tomb && !need_tzuntil) {
2375 proleptic_prop = prop;
2380 memset(&tombstone, 0,
sizeof(
struct observance));
2381 tombstone.name =
icalmemory_tmp_copy(proleptic_prop ? icalproperty_get_x(proleptic_prop) :
"LMT");
2382 if (!proleptic_prop ||
2384 tombstone.onset.
year = -1;
2389 comp; comp = nextc) {
2390 icalproperty *dtstart_prop = NULL, *rrule_prop = NULL;
2391 icalarray *rdates =
icalarray_new(
sizeof(
struct rdate), 10);
2393 struct observance obs;
2395 unsigned trunc_dtstart = 0;
2400 memset(&obs, 0,
sizeof(
struct observance));
2401 obs.offset_from = obs.offset_to = INT_MAX;
2410 case ICAL_TZNAME_PROPERTY:
2411 obs.name = icalproperty_get_tzname(prop);
2414 case ICAL_DTSTART_PROPERTY:
2415 dtstart_prop = prop;
2416 obs.onset = dtstart = icalproperty_get_dtstart(prop);
2419 case ICAL_TZOFFSETFROM_PROPERTY:
2420 obs.offset_from = icalproperty_get_tzoffsetfrom(prop);
2423 case ICAL_TZOFFSETTO_PROPERTY:
2424 obs.offset_to = icalproperty_get_tzoffsetto(prop);
2427 case ICAL_RRULE_PROPERTY:
2431 case ICAL_RDATE_PROPERTY: {
2436 rdate.date.time = dtp.
time;
2437 rdate.date.period = dtp.
period;
2450 if (!dtstart_prop || !obs.name ||
2451 obs.offset_from == INT_MAX || obs.offset_to == INT_MAX) {
2476 check_tombstone(&tombstone, &obs);
2481 }
else if (r == 0) {
2490 icalrecur_iterator *ritr = NULL;
2491 unsigned trunc_until = 0;
2497 obs.onset = rrule->
until;
2499 check_tombstone(&tombstone, &obs);
2521 if (ritr && trunc_dtstart) {
2526 newstart.
day = start.
day;
2549 ydiff = (unsigned)(prev_onset.
year - dtstart.
year);
2558 rdate.
time = prev_onset;
2561 prop = icalproperty_new_rdate(rdate);
2566 rrule->
until = prev_onset;
2567 icalproperty_set_rrule(rrule_prop, rrule);
2579 if (ms_compatible) {
2585 if (proleptic_prop) {
2589 proleptic_prop = NULL;
2593 check_tombstone(&tombstone, &obs);
2601 if (trunc_dtstart) {
2603 icalproperty_set_dtstart(dtstart_prop, recur);
2604 dtstart = obs.onset;
2609 if (!trunc_until && ydiff <= 1) {
2620 prop = icalproperty_new_rdate(rdate);
2632 ydiff = (unsigned)(end.
year - recur.
year);
2642 prev_onset = obs.onset;
2653 for (n = 0; n < rdates->num_elements; n++) {
2658 rdate->date.time.hour = dtstart.
hour;
2659 rdate->date.time.minute = dtstart.
minute;
2660 rdate->date.time.second = dtstart.
second;
2670 obs.onset = rdate->date.time;
2688 check_tombstone(&tombstone, &obs);
2700 if (trunc_dtstart) {
2702 icalproperty_set_dtstart(dtstart_prop,
2714 if (trunc_dtstart) {
2722 }
else if (!tomb_std) {
2737 icalcomponent *tomb;
2738 icalproperty *tomb_prop, *nextp;
2751 tomb_prop; tomb_prop = nextp) {
2755 case ICAL_TZNAME_PROPERTY:
2756 icalproperty_set_tzname(tomb_prop, tombstone.name);
2758 case ICAL_TZOFFSETFROM_PROPERTY:
2759 icalproperty_set_tzoffsetfrom(tomb_prop, tombstone.offset_from);
2761 case ICAL_TZOFFSETTO_PROPERTY:
2762 icalproperty_set_tzoffsetto(tomb_prop, tombstone.offset_to);
2764 case ICAL_DTSTART_PROPERTY:
2769 icalproperty_set_dtstart(tomb_prop, start);
2779 if (proleptic_prop) {
2800 icalproperty_set_tzuntil(prop, end);
icalarray * icalarray_copy(const icalarray *originalarray)
void * icalarray_element_at(icalarray *array, size_t position)
Access an array element.
void icalarray_free(icalarray *array)
void icalarray_sort(icalarray *array, int(*compare)(const void *, const void *))
Sorts the elements of an icalarray using the given comparison function.
void icalarray_append(icalarray *array, const void *element)
Appends an element to an array.
icalarray * icalarray_new(size_t element_size, size_t increment_size)
An array of arbitrarily-sized elements which grows dynamically as elements are added.
icalproperty * icalcomponent_get_first_property(icalcomponent *c, icalproperty_kind kind)
icalcomponent * icalcomponent_get_next_component(icalcomponent *c, icalcomponent_kind kind)
icalcomponent * icalcomponent_get_first_component(icalcomponent *c, icalcomponent_kind kind)
void icalcomponent_remove_property(icalcomponent *component, icalproperty *property)
void icalcomponent_remove_component(icalcomponent *parent, icalcomponent *child)
void icalcomponent_add_property(icalcomponent *component, icalproperty *property)
icalcomponent_kind icalcomponent_isa(const icalcomponent *component)
void icalcomponent_free(icalcomponent *c)
icalproperty * icalcomponent_get_next_property(icalcomponent *c, icalproperty_kind kind)
@ ICAL_XDAYLIGHT_COMPONENT
@ ICAL_XSTANDARD_COMPONENT
@ ICAL_VTIMEZONE_COMPONENT
void icalerror_set_errno(icalerrorenum x)
Sets the icalerrno to a given error.
Error handling for libical.
size_t icallimit_get(icallimits_kind kind)
Defines the interface for getting/setting internal library limits.
@ ICAL_LIMIT_RRULE_SEARCH
void icalmemory_free_buffer(void *buf)
Releases a buffer.
char * icalmemory_strdup(const char *s)
Creates a duplicate of a string.
void * icalmemory_new_buffer(size_t size)
Creates new buffer with the specified size.
char * icalmemory_tmp_copy(const char *str)
Creates a copy of the given string, stored on the ring buffer, and returns it.
Common memory management routines.
icalcomponent * icalparser_parse(icalparser *parser, icalparser_line_gen_func line_gen_func)
Message oriented parsing.
void icalparser_free(icalparser *parser)
Frees an icalparser object.
icalparser * icalparser_new(void)
Creates a new icalparser.
void icalparser_set_gen_data(icalparser *parser, void *data)
Sets the data that icalparser_parse will give to the line_gen_func as the parameter 'd'.
struct icalperiodtype icalperiodtype_null_period(void)
void icalproperty_free(icalproperty *p)
icalproperty_kind icalproperty_isa(const icalproperty *p)
const char * icalproperty_get_x_name(const icalproperty *prop)
const char * icalproperty_get_parameter_as_string(icalproperty *prop, const char *name)
bool icalrecur_iterator_set_start(icalrecur_iterator *impl, struct icaltimetype start)
struct icalrecurrencetype * icalrecurrencetype_clone(struct icalrecurrencetype *recur)
void icalrecur_iterator_free(icalrecur_iterator *impl)
void icalrecurrencetype_unref(struct icalrecurrencetype *recur)
icalrecur_iterator * icalrecur_iterator_new(struct icalrecurrencetype *rule, struct icaltimetype dtstart)
struct icaltimetype icalrecur_iterator_next(icalrecur_iterator *impl)
bool icaltime_is_date(const struct icaltimetype t)
struct icaltimetype icaltime_today(void)
Convenience constructor.
int icaltime_days_in_month(const int month, const int year)
bool icaltime_is_utc(const struct icaltimetype t)
bool icaltime_is_null_time(const struct icaltimetype t)
int icaltime_compare(const struct icaltimetype a_in, const struct icaltimetype b_in)
struct icaltimetype icaltime_set_timezone(struct icaltimetype *t, const icaltimezone *zone)
void icaltime_adjust(struct icaltimetype *tt, const int days, const int hours, const int minutes, const int seconds)
struct icaltimetype icaltime_null_time(void)
icaltimezone * icaltimezone_new(void)
void icaltimezone_set_tzid_prefix(const char *new_prefix)
icaltimezone * icaltimezone_get_builtin_timezone_from_offset(int offset, const char *tzname)
double icaltimezone_get_longitude(const icaltimezone *zone)
void icaltimezone_free_zone_directory(void)
const char * icaltimezone_get_location(const icaltimezone *zone)
void icaltimezone_truncate_vtimezone(icalcomponent *vtz, icaltimetype start, icaltimetype end, bool ms_compatible)
void icaltimezone_set_builtin_tzdata(bool set)
icaltimezone * icaltimezone_copy(const icaltimezone *originalzone)
#define BUILTIN_TZID_PREFIX
#define ZONES_TAB_FILENAME
char * icaltimezone_get_location_from_vtimezone(icalcomponent *component)
#define ICALTIMEZONE_MAX_YEAR
char * icaltimezone_get_tznames_from_vtimezone(icalcomponent *component)
double icaltimezone_get_latitude(const icaltimezone *zone)
void icaltimezone_expand_vtimezone(icalcomponent *comp, int end_year, icalarray *changes)
void icaltimezone_set_system_zone_directory(const char *zonepath)
int icaltimezone_get_utc_offset_of_utc_time(icaltimezone *zone, const struct icaltimetype *tt, int *is_daylight)
icalcomponent * icaltimezone_get_component(icaltimezone *zone)
const char * icaltimezone_get_system_zone_directory(void)
#define BUILTIN_TZID_PREFIX_LEN
void icaltimezone_free_builtin_timezones(void)
Releases builtin timezone memory.
int icaltimezone_get_utc_offset(icaltimezone *zone, const struct icaltimetype *tt, int *is_daylight)
const char * icaltimezone_get_tznames(icaltimezone *zone)
icaltimezone * icaltimezone_get_builtin_timezone(const char *location)
icalarray * icaltimezone_get_builtin_timezones(void)
const char * icaltimezone_get_tzid(icaltimezone *zone)
icaltimezone * icaltimezone_get_utc_timezone(void)
void icaltimezone_set_zone_directory(const char *path)
#define ZONEINFO_DIRECTORY
void icaltimezone_free(icaltimezone *zone, int free_struct)
Frees all memory used for the icaltimezone.
const char * icaltimezone_get_display_name(icaltimezone *zone)
const char * icaltimezone_get_zone_directory(void)
icaltimezone * icaltimezone_get_builtin_timezone_from_tzid(const char *tzid)
void icaltimezone_convert_time(struct icaltimetype *tt, icaltimezone *from_zone, icaltimezone *to_zone)
#define ICALTIMEZONE_EXTRA_COVERAGE
bool icaltimezone_dump_changes(icaltimezone *zone, int max_year, FILE *fp)
bool icaltimezone_get_builtin_tzdata(void)
bool icaltimezone_set_component(icaltimezone *zone, icalcomponent *comp)
const char * icaltimezone_tzid_prefix(void)
Timezone handling routines.
struct _icaltimezone icaltimezone
struct icalperiodtype period
struct icaltimetype until
const icaltimezone * zone