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
bool use_builtin_tzdata =
true;
152static ICAL_GLOBAL_VAR
bool 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 bool found_dtstart =
false, found_tzoffsetto =
false, found_tzoffsetfrom =
false;
613 int has_rdate =
false, has_rrule =
false;
618 change.is_daylight = 0;
620 change.is_daylight = 1;
625 change.utc_offset = 0;
626 change.prev_utc_offset = 0;
636 found_dtstart =
true;
641 if ((tmpl > -INT_MAX) && (tmpl < INT_MAX)) {
642 change.utc_offset = (int)tmpl;
644 found_tzoffsetto =
true;
650 if ((tmpl > -INT_MAX) && (tmpl < INT_MAX)) {
651 change.prev_utc_offset = (int)tmpl;
652 found_tzoffsetfrom =
true;
673 if (found_tzoffsetto && !found_tzoffsetfrom) {
674 change.prev_utc_offset = change.utc_offset;
675 found_tzoffsetfrom =
true;
680 if (!found_dtstart || !found_tzoffsetto || !found_tzoffsetfrom) {
684#ifdef ICALTIMEZONE_DEBUG_PRINT
685 printf(
"\n Expanding component DTSTART (Y/M/D): %i/%i/%i %i:%02i:%02i\n",
692 change.year = dtstart.
year;
693 change.month = dtstart.
month;
694 change.day = dtstart.
day;
695 change.hour = dtstart.
hour;
696 change.minute = dtstart.
minute;
697 change.second = dtstart.
second;
700 icaltimezone_adjust_change(&change, 0, 0, 0, -change.prev_utc_offset);
702#ifdef ICALTIMEZONE_DEBUG_PRINT
703 printf(
" Appending single DTSTART (Y/M/D): %i/%02i/%02i %i:%02i:%02i\n",
704 change.year, change.month, change.day, change.hour, change.minute, change.second);
715 while (prop && (has_rdate || has_rrule)) {
716#ifdef ICALTIMEZONE_DEBUG_PRINT
717 printf(
"Expanding property...\n");
722 change.year = rdate.time.year;
723 change.month = rdate.time.month;
724 change.day = rdate.time.day;
728 change.hour = dtstart.
hour;
729 change.minute = dtstart.
minute;
730 change.second = dtstart.
second;
732 change.hour = rdate.time.hour;
733 change.minute = rdate.time.minute;
734 change.second = rdate.time.second;
740 icaltimezone_adjust_change(&change, 0, 0, 0, -change.prev_utc_offset);
744#ifdef ICALTIMEZONE_DEBUG_PRINT
745 printf(
" Appending RDATE element (Y/M/D): %i/%02i/%02i %i:%02i:%02i\n",
746 change.year, change.month, change.day,
747 change.hour, change.minute, change.second);
763#ifdef ICALTIMEZONE_DEBUG_PRINT
764 printf(
" Found RRULE UNTIL in UTC.\n");
776 change.year = dtstart.
year;
777 change.month = dtstart.
month;
778 change.day = dtstart.
day;
779 change.hour = dtstart.
hour;
780 change.minute = dtstart.
minute;
781 change.second = dtstart.
second;
783#ifdef ICALTIMEZONE_DEBUG_PRINT
784 printf(
" Appending RRULE element (Y/M/D): %i/%02i/%02i %i:%02i:%02i\n",
785 change.year, change.month, change.day,
786 change.hour, change.minute, change.second);
789 icaltimezone_adjust_change(&change, 0, 0, 0, -change.prev_utc_offset);
794 for (
size_t rrule_iterator_count = 0; rrule_iterator && rrule_iterator_count < max_rrule_search; rrule_iterator_count++) {
803 change.year = occ.
year;
804 change.month = occ.
month;
805 change.day = occ.
day;
806 change.hour = occ.
hour;
807 change.minute = occ.
minute;
808 change.second = occ.
second;
810#ifdef ICALTIMEZONE_DEBUG_PRINT
811 printf(
" Appending RRULE element (Y/M/D): %i/%02i/%02i %i:%02i:%02i\n",
812 change.year, change.month, change.day,
813 change.hour, change.minute, change.second);
816 icaltimezone_adjust_change(&change, 0, 0, 0, -change.prev_utc_offset);
836static int icaltimezone_compare_change_fn(
const void *elem1,
const void *elem2)
838 const icaltimezonechange *change1, *change2;
841 change1 = (
const icaltimezonechange *)elem1;
842 change2 = (
const icaltimezonechange *)elem2;
844 if (change1->year < change2->year) {
846 }
else if (change1->year > change2->year) {
848 }
else if (change1->month < change2->month) {
850 }
else if (change1->month > change2->month) {
852 }
else if (change1->day < change2->day) {
854 }
else if (change1->day > change2->day) {
856 }
else if (change1->hour < change2->hour) {
858 }
else if (change1->hour > change2->hour) {
860 }
else if (change1->minute < change2->minute) {
862 }
else if (change1->minute > change2->minute) {
864 }
else if (change1->second < change2->second) {
866 }
else if (change1->second > change2->second) {
878 int utc_offset, is_daylight;
900 const icaltimezonechange *zone_change;
901 const icaltimezonechange *prev_zone_change;
902 icaltimezonechange tt_change = {0}, tmp_change;
903 size_t change_num, change_num_to_use;
905 int step, utc_offset_change, cmp;
917 if (zone == NULL || zone == &utc_timezone) {
922 if (zone->builtin_timezone) {
923 zone = zone->builtin_timezone;
926 if (!icaltimezone_changes_lock()) {
931 if (!icaltimezone_ensure_coverage(zone, tt->
year)) {
935 if (!zone->changes || zone->changes->num_elements == 0) {
936 (void)icaltimezone_changes_unlock();
942 tt_change.year = tt->
year;
943 tt_change.month = tt->
month;
944 tt_change.day = tt->
day;
945 tt_change.hour = tt->
hour;
946 tt_change.minute = tt->
minute;
947 tt_change.second = tt->
second;
951 change_num = icaltimezone_find_nearby_change(zone, &tt_change);
958 change_num_to_use = (size_t)-1;
961 tmp_change = *zone_change;
966 icaltimezone_adjust_change(&tmp_change, 0, 0, 0, tmp_change.utc_offset);
968 cmp = icaltimezone_compare_change_fn(&tt_change, &tmp_change);
977 change_num_to_use = change_num;
985 if (step == -1 && found_change == 1) {
991 if (change_num == 0 && step < 0) {
993 *is_daylight = !tmp_change.is_daylight;
996 if (!icaltimezone_changes_unlock()) {
1000 return tmp_change.prev_utc_offset;
1009 if (change_num >= zone->changes->num_elements) {
1017 icalerror_assert(found_change == 1,
"No applicable timezone change found");
1023 utc_offset_change = zone_change->utc_offset - zone_change->prev_utc_offset;
1024 if (utc_offset_change < 0 && change_num_to_use > 0) {
1025 tmp_change = *zone_change;
1026 icaltimezone_adjust_change(&tmp_change, 0, 0, 0, tmp_change.prev_utc_offset);
1028 if (icaltimezone_compare_change_fn(&tt_change, &tmp_change) < 0) {
1038#ifdef ICALTIMEZONE_DEBUG_PRINT
1039 if (zone_change->is_daylight == prev_zone_change->is_daylight) {
1040 printf(
" **** Same is_daylight setting\n");
1044 if (!want_daylight ||
1045 (zone_change->is_daylight != want_daylight &&
1046 prev_zone_change->is_daylight == want_daylight)) {
1047 zone_change = prev_zone_change;
1055 *is_daylight = zone_change->is_daylight;
1057 utc_offset_change = zone_change->utc_offset;
1059 if (!icaltimezone_changes_unlock()) {
1063 return utc_offset_change;
1069 const icaltimezonechange *zone_change;
1070 icaltimezonechange tt_change, tmp_change;
1071 size_t change_num, change_num_to_use;
1072 int found_change = 1;
1073 int step, utc_offset;
1080 if (zone == NULL || zone == &utc_timezone) {
1085 if (zone->builtin_timezone) {
1086 zone = zone->builtin_timezone;
1089 if (!icaltimezone_changes_lock()) {
1094 if (!icaltimezone_ensure_coverage(zone, tt->
year)) {
1098 if (!zone->changes || zone->changes->num_elements == 0) {
1099 (void)icaltimezone_changes_unlock();
1105 tt_change.year = tt->
year;
1106 tt_change.month = tt->
month;
1107 tt_change.day = tt->
day;
1108 tt_change.hour = tt->
hour;
1109 tt_change.minute = tt->
minute;
1110 tt_change.second = tt->
second;
1114 change_num = icaltimezone_find_nearby_change(zone, &tt_change);
1121 change_num_to_use = (size_t)-1;
1124 tmp_change = *zone_change;
1131 if (icaltimezone_compare_change_fn(&tt_change, &tmp_change) >= 0) {
1133 change_num_to_use = change_num;
1141 if (step == -1 && found_change == 1) {
1147 if (change_num == 0 && step < 0) {
1149 *is_daylight = !tmp_change.is_daylight;
1152 if (!icaltimezone_changes_unlock()) {
1156 return tmp_change.prev_utc_offset;
1165 if (change_num >= zone->changes->num_elements) {
1173 icalerror_assert(found_change == 1,
"No applicable timezone change found");
1179 *is_daylight = zone_change->is_daylight;
1181 utc_offset = zone_change->utc_offset;
1183 if (!icaltimezone_changes_unlock()) {
1196static size_t icaltimezone_find_nearby_change(
icaltimezone *zone,
const icaltimezonechange *change)
1198 size_t lower, middle, upper;
1202 upper = zone->changes->num_elements;
1204 while (lower < upper) {
1205 middle = (lower + upper) / 2;
1207 int cmp = icaltimezone_compare_change_fn(change, zone_change);
1210 }
else if (cmp < 0) {
1226static void icaltimezone_adjust_change(icaltimezonechange *tt,
1227 int days,
int hours,
int minutes,
int seconds)
1229 int second, minute, hour, day;
1230 int minutes_overflow, hours_overflow, days_overflow;
1233 second = tt->second + seconds;
1234 tt->second = second % 60;
1235 minutes_overflow = second / 60;
1236 if (tt->second < 0) {
1242 minute = tt->minute + minutes + minutes_overflow;
1243 tt->minute = minute % 60;
1244 hours_overflow = minute / 60;
1245 if (tt->minute < 0) {
1251 hour = tt->hour + hours + hours_overflow;
1252 tt->hour = hour % 24;
1253 days_overflow = hour / 24;
1260 day = tt->day + days + days_overflow;
1264 if (day <= days_in_month) {
1269 if (tt->month >= 13) {
1274 day -= days_in_month;
1278 if (tt->month == 1) {
1298 icaltimezone_load_builtin_timezone(zone);
1312 return zone->location;
1322 icaltimezone_load_builtin_timezone(zone);
1324 return zone->tznames;
1336 return zone->latitude;
1348 return zone->longitude;
1358 icaltimezone_load_builtin_timezone(zone);
1360 return zone->component;
1365 icaltimezone_reset(zone);
1366 return icaltimezone_get_vtimezone_properties(zone, comp);
1369static const char *skip_slashes(
const char *text,
int n_slashes)
1372 int num_slashes = 0;
1378 for (pp = text; *pp; pp++) {
1381 if (num_slashes == n_slashes) {
1396 if (!display_name) {
1399 if (!display_name) {
1406 if (!strncmp(display_name, tzid_prefix, strlen(tzid_prefix))) {
1408 display_name += strlen(tzid_prefix);
1413 return display_name;
1417icalarray *icaltimezone_array_new(
void)
1422void icaltimezone_array_append_from_vtimezone(icalarray *timezones, icalcomponent *child)
1426 icaltimezone_init(&zone);
1427 if (icaltimezone_get_vtimezone_properties(&zone, child)) {
1432void icaltimezone_array_free(icalarray *timezones)
1435 for (
size_t i = 0; i < timezones->num_elements; i++) {
1451 if (!builtin_timezones) {
1452 icaltimezone_init_builtin_timezones();
1455 return builtin_timezones;
1460 (void)icaltimezone_builtin_lock();
1461 icaltimezone_array_free(builtin_timezones);
1462 builtin_timezones = 0;
1463 (void)icaltimezone_builtin_unlock();
1468 icalcomponent *comp;
1472 if (!location || !location[0]) {
1476 if (!builtin_timezones) {
1477 icaltimezone_init_builtin_timezones();
1480 if (strcmp(location,
"UTC") == 0 || strcmp(location,
"GMT") == 0) {
1481 return &utc_timezone;
1486 for (lower = 0; lower < builtin_timezones->num_elements; lower++) {
1489 if (zone_location && strcmp(location, zone_location) == 0) {
1496 comp = icaltimezone_fetch_timezone(location);
1500 icaltimezone_init(&tz);
1512static struct icaltimetype tm_to_icaltimetype(const struct tm *tm)
1518 itt.second = tm->tm_sec;
1519 itt.minute = tm->tm_min;
1520 itt.hour = tm->tm_hour;
1522 itt.day = tm->tm_mday;
1523 itt.month = tm->tm_mon + 1;
1524 itt.year = tm->tm_year + 1900;
1536 const icaltime_t now = icaltime(NULL);
1538 memset(&local, 0,
sizeof(
struct tm));
1539 if (!icalgmtime_r(&now, &local)) {
1543 tt = tm_to_icaltimetype(&local);
1551 if (!builtin_timezones) {
1552 icaltimezone_init_builtin_timezones();
1556 return &utc_timezone;
1563 size_t count = builtin_timezones->num_elements;
1565 for (
size_t i = 0; i < count; i++) {
1568 icaltimezone_load_builtin_timezone(
zone);
1569 int z_offset = get_offset(
zone);
1570 if (z_offset == offset &&
zone->tznames && !strcmp(tzname,
zone->tznames)) {
1581 const char *p, *zone_tzid, *tzid_prefix;
1585 if (!tzid || !tzid[0]) {
1589 if (strcmp(tzid,
"UTC") == 0 || strcmp(tzid,
"GMT") == 0) {
1595 if (strncmp(tzid, tzid_prefix, strlen(tzid_prefix)) != 0) {
1598 for (ii = 0; glob_compat_tzids[ii].tzid; ii++) {
1599 if (strncmp(tzid, glob_compat_tzids[ii].tzid, strlen(glob_compat_tzids[ii].tzid)) == 0) {
1600 p = skip_slashes(tzid, glob_compat_tzids[ii].slashes);
1615 p = tzid + strlen(tzid_prefix);
1620 strncmp(p,
"Tzfile/", 7) == 0) {
1628 if (!
zone || compat) {
1632#if defined(USE_BUILTIN_TZDATA)
1633 if (use_builtin_tzdata) {
1641 if (!strcmp(zone_tzid, tzid)) {
1650 if (!builtin_timezones) {
1651 icaltimezone_init_builtin_timezones();
1654 return &utc_timezone;
1663static bool icaltimezone_init_builtin_timezones(
void)
1666 utc_timezone.tzid = (
char *)
"UTC";
1668 if (!icaltimezone_builtin_lock()) {
1671 if (!builtin_timezones) {
1672 icaltimezone_parse_zone_tab();
1674 if (!icaltimezone_builtin_unlock()) {
1680static bool parse_coord(
const char *coord,
int len,
int *degrees,
int *minutes,
int *seconds)
1682 icalerror_check_arg_rz((coord != 0),
"coord");
1691 if (sscanf(coord + 1,
"%2d%2d", degrees, minutes) == 2) {
1694 }
else if (len == 6) {
1695 if (sscanf(coord + 1,
"%3d%2d", degrees, minutes) == 2) {
1698 }
else if (len == 7) {
1699 if (sscanf(coord + 1,
"%2d%2d%2d", degrees, minutes, seconds) == 3) {
1702 }
else if (len == 8) {
1703 if (sscanf(coord + 1,
"%3d%2d%2d", degrees, minutes, seconds) == 3) {
1710 icalerrprintf(
"Invalid coordinate: %s\n", coord);
1712 if (coord[0] ==
'-') {
1713 *degrees = -*degrees;
1720static bool fetch_lat_long_from_string(
const char *
str,
1721 int *latitude_degrees,
int *latitude_minutes,
1722 int *latitude_seconds,
1723 int *longitude_degrees,
int *longitude_minutes,
1724 int *longitude_seconds,
1725 char *location,
size_t len_location)
1728 const char *loc, *temp;
1732 if (!location || !len_location) {
1737 const size_t len_str = strlen(
str);
1740 while ((*sptr !=
'\t') && (*sptr !=
'\0')) {
1748 while (*sptr !=
'\t' && *sptr !=
'\0') {
1751 len = (size_t)(ptrdiff_t)(sptr - temp);
1759 memset(lat,
'\0', len + 1);
1760 strncpy(lat, temp, len + 1);
1762 while ((*sptr !=
'\t') && (*sptr !=
'\0')) {
1766 while (!isspace((
int)(*sptr)) && (*sptr !=
'\0')) {
1769 len = (size_t)(ptrdiff_t)(sptr - loc);
1770 if (len >= len_location) {
1771 len = len_location - 1;
1773 strncpy(location, loc, len);
1774 location[len] =
'\0';
1777 while (*lon !=
'\0' && *lon !=
'+' && *lon !=
'-') {
1781 if (parse_coord(lat, (
int)(lon - lat),
1784 latitude_seconds) ||
1785 parse_coord(lon, (
int)strlen(lon),
1786 longitude_degrees, longitude_minutes, longitude_seconds)) {
1806static void icaltimezone_parse_zone_tab(
void)
1808 const char *zonedir, *zonetab;
1811 size_t filename_len;
1813 icalerror_assert(builtin_timezones == NULL,
"Parsing zones.tab file multiple times");
1817 if (!use_builtin_tzdata) {
1819 zonetab = ZONES_TAB_SYSTEM_FILENAME;
1821 zonedir = get_zone_directory_builtin();
1827 filename_len = strlen(zonedir);
1830 icalerror_assert(filename_len > 0,
"Unable to locate a zoneinfo dir");
1831 if (filename_len == 0) {
1833 builtin_timezones = timezones;
1839 filename_len += strlen(zonetab);
1845 builtin_timezones = timezones;
1850 snprintf(filename, filename_len,
"%s/%s", zonedir, zonetab);
1852 fp = fopen(filename,
"r");
1853 icalerror_assert(fp,
"Cannot open the zonetab file for reading");
1856 builtin_timezones = timezones;
1863#if !defined(__clang_analyzer__)
1865 while (!feof(fp) && !ferror(fp) && fgets(buf, (
int)
sizeof(buf), fp)) {
1866 char location[1024] = {0};
1867 int latitude_degrees = 360;
1868 int longitude_degrees = 360;
1869 int latitude_minutes = 0;
1870 int longitude_minutes = 0;
1871 int latitude_seconds = 0;
1872 int longitude_seconds = 0;
1874 if (buf[0] ==
'\0') {
1881 if (use_builtin_tzdata) {
1883 if (buf[0] !=
'+' && buf[0] !=
'-') {
1884 if (sscanf(buf,
"%1000s", location) != 1) {
1887 icalerrprintf(
"%s: Invalid timezone description line: %s\n", filename, buf);
1890 }
else if (sscanf(buf,
"%4d%2d%2d %4d%2d%2d %1000s",
1893 &latitude_degrees, &latitude_minutes,
1895 &longitude_degrees, &longitude_minutes,
1896 &longitude_seconds, location) != 7) {
1897 icalerrprintf(
"%s: Invalid timezone description line: %s\n", filename, buf);
1901 if (fetch_lat_long_from_string(buf, &latitude_degrees, &latitude_minutes,
1903 &longitude_degrees, &longitude_minutes,
1904 &longitude_seconds, location,
sizeof(location))) {
1905 icalerrprintf(
"%s: Invalid timezone description line: %s\n", filename, buf);
1908 if (location[0] ==
'\0') {
1909 icalerrprintf(
"%s: Invalid timezone location: %s\n", filename, buf);
1914 icaltimezone_init(&
zone);
1918 if (latitude_degrees >= 0) {
1920 (double)latitude_degrees +
1921 (
double)latitude_minutes / 60 +
1922 (double)latitude_seconds / 3600;
1925 (double)latitude_degrees -
1926 (
double)latitude_minutes / 60 -
1927 (double)latitude_seconds / 3600;
1930 if (longitude_degrees >= 0) {
1932 (double)longitude_degrees +
1933 (
double)longitude_minutes / 60 +
1934 (double)longitude_seconds / 3600;
1937 (double)longitude_degrees -
1938 (
double)longitude_minutes / 60 -
1939 (double)longitude_seconds / 3600;
1944#ifdef ICALTIMEZONE_DEBUG_PRINT
1945 printf(
"Found zone: %s %f %f\n", location,
zone.latitude,
zone.longitude);
1950 builtin_timezones = timezones;
1961 icalcomponent *comp = 0, *subcomp;
1964 if (
zone->component) {
1968 if (!icaltimezone_builtin_lock()) {
1973 if (
zone->component) {
1974 if (!icaltimezone_builtin_unlock()) {
1981 if (!
zone->location || !
zone->location[0]) {
1982 if (!icaltimezone_builtin_unlock()) {
1988 if (use_builtin_tzdata) {
1990 size_t filename_len;
1994 filename_len = strlen(get_zone_directory_builtin()) + strlen(
zone->location) + 6;
2002 snprintf(filename, filename_len,
"%s/%s.ics", get_zone_directory_builtin(),
zone->location);
2004 fp = fopen(filename,
"r");
2033 size_t new_tzid_len;
2036 new_tzid_len = strlen(tzid_prefix) + strlen(
zone->location) + 1;
2039 snprintf(new_tzid, new_tzid_len,
"%s%s", tzid_prefix,
zone->location);
2062 if (name && !strcasecmp(name,
"X-LIC-LOCATION")) {
2069 subcomp = icaltimezone_fetch_timezone(
zone->location);
2077 icaltimezone_get_vtimezone_properties(
zone, subcomp);
2079 if (use_builtin_tzdata) {
2085 if (!icaltimezone_builtin_unlock()) {
2094static char *icaltimezone_load_get_line_fn(
char *s,
size_t size,
void *data)
2096 return fgets(s, (
int)size, (FILE *)data);
2105 static const char months[][4] = {
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
2106 "Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"};
2107 const icaltimezonechange *zone_change;
2112 if (!icaltimezone_ensure_coverage(
zone, max_year)) {
2116#ifdef ICALTIMEZONE_DEBUG_PRINT
2117 printf(
"Num changes: %zu\n",
zone->changes->num_elements);
2120 if (!icaltimezone_changes_lock()) {
2124 for (change_num = 0; change_num <
zone->changes->num_elements; change_num++) {
2127 if (zone_change->year > max_year) {
2131 fprintf(fp,
"%s\t%2i %s %04i\t%2i:%02i:%02i",
2133 zone_change->day, months[zone_change->month - 1],
2134 zone_change->year, zone_change->hour, zone_change->minute, zone_change->second);
2137 format_utc_offset(zone_change->utc_offset, buffer,
sizeof(buffer));
2138 fprintf(fp,
"\t%s", buffer);
2143 if (!icaltimezone_changes_unlock()) {
2155static void format_utc_offset(
int utc_offset,
char *buffer,
size_t buffer_size)
2157 const char *sign =
"+";
2158 int hours, minutes, seconds;
2160 if (utc_offset < 0) {
2161 utc_offset = -utc_offset;
2165 hours = utc_offset / 3600;
2166 minutes = (utc_offset % 3600) / 60;
2167 seconds = utc_offset % 60;
2172 if (hours < 0 || hours >= 24 || minutes < 0 || minutes >= 60 || seconds < 0) {
2173 icalerrprintf(
"Warning: Strange timezone offset: H:%i M:%i S:%i\n",
2174 hours, minutes, seconds);
2176#if defined(__GNUC__) && !defined(__clang__)
2177#pragma GCC diagnostic push
2178#pragma GCC diagnostic ignored "-Wformat-truncation"
2181 snprintf(buffer, buffer_size,
"%s%02i%02i", sign, hours, minutes);
2183 snprintf(buffer, buffer_size,
"%s%02i%02i%02i", sign, hours, minutes, seconds);
2186#if defined(__GNUC__) && !defined(__clang__)
2187#pragma GCC diagnostic pop
2190static const char *get_zone_directory_builtin(
void)
2195 wchar_t wbuffer[1000];
2197#if !defined(_WIN32_WCE)
2198 char buffer[1000], zoneinfodir[1000], dirname[1000];
2201 wchar_t zoneinfodir[1000], dirname[1000];
2203 static ICAL_GLOBAL_VAR
char *cache = NULL;
2205#if !defined(_WIN32_WCE)
2206 unsigned char *dirslash, *zislash;
2207 const char *zislashp1;
2209 wchar_t *dirslash, *zislash;
2213 if (zone_files_directory) {
2214 return zone_files_directory;
2223 if (!GetModuleFileNameW(NULL, wbuffer,
sizeof(wbuffer) /
sizeof(wbuffer[0]))) {
2228#if !defined(_WIN32_WCE)
2230 if (!WideCharToMultiByte(CP_ACP, 0, wbuffer, -1, buffer,
sizeof(buffer),
2231 NULL, &used_default) ||
2234 if (!GetShortPathNameW(wbuffer, wbuffer,
2235 sizeof(wbuffer) /
sizeof(wbuffer[0])) ||
2236 !WideCharToMultiByte(CP_ACP, 0, wbuffer, -1, buffer,
sizeof(buffer),
2237 NULL, &used_default) ||
2278#if !defined(_WIN32_WCE)
2279 dirslash = _mbsrchr((
unsigned char *)buffer,
'\\');
2281 dirslash = wcsrchr(wbuffer, L
'\\');
2284#if !defined(_WIN32_WCE)
2291#if defined(_WIN32_WCE)
2292 while ((dirslash = wcsrchr(wbuffer,
'\\'))) {
2298 while ((zislash = wcschr(zoneinfodir, L
'/'))) {
2300 wcscpy(dirname, wbuffer);
2301 wcscat(dirname,
"/");
2302 wcscat(dirname, zislash + 1);
2303 if (stat(dirname, &st) == 0 && S_ISDIR(st.st_mode)) {
2304 cache = wce_wctomb(dirname);
2310 while ((dirslash = _mbsrchr((
unsigned char *)buffer,
'\\'))) {
2315 while ((zislash = _mbschr((
unsigned char *)zoneinfodir,
'/'))) {
2317 strcpy(dirname, buffer);
2318 strcat(dirname,
"/");
2319 zislashp1 = (
const char *)(zislash + 1);
2320 strcat(dirname, zislashp1);
2321 if (stat(dirname, &st) == 0 && S_ISDIR(st.st_mode)) {
2334 if ((zonepath == NULL) || (zonepath[0] ==
'\0')) {
2335 memset(s_zoneinfopath, 0, MAXPATHLEN);
2337 strncpy(s_zoneinfopath, zonepath, MAXPATHLEN - 1);
2341static void set_zoneinfopath(
void)
2343 char file_path[MAXPATHLEN] = {0};
2344 const char *fname = ZONES_TAB_SYSTEM_FILENAME;
2345 size_t i, num_zi_search_paths;
2348 const char *env_tzdir = getenv(
"TZDIR");
2349 if (env_tzdir != NULL) {
2350 snprintf(file_path, MAXPATHLEN,
"%s/%s", env_tzdir, fname);
2351 if (!access(file_path, F_OK | R_OK)) {
2352 strncpy(s_zoneinfopath, env_tzdir, MAXPATHLEN - 1);
2358 num_zi_search_paths =
sizeof(s_zoneinfo_search_paths) /
sizeof(s_zoneinfo_search_paths[0]);
2359 for (i = 0; i < num_zi_search_paths; i++) {
2360 snprintf(file_path, MAXPATHLEN,
"%s/%s", s_zoneinfo_search_paths[i], fname);
2361 if (!access(file_path, F_OK | R_OK)) {
2362 strncpy(s_zoneinfopath, s_zoneinfo_search_paths[i], MAXPATHLEN - 1);
2369 if (s_zoneinfopath[0] ==
'\0') {
2373 return s_zoneinfopath;
2378 if (use_builtin_tzdata) {
2379 return get_zone_directory_builtin();
2387 if (zone_files_directory) {
2391 const size_t len_path = strlen(path) + 1;
2394 if (zone_files_directory != NULL) {
2395 strncpy(zone_files_directory, path, len_path);
2396 zone_files_directory[len_path - 1] =
'\0';
2402 if (zone_files_directory != NULL) {
2404 zone_files_directory = NULL;
2417 use_builtin_tzdata = set;
2422 return use_builtin_tzdata;
2432static void check_tombstone(
struct observance *tombstone,
2433 struct observance *obs)
2438 tombstone->offset_from = tombstone->offset_to = obs->offset_to;
2439 tombstone->onset = obs->onset;
2445 struct icaldatetimeperiodtype date;
2448static int rdate_compare(
const void *rdate1,
const void *rdate2)
2451 ((
struct rdate *)rdate2)->date.time);
2458 icalcomponent *comp, *nextc, *tomb_std = NULL, *tomb_day = NULL;
2459 icalproperty *prop, *proleptic_prop = NULL;
2460 struct observance tombstone;
2464 if (!need_tomb && !need_tzuntil) {
2474 proleptic_prop = prop;
2479 memset(&tombstone, 0,
sizeof(
struct observance));
2481 if (!proleptic_prop ||
2483 tombstone.onset.
year = -1;
2488 comp; comp = nextc) {
2489 icalproperty *dtstart_prop = NULL, *rrule_prop = NULL;
2490 icalarray *rdates =
icalarray_new(
sizeof(
struct rdate), 10);
2492 struct observance obs;
2494 unsigned trunc_dtstart = 0;
2499 memset(&obs, 0,
sizeof(
struct observance));
2500 obs.offset_from = obs.offset_to = INT_MAX;
2514 dtstart_prop = prop;
2535 rdate.date.time = dtp.
time;
2536 rdate.date.period = dtp.
period;
2549 if (!dtstart_prop || !obs.name ||
2550 obs.offset_from == INT_MAX || obs.offset_to == INT_MAX) {
2575 check_tombstone(&tombstone, &obs);
2580 }
else if (r == 0) {
2589 icalrecur_iterator *ritr = NULL;
2590 unsigned trunc_until = 0;
2596 obs.onset = rrule->
until;
2598 check_tombstone(&tombstone, &obs);
2620 if (ritr && trunc_dtstart) {
2625 newstart.
day = start.
day;
2648 ydiff = (unsigned)(prev_onset.
year - dtstart.
year);
2657 rdate.
time = prev_onset;
2665 rrule->
until = prev_onset;
2678 if (ms_compatible) {
2684 if (proleptic_prop) {
2688 proleptic_prop = NULL;
2692 check_tombstone(&tombstone, &obs);
2700 if (trunc_dtstart) {
2703 dtstart = obs.onset;
2708 if (!trunc_until && ydiff <= 1) {
2731 ydiff = (unsigned)(end.
year - recur.
year);
2741 prev_onset = obs.onset;
2752 for (n = 0; n < rdates->num_elements; n++) {
2757 rdate->date.time.hour = dtstart.
hour;
2758 rdate->date.time.minute = dtstart.
minute;
2759 rdate->date.time.second = dtstart.
second;
2769 obs.onset = rdate->date.time;
2787 check_tombstone(&tombstone, &obs);
2799 if (trunc_dtstart) {
2813 if (trunc_dtstart) {
2821 }
else if (!tomb_std) {
2836 icalcomponent *tomb;
2837 icalproperty *tomb_prop, *nextp;
2850 tomb_prop; tomb_prop = nextp) {
2878 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