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 bool icaltimezone_load_builtin_timezone(
icaltimezone *zone)
179#if defined(THREAD_SANITIZER)
180 __attribute__((no_sanitize(
"thread")))
184static bool icaltimezone_ensure_coverage(
icaltimezone *zone,
int end_year);
186static bool 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 bool icaltimezone_builtin_lock(
void)
197#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
198 if (pthread_mutex_lock(&builtin_mutex) != 0) {
206static bool icaltimezone_builtin_unlock(
void)
208#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
209 if (pthread_mutex_unlock(&builtin_mutex) != 0) {
217static bool icaltimezone_changes_lock(
void)
219#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
220 if (pthread_mutex_lock(&changes_mutex) != 0) {
228static bool icaltimezone_changes_unlock(
void)
230#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
231 if (pthread_mutex_unlock(&changes_mutex) != 0) {
241 return s_ical_tzid_prefix;
254 icaltimezone_init(zone);
270 if (zone->tzid != NULL) {
273 if (zone->location != NULL) {
276 if (zone->tznames != NULL) {
280 if (!icaltimezone_changes_lock()) {
287 if (zone->changes != NULL) {
290 if (!icaltimezone_changes_unlock()) {
303 zone->component = NULL;
310 icaltimezone_reset(zone);
324 if (zone->component) {
331 zone->changes = NULL;
335 icaltimezone_init(zone);
344 zone->location = NULL;
345 zone->tznames = NULL;
346 zone->latitude = 0.0;
347 zone->longitude = 0.0;
348 zone->component = NULL;
349 zone->builtin_timezone = NULL;
351 zone->changes = NULL;
363static bool icaltimezone_get_vtimezone_properties(
icaltimezone *zone, icalcomponent *component)
382 if (zone->component) {
385 zone->component = component;
399 const char *location;
412 if (name && !strcasecmp(name,
"X-LIC-LOCATION")) {
429 const char *current_tzname;
430 const char *standard_tzname = NULL, *daylight_tzname = NULL;
431 struct icaltimetype standard_max_date, daylight_max_date;
442 current_tzname = NULL;
456 current_max_date = dtstart;
464 current_max_date = rdate.time;
476 if (current_tzname) {
478 if (!standard_tzname ||
480 standard_max_date = current_max_date;
481 standard_tzname = current_tzname;
484 if (!daylight_tzname ||
486 daylight_max_date = current_max_date;
487 daylight_tzname = current_tzname;
498 if (standard_tzname && !strcmp(standard_tzname,
"Standard Time")) {
504 if (standard_tzname && daylight_tzname) {
505 size_t standard_len, daylight_len;
508 if (!strcmp(standard_tzname, daylight_tzname)) {
512 standard_len = strlen(standard_tzname);
513 daylight_len = strlen(daylight_tzname);
514 const size_t len_tznames = standard_len + daylight_len + 2;
516 strncpy(tznames, standard_tzname, len_tznames);
517 tznames[standard_len] =
'/';
518 strncpy(tznames + standard_len + 1, daylight_tzname, len_tznames - standard_len - 1);
519 tznames[len_tznames - 1] =
'\0';
525 tznames = standard_tzname ? standard_tzname : daylight_tzname;
534 static ICAL_GLOBAL_VAR
int icaltimezone_minimum_expansion_year = -1;
540 if (!icaltimezone_load_builtin_timezone(
zone)) {
544 if (icaltimezone_minimum_expansion_year == -1) {
547 icaltimezone_minimum_expansion_year = today.
year;
550 int changes_end_year = end_year;
551 if (changes_end_year < icaltimezone_minimum_expansion_year) {
552 changes_end_year = icaltimezone_minimum_expansion_year;
561 if (!
zone->changes ||
zone->end_year < end_year) {
562 icaltimezone_expand_changes(
zone, changes_end_year);
576#ifdef ICALTIMEZONE_DEBUG_PRINT
577 printf(
"\nExpanding changes for: %s to year: %i\n",
zone->tzid, end_year);
600 zone->changes = changes;
601 zone->end_year = end_year;
606 icaltimezonechange change;
610 icalrecur_iterator *rrule_iterator;
612 int found_dtstart = 0, found_tzoffsetto = 0, found_tzoffsetfrom = 0;
613 int has_rdate = 0, has_rrule = 0;
618 change.is_daylight = 0;
620 change.is_daylight = 1;
638 found_tzoffsetto = 1;
643 found_tzoffsetfrom = 1;
662 if (found_tzoffsetto && !found_tzoffsetfrom) {
663 change.prev_utc_offset = change.utc_offset;
664 found_tzoffsetfrom = 1;
669 if (!found_dtstart || !found_tzoffsetto || !found_tzoffsetfrom) {
673#ifdef ICALTIMEZONE_DEBUG_PRINT
674 printf(
"\n Expanding component DTSTART (Y/M/D): %i/%i/%i %i:%02i:%02i\n",
681 change.year = dtstart.
year;
682 change.month = dtstart.
month;
683 change.day = dtstart.
day;
684 change.hour = dtstart.
hour;
685 change.minute = dtstart.
minute;
686 change.second = dtstart.
second;
689 icaltimezone_adjust_change(&change, 0, 0, 0, -change.prev_utc_offset);
691#ifdef ICALTIMEZONE_DEBUG_PRINT
692 printf(
" Appending single DTSTART (Y/M/D): %i/%02i/%02i %i:%02i:%02i\n",
693 change.year, change.month, change.day, change.hour, change.minute, change.second);
704 while (prop && (has_rdate || has_rrule)) {
705#ifdef ICALTIMEZONE_DEBUG_PRINT
706 printf(
"Expanding property...\n");
711 change.year = rdate.time.year;
712 change.month = rdate.time.month;
713 change.day = rdate.time.day;
717 change.hour = dtstart.
hour;
718 change.minute = dtstart.
minute;
719 change.second = dtstart.
second;
721 change.hour = rdate.time.hour;
722 change.minute = rdate.time.minute;
723 change.second = rdate.time.second;
729 icaltimezone_adjust_change(&change, 0, 0, 0, -change.prev_utc_offset);
733#ifdef ICALTIMEZONE_DEBUG_PRINT
734 printf(
" Appending RDATE element (Y/M/D): %i/%02i/%02i %i:%02i:%02i\n",
735 change.year, change.month, change.day,
736 change.hour, change.minute, change.second);
752#ifdef ICALTIMEZONE_DEBUG_PRINT
753 printf(
" Found RRULE UNTIL in UTC.\n");
765 change.year = dtstart.
year;
766 change.month = dtstart.
month;
767 change.day = dtstart.
day;
768 change.hour = dtstart.
hour;
769 change.minute = dtstart.
minute;
770 change.second = dtstart.
second;
772#ifdef ICALTIMEZONE_DEBUG_PRINT
773 printf(
" Appending RRULE element (Y/M/D): %i/%02i/%02i %i:%02i:%02i\n",
774 change.year, change.month, change.day,
775 change.hour, change.minute, change.second);
778 icaltimezone_adjust_change(&change, 0, 0, 0, -change.prev_utc_offset);
783 for (
size_t rrule_iterator_count = 0; rrule_iterator && rrule_iterator_count < max_rrule_search; rrule_iterator_count++) {
792 change.year = occ.
year;
793 change.month = occ.
month;
794 change.day = occ.
day;
795 change.hour = occ.
hour;
796 change.minute = occ.
minute;
797 change.second = occ.
second;
799#ifdef ICALTIMEZONE_DEBUG_PRINT
800 printf(
" Appending RRULE element (Y/M/D): %i/%02i/%02i %i:%02i:%02i\n",
801 change.year, change.month, change.day,
802 change.hour, change.minute, change.second);
805 icaltimezone_adjust_change(&change, 0, 0, 0, -change.prev_utc_offset);
825static int icaltimezone_compare_change_fn(
const void *elem1,
const void *elem2)
827 const icaltimezonechange *change1, *change2;
830 change1 = (
const icaltimezonechange *)elem1;
831 change2 = (
const icaltimezonechange *)elem2;
833 if (change1->year < change2->year) {
835 }
else if (change1->year > change2->year) {
837 }
else if (change1->month < change2->month) {
839 }
else if (change1->month > change2->month) {
841 }
else if (change1->day < change2->day) {
843 }
else if (change1->day > change2->day) {
845 }
else if (change1->hour < change2->hour) {
847 }
else if (change1->hour > change2->hour) {
849 }
else if (change1->minute < change2->minute) {
851 }
else if (change1->minute > change2->minute) {
853 }
else if (change1->second < change2->second) {
855 }
else if (change1->second > change2->second) {
867 int utc_offset, is_daylight;
889 const icaltimezonechange *zone_change;
890 const icaltimezonechange *prev_zone_change;
891 icaltimezonechange tt_change = {0}, tmp_change;
892 size_t change_num, change_num_to_use;
894 int step, utc_offset_change, cmp;
906 if (zone == NULL || zone == &utc_timezone) {
911 if (zone->builtin_timezone) {
912 zone = zone->builtin_timezone;
915 if (!icaltimezone_changes_lock()) {
920 if (!icaltimezone_ensure_coverage(zone, tt->
year)) {
924 if (!zone->changes || zone->changes->num_elements == 0) {
925 (void)icaltimezone_changes_unlock();
931 tt_change.year = tt->
year;
932 tt_change.month = tt->
month;
933 tt_change.day = tt->
day;
934 tt_change.hour = tt->
hour;
935 tt_change.minute = tt->
minute;
936 tt_change.second = tt->
second;
940 change_num = icaltimezone_find_nearby_change(zone, &tt_change);
947 change_num_to_use = (size_t)-1;
950 tmp_change = *zone_change;
955 if (tmp_change.utc_offset < tmp_change.prev_utc_offset) {
959 icaltimezone_adjust_change(&tmp_change, 0, 0, 0, tmp_change.utc_offset);
961 icaltimezone_adjust_change(&tmp_change, 0, 0, 0, tmp_change.prev_utc_offset);
964 cmp = icaltimezone_compare_change_fn(&tt_change, &tmp_change);
973 change_num_to_use = change_num;
981 if (step == -1 && found_change == 1) {
987 if (change_num == 0 && step < 0) {
989 *is_daylight = !tmp_change.is_daylight;
992 if (!icaltimezone_changes_unlock()) {
996 return tmp_change.prev_utc_offset;
999 change_num += (size_t)step;
1001 if (change_num >= zone->changes->num_elements) {
1009 icalerror_assert(found_change == 1,
"No applicable timezone change found");
1015 utc_offset_change = zone_change->utc_offset - zone_change->prev_utc_offset;
1016 if (utc_offset_change < 0 && change_num_to_use > 0) {
1017 tmp_change = *zone_change;
1018 icaltimezone_adjust_change(&tmp_change, 0, 0, 0, tmp_change.prev_utc_offset);
1020 if (icaltimezone_compare_change_fn(&tt_change, &tmp_change) < 0) {
1033#ifdef ICALTIMEZONE_DEBUG_PRINT
1034 if (zone_change->is_daylight == prev_zone_change->is_daylight) {
1035 printf(
" **** Same is_daylight setting\n");
1039 if (zone_change->is_daylight != want_daylight &&
1040 prev_zone_change->is_daylight == want_daylight) {
1041 zone_change = prev_zone_change;
1049 *is_daylight = zone_change->is_daylight;
1051 utc_offset_change = zone_change->utc_offset;
1053 if (!icaltimezone_changes_unlock()) {
1057 return utc_offset_change;
1063 const icaltimezonechange *zone_change;
1064 icaltimezonechange tt_change, tmp_change;
1065 size_t change_num, change_num_to_use;
1066 int found_change = 1;
1067 int step, utc_offset;
1074 if (zone == NULL || zone == &utc_timezone) {
1079 if (zone->builtin_timezone) {
1080 zone = zone->builtin_timezone;
1083 if (!icaltimezone_changes_lock()) {
1088 if (!icaltimezone_ensure_coverage(zone, tt->
year)) {
1092 if (!zone->changes || zone->changes->num_elements == 0) {
1093 (void)icaltimezone_changes_unlock();
1099 tt_change.year = tt->
year;
1100 tt_change.month = tt->
month;
1101 tt_change.day = tt->
day;
1102 tt_change.hour = tt->
hour;
1103 tt_change.minute = tt->
minute;
1104 tt_change.second = tt->
second;
1108 change_num = icaltimezone_find_nearby_change(zone, &tt_change);
1115 change_num_to_use = (size_t)-1;
1118 tmp_change = *zone_change;
1125 if (icaltimezone_compare_change_fn(&tt_change, &tmp_change) >= 0) {
1127 change_num_to_use = change_num;
1135 if (step == -1 && found_change == 1) {
1141 if (change_num == 0 && step < 0) {
1143 *is_daylight = !tmp_change.is_daylight;
1146 if (!icaltimezone_changes_unlock()) {
1150 return tmp_change.prev_utc_offset;
1153 change_num += (size_t)step;
1155 if (change_num >= zone->changes->num_elements) {
1163 icalerror_assert(found_change == 1,
"No applicable timezone change found");
1169 *is_daylight = zone_change->is_daylight;
1171 utc_offset = zone_change->utc_offset;
1173 if (!icaltimezone_changes_unlock()) {
1186static size_t icaltimezone_find_nearby_change(
icaltimezone *zone,
const icaltimezonechange *change)
1188 size_t lower, middle, upper;
1192 upper = zone->changes->num_elements;
1194 while (lower < upper) {
1195 middle = (lower + upper) / 2;
1197 int cmp = icaltimezone_compare_change_fn(change, zone_change);
1200 }
else if (cmp < 0) {
1216static void icaltimezone_adjust_change(icaltimezonechange *tt,
1217 int days,
int hours,
int minutes,
int seconds)
1219 int second, minute, hour, day;
1220 int minutes_overflow, hours_overflow, days_overflow;
1223 second = tt->second + seconds;
1224 tt->second = second % 60;
1225 minutes_overflow = second / 60;
1226 if (tt->second < 0) {
1232 minute = tt->minute + minutes + minutes_overflow;
1233 tt->minute = minute % 60;
1234 hours_overflow = minute / 60;
1235 if (tt->minute < 0) {
1241 hour = tt->hour + hours + hours_overflow;
1242 tt->hour = hour % 24;
1243 days_overflow = hour / 24;
1250 day = tt->day + days + days_overflow;
1254 if (day <= days_in_month) {
1259 if (tt->month >= 13) {
1264 day -= days_in_month;
1268 if (tt->month == 1) {
1288 icaltimezone_load_builtin_timezone(zone);
1302 return zone->location;
1312 icaltimezone_load_builtin_timezone(zone);
1314 return zone->tznames;
1326 return zone->latitude;
1338 return zone->longitude;
1348 icaltimezone_load_builtin_timezone(zone);
1350 return zone->component;
1355 icaltimezone_reset(zone);
1356 return icaltimezone_get_vtimezone_properties(zone, comp);
1359static const char *skip_slashes(
const char *text,
int n_slashes)
1362 int num_slashes = 0;
1368 for (pp = text; *pp; pp++) {
1371 if (num_slashes == n_slashes) {
1386 if (!display_name) {
1389 if (!display_name) {
1396 if (!strncmp(display_name, tzid_prefix, strlen(tzid_prefix))) {
1398 display_name += strlen(tzid_prefix);
1403 return display_name;
1407icalarray *icaltimezone_array_new(
void)
1412void icaltimezone_array_append_from_vtimezone(icalarray *timezones, icalcomponent *child)
1416 icaltimezone_init(&zone);
1417 if (icaltimezone_get_vtimezone_properties(&zone, child)) {
1422void icaltimezone_array_free(icalarray *timezones)
1425 for (
size_t i = 0; i < timezones->num_elements; i++) {
1441 if (!builtin_timezones) {
1442 icaltimezone_init_builtin_timezones();
1445 return builtin_timezones;
1450 (void)icaltimezone_builtin_lock();
1451 icaltimezone_array_free(builtin_timezones);
1452 builtin_timezones = 0;
1453 (void)icaltimezone_builtin_unlock();
1458 icalcomponent *comp;
1462 if (!location || !location[0]) {
1466 if (!builtin_timezones) {
1467 icaltimezone_init_builtin_timezones();
1470 if (strcmp(location,
"UTC") == 0 || strcmp(location,
"GMT") == 0) {
1471 return &utc_timezone;
1476 for (lower = 0; lower < builtin_timezones->num_elements; lower++) {
1479 if (zone_location && strcmp(location, zone_location) == 0) {
1486 comp = icaltimezone_fetch_timezone(location);
1490 icaltimezone_init(&tz);
1502static struct icaltimetype tm_to_icaltimetype(const struct tm *tm)
1508 itt.second = tm->tm_sec;
1509 itt.minute = tm->tm_min;
1510 itt.hour = tm->tm_hour;
1512 itt.day = tm->tm_mday;
1513 itt.month = tm->tm_mon + 1;
1514 itt.year = tm->tm_year + 1900;
1526 const icaltime_t now = icaltime(NULL);
1528 memset(&local, 0,
sizeof(
struct tm));
1529 if (!icalgmtime_r(&now, &local)) {
1533 tt = tm_to_icaltimetype(&local);
1541 if (!builtin_timezones) {
1542 icaltimezone_init_builtin_timezones();
1546 return &utc_timezone;
1553 size_t count = builtin_timezones->num_elements;
1555 for (
size_t i = 0; i < count; i++) {
1558 icaltimezone_load_builtin_timezone(
zone);
1559 int z_offset = get_offset(
zone);
1560 if (z_offset == offset &&
zone->tznames && !strcmp(tzname,
zone->tznames)) {
1571 const char *p, *zone_tzid, *tzid_prefix;
1575 if (!tzid || !tzid[0]) {
1579 if (strcmp(tzid,
"UTC") == 0 || strcmp(tzid,
"GMT") == 0) {
1585 if (strncmp(tzid, tzid_prefix, strlen(tzid_prefix)) != 0) {
1588 for (ii = 0; glob_compat_tzids[ii].tzid; ii++) {
1589 if (strncmp(tzid, glob_compat_tzids[ii].tzid, strlen(glob_compat_tzids[ii].tzid)) == 0) {
1590 p = skip_slashes(tzid, glob_compat_tzids[ii].slashes);
1605 p = tzid + strlen(tzid_prefix);
1610 strncmp(p,
"Tzfile/", 7) == 0) {
1618 if (!
zone || compat) {
1622#if defined(USE_BUILTIN_TZDATA)
1623 if (use_builtin_tzdata) {
1631 if (!strcmp(zone_tzid, tzid)) {
1640 if (!builtin_timezones) {
1641 icaltimezone_init_builtin_timezones();
1644 return &utc_timezone;
1653static bool icaltimezone_init_builtin_timezones(
void)
1656 utc_timezone.tzid = (
char *)
"UTC";
1658 if (!icaltimezone_builtin_lock()) {
1661 if (!builtin_timezones) {
1662 icaltimezone_parse_zone_tab();
1664 if (!icaltimezone_builtin_unlock()) {
1670static bool parse_coord(
const char *coord,
int len,
int *degrees,
int *minutes,
int *seconds)
1672 icalerror_check_arg_rz((coord != 0),
"coord");
1680 if (sscanf(coord + 1,
"%2d%2d", degrees, minutes) == 2) {
1683 }
else if (len == 6) {
1684 if (sscanf(coord + 1,
"%3d%2d", degrees, minutes) == 2) {
1687 }
else if (len == 7) {
1688 if (sscanf(coord + 1,
"%2d%2d%2d", degrees, minutes, seconds) == 3) {
1691 }
else if (len == 8) {
1692 if (sscanf(coord + 1,
"%3d%2d%2d", degrees, minutes, seconds) == 3) {
1698 icalerrprintf(
"Invalid coordinate: %s\n", coord);
1700 if (coord[0] ==
'-') {
1701 *degrees = -*degrees;
1708static bool fetch_lat_long_from_string(
const char *
str,
1709 int *latitude_degrees,
int *latitude_minutes,
1710 int *latitude_seconds,
1711 int *longitude_degrees,
int *longitude_minutes,
1712 int *longitude_seconds,
1713 char *location,
size_t len_location)
1716 const char *loc, *temp;
1720 if (!location || !len_location) {
1725 const size_t len_str = strlen(
str);
1728 while ((*sptr !=
'\t') && (*sptr !=
'\0')) {
1736 while (*sptr !=
'\t' && *sptr !=
'\0') {
1739 len = (size_t)(ptrdiff_t)(sptr - temp);
1747 memset(lat,
'\0', len + 1);
1748 strncpy(lat, temp, len + 1);
1750 while ((*sptr !=
'\t') && (*sptr !=
'\0')) {
1754 while (!isspace((
int)(*sptr)) && (*sptr !=
'\0')) {
1757 len = (size_t)(ptrdiff_t)(sptr - loc);
1758 if (len >= len_location) {
1759 len = len_location - 1;
1761 strncpy(location, loc, len);
1762 location[len] =
'\0';
1765 while (*lon !=
'\0' && *lon !=
'+' && *lon !=
'-') {
1769 if (parse_coord(lat, (
int)(lon - lat),
1772 latitude_seconds) == 1 ||
1773 parse_coord(lon, (
int)strlen(lon),
1774 longitude_degrees, longitude_minutes, longitude_seconds) == 1) {
1794static void icaltimezone_parse_zone_tab(
void)
1796 const char *zonedir, *zonetab;
1799 size_t filename_len;
1801 icalerror_assert(builtin_timezones == NULL,
"Parsing zones.tab file multiple times");
1805 if (!use_builtin_tzdata) {
1807 zonetab = ZONES_TAB_SYSTEM_FILENAME;
1809 zonedir = get_zone_directory_builtin();
1815 filename_len = strlen(zonedir);
1818 icalerror_assert(filename_len > 0,
"Unable to locate a zoneinfo dir");
1819 if (filename_len == 0) {
1821 builtin_timezones = timezones;
1827 filename_len += strlen(zonetab);
1833 builtin_timezones = timezones;
1838 snprintf(filename, filename_len,
"%s/%s", zonedir, zonetab);
1840 fp = fopen(filename,
"r");
1841 icalerror_assert(fp,
"Cannot open the zonetab file for reading");
1844 builtin_timezones = timezones;
1851#if !defined(__clang_analyzer__)
1853 while (!feof(fp) && !ferror(fp) && fgets(buf, (
int)
sizeof(buf), fp)) {
1854 char location[1024] = {0};
1855 int latitude_degrees = 360;
1856 int longitude_degrees = 360;
1857 int latitude_minutes = 0;
1858 int longitude_minutes = 0;
1859 int latitude_seconds = 0;
1860 int longitude_seconds = 0;
1862 if (buf[0] ==
'\0') {
1869 if (use_builtin_tzdata) {
1871 if (buf[0] !=
'+' && buf[0] !=
'-') {
1872 if (sscanf(buf,
"%1000s", location) != 1) {
1875 icalerrprintf(
"%s: Invalid timezone description line: %s\n", filename, buf);
1878 }
else if (sscanf(buf,
"%4d%2d%2d %4d%2d%2d %1000s",
1881 &latitude_degrees, &latitude_minutes,
1883 &longitude_degrees, &longitude_minutes,
1884 &longitude_seconds, location) != 7) {
1885 icalerrprintf(
"%s: Invalid timezone description line: %s\n", filename, buf);
1889 if (fetch_lat_long_from_string(buf, &latitude_degrees, &latitude_minutes,
1891 &longitude_degrees, &longitude_minutes,
1892 &longitude_seconds, location,
sizeof(location))) {
1893 icalerrprintf(
"%s: Invalid timezone description line: %s\n", filename, buf);
1896 if (location[0] ==
'\0') {
1897 icalerrprintf(
"%s: Invalid timezone location: %s\n", filename, buf);
1902 icaltimezone_init(&
zone);
1906 if (latitude_degrees >= 0) {
1908 (double)latitude_degrees +
1909 (
double)latitude_minutes / 60 +
1910 (double)latitude_seconds / 3600;
1913 (double)latitude_degrees -
1914 (
double)latitude_minutes / 60 -
1915 (double)latitude_seconds / 3600;
1918 if (longitude_degrees >= 0) {
1920 (double)longitude_degrees +
1921 (
double)longitude_minutes / 60 +
1922 (double)longitude_seconds / 3600;
1925 (double)longitude_degrees -
1926 (
double)longitude_minutes / 60 -
1927 (double)longitude_seconds / 3600;
1932#ifdef ICALTIMEZONE_DEBUG_PRINT
1933 printf(
"Found zone: %s %f %f\n", location,
zone.latitude,
zone.longitude);
1938 builtin_timezones = timezones;
1949 icalcomponent *comp = 0, *subcomp;
1952 if (
zone->component) {
1956 if (!icaltimezone_builtin_lock()) {
1961 if (
zone->component) {
1962 if (!icaltimezone_builtin_unlock()) {
1969 if (!
zone->location || !
zone->location[0]) {
1970 if (!icaltimezone_builtin_unlock()) {
1976 if (use_builtin_tzdata) {
1978 size_t filename_len;
1982 filename_len = strlen(get_zone_directory_builtin()) + strlen(
zone->location) + 6;
1990 snprintf(filename, filename_len,
"%s/%s.ics", get_zone_directory_builtin(),
zone->location);
1992 fp = fopen(filename,
"r");
2021 size_t new_tzid_len;
2024 new_tzid_len = strlen(tzid_prefix) + strlen(
zone->location) + 1;
2027 snprintf(new_tzid, new_tzid_len,
"%s%s", tzid_prefix,
zone->location);
2050 if (name && !strcasecmp(name,
"X-LIC-LOCATION")) {
2057 subcomp = icaltimezone_fetch_timezone(
zone->location);
2065 icaltimezone_get_vtimezone_properties(
zone, subcomp);
2067 if (use_builtin_tzdata) {
2073 if (!icaltimezone_builtin_unlock()) {
2082static char *icaltimezone_load_get_line_fn(
char *s,
size_t size,
void *data)
2084 return fgets(s, (
int)size, (FILE *)data);
2093 static const char months[][4] = {
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
2094 "Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"};
2095 const icaltimezonechange *zone_change;
2100 if (!icaltimezone_ensure_coverage(
zone, max_year)) {
2104#ifdef ICALTIMEZONE_DEBUG_PRINT
2105 printf(
"Num changes: %zu\n",
zone->changes->num_elements);
2108 if (!icaltimezone_changes_lock()) {
2112 for (change_num = 0; change_num <
zone->changes->num_elements; change_num++) {
2115 if (zone_change->year > max_year) {
2119 fprintf(fp,
"%s\t%2i %s %04i\t%2i:%02i:%02i",
2121 zone_change->day, months[zone_change->month - 1],
2122 zone_change->year, zone_change->hour, zone_change->minute, zone_change->second);
2125 format_utc_offset(zone_change->utc_offset, buffer,
sizeof(buffer));
2126 fprintf(fp,
"\t%s", buffer);
2131 if (!icaltimezone_changes_unlock()) {
2143static void format_utc_offset(
int utc_offset,
char *buffer,
size_t buffer_size)
2145 const char *sign =
"+";
2146 int hours, minutes, seconds;
2148 if (utc_offset < 0) {
2149 utc_offset = -utc_offset;
2153 hours = utc_offset / 3600;
2154 minutes = (utc_offset % 3600) / 60;
2155 seconds = utc_offset % 60;
2160 if (hours < 0 || hours >= 24 || minutes < 0 || minutes >= 60 || seconds < 0) {
2161 icalerrprintf(
"Warning: Strange timezone offset: H:%i M:%i S:%i\n",
2162 hours, minutes, seconds);
2164#if defined(__GNUC__) && !defined(__clang__)
2165#pragma GCC diagnostic push
2166#pragma GCC diagnostic ignored "-Wformat-truncation"
2169 snprintf(buffer, buffer_size,
"%s%02i%02i", sign, hours, minutes);
2171 snprintf(buffer, buffer_size,
"%s%02i%02i%02i", sign, hours, minutes, seconds);
2174#if defined(__GNUC__) && !defined(__clang__)
2175#pragma GCC diagnostic pop
2178static const char *get_zone_directory_builtin(
void)
2183 wchar_t wbuffer[1000];
2185#if !defined(_WIN32_WCE)
2186 char buffer[1000], zoneinfodir[1000], dirname[1000];
2189 wchar_t zoneinfodir[1000], dirname[1000];
2191 static ICAL_GLOBAL_VAR
char *cache = NULL;
2193#if !defined(_WIN32_WCE)
2194 unsigned char *dirslash, *zislash;
2195 const char *zislashp1;
2197 wchar_t *dirslash, *zislash;
2201 if (zone_files_directory) {
2202 return zone_files_directory;
2211 if (!GetModuleFileNameW(NULL, wbuffer,
sizeof(wbuffer) /
sizeof(wbuffer[0]))) {
2216#if !defined(_WIN32_WCE)
2218 if (!WideCharToMultiByte(CP_ACP, 0, wbuffer, -1, buffer,
sizeof(buffer),
2219 NULL, &used_default) ||
2222 if (!GetShortPathNameW(wbuffer, wbuffer,
2223 sizeof(wbuffer) /
sizeof(wbuffer[0])) ||
2224 !WideCharToMultiByte(CP_ACP, 0, wbuffer, -1, buffer,
sizeof(buffer),
2225 NULL, &used_default) ||
2266#if !defined(_WIN32_WCE)
2267 dirslash = _mbsrchr((
unsigned char *)buffer,
'\\');
2269 dirslash = wcsrchr(wbuffer, L
'\\');
2272#if !defined(_WIN32_WCE)
2279#if defined(_WIN32_WCE)
2280 while ((dirslash = wcsrchr(wbuffer,
'\\'))) {
2286 while ((zislash = wcschr(zoneinfodir, L
'/'))) {
2288 wcscpy(dirname, wbuffer);
2289 wcscat(dirname,
"/");
2290 wcscat(dirname, zislash + 1);
2291 if (stat(dirname, &st) == 0 && S_ISDIR(st.st_mode)) {
2292 cache = wce_wctomb(dirname);
2298 while ((dirslash = _mbsrchr((
unsigned char *)buffer,
'\\'))) {
2303 while ((zislash = _mbschr((
unsigned char *)zoneinfodir,
'/'))) {
2305 strcpy(dirname, buffer);
2306 strcat(dirname,
"/");
2307 zislashp1 = (
const char *)(zislash + 1);
2308 strcat(dirname, zislashp1);
2309 if (stat(dirname, &st) == 0 && S_ISDIR(st.st_mode)) {
2322 if ((zonepath == NULL) || (zonepath[0] ==
'\0')) {
2323 memset(s_zoneinfopath, 0, MAXPATHLEN);
2325 strncpy(s_zoneinfopath, zonepath, MAXPATHLEN - 1);
2329static void set_zoneinfopath(
void)
2331 char file_path[MAXPATHLEN] = {0};
2332 const char *fname = ZONES_TAB_SYSTEM_FILENAME;
2333 size_t i, num_zi_search_paths;
2336 const char *env_tzdir = getenv(
"TZDIR");
2337 if (env_tzdir != NULL) {
2338 snprintf(file_path, MAXPATHLEN,
"%s/%s", env_tzdir, fname);
2339 if (!access(file_path, F_OK | R_OK)) {
2340 strncpy(s_zoneinfopath, env_tzdir, MAXPATHLEN - 1);
2346 num_zi_search_paths =
sizeof(s_zoneinfo_search_paths) /
sizeof(s_zoneinfo_search_paths[0]);
2347 for (i = 0; i < num_zi_search_paths; i++) {
2348 snprintf(file_path, MAXPATHLEN,
"%s/%s", s_zoneinfo_search_paths[i], fname);
2349 if (!access(file_path, F_OK | R_OK)) {
2350 strncpy(s_zoneinfopath, s_zoneinfo_search_paths[i], MAXPATHLEN - 1);
2357 if (s_zoneinfopath[0] ==
'\0') {
2361 return s_zoneinfopath;
2366 if (use_builtin_tzdata) {
2367 return get_zone_directory_builtin();
2375 if (zone_files_directory) {
2379 const size_t len_path = strlen(path) + 1;
2382 if (zone_files_directory != NULL) {
2383 strncpy(zone_files_directory, path, len_path);
2384 zone_files_directory[len_path - 1] =
'\0';
2390 if (zone_files_directory != NULL) {
2392 zone_files_directory = NULL;
2405 use_builtin_tzdata = set;
2410 return use_builtin_tzdata;
2420static void check_tombstone(
struct observance *tombstone,
2421 struct observance *obs)
2426 tombstone->offset_from = tombstone->offset_to = obs->offset_to;
2427 tombstone->onset = obs->onset;
2433 struct icaldatetimeperiodtype date;
2436static int rdate_compare(
const void *rdate1,
const void *rdate2)
2439 ((
struct rdate *)rdate2)->date.time);
2446 icalcomponent *comp, *nextc, *tomb_std = NULL, *tomb_day = NULL;
2447 icalproperty *prop, *proleptic_prop = NULL;
2448 struct observance tombstone;
2452 if (!need_tomb && !need_tzuntil) {
2462 proleptic_prop = prop;
2467 memset(&tombstone, 0,
sizeof(
struct observance));
2469 if (!proleptic_prop ||
2471 tombstone.onset.
year = -1;
2476 comp; comp = nextc) {
2477 icalproperty *dtstart_prop = NULL, *rrule_prop = NULL;
2478 icalarray *rdates =
icalarray_new(
sizeof(
struct rdate), 10);
2480 struct observance obs;
2482 unsigned trunc_dtstart = 0;
2487 memset(&obs, 0,
sizeof(
struct observance));
2488 obs.offset_from = obs.offset_to = INT_MAX;
2502 dtstart_prop = prop;
2523 rdate.date.time = dtp.
time;
2524 rdate.date.period = dtp.
period;
2537 if (!dtstart_prop || !obs.name ||
2538 obs.offset_from == INT_MAX || obs.offset_to == INT_MAX) {
2563 check_tombstone(&tombstone, &obs);
2568 }
else if (r == 0) {
2577 icalrecur_iterator *ritr = NULL;
2578 unsigned trunc_until = 0;
2584 obs.onset = rrule->
until;
2586 check_tombstone(&tombstone, &obs);
2608 if (ritr && trunc_dtstart) {
2613 newstart.
day = start.
day;
2636 ydiff = (unsigned)(prev_onset.
year - dtstart.
year);
2645 rdate.
time = prev_onset;
2653 rrule->
until = prev_onset;
2666 if (ms_compatible) {
2672 if (proleptic_prop) {
2676 proleptic_prop = NULL;
2680 check_tombstone(&tombstone, &obs);
2688 if (trunc_dtstart) {
2691 dtstart = obs.onset;
2696 if (!trunc_until && ydiff <= 1) {
2719 ydiff = (unsigned)(end.
year - recur.
year);
2729 prev_onset = obs.onset;
2740 for (n = 0; n < rdates->num_elements; n++) {
2745 rdate->date.time.hour = dtstart.
hour;
2746 rdate->date.time.minute = dtstart.
minute;
2747 rdate->date.time.second = dtstart.
second;
2757 obs.onset = rdate->date.time;
2775 check_tombstone(&tombstone, &obs);
2787 if (trunc_dtstart) {
2801 if (trunc_dtstart) {
2809 }
else if (!tomb_std) {
2824 icalcomponent *tomb;
2825 icalproperty *tomb_prop, *nextp;
2838 tomb_prop; tomb_prop = nextp) {
2866 if (proleptic_prop) {
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)
void icalproperty_set_dtstart(icalproperty *prop, struct icaltimetype v)
void icalproperty_set_tzuntil(icalproperty *prop, struct icaltimetype v)
void icalproperty_set_x(icalproperty *prop, const char *v)
icalproperty * icalproperty_new_rdate(struct icaldatetimeperiodtype v)
void icalproperty_set_tzid(icalproperty *prop, const char *v)
void icalproperty_set_tzoffsetfrom(icalproperty *prop, int v)
const char * icalproperty_get_tzname(const icalproperty *prop)
void icalproperty_set_tzoffsetto(icalproperty *prop, int v)
void icalproperty_set_rrule(icalproperty *prop, struct icalrecurrencetype *v)
icalproperty * icalproperty_new_tzuntil(struct icaltimetype v)
void icalproperty_set_tzname(icalproperty *prop, const char *v)
void icalproperty_set_location(icalproperty *prop, const char *v)
const char * icalproperty_get_x(const icalproperty *prop)
struct icalrecurrencetype * icalproperty_get_rrule(const icalproperty *prop)
const char * icalproperty_get_tzid(const icalproperty *prop)
struct icaldatetimeperiodtype icalproperty_get_rdate(const icalproperty *prop)
struct icaltimetype icalproperty_get_dtstart(const icalproperty *prop)
int icalproperty_get_tzoffsetto(const icalproperty *prop)
int icalproperty_get_tzoffsetfrom(const icalproperty *prop)
@ ICAL_TZOFFSETFROM_PROPERTY
@ ICAL_TZOFFSETTO_PROPERTY
const char * icalproperty_get_location(const icalproperty *prop)
@ 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