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)
374 tzid = icalproperty_get_tzid(prop);
382 if (zone->component) {
385 zone->component = component;
399 const char *location;
403 location = icalproperty_get_location(prop);
412 if (name && !strcasecmp(name,
"X-LIC-LOCATION")) {
413 location = icalproperty_get_x(prop);
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;
449 case ICAL_TZNAME_PROPERTY:
450 current_tzname = icalproperty_get_tzname(prop);
453 case ICAL_DTSTART_PROPERTY:
454 dtstart = icalproperty_get_dtstart(prop);
456 current_max_date = dtstart;
461 case ICAL_RDATE_PROPERTY:
462 rdate = icalproperty_get_rdate(prop);
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;
631 case ICAL_DTSTART_PROPERTY:
632 dtstart = icalproperty_get_dtstart(prop);
635 case ICAL_TZOFFSETTO_PROPERTY:
636 change.utc_offset = icalproperty_get_tzoffsetto(prop);
638 found_tzoffsetto = 1;
640 case ICAL_TZOFFSETFROM_PROPERTY:
641 change.prev_utc_offset = icalproperty_get_tzoffsetfrom(prop);
643 found_tzoffsetfrom = 1;
645 case ICAL_RDATE_PROPERTY:
648 case ICAL_RRULE_PROPERTY:
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");
709 case ICAL_RDATE_PROPERTY:
710 rdate = icalproperty_get_rdate(prop);
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);
741 case ICAL_RRULE_PROPERTY:
742 rrule = icalproperty_get_rrule(prop);
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)
1673 sscanf(coord + 1,
"%2d%2d", degrees, minutes);
1674 }
else if (len == 6) {
1675 sscanf(coord + 1,
"%3d%2d", degrees, minutes);
1676 }
else if (len == 7) {
1677 sscanf(coord + 1,
"%2d%2d%2d", degrees, minutes, seconds);
1678 }
else if (len == 8) {
1679 sscanf(coord + 1,
"%3d%2d%2d", degrees, minutes, seconds);
1681 icalerrprintf(
"Invalid coordinate: %s\n", coord);
1685 if (coord[0] ==
'-') {
1686 *degrees = -*degrees;
1692static bool fetch_lat_long_from_string(
const char *str,
1693 int *latitude_degrees,
int *latitude_minutes,
1694 int *latitude_seconds,
1695 int *longitude_degrees,
int *longitude_minutes,
1696 int *longitude_seconds,
1697 char *location,
size_t len_location)
1700 const char *loc, *temp;
1704 if (!location || !len_location) {
1709 const size_t len_str = strlen(str);
1712 while ((*sptr !=
'\t') && (*sptr !=
'\0')) {
1720 while (*sptr !=
'\t' && *sptr !=
'\0') {
1723 len = (size_t)(ptrdiff_t)(sptr - temp);
1731 memset(lat,
'\0', len + 1);
1732 strncpy(lat, temp, len + 1);
1734 while ((*sptr !=
'\t') && (*sptr !=
'\0')) {
1738 while (!isspace((
int)(*sptr)) && (*sptr !=
'\0')) {
1741 len = (size_t)(ptrdiff_t)(sptr - loc);
1742 if (len >= len_location) {
1743 len = len_location - 1;
1745 strncpy(location, loc, len);
1746 location[len] =
'\0';
1749 while (*lon !=
'\0' && *lon !=
'+' && *lon !=
'-') {
1753 if (parse_coord(lat, (
int)(lon - lat),
1756 latitude_seconds) == 1 ||
1757 parse_coord(lon, (
int)strlen(lon),
1758 longitude_degrees, longitude_minutes, longitude_seconds) == 1) {
1778static void icaltimezone_parse_zone_tab(
void)
1780 const char *zonedir, *zonetab;
1783 size_t filename_len;
1785 icalerror_assert(builtin_timezones == NULL,
"Parsing zones.tab file multiple times");
1789 if (!use_builtin_tzdata) {
1791 zonetab = ZONES_TAB_SYSTEM_FILENAME;
1793 zonedir = get_zone_directory_builtin();
1799 filename_len = strlen(zonedir);
1802 icalerror_assert(filename_len > 0,
"Unable to locate a zoneinfo dir");
1803 if (filename_len == 0) {
1805 builtin_timezones = timezones;
1811 filename_len += strlen(zonetab);
1817 builtin_timezones = timezones;
1822 snprintf(filename, filename_len,
"%s/%s", zonedir, zonetab);
1824 fp = fopen(filename,
"r");
1825 icalerror_assert(fp,
"Cannot open the zonetab file for reading");
1828 builtin_timezones = timezones;
1835#if !defined(__clang_analyzer__)
1837 while (!feof(fp) && !ferror(fp) && fgets(buf, (
int)
sizeof(buf), fp)) {
1838 char location[1024] = {0};
1839 int longitude_degrees, longitude_minutes, longitude_seconds;
1840 int latitude_degrees, latitude_minutes, latitude_seconds;
1842 if (buf[0] ==
'\0') {
1849 if (use_builtin_tzdata) {
1851 if (buf[0] !=
'+' && buf[0] !=
'-') {
1852 latitude_degrees = longitude_degrees = 360;
1853 latitude_minutes = longitude_minutes = 0;
1854 latitude_seconds = longitude_seconds = 0;
1855 if (sscanf(buf,
"%1000s", location) != 1) {
1858 icalerrprintf(
"%s: Invalid timezone description line: %s\n", filename, buf);
1861 }
else if (sscanf(buf,
"%4d%2d%2d %4d%2d%2d %1000s",
1864 &latitude_degrees, &latitude_minutes,
1866 &longitude_degrees, &longitude_minutes,
1867 &longitude_seconds, location) != 7) {
1868 icalerrprintf(
"%s: Invalid timezone description line: %s\n", filename, buf);
1872 if (fetch_lat_long_from_string(buf, &latitude_degrees, &latitude_minutes,
1874 &longitude_degrees, &longitude_minutes,
1875 &longitude_seconds, location,
sizeof(location))) {
1876 icalerrprintf(
"%s: Invalid timezone description line: %s\n", filename, buf);
1879 if (location[0] ==
'\0') {
1880 icalerrprintf(
"%s: Invalid timezone location: %s\n", filename, buf);
1885 icaltimezone_init(&
zone);
1889 if (latitude_degrees >= 0) {
1891 (double)latitude_degrees +
1892 (
double)latitude_minutes / 60 +
1893 (double)latitude_seconds / 3600;
1896 (double)latitude_degrees -
1897 (
double)latitude_minutes / 60 -
1898 (double)latitude_seconds / 3600;
1901 if (longitude_degrees >= 0) {
1903 (double)longitude_degrees +
1904 (
double)longitude_minutes / 60 +
1905 (double)longitude_seconds / 3600;
1908 (double)longitude_degrees -
1909 (
double)longitude_minutes / 60 -
1910 (double)longitude_seconds / 3600;
1915#ifdef ICALTIMEZONE_DEBUG_PRINT
1916 printf(
"Found zone: %s %f %f\n", location,
zone.latitude,
zone.longitude);
1921 builtin_timezones = timezones;
1932 icalcomponent *comp = 0, *subcomp;
1935 if (
zone->component) {
1939 if (!icaltimezone_builtin_lock()) {
1944 if (
zone->component) {
1945 if (!icaltimezone_builtin_unlock()) {
1952 if (!
zone->location || !
zone->location[0]) {
1953 if (!icaltimezone_builtin_unlock()) {
1959 if (use_builtin_tzdata) {
1961 size_t filename_len;
1965 filename_len = strlen(get_zone_directory_builtin()) + strlen(
zone->location) + 6;
1973 snprintf(filename, filename_len,
"%s/%s.ics", get_zone_directory_builtin(),
zone->location);
1975 fp = fopen(filename,
"r");
2004 size_t new_tzid_len;
2007 new_tzid_len = strlen(tzid_prefix) + strlen(
zone->location) + 1;
2010 snprintf(new_tzid, new_tzid_len,
"%s%s", tzid_prefix,
zone->location);
2011 icalproperty_set_tzid(prop, new_tzid);
2024 icalproperty_set_location(prop,
zone->location);
2033 if (name && !strcasecmp(name,
"X-LIC-LOCATION")) {
2034 icalproperty_set_x(prop,
zone->location);
2040 subcomp = icaltimezone_fetch_timezone(
zone->location);
2048 icaltimezone_get_vtimezone_properties(
zone, subcomp);
2050 if (use_builtin_tzdata) {
2056 if (!icaltimezone_builtin_unlock()) {
2065static char *icaltimezone_load_get_line_fn(
char *s,
size_t size,
void *data)
2067 return fgets(s, (
int)size, (FILE *)data);
2076 static const char months[][4] = {
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
2077 "Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"};
2078 const icaltimezonechange *zone_change;
2083 if (!icaltimezone_ensure_coverage(
zone, max_year)) {
2087#ifdef ICALTIMEZONE_DEBUG_PRINT
2088 printf(
"Num changes: %zu\n",
zone->changes->num_elements);
2091 if (!icaltimezone_changes_lock()) {
2095 for (change_num = 0; change_num <
zone->changes->num_elements; change_num++) {
2098 if (zone_change->year > max_year) {
2102 fprintf(fp,
"%s\t%2i %s %04i\t%2i:%02i:%02i",
2104 zone_change->day, months[zone_change->month - 1],
2105 zone_change->year, zone_change->hour, zone_change->minute, zone_change->second);
2108 format_utc_offset(zone_change->utc_offset, buffer,
sizeof(buffer));
2109 fprintf(fp,
"\t%s", buffer);
2114 if (!icaltimezone_changes_unlock()) {
2126static void format_utc_offset(
int utc_offset,
char *buffer,
size_t buffer_size)
2128 const char *sign =
"+";
2129 int hours, minutes, seconds;
2131 if (utc_offset < 0) {
2132 utc_offset = -utc_offset;
2136 hours = utc_offset / 3600;
2137 minutes = (utc_offset % 3600) / 60;
2138 seconds = utc_offset % 60;
2143 if (hours < 0 || hours >= 24 || minutes < 0 || minutes >= 60 || seconds < 0) {
2144 icalerrprintf(
"Warning: Strange timezone offset: H:%i M:%i S:%i\n",
2145 hours, minutes, seconds);
2147#if defined(__GNUC__) && !defined(__clang__)
2148#pragma GCC diagnostic push
2149#pragma GCC diagnostic ignored "-Wformat-truncation"
2152 snprintf(buffer, buffer_size,
"%s%02i%02i", sign, hours, minutes);
2154 snprintf(buffer, buffer_size,
"%s%02i%02i%02i", sign, hours, minutes, seconds);
2157#if defined(__GNUC__) && !defined(__clang__)
2158#pragma GCC diagnostic pop
2161static const char *get_zone_directory_builtin(
void)
2166 wchar_t wbuffer[1000];
2168#if !defined(_WIN32_WCE)
2169 char buffer[1000], zoneinfodir[1000], dirname[1000];
2172 wchar_t zoneinfodir[1000], dirname[1000];
2174 static ICAL_GLOBAL_VAR
char *cache = NULL;
2176#if !defined(_WIN32_WCE)
2177 unsigned char *dirslash, *zislash;
2178 const char *zislashp1;
2180 wchar_t *dirslash, *zislash;
2184 if (zone_files_directory) {
2185 return zone_files_directory;
2194 if (!GetModuleFileNameW(NULL, wbuffer,
sizeof(wbuffer) /
sizeof(wbuffer[0]))) {
2199#if !defined(_WIN32_WCE)
2201 if (!WideCharToMultiByte(CP_ACP, 0, wbuffer, -1, buffer,
sizeof(buffer),
2202 NULL, &used_default) ||
2205 if (!GetShortPathNameW(wbuffer, wbuffer,
2206 sizeof(wbuffer) /
sizeof(wbuffer[0])) ||
2207 !WideCharToMultiByte(CP_ACP, 0, wbuffer, -1, buffer,
sizeof(buffer),
2208 NULL, &used_default) ||
2249#if !defined(_WIN32_WCE)
2250 dirslash = _mbsrchr((
unsigned char *)buffer,
'\\');
2252 dirslash = wcsrchr(wbuffer, L
'\\');
2255#if !defined(_WIN32_WCE)
2262#if defined(_WIN32_WCE)
2263 while ((dirslash = wcsrchr(wbuffer,
'\\'))) {
2269 while ((zislash = wcschr(zoneinfodir, L
'/'))) {
2271 wcscpy(dirname, wbuffer);
2272 wcscat(dirname,
"/");
2273 wcscat(dirname, zislash + 1);
2274 if (stat(dirname, &st) == 0 && S_ISDIR(st.st_mode)) {
2275 cache = wce_wctomb(dirname);
2281 while ((dirslash = _mbsrchr((
unsigned char *)buffer,
'\\'))) {
2286 while ((zislash = _mbschr((
unsigned char *)zoneinfodir,
'/'))) {
2288 strcpy(dirname, buffer);
2289 strcat(dirname,
"/");
2290 zislashp1 = (
const char *)(zislash + 1);
2291 strcat(dirname, zislashp1);
2292 if (stat(dirname, &st) == 0 && S_ISDIR(st.st_mode)) {
2305 if ((zonepath == NULL) || (zonepath[0] ==
'\0')) {
2306 memset(s_zoneinfopath, 0, MAXPATHLEN);
2308 strncpy(s_zoneinfopath, zonepath, MAXPATHLEN - 1);
2312static void set_zoneinfopath(
void)
2314 char file_path[MAXPATHLEN] = {0};
2315 const char *fname = ZONES_TAB_SYSTEM_FILENAME;
2316 size_t i, num_zi_search_paths;
2319 const char *env_tzdir = getenv(
"TZDIR");
2320 if (env_tzdir != NULL) {
2321 snprintf(file_path, MAXPATHLEN,
"%s/%s", env_tzdir, fname);
2322 if (!access(file_path, F_OK | R_OK)) {
2323 strncpy(s_zoneinfopath, env_tzdir, MAXPATHLEN - 1);
2329 num_zi_search_paths =
sizeof(s_zoneinfo_search_paths) /
sizeof(s_zoneinfo_search_paths[0]);
2330 for (i = 0; i < num_zi_search_paths; i++) {
2331 snprintf(file_path, MAXPATHLEN,
"%s/%s", s_zoneinfo_search_paths[i], fname);
2332 if (!access(file_path, F_OK | R_OK)) {
2333 strncpy(s_zoneinfopath, s_zoneinfo_search_paths[i], MAXPATHLEN - 1);
2340 if (s_zoneinfopath[0] ==
'\0') {
2344 return s_zoneinfopath;
2349 if (use_builtin_tzdata) {
2350 return get_zone_directory_builtin();
2358 if (zone_files_directory) {
2362 const size_t len_path = strlen(path) + 1;
2365 if (zone_files_directory != NULL) {
2366 strncpy(zone_files_directory, path, len_path);
2367 zone_files_directory[len_path - 1] =
'\0';
2373 if (zone_files_directory != NULL) {
2375 zone_files_directory = NULL;
2388 use_builtin_tzdata = set;
2393 return use_builtin_tzdata;
2403static void check_tombstone(
struct observance *tombstone,
2404 struct observance *obs)
2409 tombstone->offset_from = tombstone->offset_to = obs->offset_to;
2410 tombstone->onset = obs->onset;
2416 struct icaldatetimeperiodtype date;
2419static int rdate_compare(
const void *rdate1,
const void *rdate2)
2422 ((
struct rdate *)rdate2)->date.time);
2429 icalcomponent *comp, *nextc, *tomb_std = NULL, *tomb_day = NULL;
2430 icalproperty *prop, *proleptic_prop = NULL;
2431 struct observance tombstone;
2435 if (!need_tomb && !need_tzuntil) {
2445 proleptic_prop = prop;
2450 memset(&tombstone, 0,
sizeof(
struct observance));
2451 tombstone.name =
icalmemory_tmp_copy(proleptic_prop ? icalproperty_get_x(proleptic_prop) :
"LMT");
2452 if (!proleptic_prop ||
2454 tombstone.onset.
year = -1;
2459 comp; comp = nextc) {
2460 icalproperty *dtstart_prop = NULL, *rrule_prop = NULL;
2461 icalarray *rdates =
icalarray_new(
sizeof(
struct rdate), 10);
2463 struct observance obs;
2465 unsigned trunc_dtstart = 0;
2470 memset(&obs, 0,
sizeof(
struct observance));
2471 obs.offset_from = obs.offset_to = INT_MAX;
2480 case ICAL_TZNAME_PROPERTY:
2481 obs.name = icalproperty_get_tzname(prop);
2484 case ICAL_DTSTART_PROPERTY:
2485 dtstart_prop = prop;
2486 obs.onset = dtstart = icalproperty_get_dtstart(prop);
2489 case ICAL_TZOFFSETFROM_PROPERTY:
2490 obs.offset_from = icalproperty_get_tzoffsetfrom(prop);
2493 case ICAL_TZOFFSETTO_PROPERTY:
2494 obs.offset_to = icalproperty_get_tzoffsetto(prop);
2497 case ICAL_RRULE_PROPERTY:
2501 case ICAL_RDATE_PROPERTY: {
2506 rdate.date.time = dtp.
time;
2507 rdate.date.period = dtp.
period;
2520 if (!dtstart_prop || !obs.name ||
2521 obs.offset_from == INT_MAX || obs.offset_to == INT_MAX) {
2546 check_tombstone(&tombstone, &obs);
2551 }
else if (r == 0) {
2560 icalrecur_iterator *ritr = NULL;
2561 unsigned trunc_until = 0;
2567 obs.onset = rrule->
until;
2569 check_tombstone(&tombstone, &obs);
2591 if (ritr && trunc_dtstart) {
2596 newstart.
day = start.
day;
2619 ydiff = (unsigned)(prev_onset.
year - dtstart.
year);
2628 rdate.
time = prev_onset;
2631 prop = icalproperty_new_rdate(rdate);
2636 rrule->
until = prev_onset;
2637 icalproperty_set_rrule(rrule_prop, rrule);
2649 if (ms_compatible) {
2655 if (proleptic_prop) {
2659 proleptic_prop = NULL;
2663 check_tombstone(&tombstone, &obs);
2671 if (trunc_dtstart) {
2673 icalproperty_set_dtstart(dtstart_prop, recur);
2674 dtstart = obs.onset;
2679 if (!trunc_until && ydiff <= 1) {
2690 prop = icalproperty_new_rdate(rdate);
2702 ydiff = (unsigned)(end.
year - recur.
year);
2712 prev_onset = obs.onset;
2723 for (n = 0; n < rdates->num_elements; n++) {
2728 rdate->date.time.hour = dtstart.
hour;
2729 rdate->date.time.minute = dtstart.
minute;
2730 rdate->date.time.second = dtstart.
second;
2740 obs.onset = rdate->date.time;
2758 check_tombstone(&tombstone, &obs);
2770 if (trunc_dtstart) {
2772 icalproperty_set_dtstart(dtstart_prop,
2784 if (trunc_dtstart) {
2792 }
else if (!tomb_std) {
2807 icalcomponent *tomb;
2808 icalproperty *tomb_prop, *nextp;
2821 tomb_prop; tomb_prop = nextp) {
2825 case ICAL_TZNAME_PROPERTY:
2826 icalproperty_set_tzname(tomb_prop, tombstone.name);
2828 case ICAL_TZOFFSETFROM_PROPERTY:
2829 icalproperty_set_tzoffsetfrom(tomb_prop, tombstone.offset_from);
2831 case ICAL_TZOFFSETTO_PROPERTY:
2832 icalproperty_set_tzoffsetto(tomb_prop, tombstone.offset_to);
2834 case ICAL_DTSTART_PROPERTY:
2839 icalproperty_set_dtstart(tomb_prop, start);
2849 if (proleptic_prop) {
2870 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