File tz_compile.c¶
File List > base > tz_compile.c
Go to the documentation of this file
/*
* Copyright 2008 Search Solution Corporation
* Copyright 2016 CUBRID Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
/*
* tz_compile.c : Functions for parsing and compiling IANA's timezone files
*/
#include "config.h"
#include <stdio.h>
#include <assert.h>
#include "tz_support.h"
#include "authenticate.h"
#include "porting.h"
#include "byte_order.h"
#include "utility.h"
#include "db_date.h"
#include "environment_variable.h"
#include "chartype.h"
#include "error_manager.h"
#include "memory_alloc.h"
#include "tz_compile.h"
#include "xml_parser.h"
#include "crypt_opfunc.h"
#include "db_query.h"
#include "dbtype.h"
#if defined (SA_MODE)
#include "db.h"
#endif /* SA_MODE */
#if defined (SUPPRESS_STRLEN_WARNING)
#define strlen(s1) ((int) strlen(s1))
#endif /* defined (SUPPRESS_STRLEN_WARNING) */
#define TZ_FILENAME_MAX_LEN 17
#define TZ_MAX_LINE_LEN 512
#define TZ_OFFRULE_PREFIX_TAB_COUNT 3
#define TZ_COORDINATES_MAX_SIZE 16
#define TZ_COMMENTS_MAX_SIZE 92
#define TZ_RULE_LETTER_ABBREV_MAX_SIZE 8
#define TZ_RULE_TYPE_MAX_SIZE 4
#if defined(_WIN32) || defined(WINDOWS) || defined(WIN64)
#define PATH_PARTIAL_TIMEZONES_FILE "timezones\\tzlib\\timezones.c"
#else
#define PATH_PARTIAL_TIMEZONES_FILE "timezones/tzlib/timezones.c"
#endif
/*
* Data structures
*/
typedef enum
{
/* file types */
TZF_COUNTRIES = 0, /* tabbed country list (ISO3166) */
TZF_ZONES, /* tabbed time zones */
TZF_RULES, /* daylight saving rules */
TZF_BACKWARD, /* time zone aliases for backward compatibility */
#if defined(WINDOWS)
TZF_LEAP, /* leap data (leap seconds) */
TZF_WINDOWS_IANA_ZONES_MAP
#else
TZF_LEAP
#endif
} TZ_FILE_TYPE;
typedef struct tz_file_descriptor TZ_FILE_DESCRIPTOR;
struct tz_file_descriptor
{
TZ_FILE_TYPE type; /* type of tz file contents */
char name[TZ_FILENAME_MAX_LEN]; /* file name */
};
/* The list of files from IANA's timezone database, in alphabetical order,
* with attached description flags. This list is necessary for detecting file
* addition or removal to/from future TZ releases by IANA. The reference for
* building this list is IANA's tzdata2013b.tar.gz, released on 11 March 2013.
* Visit http://www.iana.org/time-zones for the latest release.
* NOTE: the array below is sorted by type. This order is used in
* timezone_data_load(), so it must be preserved.
*/
static const TZ_FILE_DESCRIPTOR tz_Files[] = {
{TZF_COUNTRIES, "iso3166.tab"},
{TZF_ZONES, "zone.tab"},
{TZF_RULES, "africa"},
{TZF_RULES, "antarctica"},
{TZF_RULES, "asia"},
{TZF_RULES, "australasia"},
{TZF_RULES, "europe"},
{TZF_RULES, "northamerica"},
{TZF_RULES, "southamerica"},
{TZF_RULES, "etcetera"},
{TZF_RULES, "pacificnew"},
{TZF_BACKWARD, "backward"},
{TZF_LEAP, "leapseconds"},
#if defined(WINDOWS)
{TZF_LEAP, "leapseconds"},
{TZF_WINDOWS_IANA_ZONES_MAP, "windowsZones.xml"}
#else
{TZF_LEAP, "leapseconds"}
#endif
};
static int tz_File_count = DIM (tz_Files);
typedef struct tz_raw_country TZ_RAW_COUNTRY;
struct tz_raw_country
{
int id; /* this is not read, but assigned after reading all tz data */
char code[TZ_COUNTRY_CODE_SIZE];
char full_name[TZ_COUNTRY_NAME_SIZE];
bool is_used;
};
typedef struct tz_raw_link TZ_RAW_LINK;
struct tz_raw_link
{
char name[TZ_GENERIC_NAME_SIZE];
char alias[TZ_GENERIC_NAME_SIZE];
int zone_id; /* this is not read, but assigned after reading all tz data */
};
typedef struct tz_raw_offset_rule TZ_RAW_OFFSET_RULE;
struct tz_raw_offset_rule
{
int gmt_off; /* time offset from UTC, in seconds */
unsigned short until_year;
unsigned char until_mon; /* 0 - 11 */
unsigned char until_day; /* 0 - 27,30 */
unsigned char until_hour;
unsigned char until_min;
unsigned char until_sec;
char ds_ruleset_name[TZ_DS_RULESET_NAME_SIZE];
char format[TZ_MAX_FORMAT_SIZE];
TZ_TIME_TYPE until_time_type; /* type for until: standard, wall, UTC */
TZ_UNTIL_FLAG until_flag; /* true if no ending time is specified; false otherwise */
};
typedef struct tz_raw_zone_info TZ_RAW_ZONE_INFO;
struct tz_raw_zone_info
{
int offset_rule_count;
TZ_RAW_OFFSET_RULE *offset_rules;
int id; /* this is not read, but assigned after reading all tz data */
int country_id; /* parent country ID */
int alias_count;
char **aliases;
char clone_of[TZ_GENERIC_NAME_SIZE]; /* timezone from where to use rules and settings */
int clone_of_id;
char code[TZ_COUNTRY_CODE_SIZE];
char coordinates[TZ_COORDINATES_MAX_SIZE];
char full_name[TZ_GENERIC_NAME_SIZE];
char comments[TZ_COMMENTS_MAX_SIZE];
};
/* TZ_DS_RULE is the representation of a daylight saving rule and tells
* when and how the DS event occurs */
typedef struct tz_raw_ds_rule TZ_RAW_DS_RULE;
struct tz_raw_ds_rule
{
short from_year;
short to_year; /* TZ_MAX_YEAR if column value is "max" e.g. up to now */
char type[TZ_RULE_TYPE_MAX_SIZE]; /* always '-'; kept for possible future extensions */
unsigned char in_month; /* month when the daylight saving event occurs valid values : 0 - 11 */
TZ_DS_CHANGE_ON change_on; /* day of month, fixed or relative */
int at_time; /* time when DS event occurs */
TZ_TIME_TYPE at_time_type; /* type for at_time: local, absolute etc. */
int save_time; /* amount of time saved, in seconds */
/* letter(s) to be used in the time zone string ID */
char letter_abbrev[TZ_RULE_LETTER_ABBREV_MAX_SIZE];
};
typedef struct tz_raw_ds_ruleset TZ_RAW_DS_RULESET;
struct tz_raw_ds_ruleset
{
int rule_count;
TZ_RAW_DS_RULE *rules;
char name[TZ_DS_RULESET_NAME_SIZE];
bool is_used;
};
typedef struct tz_raw_context TZ_RAW_CONTEXT;
struct tz_raw_context
{
int current_line;
char current_file[PATH_MAX];
};
/*
* TZ_RAW_DATA is a structure for holding the information read from the files
* found in IANA's timezone database. After fully loading and interpreting the
* data, time zone information will be processed, optimized and moved to a new
* structure (TZ_DATA) to be used at runtime. (incl. TZ shared library)
*/
typedef struct tz_raw_data TZ_RAW_DATA;
struct tz_raw_data
{
int country_count;
TZ_RAW_COUNTRY *countries;
int zone_count;
TZ_RAW_ZONE_INFO *zones;
int ruleset_count;
TZ_RAW_DS_RULESET *ds_rulesets;
int link_count;
TZ_RAW_LINK *links;
int leap_sec_count;
TZ_LEAP_SEC *leap_sec;
TZ_RAW_CONTEXT context;
};
typedef struct offset_rule_interval OFFSET_RULE_INTERVAL;
struct offset_rule_interval
{
int original_offset_rule_start;
int len;
int final_offset_rule_start;
};
typedef struct query_buf QUERY_BUF;
struct query_buf
{
char *buf;
char *last;
int size;
int len;
};
#define TZ_CAL_ABBREV_SIZE 4
static const char MONTH_NAMES_ABBREV[TZ_MON_COUNT][TZ_CAL_ABBREV_SIZE] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
static const char DAY_NAMES_ABBREV[TZ_WEEK_DAY_COUNT][TZ_CAL_ABBREV_SIZE] =
{ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
#define STR_SKIP_LEADING_SPACES(str) \
while ((str) != NULL && *(str) == ' ') \
{ \
(str)++; \
}
/*
* Time zone errors. For a more flexible error reporting feature, CUBRID's
* error codes are not used inside the timezone feature. Instead, internal
* custom errors are used, and are handled only in one place (when exiting)
*/
#ifndef NO_ERROR
#define NO_ERROR 0
#endif
#define TZC_ERR_GENERIC -1
#define TZC_ERR_INVALID_PACKAGE -2
#define TZC_ERR_BAD_TZ_LINK -3
#define TZC_ERR_OUT_OF_MEMORY -4
#define TZC_ERR_INVALID_VALUE -5
#define TZC_ERR_INVALID_TIME -6
#define TZC_ERR_ZONE_RULE_UNORDERED -7
#define TZC_ERR_INVALID_DATETIME -8
#define TZC_ERR_INVALID_DS_RULE -9
#define TZC_ERR_CANT_READ_VALUE -10
#define TZC_ERR_PARSING_FAILED -11
#define TZC_ERR_DS_INVALID_DATE -12
#define TZC_ERR_FILE_NOT_ACCESSIBLE -13
#define TZC_ERR_INVALID_COUNTRY -14
#define TZC_ERR_INVALID_ZONE -15
#define TZC_ERR_ADD_ZONE -16
#define TZC_ERR_LINKING_TRUE_ZONES -17
#define TZC_ERR_LAST_ERROR -18
static const char *tzc_Err_messages[] = {
/* NO_ERROR */
"",
/* TZC_ERR_GENERIC */
"Error encountered when %s %s!",
/* TZC_ERR_INVALID_PACKAGE */
"Invalid timezone package! File %s not found in folder %s.",
/* TZC_ERR_BAD_TZ_LINK */
"Invalid link definition (s1: %s, s2: %s). " "Format error or invalid data encountered.",
/* TZC_ERR_OUT_OF_MEMORY */
"Memory exhausted when allocating %d items of type '%s'.",
/* TZC_ERR_INVALID_VALUE */
"Invalid %s. Value %s is empty or invalid.",
/* TZC_ERR_INVALID_TIME */
"Invalid or empty time value %s found in %s.",
/* TZC_ERR_ZONE_RULE_UNORDERED */
"Timezone offset rules are not fully sorted. Rule %s is out of order. %s",
/* TZC_ERR_INVALID_DATETIME */
"Invalid datetime value %s found in %s.",
/* TZC_ERR_INVALID_DS_RULE */
"Invalid daylight saving rule found: %s %s",
/* TZC_ERR_CANT_READ_VALUE */
"Unable to read %s value. Context: %s.",
/* TZC_ERR_PARSING_FAILED */
"Error encountered when parsing %. Context: %s.",
/* TZC_ERR_DS_INVALID_DATE */
"Invalid %d. The resulting date is not valid in the given context (%s).",
/* TZC_ERR_FILE_NOT_ACCESSIBLE */
"The file at %s is missing or not accessible for %s.",
/* TZC_ERR_INVALID_COUNTRY */
"Invalid line for country definition:%s. %s",
/* TZC_ERR_INVALID_ZONE */
"Invalid line for zone definition. Line: %s. Values: %s",
/* TZC_ERR_ADD_ZONE */
"Error encountered when adding zone %s %s",
/* TZC_ERR_LINKING_TRUE_ZONES */
"Error! Found a link between %s and %s, which are fully defined timezones."
};
static const int tzc_Err_message_count = -TZC_ERR_LAST_ERROR;
extern const char *tz_timezone_names[];
extern const TZ_COUNTRY tz_countries[];
#define LOG_TZC_SET_CURRENT_CONTEXT(tzd_raw, f, l) \
do { \
strcpy (tzd_raw->context.current_file, f); \
tzd_raw->context.current_line = l; \
} while (0)
#define TZC_ERR_MSG_MAX_SIZE 512
#define TZC_CONTEXT(tzd_raw) (&((tzd_raw)->context))
#define TZC_LOG_ERROR_1ARG(context, err_code, s1) \
tzc_log_error ((context), (err_code), (s1), "")
#define TZC_LOG_ERROR_2ARG(context, err_code, s1, s2) \
tzc_log_error ((context), (err_code), (s1), (s2))
#define DUPLICATE_STR(a, b) \
do { \
(a) = strdup((b)); \
if((a) == NULL) { \
err_status = ER_OUT_OF_VIRTUAL_MEMORY; \
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, err_status, 1, (size_t) 0); \
goto exit; \
} \
} while (0)
#define INIT_COUNTRY(dst, src) \
do { \
strcpy ((dst)->code, (src)->code); \
strcpy ((dst)->full_name, (src)->full_name); \
} while (0)
#define PRINT_STRING_TO_C_FILE(fp, val, len) \
do { \
int istr; \
fprintf (fp, "\""); \
for (istr = 0; istr < len; istr++) \
{ \
fprintf (fp, "\\x%02X", (unsigned char) val[istr]); \
} \
fprintf (fp, "\""); \
} while (0)
#define PRINT_STRING_VAR_TO_C_FILE(fp, valname, val) \
do { \
fprintf (fp, "\n" SHLIB_EXPORT_PREFIX "const char " valname "[] = "); \
PRINT_STRING_TO_C_FILE (fp, val, strlen (val)); \
fprintf (fp, ";\n"); \
} while (0);
#define BUF_PUT_INT32(buf,v) \
do { \
unsigned int nv = htonl(v); \
*((unsigned char *) (buf)) = ((unsigned char *) &nv)[3]; \
buf = (char *) (buf) + 1; \
*((unsigned char *) (buf)) = ((unsigned char *) &nv)[2]; \
buf = (char *) (buf) + 1; \
*((unsigned char *) (buf)) = ((unsigned char *) &nv)[1]; \
buf = (char *) (buf) + 1; \
*((unsigned char *) (buf)) = ((unsigned char *) &nv)[0]; \
buf = (char *) (buf) + 1; \
} while (0)
#define BUF_PUT_INT16(buf,v) \
do { \
unsigned short nv = htons(v); \
*((unsigned char *) (buf)) = ((unsigned char *) &nv)[1];\
buf = (char *) (buf) + 1; \
*((unsigned char *) (buf)) = ((unsigned char *) &nv)[0];\
buf = (char *) (buf) + 1; \
} while (0)
static int tzc_check_new_package_validity (const char *input_folder);
static int tzc_load_countries (TZ_RAW_DATA * tzd_raw, const char *input_folder);
static int tzc_load_zone_names (TZ_RAW_DATA * tzd_raw, const char *input_folder);
static int tzc_load_rule_file (TZ_RAW_DATA * tzd_raw, const int file_index, const char *input_folder);
static int tzc_load_backward_zones (TZ_RAW_DATA * tzd_raw, const char *input_folder);
static int tzc_load_leap_secs (TZ_RAW_DATA * tzd_raw, const char *input_folder);
static int tzc_get_zone (const TZ_RAW_DATA * tzd_raw, const char *zone_name, TZ_RAW_ZONE_INFO ** zone);
static int tzc_add_zone (const char *zone, const char *coord, const char *code, const char *comments,
TZ_RAW_DATA * tzd_raw, TZ_RAW_ZONE_INFO ** new_zone);
static int tzc_add_link (TZ_RAW_DATA * tzd_raw, const char *zone, const char *alias);
static int tzc_add_offset_rule (TZ_RAW_ZONE_INFO * zone, char *rule_text);
static int tzc_add_leap_sec (TZ_RAW_DATA * tzd_raw, int year, int month, int day, unsigned char hour, unsigned char min,
unsigned char sec, bool corr_minus, bool leap_is_rolling);
static int tzc_read_time_type (const char *str, const char **next, TZ_TIME_TYPE * time_type);
static int tzc_add_ds_rule (TZ_RAW_DATA * tzd_raw, char *rule_text);
static int tzc_parse_ds_change_on (TZ_RAW_DS_RULE * dest, const char *str);
static bool tzc_is_valid_date (const int day, const int month, const int year_start, const int year_end);
static int tzc_get_ds_ruleset_by_name (const TZ_DS_RULESET * ds_rulesets, int ds_ruleset_count, const char *ruleset);
static void tzc_free_raw_data (TZ_RAW_DATA * tzd_raw);
static int tzc_check_links_raw_data (TZ_RAW_DATA * tzd_raw);
static void tzc_sort_raw_data (TZ_RAW_DATA * tzd_raw);
static void tzc_index_raw_data (TZ_RAW_DATA * tzd_raw);
static int tzc_index_raw_data_w_static (TZ_RAW_DATA * tzd_raw, const TZ_GEN_TYPE mode);
static int tzc_index_raw_subdata (TZ_RAW_DATA * tzd_raw, const TZ_GEN_TYPE mode);
static int compare_ints (const void *a, const void *b);
static int tzc_compile_data (TZ_RAW_DATA * tzd_raw, TZ_DATA * tzd);
static int tzc_compile_ds_rules (TZ_RAW_DATA * tzd_raw, TZ_DATA * tzd);
static int str_to_offset_rule_until (TZ_RAW_OFFSET_RULE * offset_rule, char *str);
static int str_month_to_int (const char *month, int *month_num, const char **str_next);
static int str_day_to_int (const char *str_in, int *day_num, const char **str_next);
static int str_read_day_var (const char *str, const int month, int *type, int *day, int *bound, const char **str_next);
static int comp_func_raw_countries (const void *arg1, const void *arg2);
static int comp_func_raw_zones (const void *arg1, const void *arg2);
static int comp_func_raw_links (const void *arg1, const void *arg2);
static int comp_func_raw_offset_rules (const void *arg1, const void *arg2);
static int comp_func_raw_ds_rulesets (const void *arg1, const void *arg2);
static int comp_func_raw_ds_rules (const void *arg1, const void *arg2);
static int comp_func_tz_names (const void *arg1, const void *arg2);
static void print_seconds_as_time_hms_var (int seconds);
static void tzc_get_timezones_dot_c_filepath (size_t size, char *timezones_dot_c_file_path);
static int tzc_export_timezone_dot_c (const TZ_DATA * tzd, const char *tz_C_filepath);
static int tzc_load_raw_data (TZ_RAW_DATA * tzd_raw, const char *input_folder);
static int tzc_import_old_data (TZ_RAW_DATA * tzd_raw, const TZ_GEN_TYPE mode);
static int tzc_del_unused_raw_data (TZ_RAW_DATA * tzd_raw);
static int tzc_index_data (TZ_RAW_DATA * tzd_raw, const TZ_GEN_TYPE mode);
static void tzc_free_tz_data (TZ_DATA * tzd, bool full);
static void tzc_build_filepath (char *path, size_t size, const char *dir, const char *filename);
static void trim_comments_whitespaces (char *str);
static int tzc_get_timezone_aliases (const TZ_DATA * tzd, const int zone_id, int **aliases, int *alias_count);
static void tzc_dump_one_offset_rule (const TZ_DATA * tzd, const TZ_OFFSET_RULE * offset_rule);
static void tzc_dump_ds_ruleset (const TZ_DATA * tzd, const int ruleset_id);
static void tzc_log_error (const TZ_RAW_CONTEXT * context, const int code, const char *msg1, const char *msg2);
static void tzc_summary (TZ_RAW_DATA * tzd_raw, TZ_DATA * tzd);
static int tzc_find_timezone_names (const TZ_DATA * tzd, const char *timezone_name);
static int tzc_find_country_names (const TZ_COUNTRY * countries, const int country_count, const char *country_name);
static bool comp_ds_rules (const TZ_DS_RULE * rule1, const TZ_DS_RULE * rule2);
static bool comp_offset_rules (const TZ_OFFSET_RULE * rule1, const TZ_OFFSET_RULE * rule2);
static int copy_offset_rule (TZ_OFFSET_RULE * dst, const TZ_DATA * tzd, const int index);
static int init_ds_ruleset (TZ_DS_RULESET * dst_ruleset, const TZ_DATA * tzd, const int index, const int start);
static int copy_ds_rule (TZ_DS_RULE * dst, const TZ_DATA * tzd, const int index);
static int tz_data_partial_clone (char **timezone_names, TZ_TIMEZONE * timezones, TZ_NAME * names, const TZ_DATA * tzd);
static int init_tz_name (TZ_NAME * dst, TZ_NAME * src);
#if defined(SA_MODE)
static int tzc_extend (TZ_DATA * tzd);
static int tzc_update (TZ_DATA * tzd, const char *database_name);
static int tzc_update_internal (const char *database_name);
static QUERY_BUF *tz_write_query_string (QUERY_BUF * query, const char *format, ...);
#endif
static int tzc_compute_timezone_checksum (TZ_DATA * tzd, TZ_GEN_TYPE type);
static int get_day_of_week_for_raw_rule (const TZ_RAW_DS_RULE * rule, const int year);
#if defined (SA_MODE)
static int execute_query (const char *str, DB_QUERY_RESULT ** result);
#endif /* defined (SA_MODE) */
#if defined(WINDOWS)
static int comp_func_tz_windows_zones (const void *arg1, const void *arg2);
static int xml_start_mapZone (void *data, const char **attr);
static int tzc_load_windows_iana_map (TZ_DATA * tz_data, const char *input_folder);
const XML_ELEMENT_DEF windows_zones_elem_supplementalData = { "supplementalData", 1, NULL,
NULL, NULL
};
const XML_ELEMENT_DEF windows_zones_elem_windowsZones = { "supplementalData windowsZones", 2, NULL,
NULL, NULL
};
const XML_ELEMENT_DEF windows_zones_elem_mapTimezones = { "supplementalData windowsZones mapTimezones", 3, NULL,
NULL, NULL
};
const XML_ELEMENT_DEF windows_zones_elem_mapZone = { "supplementalData windowsZones mapTimezones mapZone", 4,
(ELEM_START_FUNC) & xml_start_mapZone,
NULL, NULL
};
const XML_ELEMENT_DEF *windows_zones_elements[] = {
&windows_zones_elem_supplementalData,
&windows_zones_elem_windowsZones,
&windows_zones_elem_mapTimezones,
&windows_zones_elem_mapZone
};
#endif
/*
* tz_build_filepath () - concat a folder path and a file name to obtain a
* full file path
* Returns:
* path(in/out): preallocated string where to store the full file path
* size (in): size in bytes of th preallocated string
* dir(in): folder part of the output full path
* filename(in): file name part of the output full file path
*/
static void
tzc_build_filepath (char *path, size_t size, const char *dir, const char *filename)
{
assert (path != NULL && size > 0);
assert (dir != NULL);
assert (filename != NULL);
#if !defined(WINDOWS)
if (snprintf (path, size - 1, "%s/%s", dir, filename) < 0)
{
assert_release (false);
}
#else
if (snprintf (path, size - 1, "%s\\%s", dir, filename) < 0)
{
assert_release (false);
}
#endif
}
/*
* trim_comments_whitespaces() - remove whitespaces and comments found at the
* end of a string. In this context, the
* whitespaces to be removed are spaces, tabs and
* the 0x0a character. Comments are identified as a
* substring starting with '#' and stretching until
* the end of the string/line.
* Returns:
* str(in/out): string from where to remove the whitespaces described above.
*
*/
static void
trim_comments_whitespaces (char *str)
{
int i, str_len = 0;
char *sharp = NULL;
if (IS_EMPTY_STR (str))
{
return;
}
sharp = strchr (str, '#');
if (sharp != NULL)
{
*sharp = 0;
}
str_len = strlen (str);
for (i = str_len - 1; i >= 0 && char_isspace (str[i]); i--)
{
str[i] = 0;
}
}
/*
* tzc_check_new_package_validity() - match the above list of files to the
* list of files found in the input folder
* Returns: 0(NO_ERROR) if the file lists are the same, TZ_ERR_INVALID_PACKAGE
* if the input folder is missing any of the files marked above as
* TZ_RULES, TZ_ZONES, TZ_COUNTRIES, TZ_BACKWARD or TZ_LEAP_DATA
* input_folder(in): path to the input folder
*/
static int
tzc_check_new_package_validity (const char *input_folder)
{
int err_status = NO_ERROR;
FILE *fp;
int i;
char temp_path[PATH_MAX];
for (i = 0; i < tz_File_count && err_status == NO_ERROR; i++)
{
tzc_build_filepath (temp_path, sizeof (temp_path), input_folder, tz_Files[i].name);
fp = fopen_ex (temp_path, "rb");
if (fp == NULL)
{
err_status = TZC_ERR_INVALID_PACKAGE;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_INVALID_PACKAGE, tz_Files[i].name, input_folder);
goto exit;
}
else
{
fclose (fp);
fp = NULL;
}
}
exit:
return err_status;
}
#if defined (SA_MODE)
/*
* timezone_compile_data() - loads data from all relevant tz files into
* temporary structures, reorganizes & optimizes data
* and generates TZ specific files
* Returns: NO_ERROR if successful, internal error code otherwise
* input_folder(in): path to the input folder containing timezone data
* tz_gen_type(in): control flag (type of TZ build/gen to perform)
* database_name(in): database name for which to do data migration if an update is necessary, if it is NULL
* then data migration will be done for all the databases
* timezones_dot_c_filepath(in): path for timezones.c output file. if NULL, it will be saved in default path (in CUBRID install
* folder).
* checksum(out): new checksum to write in the database when extend option is used
*/
int
timezone_compile_data (const char *input_folder, const TZ_GEN_TYPE tz_gen_type, char *database_name,
const char *timezones_dot_c_filepath, char *checksum)
{
int err_status = NO_ERROR;
TZ_RAW_DATA tzd_raw;
TZ_DATA tzd;
bool write_checksum = false;
char default_output_file_path[PATH_MAX] = { 0 };
memset (&tzd, 0, sizeof (tzd));
memset (&tzd_raw, 0, sizeof (tzd_raw));
err_status = tzc_check_new_package_validity (input_folder);
if (err_status != NO_ERROR)
{
goto exit;
}
/* load raw data */
err_status = tzc_load_raw_data (&tzd_raw, input_folder);
if (err_status != NO_ERROR)
{
goto exit;
}
/* load old data */
err_status = tzc_import_old_data (&tzd_raw, tz_gen_type);
if (err_status != NO_ERROR)
{
goto exit;
}
err_status = tzc_del_unused_raw_data (&tzd_raw);
if (err_status != NO_ERROR)
{
goto exit;
}
tzc_sort_raw_data (&tzd_raw);
err_status = tzc_index_data (&tzd_raw, tz_gen_type);
if (err_status != NO_ERROR)
{
goto exit;
}
err_status = tzc_compile_data (&tzd_raw, &tzd);
if (err_status != NO_ERROR)
{
goto exit;
}
#if defined(WINDOWS)
/* load windows_iana_map */
err_status = tzc_load_windows_iana_map (&tzd, input_folder);
if (err_status != NO_ERROR)
{
/* failed to load file */
goto exit;
}
#endif
if (tz_gen_type == TZ_GEN_TYPE_EXTEND)
{
write_checksum = true;
err_status = tzc_extend (&tzd);
if (err_status != NO_ERROR)
{
/* In this case a data migration is needed because the data could not
* be made backward compatible
*/
if (err_status == ER_TZ_COMPILE_ERROR)
{
err_status = tzc_update (&tzd, database_name);
if (err_status != NO_ERROR)
{
goto exit;
}
}
else
{
goto exit;
}
}
}
err_status = tzc_compute_timezone_checksum (&tzd, tz_gen_type);
if (err_status != NO_ERROR)
{
goto exit;
}
if (timezones_dot_c_filepath == NULL)
{
tzc_get_timezones_dot_c_filepath (sizeof (default_output_file_path), default_output_file_path);
timezones_dot_c_filepath = default_output_file_path;
}
err_status = tzc_export_timezone_dot_c (&tzd, timezones_dot_c_filepath);
if (err_status != NO_ERROR)
{
goto exit;
}
if (write_checksum == true)
{
strcpy (checksum, tzd.checksum);
}
tzc_summary (&tzd_raw, &tzd);
exit:
tzc_free_raw_data (&tzd_raw);
tzc_free_tz_data (&tzd, true);
return err_status;
}
#endif
/*
* tzc_free_tz_data () - frees members of a TZ_DATA* structure
* Returns:
* tzd(in/out): timezone data to free
*/
static void
tzc_free_tz_data (TZ_DATA * tzd, bool full)
{
int i;
if (tzd->countries != NULL)
{
free (tzd->countries);
}
if (tzd->timezones != NULL)
{
free (tzd->timezones);
}
if (tzd->timezone_names != NULL)
{
for (i = 0; i < tzd->timezone_count; i++)
{
if (tzd->timezone_names[i] != NULL)
{
free (tzd->timezone_names[i]);
}
}
free (tzd->timezone_names);
}
if (tzd->offset_rules != NULL)
{
for (i = 0; i < tzd->offset_rule_count; i++)
{
if (tzd->offset_rules[i].std_format != NULL)
{
free ((void *) (tzd->offset_rules[i].std_format));
}
if (tzd->offset_rules[i].save_format != NULL)
{
free ((void *) (tzd->offset_rules[i].save_format));
}
if (tzd->offset_rules[i].var_format != NULL)
{
free ((void *) (tzd->offset_rules[i].var_format));
}
}
free (tzd->offset_rules);
}
if (tzd->names != NULL)
{
for (i = 0; i < tzd->name_count; i++)
{
if (tzd->names[i].name != NULL)
{
free ((void *) (tzd->names[i].name));
}
}
free (tzd->names);
}
if (tzd->ds_rulesets != NULL)
{
for (i = 0; i < tzd->ds_ruleset_count; i++)
{
if (tzd->ds_rulesets[i].ruleset_name != NULL)
{
free ((void *) (tzd->ds_rulesets[i].ruleset_name));
}
if (tzd->ds_rulesets[i].default_abrev != NULL)
{
free ((void *) (tzd->ds_rulesets[i].default_abrev));
}
}
free (tzd->ds_rulesets);
}
if (tzd->ds_rules != NULL)
{
for (i = 0; i < tzd->ds_rule_count; i++)
{
if (tzd->ds_rules[i].letter_abbrev != NULL)
{
free ((void *) (tzd->ds_rules[i].letter_abbrev));
}
}
free (tzd->ds_rules);
}
if (full == true)
{
if (tzd->ds_leap_sec != NULL)
{
free (tzd->ds_leap_sec);
}
#if defined(WINDOWS)
if (tzd->windows_iana_map != NULL)
{
free (tzd->windows_iana_map);
}
#endif
}
memset (&tzd, 0, sizeof (tzd));
}
/*
* tzc_load_raw_data() - loads data from all relevant tz files into
* temporary structures
* Returns: NO_ERROR if successful, internal error code otherwise
* tzd_raw(out): loaded timezone data
* input_folder(in): path to the input folder containing timezone data
*/
static int
tzc_load_raw_data (TZ_RAW_DATA * tzd_raw, const char *input_folder)
{
int err_status = NO_ERROR;
int i;
/* load countries */
err_status = tzc_load_countries (tzd_raw, input_folder);
if (err_status != NO_ERROR)
{
/* failed to load country file */
goto exit;
}
/* load zones */
err_status = tzc_load_zone_names (tzd_raw, input_folder);
if (err_status != NO_ERROR)
{
/* failed to load zone file */
goto exit;
}
/* load zones (from the file names "backward" ) */
err_status = tzc_load_backward_zones (tzd_raw, input_folder);
if (err_status != NO_ERROR)
{
/* failed to load file */
goto exit;
}
/* load daylight saving rules, zone aliases and zone offset information */
for (i = 0; i < tz_File_count; i++)
{
if (tz_Files[i].type != TZF_RULES)
{
continue;
}
err_status = tzc_load_rule_file (tzd_raw, i, input_folder);
if (err_status != NO_ERROR)
{
goto exit;
}
}
/* load leap seconds */
err_status = tzc_load_leap_secs (tzd_raw, input_folder);
if (err_status != NO_ERROR)
{
/* failed to load file */
goto exit;
}
LOG_TZC_SET_CURRENT_CONTEXT (tzd_raw, "", -1);
err_status = tzc_check_links_raw_data (tzd_raw);
if (err_status != NO_ERROR)
{
goto exit;
}
exit:
return err_status;
}
/*
* tzc_import_old_data () - this function reads the existing timezone data and
* decides which data should be kept. The data to be kept
* consists of certain IDs, timezones and their
* associated offset rules and daylight saving rules.
* Choosing the data to keep is done based on the input
* parameter 'mode'.
* Returns: 0 (NO_ERROR) if success, error code otherwise
* tzd_raw(in/out) :raw timezone structure where to partially load the data to
* mode(in): loading mode e.g. new, update or extend
*/
static int
tzc_import_old_data (TZ_RAW_DATA * tzd_raw, const TZ_GEN_TYPE mode)
{
int err_status = NO_ERROR;
if (mode == TZ_GEN_TYPE_NEW)
{
/* nothing to do */
return NO_ERROR;
}
return err_status;
}
/*
* tzc_del_unused_raw_data () - cleans up the loaded raw timezone data and
* removes any unused information, such as daylight
* saving rules and rulesets
* Returns: 0 (NO_ERROR) if success, error code otherwise
* tzd_raw(out): raw timezone data structure to cleanup
*/
static int
tzc_del_unused_raw_data (TZ_RAW_DATA * tzd_raw)
{
int err_status = NO_ERROR;
int i, j, k;
bool found = false;
TZ_RAW_DS_RULESET *ruleset = NULL;
TZ_RAW_ZONE_INFO *zone = NULL;
TZ_RAW_OFFSET_RULE *offset_rule = NULL;
/* mark unused rulesets */
for (i = 0; i < tzd_raw->ruleset_count; i++)
{
ruleset = &(tzd_raw->ds_rulesets[i]);
found = false;
for (j = 0; j < tzd_raw->zone_count && !found; j++)
{
zone = &(tzd_raw->zones[j]);
for (k = 0; k < zone->offset_rule_count && !found; k++)
{
offset_rule = &(zone->offset_rules[k]);
if (strcmp (offset_rule->ds_ruleset_name, ruleset->name) == 0)
{
ruleset->is_used = true;
found = true;
}
}
}
}
/* remove unused rulesets */
i = 0;
while (i < tzd_raw->ruleset_count)
{
if (tzd_raw->ds_rulesets[i].is_used == false)
{
if (i < tzd_raw->ruleset_count - 1)
{
free (tzd_raw->ds_rulesets[i].rules);
memcpy (&(tzd_raw->ds_rulesets[i]), &(tzd_raw->ds_rulesets[tzd_raw->ruleset_count - 1]),
sizeof (TZ_RAW_DS_RULESET));
}
/* decrease count; tzd_raw->rulesets will be realloc'ed below */
tzd_raw->ruleset_count--;
}
else
{
i++;
}
}
assert (tzd_raw->ruleset_count > 0);
/* realloc rulesets, in case some rulesets were removed above */
ruleset = (TZ_RAW_DS_RULESET *) realloc (tzd_raw->ds_rulesets, tzd_raw->ruleset_count * sizeof (TZ_RAW_DS_RULESET));
if (ruleset == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", tzd_raw->ruleset_count);
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_OUT_OF_MEMORY, err_msg, "TZ_RAW_DS_RULESET");
goto exit;
}
tzd_raw->ds_rulesets = ruleset;
exit:
return err_status;
}
/*
* tzc_index_data () - build IDs for all time zone data
* Returns: 0(NO_ERROR) if success, error code otherwise
* tzd_raw (in/out): working timezone data structure
* mode(in): control flag
*/
static int
tzc_index_data (TZ_RAW_DATA * tzd_raw, const TZ_GEN_TYPE mode)
{
int err_status = NO_ERROR;
char err_msg[TZC_ERR_MSG_MAX_SIZE];
if (mode == TZ_GEN_TYPE_NEW || mode == TZ_GEN_TYPE_EXTEND)
{
tzc_index_raw_data (tzd_raw);
}
else
{
snprintf (err_msg, sizeof (err_msg) - 1, "UPDATE OPTION NOT IMPLEMENTED!");
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TZ_COMPILE_ERROR, 1, err_msg);
err_status = ER_TZ_COMPILE_ERROR;
goto exit;
}
err_status = tzc_index_raw_subdata (tzd_raw, mode);
if (err_status != NO_ERROR)
{
goto exit;
}
exit:
return err_status;
}
/*
* tzc_load_countries() - loads the list of countries from the files marked
* as TZ_COUNTRIES type (e.g. iso3166.tab)
* Returns: NO_ERROR(0) if success, error code or -1 otherwise
* tzd_raw(out): timezone data structure to hold the loaded information
* input_folder(in): folder containing IANA's timezone database
*/
static int
tzc_load_countries (TZ_RAW_DATA * tzd_raw, const char *input_folder)
{
int err_status = NO_ERROR;
int i, file_index = -1;
char country_filepath[PATH_MAX] = { 0 };
char str[256];
char *str_country_name;
FILE *fp = NULL;
TZ_RAW_COUNTRY *temp_tz_country = NULL;
for (i = 0; i < tz_File_count; i++)
{
if (tz_Files[i].type == TZF_COUNTRIES)
{
/* Only one file containing country data is allowed */
assert (file_index == -1);
file_index = i;
}
}
/* the list of files is hardcoded above, so if a file with TZ_COUNTRY flag is not found in tz_Files, code fixes are
* needed */
assert (file_index != -1);
tzc_build_filepath (country_filepath, sizeof (country_filepath), input_folder, tz_Files[file_index].name);
fp = fopen_ex (country_filepath, "rt");
if (fp == NULL)
{
/* file not found or not accessible */
err_status = TZC_ERR_FILE_NOT_ACCESSIBLE;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_FILE_NOT_ACCESSIBLE, country_filepath, "read");
goto exit;
}
tzd_raw->country_count = 0;
tzd_raw->countries = NULL;
LOG_TZC_SET_CURRENT_CONTEXT (tzd_raw, tz_Files[file_index].name, 0);
while (fgets (str, sizeof (str), fp))
{
tzd_raw->context.current_line++;
trim_comments_whitespaces (str);
if (IS_EMPTY_STR (str))
{
continue;
}
str_country_name = strchr (str, '\t');
if (str_country_name == NULL || strlen (str_country_name + 1) == 0
|| str_country_name - str != TZ_COUNTRY_CODE_LEN)
{
/* data formatting error on line <line_count> */
err_status = TZC_ERR_INVALID_COUNTRY;
TZC_LOG_ERROR_1ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_INVALID_COUNTRY, str);
goto exit;
}
temp_tz_country =
(TZ_RAW_COUNTRY *) realloc (tzd_raw->countries, (tzd_raw->country_count + 1) * sizeof (TZ_RAW_COUNTRY));
if (temp_tz_country == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", tzd_raw->country_count + 1);
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_OUT_OF_MEMORY, err_msg, "TZ_RAW_COUNTRY");
goto exit;
}
tzd_raw->countries = temp_tz_country;
/* grasp the newly added item */
temp_tz_country = &(tzd_raw->countries[tzd_raw->country_count]);
tzd_raw->country_count++;
memset (temp_tz_country, 0, sizeof (temp_tz_country[0]));
/* store parsed data */
memcpy (temp_tz_country->code, str, TZ_COUNTRY_CODE_LEN);
strncpy_bufsize (temp_tz_country->full_name, str_country_name + 1);
temp_tz_country->id = -1;
}
exit:
if (fp != NULL)
{
fclose (fp);
}
return err_status;
}
/*
* tzc_load_zones() - loads the list of countries from the files marked
* as TZ_ZONES type (e.g. zone.tab)
* Returns: 0 (NO_ERROR) if success, error code or -1 otherwise
* tzd_raw(out): timezone data structure to hold the loaded information
* input_folder(in): folder containing IANA's timezone database
*/
static int
tzc_load_zone_names (TZ_RAW_DATA * tzd_raw, const char *input_folder)
{
int err_status = NO_ERROR;
int i, file_index = -1;
char zone_filepath[PATH_MAX] = { 0 };
char str[256];
FILE *fp = NULL;
TZ_RAW_ZONE_INFO *temp_zone_info = NULL;
char *col_code, *col_coord, *col_tz_name, *col_comments;
col_code = col_coord = col_tz_name = col_comments = NULL;
for (i = 0; i < tz_File_count; i++)
{
if (tz_Files[i].type == TZF_ZONES)
{
/* Only one file containing zone data is allowed */
assert (file_index == -1);
file_index = i;
}
}
/* the list of files is hardcoded above, so if a file with TZ_COUNTRY flag is not found in tz_Files, code fixes are
* needed */
assert (file_index != -1);
tzc_build_filepath (zone_filepath, sizeof (zone_filepath), input_folder, tz_Files[file_index].name);
fp = fopen_ex (zone_filepath, "rt");
if (fp == NULL)
{
/* file not found or not accessible */
err_status = TZC_ERR_FILE_NOT_ACCESSIBLE;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_FILE_NOT_ACCESSIBLE, zone_filepath, "read");
goto exit;
}
tzd_raw->zone_count = 0;
tzd_raw->zones = NULL;
LOG_TZC_SET_CURRENT_CONTEXT (tzd_raw, tz_Files[file_index].name, 0);
while (fgets (str, sizeof (str), fp))
{
tzd_raw->context.current_line++;
trim_comments_whitespaces (str);
if (IS_EMPTY_STR (str))
{
continue;
}
col_code = strtok (str, "\t");
col_coord = strtok (NULL, "\t");
col_tz_name = strtok (NULL, "\t");
col_comments = strtok (NULL, "\t");
assert (col_code != NULL && col_coord != NULL && col_tz_name != NULL);
if (col_code == NULL || strlen (col_code) != 2 || IS_EMPTY_STR (col_coord) || IS_EMPTY_STR (col_tz_name))
{
/* data formatting error on line <line_count> */
char temp_msg[TZC_ERR_MSG_MAX_SIZE] = { 0 };
sprintf (temp_msg, "code: %s, coordinates: %s, name: %s, comments: %s", col_code == NULL ? "null" : col_code,
col_coord == NULL ? "null" : col_coord, col_tz_name == NULL ? "null" : col_tz_name,
col_comments == NULL ? "null/empty" : col_comments);
err_status = TZC_ERR_INVALID_ZONE;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_INVALID_ZONE, str, temp_msg);
goto exit;
}
/* new zone found, expand data structure and store it */
err_status = tzc_add_zone (col_tz_name, col_code, col_coord, col_comments, tzd_raw, &temp_zone_info);
if (err_status != NO_ERROR)
{
goto exit;
}
}
exit:
if (fp != NULL)
{
fclose (fp);
}
return err_status;
}
/*
* tzc_load_rule_files() - loads the data from the files marked as TZ_RULES
* (e.g. europe, asia etc.)
* Returns: 0 (NO_ERROR) if success, error code or -1 otherwise
* tzd_raw(out): timezone data structure to hold the loaded information
* file_index(in): index in tz_Files file list for the file to be loaded
* input_folder(in): folder containing IANA's timezone database
*
* NOTE: tz_load_rules() must be called after tz_load_zones(), because it
* needs the data structures and loaded data created by tz_load_zones()
*/
static int
tzc_load_rule_file (TZ_RAW_DATA * tzd_raw, const int file_index, const char *input_folder)
{
int err_status = NO_ERROR;
char filepath[PATH_MAX] = { 0 };
char str[TZ_MAX_LINE_LEN] = { 0 };
FILE *fp = NULL;
char *entry_type_str = NULL;
TZ_RAW_ZONE_INFO *last_zone = NULL;
char *next_token = NULL;
bool check_zone = false;
assert (tzd_raw != NULL);
assert (input_folder != NULL);
assert (file_index > 0 && file_index < tz_File_count);
assert (tz_Files[file_index].type == TZF_RULES);
tzc_build_filepath (filepath, sizeof (filepath), input_folder, tz_Files[file_index].name);
fp = fopen_ex (filepath, "rt");
if (fp == NULL)
{
/* file not found or not accessible */
err_status = TZC_ERR_FILE_NOT_ACCESSIBLE;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_FILE_NOT_ACCESSIBLE, filepath, "read");
goto exit;
}
LOG_TZC_SET_CURRENT_CONTEXT (tzd_raw, tz_Files[file_index].name, 0);
while (fgets (str, sizeof (str), fp))
{
tzd_raw->context.current_line++;
trim_comments_whitespaces (str);
if (strlen (str) < TZ_OFFRULE_PREFIX_TAB_COUNT)
{
continue;
}
if (check_zone)
{
if (str[0] == '\t' && str[1] == '\t' && str[2] == '\t')
{
err_status = tzc_add_offset_rule (last_zone, str + 3);
if (err_status != NO_ERROR)
{
goto exit;
}
continue;
}
else
{
check_zone = false;
}
}
entry_type_str = strtok (str, " \t");
if (strcmp (entry_type_str, "Link") == 0)
{
char *zone_name = NULL, *alias = NULL;
zone_name = strtok (NULL, " \t");
alias = strtok (NULL, "\t");
if (IS_EMPTY_STR (zone_name) || IS_EMPTY_STR (alias))
{
/* error, empty or misformed line */
err_status = TZC_ERR_BAD_TZ_LINK;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_BAD_TZ_LINK, zone_name, alias);
goto exit;
}
err_status = tzc_add_link (tzd_raw, zone_name, alias);
if (err_status != NO_ERROR)
{
goto exit;
}
continue;
}
else if (strcmp (entry_type_str, "Zone") == 0)
{
next_token = strtok (NULL, " \t");
if (IS_EMPTY_STR (next_token))
{
err_status = TZC_ERR_INVALID_VALUE;
TZC_LOG_ERROR_1ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_INVALID_VALUE, "timezone name");
goto exit;
}
err_status = tzc_get_zone (tzd_raw, next_token, &last_zone);
if (err_status != NO_ERROR)
{
/* some timezones (such as WET, CET, MET, EET) are not listed in the zone file, but are included in the
* rule files (ex.: europe file) in zone rules; they need to be added to the list of zones */
err_status = tzc_add_zone (next_token, NULL, NULL, NULL, tzd_raw, &last_zone);
if (err_status != NO_ERROR)
{
goto exit;
}
}
if (last_zone == NULL)
{
/* zones should have already been fully loaded */
err_status = TZC_ERR_ADD_ZONE;
TZC_LOG_ERROR_1ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_ADD_ZONE, next_token);
goto exit;
}
err_status = tzc_add_offset_rule (last_zone, next_token + strlen (next_token) + 1);
if (err_status != NO_ERROR)
{
goto exit;
}
check_zone = true;
}
else if (strcmp (entry_type_str, "Rule") == 0)
{
err_status = tzc_add_ds_rule (tzd_raw, str + strlen (entry_type_str) + 1);
if (err_status != NO_ERROR)
{
goto exit;
}
}
else
{
/* non-empty line, which is not a comment, nor a valid zone/rule/link definition */
assert (false);
}
/* reset line buffer */
memset (str, 0, TZ_MAX_LINE_LEN);
}
exit:
if (fp != NULL)
{
fclose (fp);
}
return err_status;
}
/*
* tzc_load_backward_zones() - loads the "backward" file containing links
* between obsolete (more or less) zone names and their current
* names
* Returns: 0 (NO_ERROR) if success, error code or -1 otherwise
* tzd_raw(out): timezone data structure to hold the loaded information
* input_folder(in): folder containing IANA's timezone database
*
* NOTE: the "backward" file is actually usefull, because most SQL samples
* found online sometimes use old zone names.
*/
static int
tzc_load_backward_zones (TZ_RAW_DATA * tzd_raw, const char *input_folder)
{
int err_status = NO_ERROR;
int i, file_index = -1;
char filepath[PATH_MAX] = { 0 };
char str[TZ_MAX_LINE_LEN] = { 0 };
FILE *fp = NULL;
char *entry_type_str = NULL;
char *next_token = NULL, *zone_name = NULL, *alias = NULL;
assert (tzd_raw != NULL);
assert (input_folder != NULL);
for (i = 0; i < tz_File_count; i++)
{
if (tz_Files[i].type == TZF_BACKWARD)
{
file_index = i;
break;
}
}
assert (file_index != -1);
tzc_build_filepath (filepath, sizeof (filepath), input_folder, tz_Files[file_index].name);
fp = fopen_ex (filepath, "rt");
if (fp == NULL)
{
/* file not found or not accessible */
err_status = TZC_ERR_FILE_NOT_ACCESSIBLE;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_FILE_NOT_ACCESSIBLE, filepath, "read");
goto exit;
}
LOG_TZC_SET_CURRENT_CONTEXT (tzd_raw, tz_Files[file_index].name, 0);
while (fgets (str, sizeof (str), fp))
{
tzd_raw->context.current_line++;
trim_comments_whitespaces (str);
if (IS_EMPTY_STR (str))
{
continue;
}
entry_type_str = strtok (str, "\t");
assert (entry_type_str != NULL && strcmp (entry_type_str, "Link") == 0);
next_token = str + strlen (entry_type_str) + 1;
zone_name = strtok (next_token, "\t");
if (IS_EMPTY_STR (zone_name))
{
err_status = TZC_ERR_BAD_TZ_LINK;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_BAD_TZ_LINK, zone_name, "<alias not parsed yet>");
goto exit;
}
next_token = zone_name + strlen (zone_name) + 1;
while (*next_token == '\t')
{
next_token++;
}
alias = strtok (next_token, "\t");
if (IS_EMPTY_STR (alias))
{
err_status = TZC_ERR_BAD_TZ_LINK;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_BAD_TZ_LINK, zone_name, alias);
goto exit;
}
err_status = tzc_add_link (tzd_raw, zone_name, alias);
if (err_status != NO_ERROR)
{
goto exit;
}
/* reset line buffer */
memset (str, 0, TZ_MAX_LINE_LEN);
}
exit:
if (fp != NULL)
{
fclose (fp);
}
return err_status;
}
/*
* tzc_load_leap_secs() - loads the "leapseconds" file containing leap
* second information (date of occurence, type, etc.)
* Returns: 0 (NO_ERROR) if success, error code or -1 otherwise
* tzd_raw(out): timezone data structure to hold the loaded information
* input_folder(in): folder containing IANA's timezone database
*/
static int
tzc_load_leap_secs (TZ_RAW_DATA * tzd_raw, const char *input_folder)
{
int err_status = NO_ERROR;
int i, file_index = -1, leap_year, leap_month_num, leap_day_num;
int leap_time_h, leap_time_m, leap_time_s;
char filepath[PATH_MAX] = { 0 };
char str[TZ_MAX_LINE_LEN] = { 0 };
FILE *fp = NULL;
const char *next_token, *str_next, *entry_type_str;
bool leap_corr_minus = false;
bool leap_is_rolling = false;
assert (tzd_raw != NULL);
assert (input_folder != NULL);
next_token = str_next = entry_type_str = NULL;
leap_year = leap_month_num = leap_day_num = -1;
leap_time_h = leap_time_m = leap_time_s = -1;
for (i = 0; i < tz_File_count; i++)
{
if (tz_Files[i].type == TZF_LEAP)
{
file_index = i;
break;
}
}
assert (file_index != -1);
tzc_build_filepath (filepath, sizeof (filepath), input_folder, tz_Files[file_index].name);
fp = fopen_ex (filepath, "rt");
if (fp == NULL)
{
/* file not found or not accessible */
err_status = TZC_ERR_FILE_NOT_ACCESSIBLE;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_FILE_NOT_ACCESSIBLE, filepath, "read");
goto exit;
}
tzd_raw->leap_sec_count = 0;
tzd_raw->leap_sec = NULL;
LOG_TZC_SET_CURRENT_CONTEXT (tzd_raw, tz_Files[file_index].name, 0);
while (fgets (str, sizeof (str), fp))
{
const char *str_end;
tzd_raw->context.current_line++;
trim_comments_whitespaces (str);
if (IS_EMPTY_STR (str))
{
continue;
}
str_end = str + strlen (str);
entry_type_str = strtok (str, "\t");
assert (entry_type_str != NULL && strcmp (entry_type_str, "Leap") == 0);
next_token = strtok (NULL, "\t");
if (tz_str_read_number (next_token, str_end, true, false, &leap_year, &str_next) != NO_ERROR)
{
err_status = TZC_ERR_INVALID_VALUE;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_INVALID_VALUE, "year",
next_token == NULL ? "" : next_token);
goto exit;
}
next_token = strtok (NULL, "\t");
if (str_month_to_int (next_token, &leap_month_num, &str_next) != NO_ERROR)
{
err_status = TZC_ERR_INVALID_VALUE;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_INVALID_VALUE, "month",
next_token == NULL ? "" : next_token);
goto exit;
}
next_token = strtok (NULL, "\t");
if (tz_str_read_number (next_token, str_end, true, false, &leap_day_num, &str_next) != NO_ERROR)
{
err_status = TZC_ERR_INVALID_VALUE;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_INVALID_VALUE, "day",
next_token == NULL ? "" : next_token);
goto exit;
}
next_token = strtok (NULL, "\t");
if (tz_str_read_time (next_token, str_end, false, true, &leap_time_h, &leap_time_m, &leap_time_s, &str_next) !=
NO_ERROR)
{
err_status = TZC_ERR_INVALID_VALUE;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_INVALID_VALUE, "time",
next_token == NULL ? "" : next_token);
goto exit;
}
next_token = strtok (NULL, "\t");
if (strlen (next_token) != 1 || ((*next_token != '+') && (*next_token != '-')))
{
err_status = TZC_ERR_INVALID_VALUE;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_INVALID_VALUE, "correction",
next_token == NULL ? "" : next_token);
goto exit;
}
if (*next_token == '-')
{
leap_corr_minus = true;
}
next_token = strtok (NULL, "\t");
if (strlen (next_token) != 1 || ((*next_token != 'R') && (*next_token != 'S')))
{
err_status = TZC_ERR_INVALID_VALUE;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_INVALID_VALUE, "leap second type",
next_token == NULL ? "" : next_token);
goto exit;
}
if (*next_token == 'R')
{
leap_is_rolling = true;
}
if (tzc_add_leap_sec (tzd_raw, leap_year, leap_month_num, leap_day_num, leap_time_h, leap_time_m, leap_time_s,
leap_corr_minus, leap_is_rolling) != NO_ERROR)
{
err_status = TZC_ERR_INVALID_VALUE;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_INVALID_VALUE, "leap second data", "line");
goto exit;
}
}
exit:
if (fp != NULL)
{
fclose (fp);
}
return err_status;
}
/*
* tzc_add_link() - adds a link (zone alias) to the list of links in the
* raw timezone data structure
* Returns: error code if failure, 0 (NO_ERROR) otherwise
* tzd_raw(out): timezone data structure where to store the new link
* zone(in): zone name for the link to be created
* alias(in): zone alias for the link to be created
*/
static int
tzc_add_link (TZ_RAW_DATA * tzd_raw, const char *zone, const char *alias)
{
TZ_RAW_LINK *temp_link;
int err_status = NO_ERROR;
assert (tzd_raw != NULL);
assert (!IS_EMPTY_STR (zone) && !IS_EMPTY_STR (alias));
/* add link to the link list */
assert (tzd_raw->link_count >= 0);
temp_link = (TZ_RAW_LINK *) realloc (tzd_raw->links, (tzd_raw->link_count + 1) * sizeof (TZ_RAW_LINK));
if (temp_link == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", tzd_raw->link_count + 1);
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_OUT_OF_MEMORY, err_msg, "TZ_RAW_LINK");
goto exit;
}
tzd_raw->links = temp_link;
temp_link = &(tzd_raw->links[tzd_raw->link_count]);
tzd_raw->link_count++;
memset (temp_link, 0, sizeof (temp_link[0]));
strcpy (temp_link->name, zone);
strcpy (temp_link->alias, alias);
exit:
return err_status;
}
/*
* tzc_add_leap_sec() - adds leap second info to the raw timezone structure
* Returns: error code if failure, 0 (NO_ERROR) otherwise
* year(in), month(in), day(in): parts of the date when leap second occurs
* time(in): time (number of seconds since 00:00) of day when leap sec occurs
* corr_minus(in): true if leap second correction is negative, true if '+'
* leap_is_rolling(in): true if given time is local, false if UTC
* tzd_raw(in/out): timezone data structure where to store the new link
*/
static int
tzc_add_leap_sec (TZ_RAW_DATA * tzd_raw, int year, int month, int day, unsigned char hour, unsigned char min,
unsigned char sec, bool corr_minus, bool leap_is_rolling)
{
TZ_LEAP_SEC *temp_leap_sec;
int err_status = NO_ERROR;
assert (tzd_raw != NULL);
/* add leap second to the leap second list */
assert (tzd_raw->leap_sec_count >= 0);
temp_leap_sec = (TZ_LEAP_SEC *) realloc (tzd_raw->leap_sec, (tzd_raw->leap_sec_count + 1) * sizeof (TZ_LEAP_SEC));
if (temp_leap_sec == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", tzd_raw->leap_sec_count + 1);
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_OUT_OF_MEMORY, err_msg, "TZ_RAW_LEAP_SEC");
goto exit;
}
tzd_raw->leap_sec = temp_leap_sec;
temp_leap_sec = &(tzd_raw->leap_sec[tzd_raw->leap_sec_count]);
tzd_raw->leap_sec_count++;
memset (temp_leap_sec, 0, sizeof (temp_leap_sec[0]));
/* set data */
temp_leap_sec->year = (unsigned short) year;
temp_leap_sec->month = (unsigned char) month;
temp_leap_sec->day = (unsigned char) day;
if (hour != 23 || min != 59 || sec != 60)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", tzd_raw->leap_sec_count + 1);
err_status = TZC_ERR_INVALID_TIME;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_INVALID_TIME, err_msg, "TZ_RAW_LEAP_SEC");
goto exit;
}
temp_leap_sec->corr_negative = (unsigned char) (corr_minus ? 1 : 0);
temp_leap_sec->is_rolling = (unsigned char) (leap_is_rolling ? 1 : 0);
exit:
return err_status;
}
/*
* tzc_add_zone() - adds a zone to the list of timezones in the raw
* timezone data structure
* Returns: 0 (NO_ERROR) if success, error code if failure
* zone(in): name of the timezone to add
* code (in): code of the time zone to add
* coord(in): coordinates of the timezone to add
* comments(in): comments for the timezone to add
* tzd_raw(in/out): raw timezone data structure where to add the timezone to
* new_zone(out): pointer to the newly added timezone
*/
static int
tzc_add_zone (const char *zone, const char *code, const char *coord, const char *comments, TZ_RAW_DATA * tzd_raw,
TZ_RAW_ZONE_INFO ** new_zone)
{
TZ_RAW_ZONE_INFO *temp_zone_info = NULL;
int err_status = NO_ERROR;
assert (tzd_raw != NULL);
assert (!IS_EMPTY_STR (zone));
*new_zone = NULL;
temp_zone_info = (TZ_RAW_ZONE_INFO *) realloc (tzd_raw->zones, (tzd_raw->zone_count + 1) * sizeof (TZ_RAW_ZONE_INFO));
if (temp_zone_info == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", tzd_raw->zone_count + 1);
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_OUT_OF_MEMORY, err_msg, "TZ_RAW_ZONE_INFO");
goto exit;
}
tzd_raw->zones = temp_zone_info;
/* grasp the newly added item */
temp_zone_info = &(tzd_raw->zones[tzd_raw->zone_count]);
tzd_raw->zone_count++;
memset (temp_zone_info, 0, sizeof (temp_zone_info[0]));
/* store the parsed data */
if (code != NULL)
{
strcpy (temp_zone_info->code, code);
}
else
{
temp_zone_info->code[0] = '\0';
}
if (coord != NULL)
{
strcpy (temp_zone_info->coordinates, coord);
}
else
{
temp_zone_info->coordinates[0] = '\0';
}
strcpy (temp_zone_info->full_name, zone);
if (comments != NULL)
{
strcpy (temp_zone_info->comments, comments);
}
else
{
temp_zone_info->comments[0] = '\0';
}
temp_zone_info->id = -1;
*new_zone = temp_zone_info;
exit:
return err_status;
}
/*
* tzc_get_zone() - parse the input string to get a zone name and
* searches for it in the already loaded zones from the
* TZ_RAW_DATA variable tzd_raw.
* Returns: 0 (NO_ERROR) if zone found, -1 otherwise
* tzd_raw(in/out): raw timezone data structure where to search the zone in
* zone_name(in): name of the timezone to search for
* zone (out): reference to the timezone found
*/
static int
tzc_get_zone (const TZ_RAW_DATA * tzd_raw, const char *zone_name, TZ_RAW_ZONE_INFO ** zone)
{
int i;
assert (tzd_raw != NULL);
for (i = 0; i < tzd_raw->zone_count; i++)
{
if (strcmp (tzd_raw->zones[i].full_name, zone_name) == 0)
{
*zone = &(tzd_raw->zones[i]);
return NO_ERROR;
}
}
*zone = NULL;
return TZC_ERR_GENERIC;
}
/*
* tzc_add_offset_rule() - parse the input string as an offset rule for a
* timezone and attach it to the specified timezone
* Returns: 0 (NO_ERROR) if success, or negative code if an error occurs
* zone(out): raw timezone information structure where to store the parsed
* offset rule
* rule_text(in): string to parse as an offset rule
*/
static int
tzc_add_offset_rule (TZ_RAW_ZONE_INFO * zone, char *rule_text)
{
char *gmt_off = NULL, *rules = NULL, *format = NULL, *until = NULL;
int err_status = NO_ERROR;
TZ_RAW_OFFSET_RULE *temp_rule = NULL;
const char *str_dummy = NULL;
char *rule_text_end;
int gmt_off_num = 0;
bool is_numeric_gmt_off = false;
assert (zone != NULL);
rule_text_end = rule_text + strlen (rule_text);
gmt_off = strtok (rule_text, "\t ");
if (IS_EMPTY_STR (gmt_off))
{
err_status = TZC_ERR_INVALID_VALUE;
TZC_LOG_ERROR_1ARG (NULL, TZC_ERR_INVALID_VALUE, "zone offset");
goto exit;
}
/* some offset rules have the GMTOFF column '0' instead of a valid time value in the format hh:mm */
if (strcmp (gmt_off, "0") == 0)
{
is_numeric_gmt_off = true;
gmt_off_num = 0;
}
else if (strchr (gmt_off, ':') == NULL)
{
/* string might not be a GMT offset rule; check if it's a number */
err_status = tz_str_read_number (gmt_off, rule_text_end, true, true, &gmt_off_num, &str_dummy);
if (err_status != NO_ERROR)
{
err_status = TZC_ERR_INVALID_VALUE;
goto exit;
}
is_numeric_gmt_off = true;
}
rules = strtok (NULL, " \t");
format = strtok (NULL, " \t");
if (IS_EMPTY_STR (rules) || IS_EMPTY_STR (format))
{
err_status = TZC_ERR_INVALID_VALUE;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_INVALID_VALUE, "ruleset name or format",
IS_EMPTY_STR (rules) ? (IS_EMPTY_STR (format) ? "" : format) : rules);
goto exit;
}
until = strtok (NULL, "\t");
if (zone->offset_rule_count != 0 && zone->offset_rules[zone->offset_rule_count - 1].until_flag == UNTIL_INFINITE)
{
/* last existing rule still applies, no new rules are allowed after this one */
err_status = TZC_ERR_ZONE_RULE_UNORDERED;
TZC_LOG_ERROR_1ARG (NULL, TZC_ERR_ZONE_RULE_UNORDERED, rule_text);
goto exit;
}
temp_rule =
(TZ_RAW_OFFSET_RULE *) realloc (zone->offset_rules, (zone->offset_rule_count + 1) * sizeof (TZ_RAW_OFFSET_RULE));
if (temp_rule == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", zone->offset_rule_count + 1);
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_OUT_OF_MEMORY, err_msg, "TZ_RAW_OFFSET_RULE");
goto exit;
}
zone->offset_rules = temp_rule;
temp_rule = &(zone->offset_rules[zone->offset_rule_count]);
zone->offset_rule_count++;
memset (temp_rule, 0, sizeof (temp_rule[0]));
if (is_numeric_gmt_off)
{
temp_rule->gmt_off = gmt_off_num * 3600;
}
else
{
err_status = tz_str_to_seconds (gmt_off, rule_text_end, &(temp_rule->gmt_off), &str_dummy, false);
if (err_status != NO_ERROR)
{
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_INVALID_TIME, gmt_off, "tz offset rule");
goto exit;
}
}
assert (strlen (rules) < TZ_DS_RULESET_NAME_SIZE && strlen (format) < TZ_MAX_FORMAT_SIZE);
strcpy (temp_rule->ds_ruleset_name, rules);
strcpy (temp_rule->format, format);
if (str_to_offset_rule_until (temp_rule, until) != NO_ERROR)
{
goto exit;
}
exit:
return err_status;
}
/*
* tzc_read_time_type() -
*
* Returns: 0 (NO_ERROR) if success, or negative code if an error occurs
* tzd_raw(in/out): raw timezone data structure
* rule_text(in): string to parse as a daylight saving rule
*/
static int
tzc_read_time_type (const char *str, const char **next, TZ_TIME_TYPE * time_type)
{
assert (time_type != NULL);
assert (next != NULL);
switch (*str)
{
case 's':
/* local standard time (different from wall clock time when observing daylight saving time) */
*time_type = TZ_TIME_TYPE_LOCAL_STD;
break;
case 'g':
case 'u':
case 'z':
/* g/u/z stand for GMT/UTC/Zulu which are the same thing */
*time_type = TZ_TIME_TYPE_UTC;
break;
case 'w':
case '\0':
/* wall clock time (including daylight savings, if any). This is the default, if no suffix is used. */
*time_type = TZ_TIME_TYPE_LOCAL_WALL;
break;
default:
/* No other suffixes are allowed, so this should never be hit */
return TZC_ERR_GENERIC;
break;
}
*next = (char *) str + 1;
return NO_ERROR;
}
/*
* tzc_add_ds_rule() - parse the input string as a daylight saving rule and
* append it to the rule set identified by the rule name
* Returns: 0 (NO_ERROR) if success, or negative code if an error occurs
* tzd_raw(in/out): raw timezone data structure
* rule_text(in): string to parse as a daylight saving rule
*/
static int
tzc_add_ds_rule (TZ_RAW_DATA * tzd_raw, char *rule_text)
{
int i, val_read;
int err_status = NO_ERROR;
TZ_RAW_DS_RULESET *ds_ruleset = NULL;
TZ_RAW_DS_RULE *ds_rule = NULL;
char *col_name, *col_from, *col_to, *col_type, *col_in, *col_on, *col_at;
char *col_save, *col_letters;
const char *str_cursor = NULL;
const char *rule_text_end;
assert (tzd_raw != NULL);
rule_text_end = rule_text + strlen (rule_text);
col_name = strtok (rule_text, " \t");
col_from = strtok (NULL, " \t");
col_to = strtok (NULL, " \t");
col_type = strtok (NULL, " \t");
col_in = strtok (NULL, " \t");
col_on = strtok (NULL, " \t");
col_at = strtok (NULL, " \t");
col_save = strtok (NULL, " \t");
col_letters = strtok (NULL, " \t");
/* all tokens above must be at least one character long */
if (IS_EMPTY_STR (col_name) || IS_EMPTY_STR (col_from) || IS_EMPTY_STR (col_to) || IS_EMPTY_STR (col_type)
|| IS_EMPTY_STR (col_in) || IS_EMPTY_STR (col_on) || IS_EMPTY_STR (col_at) || IS_EMPTY_STR (col_save)
|| IS_EMPTY_STR (col_letters))
{
char temp_msg[TZC_ERR_MSG_MAX_SIZE] = { 0 };
sprintf (temp_msg,
"NAME: '%s', FROM: '%s', TO: '%s', TYPE: '%s', "
"IN: '%s', ON: '%s', AT: '%s', SAVE: '%s', LETTER/S: '%s'", col_name == NULL ? "" : col_name,
col_from == NULL ? "" : col_from, col_to == NULL ? "" : col_to, col_type == NULL ? "" : col_type,
col_in == NULL ? "" : col_in, col_on == NULL ? "" : col_on, col_at == NULL ? "" : col_at,
col_save == NULL ? "" : col_save, col_letters == NULL ? "" : col_letters);
err_status = TZC_ERR_INVALID_DS_RULE;
TZC_LOG_ERROR_1ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_INVALID_DS_RULE, temp_msg);
goto exit;
}
STR_SKIP_LEADING_SPACES (col_name);
STR_SKIP_LEADING_SPACES (col_from);
STR_SKIP_LEADING_SPACES (col_to);
STR_SKIP_LEADING_SPACES (col_type);
STR_SKIP_LEADING_SPACES (col_in);
STR_SKIP_LEADING_SPACES (col_on);
STR_SKIP_LEADING_SPACES (col_at);
STR_SKIP_LEADING_SPACES (col_save);
STR_SKIP_LEADING_SPACES (col_letters);
/* find a rule set with the same name as the rule */
for (i = 0; i < tzd_raw->ruleset_count; i++)
{
if (strcmp (tzd_raw->ds_rulesets[i].name, col_name) == 0)
{
ds_ruleset = &(tzd_raw->ds_rulesets[i]);
break;
}
}
if (ds_ruleset == NULL)
{
/* no rule set with the designated name was found; create one */
ds_ruleset =
(TZ_RAW_DS_RULESET *) realloc (tzd_raw->ds_rulesets, (tzd_raw->ruleset_count + 1) * sizeof (TZ_RAW_DS_RULESET));
if (ds_ruleset == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", tzd_raw->ruleset_count + 1);
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_OUT_OF_MEMORY, err_msg, "TZ_RAW_DS_RULESET");
goto exit;
}
tzd_raw->ds_rulesets = ds_ruleset;
ds_ruleset = &(tzd_raw->ds_rulesets[tzd_raw->ruleset_count]);
tzd_raw->ruleset_count++;
memset (ds_ruleset, 0, sizeof (ds_ruleset[0]));
strcpy (ds_ruleset->name, col_name);
ds_ruleset->is_used = false;
}
/* add the daylight saving rule to the rule set (found or created) */
ds_rule = (TZ_RAW_DS_RULE *) realloc (ds_ruleset->rules, (ds_ruleset->rule_count + 1) * sizeof (TZ_RAW_DS_RULE));
if (ds_rule == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", ds_ruleset->rule_count + 1);
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_OUT_OF_MEMORY, err_msg, "TZ_RAW_DS_RULE");
goto exit;
}
ds_ruleset->rules = ds_rule;
ds_rule = &(ds_ruleset->rules[ds_ruleset->rule_count]);
ds_ruleset->rule_count++;
memset (ds_rule, 0, sizeof (ds_rule[0]));
/* process and save data into the new TZ_RULE item */
val_read = 0;
/* process and store "FROM" year */
if (tz_str_read_number (col_from, rule_text_end, true, false, &val_read, &str_cursor) != NO_ERROR)
{
err_status = TZC_ERR_CANT_READ_VALUE;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_CANT_READ_VALUE, "year", col_from);
goto exit;
}
ds_rule->from_year = (short) val_read;
/* process and store "TO" year; if field value is "only", copy "FROM" */
if (strcmp (col_to, "only") == 0)
{
ds_rule->to_year = ds_rule->from_year;
}
else if (strcmp (col_to, "max") == 0)
{
ds_rule->to_year = TZ_MAX_YEAR;
}
else if (tz_str_read_number (col_to, rule_text_end, true, false, &val_read, &str_cursor) != NO_ERROR)
{
err_status = TZC_ERR_CANT_READ_VALUE;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_CANT_READ_VALUE, "year", col_to);
goto exit;
}
else
{
ds_rule->to_year = (short) val_read;
}
assert (col_type == NULL || strlen (col_type) < TZ_RULE_TYPE_MAX_SIZE);
strcpy (ds_rule->type, col_type);
/* process and store month ("IN") */
if (str_month_to_int (col_in, &val_read, &str_cursor) != NO_ERROR)
{
err_status = TZC_ERR_CANT_READ_VALUE;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_CANT_READ_VALUE, "month", col_in);
goto exit;
}
if (val_read < TZ_MON_JAN || val_read > TZ_MON_DEC)
{
char temp_msg[TZC_ERR_MSG_MAX_SIZE] = { 0 };
sprintf (temp_msg, "found value: %d", val_read);
err_status = TZC_ERR_INVALID_VALUE;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_INVALID_VALUE, "month value", temp_msg);
goto exit;
}
ds_rule->in_month = (unsigned char) val_read;
err_status = tzc_parse_ds_change_on (ds_rule, col_on);
if (err_status != NO_ERROR)
{
goto exit;
}
/* some daylight saving rules use "0" instead of "0:00" in the AT column */
if (strcmp (col_at, "0") == 0)
{
val_read = 0;
}
else if (tz_str_to_seconds (col_at, rule_text_end, &val_read, &str_cursor, false) != NO_ERROR)
{
err_status = TZC_ERR_CANT_READ_VALUE;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_CANT_READ_VALUE, "AT column", col_at);
goto exit;
}
ds_rule->at_time = val_read;
err_status = tzc_read_time_type (str_cursor, &str_cursor, &(ds_rule->at_time_type));
if (err_status != NO_ERROR)
{
return err_status;
}
/* In a daylight saving rule, the "Save" column is either 0 or 1 hour, given as a one char string ("0" or "1"), or an
* amount of time specified as hh:mm. So first check if col_save == "<one_char>" */
if (strlen (col_save) == 1)
{
if (tz_str_read_number (col_save, rule_text_end, true, false, &val_read, &str_cursor) != NO_ERROR)
{
err_status = TZC_ERR_CANT_READ_VALUE;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_CANT_READ_VALUE, "SAVE column", col_save);
goto exit;
}
val_read *= 3600;
}
else if (tz_str_to_seconds (col_save, rule_text_end, &val_read, &str_cursor, false) != NO_ERROR)
{
err_status = TZC_ERR_CANT_READ_VALUE;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_CANT_READ_VALUE, "SAVE column", col_save);
goto exit;
}
ds_rule->save_time = val_read;
strcpy (ds_rule->letter_abbrev, col_letters);
exit:
return err_status;
}
/*
* tzc_parse_ds_change_on() - parse the string corresponding to the "ON"
* column in a daylight saving rule, and store
* data in the destination TZ_DS_CHANGE_ON variable
* Returns: 0 (NO_ERROR) if success, error code otherwise
* dest(out): destination variable where to store the parsed data
* str(in): input string to parse
*
* NOTE: The input string can be of the following forms: "21" (plain number),
* "Sun>=16", "lastSun" or other variations using other week days.
*/
static int
tzc_parse_ds_change_on (TZ_RAW_DS_RULE * dest, const char *str)
{
int err_status = NO_ERROR;
int day_num = 0; /* day of week or day of month, depending on the context */
int type = -1, bound = -1;
const char *str_cursor = NULL;
assert (str != NULL && strlen (str) > 0);
assert (dest->in_month <= TZ_MON_DEC);
str_cursor = str;
err_status = str_read_day_var (str, dest->in_month, &type, &day_num, &bound, &str_cursor);
if (err_status != NO_ERROR)
{
goto exit;
}
/* need to validate the day found; check if it is a valid value, according to the year(s) and month already read into
* the input TZ_RAW_DS_RULE dest parameter */
if (type == TZ_DS_TYPE_FIXED)
{
int upper_year;
if (day_num < 0 || day_num > 30)
{
char temp_msg[TZC_ERR_MSG_MAX_SIZE] = { 0 };
sprintf (temp_msg, "found value: %d", day_num);
err_status = TZC_ERR_DS_INVALID_DATE;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_DS_INVALID_DATE, "day of month", temp_msg);
goto exit;
}
upper_year = (dest->to_year == TZ_MAX_YEAR) ? dest->from_year : dest->to_year;
if (!tzc_is_valid_date (day_num, dest->in_month, dest->from_year, upper_year + 1))
{
char temp_msg[TZC_ERR_MSG_MAX_SIZE] = { 0 };
sprintf (temp_msg, "Day: %d, Month: %d, Year: %d", day_num, dest->in_month, dest->from_year);
err_status = TZC_ERR_DS_INVALID_DATE;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_DS_INVALID_DATE, "day of month", temp_msg);
goto exit;
}
/* This is a fixed day of month, store it as such */
dest->change_on.type = TZ_DS_TYPE_FIXED;
dest->change_on.day_of_month = (unsigned char) day_num;
dest->change_on.day_of_week = TZ_WEEK_DAY_COUNT; /* invalid value */
}
else if (type == TZ_DS_TYPE_VAR_SMALLER)
{
dest->change_on.type = TZ_DS_TYPE_VAR_SMALLER;
dest->change_on.day_of_month = (unsigned char) bound;
dest->change_on.day_of_week = (unsigned char) day_num;
}
else if (type == TZ_DS_TYPE_VAR_GREATER)
{
dest->change_on.type = TZ_DS_TYPE_VAR_GREATER;
dest->change_on.day_of_month = (unsigned char) bound;
dest->change_on.day_of_week = (unsigned char) day_num;
}
else
{
assert (false);
}
exit:
return err_status;
}
/*
* tzc_is_valid_date () - check if a given date (day and month) is valid for
* all the year values in a given range [year_start, year_end)
* Returns: true if valid, false otherwise
* day(int): day of month (0 to 27/28/29/30)
* month(in): month (0-11)
* year_start(in): starting year for the range
* year_end(in): ending year for the range
*/
static bool
tzc_is_valid_date (const int day, const int month, const int year_start, const int year_end)
{
int year;
if (day < 0 || day > 30 || month < TZ_MON_JAN || month > TZ_MON_DEC || year_start < 0 || year_end <= year_start
|| year_end > TZ_MAX_YEAR)
{
return false;
}
if (month == TZ_MON_FEB)
{
for (year = year_start; year < year_end; year++)
{
if (day >= (IS_LEAP_YEAR (year) ? 29 : 28))
{
return false;
}
}
}
else
{
if (day >= DAYS_IN_MONTH (month))
{
return false;
}
}
return true;
}
/*
* tzc_free_raw_data() - frees the structures used when loading the timezone
* data from the input folder
* Returns:
* tzd_raw(in/out): time zone bulk data structure to free
*/
static void
tzc_free_raw_data (TZ_RAW_DATA * tzd_raw)
{
int i, j;
if (tzd_raw == NULL)
{
return;
}
if (tzd_raw->countries != NULL)
{
free_and_init (tzd_raw->countries);
}
if (tzd_raw->links != NULL)
{
free_and_init (tzd_raw->links);
}
if (tzd_raw->ds_rulesets != NULL)
{
for (i = 0; i < tzd_raw->ruleset_count; i++)
{
free_and_init (tzd_raw->ds_rulesets[i].rules);
}
free_and_init (tzd_raw->ds_rulesets);
}
if (tzd_raw->zones != NULL)
{
TZ_RAW_ZONE_INFO *zone = NULL;
for (i = 0; i < tzd_raw->zone_count; i++)
{
zone = &(tzd_raw->zones[i]);
free_and_init (zone->offset_rules);
for (j = 0; j < zone->alias_count; j++)
{
if (zone->aliases[j] != NULL)
{
free_and_init (zone->aliases[j]);
}
}
free_and_init (zone->aliases);
}
free_and_init (tzd_raw->zones);
}
if (tzd_raw->leap_sec != NULL)
{
free_and_init (tzd_raw->leap_sec);
}
}
/*
* tzc_check_links_raw_data () - go through TZ_RAW_DATA.links and check if
* there is an actual link between two existing timezones
* (both defined in zone.tab) which should share the same
* time settings e.g. rules are defined for one timezone
* but not the other.
* Returns: 0 (NO_ERROR) if success, error code otherwise
* tzd_raw(in/out): time zone bulk data structure to free
*
* NOTE: an example of such a link between two timezones is
* 'Link Europe/Zurich Europe/Busingen'; Both zones are defined in
* zone.tab, but GMT offset rules are defined only for Europe/Zurich;
* in this case, the 'Link' described above is not a simple alias, but a
* cloning rule.
*/
static int
tzc_check_links_raw_data (TZ_RAW_DATA * tzd_raw)
{
int err_status = NO_ERROR;
int i, j, tz_name_index, tz_alias_index;
TZ_RAW_LINK *tz_link;
TZ_RAW_ZONE_INFO *tz_zone;
for (i = 0; i < tzd_raw->link_count; i++)
{
tz_link = &(tzd_raw->links[i]);
tz_name_index = -1;
tz_alias_index = -1;
for (j = 0; j < tzd_raw->zone_count && (tz_name_index == -1 || tz_alias_index == -1); j++)
{
tz_zone = &(tzd_raw->zones[j]);
if (strcmp (tz_zone->full_name, tz_link->name) == 0)
{
tz_name_index = j;
assert (tz_name_index != tz_alias_index);
}
if (strcmp (tz_zone->full_name, tz_link->alias) == 0)
{
tz_alias_index = j;
assert (tz_name_index != tz_alias_index);
}
}
if (tz_name_index != -1 && tz_alias_index != -1)
{
/* Both zone name and alias from this 'Link' rule are timezones; Check which one does not have any attached
* rules and mark it as sharing the other one's rules */
bool remove_link = false;
tz_zone = &(tzd_raw->zones[tz_name_index]);
if (tz_zone->offset_rule_count > 0)
{
assert (tzd_raw->zones[tz_alias_index].offset_rule_count == 0);
strcpy (tzd_raw->zones[tz_alias_index].clone_of, tz_zone->full_name);
remove_link = true;
}
else if (tzd_raw->zones[tz_alias_index].offset_rule_count > 0)
{
assert (tz_zone->offset_rule_count == 0);
strcpy (tz_zone->clone_of, tzd_raw->zones[tz_alias_index].full_name);
remove_link = true;
}
else
{
/* fatal error; can't have two fully defined timezones with a 'Link' rule defined between them */
assert (false);
err_status = TZC_ERR_LINKING_TRUE_ZONES;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_LINKING_TRUE_ZONES, tz_link->name, tz_link->alias);
goto exit;
}
if (remove_link)
{
if (i < tzd_raw->link_count - 1)
{
tz_link = &(tzd_raw->links[i]);
strcpy (tz_link->alias, tzd_raw->links[tzd_raw->link_count - 1].alias);
strcpy (tz_link->name, tzd_raw->links[tzd_raw->link_count - 1].name);
}
if (tzd_raw->link_count > 1)
{
tz_link = (TZ_RAW_LINK *) realloc (tzd_raw->links, (tzd_raw->link_count - 1) * sizeof (TZ_RAW_LINK));
if (tz_link == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", tzd_raw->link_count - 1);
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_OUT_OF_MEMORY, err_msg, "TZ_RAW_LINK");
goto exit;
}
tzd_raw->links = tz_link;
}
else if (tzd_raw->link_count == 1)
{
assert (i == 0);
free (tzd_raw->links);
tzd_raw->links = NULL;
}
tzd_raw->link_count--;
/* the element at index i changed, i needs to be decreased so the next loop will analyze the new link at
* index i */
if (i > 0)
{
i--;
}
}
}
}
exit:
return err_status;
}
/*
* tzc_sort_raw_data () - perform some sorting on the information stored
* inside the parameter of the TZ_RAW_DATA type
* Returns:
* tzd_raw(in/out): time zone bulk data structure to sort
*
* NOTE: tz_optimize_raw_data should be called immediately after or at the end
* of the bulk data loader (timezone_load_data) in order to optimize the
* loaded information for further operations. The most important
* operation that requires optimized data is checking for differences
* when compiling the timezone data library using another version of
* IANA's timezone DB (newer or older) and properly identifying those
* differences.
*/
static void
tzc_sort_raw_data (TZ_RAW_DATA * tzd_raw)
{
int i;
TZ_RAW_DS_RULESET *rs = NULL;
/* sort countries by name */
qsort (tzd_raw->countries, tzd_raw->country_count, sizeof (TZ_RAW_COUNTRY), comp_func_raw_countries);
/* sort zones by name */
qsort (tzd_raw->zones, tzd_raw->zone_count, sizeof (TZ_RAW_ZONE_INFO), comp_func_raw_zones);
/* sort zone aliases by name (aliases = links & backward links) */
qsort (tzd_raw->links, tzd_raw->link_count, sizeof (TZ_RAW_LINK), comp_func_raw_links);
/* sort offset rules by date */
for (i = 0; i < tzd_raw->zone_count; i++)
{
qsort (tzd_raw->zones[i].offset_rules, tzd_raw->zones[i].offset_rule_count, sizeof (TZ_RAW_OFFSET_RULE),
comp_func_raw_offset_rules);
}
/* sort DS rulesets by name */
qsort (tzd_raw->ds_rulesets, tzd_raw->ruleset_count, sizeof (TZ_RAW_DS_RULESET), comp_func_raw_ds_rulesets);
/* sort DS rules (in each set) by starting year */
for (i = 0; i < tzd_raw->ruleset_count; i++)
{
rs = &(tzd_raw->ds_rulesets[i]);
qsort (rs->rules, rs->rule_count, sizeof (TZ_RAW_DS_RULE), comp_func_raw_ds_rules);
}
}
/*
* tzc_index_raw_data - index the information identified as being higher in
* the hierarchy of the raw timezone data
* Returns:
* tzd_raw(in): raw time zone data to process
*/
static void
tzc_index_raw_data (TZ_RAW_DATA * tzd_raw)
{
int i;
/* countries should have been already sorted with tz_sort_raw_data */
for (i = 0; i < tzd_raw->country_count; i++)
{
tzd_raw->countries[i].id = i;
tzd_raw->countries[i].is_used = true;
}
for (i = 0; i < tzd_raw->zone_count; i++)
{
tzd_raw->zones[i].id = i;
tzd_raw->zones[i].clone_of_id = -1;
}
}
/*
* tzc_index_raw_data_w_static - index the information identified as being
* higher in the hierarchy of the raw timezone data with
* respect to the hardcoded ID arrays from timezone_list.c
* Returns: 0 (NO_ERROR) if success, error code if something goes wrong
* tzd_raw(in/out): raw time zone data to process
* mode(in): control flag (type of activity to perform)
*
* NOTE: call this after tz_index_raw_data (which sorts the important data)
*/
static int
tzc_index_raw_data_w_static (TZ_RAW_DATA * tzd_raw, const TZ_GEN_TYPE mode)
{
int i, cur_id;
int err_status = NO_ERROR;
/* set all IDs to -1, and is_used to FALSE (where needed) */
for (i = 0; i < tzd_raw->country_count; i++)
{
tzd_raw->countries[i].id = -1;
tzd_raw->countries[i].is_used = false; /* explicitly initialize all */
}
for (i = 0; i < tzd_raw->zone_count; i++)
{
tzd_raw->zones[i].id = -1;
tzd_raw->zones[i].clone_of_id = -1;
}
for (i = 0; i < tzd_raw->ruleset_count; i++)
{
tzd_raw->ds_rulesets[i].is_used = false; /* explicitly initialize all */
}
/* implementation */
/* compute country IDs */
cur_id = -1;
for (i = 0; i < tzd_raw->country_count; i++)
{
if (cur_id < tzd_raw->countries[i].id)
{
cur_id = tzd_raw->countries[i].id;
}
}
for (i = 0; i < tzd_raw->country_count; i++)
{
if (tzd_raw->countries[i].id == -1)
{
tzd_raw->countries[i].id = ++cur_id;
}
}
/* compute timezone IDs, with respect to the existing tz ID list */
cur_id = -1;
for (i = 0; i < tzd_raw->zone_count; i++)
{
if (cur_id < tzd_raw->zones[i].id)
{
cur_id = tzd_raw->zones[i].id;
}
}
/* leave IDs to -1 for zones not found in the predefined TIMEZONES array */
for (i = 0; i < tzd_raw->zone_count; i++)
{
if (tzd_raw->zones[i].id == -1)
{
tzd_raw->zones[i].id = ++cur_id;
}
}
return err_status;
}
/*
* tzc_index_raw_subdata - compute indexes for rulesets and update ruleset_id
* for offset rules (e.g. index the data not processed
* by tzc_index_raw_data/tzc_index_raw_data_w_static)
* Returns: 0 (NO_ERROR) if success, error code if something goes wrong
* tzd_raw(in): raw time zone data to process
* mode(in): control flag (type of activity to perform)
*
* NOTE: call this after tz_index_raw_data / tz_index_raw_subdata
*/
static int
tzc_index_raw_subdata (TZ_RAW_DATA * tzd_raw, const TZ_GEN_TYPE mode)
{
int i, j, found_id;
int err_status = NO_ERROR;
char *alias = NULL;
char **temp_aliases = NULL;
/* compute country_id for all timezones */
for (i = 0; i < tzd_raw->zone_count; i++)
{
found_id = -1;
for (j = 0; j < tzd_raw->country_count; j++)
{
if (!tzd_raw->countries[j].is_used)
{
continue;
}
if (strcmp (tzd_raw->zones[i].code, tzd_raw->countries[j].code) == 0)
{
found_id = tzd_raw->countries[j].id;
break;
}
}
tzd_raw->zones[i].country_id = found_id;
}
/* Match aliases to the corresponding timezones */
for (i = 0; i < tzd_raw->link_count; i++)
{
TZ_RAW_ZONE_INFO *zone = NULL;
TZ_RAW_LINK *link = &(tzd_raw->links[i]);
/* search for the corresponding timezone */
found_id = -1;
alias = NULL;
for (j = 0; j < tzd_raw->zone_count && alias == NULL; j++)
{
zone = &(tzd_raw->zones[j]);
if (strcmp (link->alias, zone->full_name) == 0)
{
alias = link->name;
break;
}
else if (strcmp (link->name, zone->full_name) == 0)
{
alias = link->alias;
break;
}
}
assert (alias != NULL);
/* put the found alias into the corresponding TZ_RAW_ZONE_INFO */
temp_aliases = (char **) realloc (zone->aliases, (zone->alias_count + 1) * sizeof (char *));
if (temp_aliases == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", zone->alias_count + 1);
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_OUT_OF_MEMORY, err_msg, "char *");
goto exit;
}
zone->aliases = temp_aliases;
zone->aliases[zone->alias_count] = strdup (alias);
if (zone->aliases[zone->alias_count] == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", zone->alias_count + 1);
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_OUT_OF_MEMORY, err_msg, "char *");
goto exit;
}
zone->alias_count++;
}
/* for timezones which clone other time zone settings, compute cloned ID */
for (i = 0; i < tzd_raw->zone_count; i++)
{
TZ_RAW_ZONE_INFO *zone = NULL;
TZ_RAW_ZONE_INFO *zone_search = NULL;
zone = &(tzd_raw->zones[i]);
if (strlen (zone->clone_of) == 0)
{
continue;
}
/* search for the zone with the name zone->clone_of, and get its ID */
for (j = 0; j < tzd_raw->zone_count; j++)
{
zone_search = &(tzd_raw->zones[j]);
if (strcmp (zone->clone_of, zone_search->full_name) == 0)
{
zone->clone_of_id = zone_search->id;
}
}
}
if (mode == TZ_GEN_TYPE_UPDATE)
{
/* TODO: if a zone is renamed, create/update an alias for it, to keep the old name found in the predefined
* TIMEZONE array */
assert (false);
}
else
{
assert (mode == TZ_GEN_TYPE_NEW || mode == TZ_GEN_TYPE_EXTEND);
/* do nothing */
}
exit:
return err_status;
}
static int
compare_ints (const void *a, const void *b)
{
if (*((int *) a) == *((int *) b))
{
return 0;
}
else if (*((int *) a) < *((int *) b))
{
return -1;
}
return 1;
}
/*
* tzc_check_ds_ruleset - Checks the validity of the daylight saving time
* ruleset
* tzd(in): timezone data
* ds_rule_set(in): day-light saving time ruleset
* ds_changes_cnt(out): total number of daylight saving time changes between
* the start year and the end year
* Return: error or no error
*/
static int
tzc_check_ds_ruleset (const TZ_DATA * tzd, const TZ_DS_RULESET * ds_rule_set, int *ds_changes_cnt)
{
#define ABS(i) ((i) >= 0 ? (i) : -(i))
#define MAX_DS_CHANGES_YEAR 1000
int start_year, end_year, year;
int i;
int all_ds_changes_julian_date[MAX_DS_CHANGES_YEAR];
int last_ds_change_julian_date;
start_year = TZ_MAX_YEAR;
end_year = 0;
*ds_changes_cnt = 0;
for (i = 0; i < ds_rule_set->count; i++)
{
TZ_DS_RULE *ds_rule;
ds_rule = &(tzd->ds_rules[i + ds_rule_set->index_start]);
if (ds_rule->from_year < start_year)
{
start_year = ds_rule->from_year;
}
if (ds_rule->to_year > end_year && ds_rule->to_year != TZ_MAX_YEAR)
{
end_year = ds_rule->to_year;
}
}
if (end_year == 0)
{
end_year = start_year;
}
end_year = end_year + 1;
if (end_year < 2038)
{
end_year = 2038;
}
/* check how many times a DS change occurs in a year */
for (year = start_year; year < end_year; year++)
{
int count = 0;
for (i = 0; i < ds_rule_set->count; i++)
{
TZ_DS_RULE *ds_rule;
int ds_change_julian_date, first_day_year_julian, last_day_year_julian;
ds_rule = &(tzd->ds_rules[i + ds_rule_set->index_start]);
if (year >= ds_rule->from_year && year <= ds_rule->to_year)
{
count++;
}
if (year + 1 < ds_rule->from_year || year - 1 > ds_rule->to_year)
{
continue;
}
if ((year < ds_rule->from_year && ds_rule->in_month > TZ_MON_JAN)
|| (year > ds_rule->to_year && ds_rule->in_month < TZ_MON_DEC))
{
continue;
}
if (tz_get_ds_change_julian_date_diff (0, ds_rule, year, &ds_change_julian_date, NULL) != NO_ERROR)
{
return ER_FAILED;
}
assert (*ds_changes_cnt < MAX_DS_CHANGES_YEAR);
all_ds_changes_julian_date[(*ds_changes_cnt)++] = ds_change_julian_date;
first_day_year_julian = julian_encode (1, 1, year);
last_day_year_julian = julian_encode (31, 12, year);
if (ABS (ds_change_julian_date - first_day_year_julian) <= 1
|| ABS (ds_change_julian_date - last_day_year_julian) <= 1)
{
printf ("DS ruleset: %s, Year: %d, Change on : %s \n", ds_rule_set->ruleset_name, year,
MONTH_NAMES_ABBREV[ds_rule->in_month]);
}
}
if (count > 2)
{
printf ("DS ruleset: %s, Year: %d, found %d matching rules\n", ds_rule_set->ruleset_name, year, count);
}
}
qsort (all_ds_changes_julian_date, *ds_changes_cnt, sizeof (int), compare_ints);
last_ds_change_julian_date = all_ds_changes_julian_date[0];
for (i = 1; i < *ds_changes_cnt; i++)
{
int date_diff;
int month1, day1, year1;
int month2, day2, year2;
date_diff = all_ds_changes_julian_date[i] - last_ds_change_julian_date;
assert (date_diff > 0);
if (date_diff < 30)
{
julian_decode (last_ds_change_julian_date, &month1, &day1, &year1, NULL);
julian_decode (all_ds_changes_julian_date[i], &month2, &day2, &year2, NULL);
printf ("DS ruleset: %s, DS change after %d days, Date1: %d-%d-%d," "Date1: %d-%d-%d\n",
ds_rule_set->ruleset_name, date_diff, day1, month1, year1, day2, month2, year2);
}
}
printf ("Ruleset %s , total changes : %d (%d - %d)\n", ds_rule_set->ruleset_name, *ds_changes_cnt, start_year,
end_year);
return NO_ERROR;
#undef ABS
}
/*
* tzc_compile_data - process the raw data loaded from IANA's timezone
* database files and put it into a TZ_DATA structure
* to be later written into a compilable C file
*
* Returns: 0 (NO_ERROR) if success, error code if failure
* tzd_raw(in): raw data to process
* tzd(out): variable where to store the processed data
*/
static int
tzc_compile_data (TZ_RAW_DATA * tzd_raw, TZ_DATA * tzd)
{
int i, j, err_status = NO_ERROR;
int alias_count, offset_rule_id;
assert (tzd != NULL);
/* countries */
assert (tzd->countries == NULL);
tzd->countries = (TZ_COUNTRY *) malloc (tzd_raw->country_count * sizeof (tzd->countries[0]));
if (tzd->countries == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", tzd_raw->country_count);
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_OUT_OF_MEMORY, err_msg, "TZ_COUNTRY");
goto exit;
}
tzd->country_count = tzd_raw->country_count;
memset (tzd->countries, 0, tzd->country_count * sizeof (tzd->countries[0]));
for (i = 0; i < tzd->country_count; i++)
{
TZ_COUNTRY *tz_country;
assert (tzd_raw->countries[i].id == i);
tz_country = &(tzd->countries[i]);
strcpy (tz_country->code, tzd_raw->countries[i].code);
strcpy (tz_country->full_name, tzd_raw->countries[i].full_name);
}
err_status = tzc_compile_ds_rules (tzd_raw, tzd);
if (err_status != NO_ERROR)
{
goto exit;
}
/* timezones and associated offset rules */
tzd->timezones = NULL;
tzd->timezone_names = NULL;
tzd->names = NULL;
tzd->timezone_count = tzd_raw->zone_count;
tzd->timezones = (TZ_TIMEZONE *) malloc (tzd->timezone_count * sizeof (tzd->timezones[0]));
if (tzd->timezones == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", tzd->timezone_count);
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_OUT_OF_MEMORY, err_msg, "TZ_TIMEZONE");
goto exit;
}
memset (tzd->timezones, 0, tzd->timezone_count * sizeof (tzd->timezones[0]));
/* count total number of offset rules */
for (i = 0; i < tzd_raw->zone_count; i++)
{
tzd->offset_rule_count += tzd_raw->zones[i].offset_rule_count;
}
tzd->offset_rules = (TZ_OFFSET_RULE *) malloc (tzd->offset_rule_count * sizeof (tzd->offset_rules[0]));
if (tzd->offset_rules == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", tzd->offset_rule_count);
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_OUT_OF_MEMORY, err_msg, "TZ_OFFSET_RULE");
goto exit;
}
offset_rule_id = 0;
memset (tzd->offset_rules, 0, tzd->offset_rule_count * sizeof (tzd->offset_rules[0]));
for (i = 0; i < tzd_raw->zone_count; i++)
{
TZ_TIMEZONE *tz_zone = &(tzd->timezones[tzd_raw->zones[i].id]);
TZ_RAW_ZONE_INFO *tz_raw_zone = &(tzd_raw->zones[i]);
tz_zone->country_id = tz_raw_zone->country_id;
tz_zone->zone_id = tz_raw_zone->id;
tz_zone->gmt_off_rule_start = offset_rule_id;
tz_zone->gmt_off_rule_count = tz_raw_zone->offset_rule_count;
if (tz_zone->gmt_off_rule_count == 0)
{
/* if zone is an alias continue */
if (tz_raw_zone->clone_of_id != -1)
{
continue;
}
/* else stop building the timezone library */
else
{
err_status = TZC_ERR_INVALID_ZONE;
TZC_LOG_ERROR_1ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_INVALID_ZONE, "Empty zone");
goto exit;
}
}
for (j = 0; j < tz_zone->gmt_off_rule_count; j++)
{
TZ_OFFSET_RULE *offrule = &(tzd->offset_rules[offset_rule_id]);
TZ_RAW_OFFSET_RULE *raw_offrule = &(tz_raw_zone->offset_rules[j]);
if (strstr (raw_offrule->format, "%s") != NULL)
{
offrule->var_format = strdup (raw_offrule->format);
if (offrule->var_format == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", strlen (raw_offrule->format));
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_OUT_OF_MEMORY, err_msg, "char");
goto exit;
}
offrule->std_format = NULL;
offrule->save_format = NULL;
}
else
{
char *save_format;
save_format = strchr (raw_offrule->format, '/');
/* already checked by TZ parser */
if (save_format != NULL)
{
*save_format = '\0';
save_format++;
}
offrule->std_format = strdup (raw_offrule->format);
if (offrule->std_format == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", strlen (raw_offrule->format));
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_OUT_OF_MEMORY, err_msg, "char");
goto exit;
}
if (save_format != NULL)
{
offrule->save_format = strdup (save_format);
if (offrule->save_format == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", strlen (save_format));
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_OUT_OF_MEMORY, err_msg, "char");
goto exit;
}
}
else
{
offrule->save_format = NULL;
}
offrule->var_format = NULL;
}
offrule->gmt_off = raw_offrule->gmt_off;
offrule->until_flag = raw_offrule->until_flag;
offrule->until_year = raw_offrule->until_year;
offrule->until_mon = raw_offrule->until_mon;
offrule->until_day = raw_offrule->until_day;
offrule->until_hour = raw_offrule->until_hour;
offrule->until_min = raw_offrule->until_min;
offrule->until_sec = raw_offrule->until_sec;
offrule->until_time_type = raw_offrule->until_time_type;
/* seek ds ruleset metadata using string identifier raw-offrule->rules */
offrule->ds_ruleset =
tzc_get_ds_ruleset_by_name (tzd->ds_rulesets, tzd->ds_ruleset_count, raw_offrule->ds_ruleset_name);
if (offrule->ds_ruleset == -1)
{
const char *dummy = NULL;
/* raw_offrule->rules is not an identifier of a ruleset; check if it is '-' or time offset */
offrule->ds_type = DS_TYPE_FIXED;
if (strcmp (raw_offrule->ds_ruleset_name, "-") == 0)
{
offrule->ds_ruleset = 0;
}
else if (tz_str_to_seconds (raw_offrule->ds_ruleset_name,
raw_offrule->ds_ruleset_name + strlen (raw_offrule->ds_ruleset_name),
&(offrule->ds_ruleset), &dummy, false) != NO_ERROR)
{
err_status = TZC_ERR_INVALID_TIME;
goto exit;
}
}
else
{
offrule->ds_type = DS_TYPE_RULESET_ID;
}
offset_rule_id++;
}
}
/* check cloned zones and properly set their offset rule references; NOTE: this can be done only after exporting all
* zones and offset rules */
for (i = 0; i < tzd_raw->zone_count; i++)
{
TZ_TIMEZONE *tz_zone_clone = &(tzd->timezones[tzd_raw->zones[i].id]);
TZ_TIMEZONE *tz_zone_original = NULL;
TZ_RAW_ZONE_INFO *tz_raw_zone = &(tzd_raw->zones[i]);
if (tz_raw_zone->clone_of_id == -1)
{
continue;
}
tz_zone_original = &(tzd->timezones[tz_raw_zone->clone_of_id]);
tz_zone_clone->gmt_off_rule_start = tz_zone_original->gmt_off_rule_start;
tz_zone_clone->gmt_off_rule_count = tz_zone_original->gmt_off_rule_count;
}
/* put aliases & timezone names into sorted arrays */
/* build timezone_names */
tzd->timezone_names = (char **) malloc (tzd->timezone_count * sizeof (tzd->timezone_names[0]));
if (tzd->timezone_names == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", tzd->timezone_count);
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_OUT_OF_MEMORY, err_msg, "char *");
goto exit;
}
memset (tzd->timezone_names, 0, tzd->timezone_count * sizeof (tzd->timezone_names[0]));
for (i = 0; i < tzd_raw->zone_count; i++)
{
int zone_id = tzd_raw->zones[i].id;
assert (!IS_EMPTY_STR (tzd_raw->zones[i].full_name));
tzd->timezone_names[zone_id] = strdup (tzd_raw->zones[i].full_name);
if (tzd->timezone_names[zone_id] == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", strlen (tzd_raw->zones[i].full_name));
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_OUT_OF_MEMORY, err_msg, "char");
goto exit;
}
}
/* build tzd->names */
alias_count = 0;
for (i = 0; i < tzd_raw->zone_count; i++)
{
alias_count += tzd_raw->zones[i].alias_count;
}
tzd->name_count = alias_count + tzd->timezone_count;
tzd->names = (TZ_NAME *) malloc (tzd->name_count * sizeof (TZ_NAME));
if (tzd->names == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", tzd->name_count);
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_OUT_OF_MEMORY, err_msg, "TZ_NAME");
goto exit;
}
memset (tzd->names, 0, tzd->name_count * sizeof (tzd->names[0]));
/* put timezone names first */
for (i = 0; i < tzd->timezone_count; i++)
{
tzd->names[i].is_alias = 0;
assert (tzd->timezones[i].zone_id == i);
tzd->names[i].zone_id = i;
tzd->names[i].name = strdup (tzd->timezone_names[i]);
if (tzd->names[i].name == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", strlen (tzd->timezone_names[i]));
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_OUT_OF_MEMORY, err_msg, "char");
goto exit;
}
}
/* put aliases */
offset_rule_id = tzd->timezone_count;
for (i = 0; i < tzd_raw->zone_count; i++)
{
TZ_RAW_ZONE_INFO *tz_raw_zone = &(tzd_raw->zones[i]);
if (tz_raw_zone->alias_count == 0)
{
continue;
}
for (j = 0; j < tz_raw_zone->alias_count; j++)
{
tzd->names[offset_rule_id].is_alias = 1;
assert (tz_raw_zone->id != -1);
tzd->names[offset_rule_id].zone_id = tz_raw_zone->id;
assert (!IS_EMPTY_STR (tz_raw_zone->aliases[j]));
tzd->names[offset_rule_id].name = strdup (tz_raw_zone->aliases[j]);
if (tzd->names[offset_rule_id].name == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", strlen (tz_raw_zone->aliases[j]));
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_OUT_OF_MEMORY, err_msg, "char");
goto exit;
}
offset_rule_id++;
}
}
/* sort zone timezone names/aliases */
qsort (tzd->names, tzd->name_count, sizeof (TZ_NAME), comp_func_tz_names);
{
int total_ds_changes = 0;
int max_ds_changes = 0;
for (i = 0; i < tzd->ds_ruleset_count; i++)
{
int ds_changes = 0;
(void) tzc_check_ds_ruleset (tzd, &(tzd->ds_rulesets[i]), &ds_changes);
if (ds_changes > max_ds_changes)
{
max_ds_changes = ds_changes;
}
total_ds_changes += ds_changes;
}
printf ("Total DS changes: %d; maximum changes in a ruleset :%d\n", total_ds_changes, max_ds_changes);
}
/* build leap second list */
tzd->ds_leap_sec_count = tzd_raw->leap_sec_count;
tzd->ds_leap_sec = (TZ_LEAP_SEC *) malloc (tzd->ds_leap_sec_count * sizeof (tzd->ds_leap_sec[0]));
if (tzd->ds_leap_sec == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", tzd->ds_leap_sec_count);
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_OUT_OF_MEMORY, err_msg, "TZ_DS_LEAP_SEC *");
goto exit;
}
memcpy (tzd->ds_leap_sec, tzd_raw->leap_sec, tzd_raw->leap_sec_count * sizeof (TZ_LEAP_SEC));
exit:
return err_status;
}
/*
* tzc_compile_ds_rules() - take the raw daylight saving rules and place them
* into the output TZ_DATA variable, from where they
* will be later exported into a source file (to be
* compiled into the timezone shared library/object)
* Returns: 0(NO_ERROR) if success, error code otherwise
* tzd_raw(in): raw data to process
* tzd(out): variable where to store the processed data
*/
static int
tzc_compile_ds_rules (TZ_RAW_DATA * tzd_raw, TZ_DATA * tzd)
{
int err_status = NO_ERROR;
int i, j, cur_rule_index = 0;
TZ_DS_RULESET *ruleset;
TZ_DS_RULE *rule;
TZ_RAW_DS_RULE *rule_raw;
assert (tzd->ds_rules == NULL);
assert (tzd->ds_rulesets == NULL);
/* compute total number of daylight saving rules */
tzd->ds_rule_count = 0;
for (i = 0; i < tzd_raw->ruleset_count; i++)
{
tzd->ds_rule_count += tzd_raw->ds_rulesets[i].rule_count;
}
tzd->ds_rules = (TZ_DS_RULE *) malloc (tzd->ds_rule_count * sizeof (TZ_DS_RULE));
if (tzd->ds_rules == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", tzd->ds_rule_count);
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_OUT_OF_MEMORY, err_msg, "TZ_DS_RULE");
goto exit;
}
/* alloc ruleset array */
tzd->ds_ruleset_count = tzd_raw->ruleset_count;
tzd->ds_rulesets = (TZ_DS_RULESET *) malloc (tzd->ds_ruleset_count * sizeof (TZ_DS_RULESET));
if (tzd->ds_rulesets == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", tzd->ds_ruleset_count);
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_OUT_OF_MEMORY, err_msg, "TZ_DS_RULESET");
goto exit;
}
memset (tzd->ds_rules, 0, tzd->ds_rule_count * sizeof (tzd->ds_rules[0]));
memset (tzd->ds_rulesets, 0, tzd->ds_ruleset_count * sizeof (tzd->ds_rulesets[0]));
for (i = 0; i < tzd->ds_ruleset_count; i++)
{
int to_year_max = 0;
bool has_default_abbrev = true;
const char *prev_letter_abbrev = NULL;
ruleset = &(tzd->ds_rulesets[i]);
ruleset->index_start = cur_rule_index;
ruleset->count = tzd_raw->ds_rulesets[i].rule_count;
ruleset->ruleset_name = strdup (tzd_raw->ds_rulesets[i].name);
if (ruleset->ruleset_name == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", strlen (tzd_raw->ds_rulesets[i].name));
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_OUT_OF_MEMORY, err_msg, "char");
goto exit;
}
for (j = 0; j < ruleset->count; j++)
{
rule = &(tzd->ds_rules[cur_rule_index]);
rule_raw = &(tzd_raw->ds_rulesets[i].rules[j]);
rule->at_time = rule_raw->at_time;
rule->at_time_type = rule_raw->at_time_type;
memcpy (&(rule->change_on), &(rule_raw->change_on), sizeof (TZ_DS_CHANGE_ON));
rule->from_year = rule_raw->from_year;
rule->in_month = rule_raw->in_month;
rule->letter_abbrev = strdup (rule_raw->letter_abbrev);
if (rule->letter_abbrev == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", strlen (rule_raw->letter_abbrev));
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_OUT_OF_MEMORY, err_msg, "char");
goto exit;
}
if (has_default_abbrev == true && rule_raw->save_time == 0)
{
/* common single letter abbreviation having daylight save time zero (common standard time) */
if (strlen (rule->letter_abbrev) > 1
|| (prev_letter_abbrev != NULL && strcmp (rule->letter_abbrev, prev_letter_abbrev) != 0))
{
has_default_abbrev = false;
}
else
{
prev_letter_abbrev = (char *) rule->letter_abbrev;
}
}
rule->save_time = rule_raw->save_time;
rule->to_year = rule_raw->to_year;
if (rule->to_year > to_year_max)
{
to_year_max = rule->to_year;
}
cur_rule_index++;
}
const char empty[2] = { '-', 0 };
if (has_default_abbrev == true && prev_letter_abbrev != NULL)
{
ruleset->default_abrev = strdup (prev_letter_abbrev);
}
else
{
prev_letter_abbrev = empty;
ruleset->default_abrev = strdup (empty);
}
if (ruleset->default_abrev == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", strlen (prev_letter_abbrev));
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (TZC_CONTEXT (tzd_raw), TZC_ERR_OUT_OF_MEMORY, err_msg, "char");
goto exit;
}
ruleset->to_year_max = to_year_max;
}
exit:
return err_status;
}
/*
* str_to_offset_rule_until() - parses a string representing the date (and
* maybe time) until an offset rule was in effect, and
* put the components into the corresponding members of
* a TZ_OFFSET_RULE output parameter
* Returns: 0 (NO_ERROR) if success, negative code if an error occurs
* offset_rule(out): the offset rule where to save the date/time parts
* str(in): string to parse
* NOTE: str may be in the following forms: empty/NULL, "1912", "1903 Mar",
* "1979 Oct 14", "1975 Nov 25 2:00".
*/
static int
str_to_offset_rule_until (TZ_RAW_OFFSET_RULE * offset_rule, char *str)
{
const char *str_cursor;
const char *str_next;
const char *str_end;
int val_read = 0;
int type = -1, day = -1, bound = -1;
int hour = 0, min = 0, sec = 0;
int err_status = NO_ERROR;
assert (offset_rule != NULL);
if (IS_EMPTY_STR (str))
{
offset_rule->until_flag = UNTIL_INFINITE;
offset_rule->until_time_type = TZ_TIME_TYPE_LOCAL_WALL;
return NO_ERROR;
}
str_end = str + strlen (str);
str_cursor = strtok (str, " ");
if (tz_str_read_number (str_cursor, str_end, true, false, &val_read, &str_next) != NO_ERROR && val_read > 0
&& val_read < TZ_MAX_YEAR)
{
err_status = TZC_ERR_CANT_READ_VALUE;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_CANT_READ_VALUE, "UNTIL (year)", str_cursor);
goto exit;
}
else
{
offset_rule->until_year = (unsigned short) val_read;
offset_rule->until_mon = 0;
offset_rule->until_day = 0;
offset_rule->until_hour = 0;
offset_rule->until_min = 0;
offset_rule->until_sec = 0;
}
offset_rule->until_flag = UNTIL_EXPLICIT;
offset_rule->until_time_type = TZ_TIME_TYPE_LOCAL_WALL;
/* read month */
str_cursor = strtok (NULL, " ");
if (IS_EMPTY_STR (str_cursor))
{
/* no more tokens; exit with NO_ERROR */
return NO_ERROR;
}
if (str_month_to_int (str_cursor, &val_read, &str_next) != NO_ERROR)
{
err_status = TZC_ERR_CANT_READ_VALUE;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_CANT_READ_VALUE, "UNTIL month", str_cursor);
goto exit;
}
if (val_read < TZ_MON_JAN || val_read > TZ_MON_DEC)
{
char temp_msg[TZC_ERR_MSG_MAX_SIZE] = { 0 };
sprintf (temp_msg, "%d", val_read);
err_status = TZC_ERR_INVALID_VALUE;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_INVALID_VALUE, "UNTIL month value", temp_msg);
goto exit;
}
offset_rule->until_mon = (unsigned char) val_read;
/* read day of month */
str_cursor = strtok (NULL, " ");
if (IS_EMPTY_STR (str_cursor))
{
/* no more tokens; exit with NO_ERROR */
err_status = NO_ERROR;
goto exit;
}
/* Some offset rules have the column UNTIL='1992 Sep lastSat 23:00' or '2012 Apr Sun>=1 4:00', instead of a fixed
* date/time value. This is a special case, and needs to be transformed into a fixed date. */
err_status = str_read_day_var (str_cursor, offset_rule->until_mon, &type, &day, &bound, &str_next);
if (err_status != NO_ERROR)
{
goto exit;
}
if (type == TZ_DS_TYPE_FIXED)
{
if (!tzc_is_valid_date (day, offset_rule->until_mon, offset_rule->until_year, offset_rule->until_year + 1))
{
char temp_msg[TZC_ERR_MSG_MAX_SIZE] = { 0 };
sprintf (temp_msg, "Day: %d, Month: %d, Year: %d", day, offset_rule->until_mon, offset_rule->until_year);
err_status = TZC_ERR_DS_INVALID_DATE;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_DS_INVALID_DATE, "day of month (UNTIL)", temp_msg);
goto exit;
}
}
else if (type == TZ_DS_TYPE_VAR_GREATER)
{
int month_day = tz_get_first_weekday_around_date (offset_rule->until_year,
offset_rule->until_mon, day, bound,
false);
if (!tzc_is_valid_date (month_day, offset_rule->until_mon, offset_rule->until_year, offset_rule->until_year + 1))
{
char temp_msg[TZC_ERR_MSG_MAX_SIZE] = { 0 };
sprintf (temp_msg, "Day: %d, Month: %d, Year: %d", month_day, offset_rule->until_mon,
offset_rule->until_year);
err_status = TZC_ERR_DS_INVALID_DATE;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_DS_INVALID_DATE, "day of month (UNTIL)", temp_msg);
goto exit;
}
day = month_day;
}
else if (type == TZ_DS_TYPE_VAR_SMALLER)
{
int month_day;
if (bound > 27)
{
int max_days_in_month;
if (offset_rule->until_mon == TZ_MON_FEB)
{
max_days_in_month = (IS_LEAP_YEAR (offset_rule->until_year) ? 29 : 28);
}
else
{
max_days_in_month = DAYS_IN_MONTH (offset_rule->until_mon);
}
bound = max_days_in_month - 1;
}
month_day = tz_get_first_weekday_around_date (offset_rule->until_year, offset_rule->until_mon, day, bound, true);
if (!tzc_is_valid_date (month_day, offset_rule->until_mon, offset_rule->until_year, offset_rule->until_year + 1))
{
char temp_msg[TZC_ERR_MSG_MAX_SIZE] = { 0 };
sprintf (temp_msg, "Day: %d, Month: %d, Year: %d", month_day, offset_rule->until_mon,
offset_rule->until_year);
err_status = TZC_ERR_DS_INVALID_DATE;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_DS_INVALID_DATE, "day of month (UNTIL)", temp_msg);
goto exit;
}
day = month_day;
}
else
{
assert (false);
err_status = TZC_ERR_DS_INVALID_DATE;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_DS_INVALID_DATE, "value for UNTIL", str);
goto exit;
}
offset_rule->until_day = (unsigned char) day;
/* read time */
str_cursor = strtok (NULL, " ");
if (IS_EMPTY_STR (str_cursor))
{
/* no more tokens; exit with NO_ERROR */
err_status = NO_ERROR;
goto exit;
}
if (tz_str_read_time (str_cursor, str_end, false, false, &hour, &min, &sec, &str_next) != NO_ERROR)
{
char temp_msg[TZC_ERR_MSG_MAX_SIZE] = { 0 };
sprintf (temp_msg, "[hour: %d, min: %d, sec: %d]", hour, min, sec);
err_status = TZC_ERR_INVALID_TIME;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_INVALID_TIME, temp_msg, "UNTIL column");
goto exit;
}
str_cursor = str_next;
err_status = tzc_read_time_type (str_cursor, &str_cursor, &(offset_rule->until_time_type));
if (err_status != NO_ERROR)
{
return err_status;
}
offset_rule->until_hour = (unsigned char) hour;
offset_rule->until_min = (unsigned char) min;
offset_rule->until_sec = (unsigned char) sec;
exit:
return err_status;
}
/*
* str_month_to_int() - get the corresponding integer value of a 3 letter
* month abbreviation
* Returns: 0 (NO_ERROR) if success, error code otherwise
* month(in): string to parse
* month_num(out): numeric value for found month
* str_next(out): char pointer to the remaining string after parsing month
*/
static int
str_month_to_int (const char *str_in, int *month_num, const char **str_next)
{
int i;
const char *str;
assert (!IS_EMPTY_STR (str_in));
str = str_in;
/* strip leading spaces and tabs */
while (*str != '\0' && char_isspace (*str))
{
str++;
}
if (strlen (str) < (int) sizeof (MONTH_NAMES_ABBREV[0]) - 1)
{
/* not enough characters to work with; exit with error */
return TZC_ERR_INVALID_VALUE;
}
for (i = 0; i < TZ_MON_COUNT; i++)
{
if (strncasecmp (MONTH_NAMES_ABBREV[i], str, sizeof (MONTH_NAMES_ABBREV[0]) - 1) == 0)
{
break;
}
}
if (i >= TZ_MON_COUNT)
{
/* month abbreviation not valid, or an error occured */
return TZC_ERR_INVALID_VALUE;
}
*month_num = i;
*str_next = str + sizeof (MONTH_NAMES_ABBREV[0]) - 1;
return NO_ERROR;
}
/*
* str_day_to_int() - get the corresponding integer value of a 3 letter
* week day abbreviation (0=Sunday, 6=Saturday)
* Returns: negative code if error, 0 (NO_ERROR) if success
* str_in(in): string to parse
* day_num(out): numeric value for found day
* str_next(out): char pointer to the remaining string after parsing day
*/
static int
str_day_to_int (const char *str_in, int *day_num, const char **str_next)
{
int i;
const char *str;
int err_status = NO_ERROR;
assert (!IS_EMPTY_STR (str_in));
str = str_in;
/* skip leading spaces and tabs */
while (*str != '\0' && char_isspace (*str))
{
str++;
}
if (strlen (str) < (int) sizeof (DAY_NAMES_ABBREV[0]) - 1)
{
/* not enough characters to work with; exit with error */
*day_num = -1;
err_status = TZC_ERR_INVALID_VALUE;
goto exit;
}
for (i = 0; i < TZ_WEEK_DAY_COUNT; i++)
{
if (strncasecmp (DAY_NAMES_ABBREV[i], str, sizeof (DAY_NAMES_ABBREV[0]) - 1) == 0)
{
break;
}
}
if (i >= TZ_WEEK_DAY_COUNT)
{
/* month abbreviation not valid, or an error occured; set day_num to -1 and exit with -1 */
*day_num = -1;
err_status = TZC_ERR_INVALID_VALUE;
goto exit;
}
*day_num = i;
*str_next = str + sizeof (DAY_NAMES_ABBREV[0]) - 1;
exit:
return err_status;
}
/*
* str_read_day_var() - parse the input string as a specification for a day of
* the month. The input string may be of the following
* forms:
* '21' (e.g. a day of the month)
* 'lastFri' (e.g. 'lastWEEKDAY')
* 'Sun>=1' (e.g. WEEKDAY>=NUMBER)
*
* Returns: 0(NO_ERROR) if success, error code otherwise
* str(in): input string to parse
* month(in): month in which this day is
* type(out): type of bound (see enum TZ_DS_TYPE)
* day(out): day value as numeric (0 based index value)
* bound(out): numeric bound for day (0 based index value)
* str_next(out): pointer to the remaining string after parsing the day rule
*/
static int
str_read_day_var (const char *str, const int month, int *type, int *day, int *bound, const char **str_next)
{
int err_status = NO_ERROR;
int day_num;
char str_last[5] = "last";
const char *str_cursor;
const char *str_end;
const int days_of_month[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
assert (str != NULL);
*str_next = str;
str_end = str + strlen (str);
/* initialize output parameters */
*type = -1;
*day = -1;
*bound = -1;
/* try reading a number */
if (tz_str_read_number (str, str_end, false, false, &day_num, str_next) != NO_ERROR)
{
err_status = TZC_ERR_CANT_READ_VALUE;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_CANT_READ_VALUE, "day numeric", str);
goto exit;
}
if (*str_next != str)
{
/* This is a fixed day of month, store it as such */
*type = TZ_DS_TYPE_FIXED;
*day = day_num - 1;
goto exit;
}
/* no number was read; check if str starts with "last" */
if (strncmp (str, str_last, strlen (str_last)) == 0)
{
str_cursor = str + strlen (str_last);
if (str_day_to_int (str_cursor, &day_num, str_next) != NO_ERROR)
{
err_status = TZC_ERR_CANT_READ_VALUE;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_CANT_READ_VALUE, "day string", str_cursor);
goto exit;
}
if (day_num < TZ_WEEK_DAY_SUN || day_num > TZ_WEEK_DAY_SAT)
{
char temp_msg[TZC_ERR_MSG_MAX_SIZE] = { 0 };
sprintf (temp_msg, "%d", day_num);
err_status = TZC_ERR_INVALID_VALUE;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_INVALID_VALUE, "day string value", temp_msg);
goto exit;
}
*type = TZ_DS_TYPE_VAR_SMALLER;
*day = day_num;
/* last valid month day from 0 - 30 */
*bound = days_of_month[month] - 1;
goto exit;
}
/* string was not a number, nor "last<Weekday>"; therefore it must be something like Sun>=3 */
str_cursor = str;
if (str_day_to_int (str_cursor, &day_num, &str_cursor) != NO_ERROR)
{
err_status = TZC_ERR_CANT_READ_VALUE;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_CANT_READ_VALUE, "day string", str_cursor);
goto exit;
}
assert (*(str_cursor + 1) == '=');
if (*str_cursor == '>')
{
*type = TZ_DS_TYPE_VAR_GREATER;
}
else if (*str_cursor == '<')
{
*type = TZ_DS_TYPE_VAR_SMALLER;
}
else
{
assert (false);
err_status = TZC_ERR_GENERIC;
goto exit;
}
str_cursor += 2; /* skip the '>=' operator */
*day = day_num;
if (tz_str_read_number (str_cursor, str_end, true, false, &day_num, &str_cursor) != NO_ERROR)
{
err_status = TZC_ERR_CANT_READ_VALUE;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_CANT_READ_VALUE, "day string", str_cursor);
goto exit;
}
*bound = day_num - 1;
exit:
return err_status;
}
/*
* comp_func_raw_countries - comparison function between country entries, used
* when optimizing TZ raw data
* Returns: -1 if arg1<arg2, 0 if arg1 = arg2, 1 if arg1 > arg2
* arg1(in): first value to compare
* arg2(in): second value to compare
* Note: comparing two TZ_RAW_COUNTRY values means comparing their full_name
* members.
*/
static int
comp_func_raw_countries (const void *arg1, const void *arg2)
{
TZ_RAW_COUNTRY *c1, *c2;
assert (arg1 != NULL && arg2 != NULL);
c1 = (TZ_RAW_COUNTRY *) arg1;
c2 = (TZ_RAW_COUNTRY *) arg2;
assert (!IS_EMPTY_STR (c1->full_name) && !IS_EMPTY_STR (c2->full_name));
if (c1->id == -1 && c2->id == -1)
{
return strcmp (c1->full_name, c2->full_name);
}
assert (c1->id != c2->id);
if (c1->id != -1 && c2->id != -1)
{
return (c1->id < c2->id) ? -1 : 1;
}
if (c1->id != -1)
{
return -1;
}
return 1;
}
/*
* comp_func_raw_zones - comparison function between zone entries, used when
* optimizing TZ raw data
* Returns: -1 if arg1 < arg2, 0 if arg1 = arg2, 1 if arg1 > arg2
* arg1(in): first value to compare
* arg2(in): second value to compare
* Note: comparing two TZ_RAW_ZONE_INFO values means comparing their full_name
* members.
*/
static int
comp_func_raw_zones (const void *arg1, const void *arg2)
{
TZ_RAW_ZONE_INFO *zone1, *zone2;
assert (arg1 != NULL && arg2 != NULL);
zone1 = (TZ_RAW_ZONE_INFO *) arg1;
zone2 = (TZ_RAW_ZONE_INFO *) arg2;
assert (!IS_EMPTY_STR (zone1->full_name) && !IS_EMPTY_STR (zone2->full_name));
if (zone1->id == -1 && zone2->id == -1)
{
return strcmp (zone1->full_name, zone2->full_name);
}
assert (zone1->id != zone2->id);
if (zone1->id != -1 && zone2->id != -1)
{
return (zone1->id < zone2->id) ? -1 : 1;
}
if (zone1->id > -1)
{
return -1;
}
return 1;
}
/*
* comp_func_raw_links - comparison function between link entries, used when
* optimizing TZ raw data
* Returns: -1 if arg1 < arg2, 0 if arg1 = arg2, 1 if arg1 > arg2
* arg1(in): first value to compare
* arg2(in): second value to compare
* Note: comparing two TZ_RAW_ZONE_INFO values means comparing their alias
* members. The TZ links need to be ordered by alias, not by the
* full_name of the corresponding timezone.
*/
static int
comp_func_raw_links (const void *arg1, const void *arg2)
{
TZ_RAW_LINK *link1, *link2;
assert (arg1 != NULL && arg2 != NULL);
link1 = (TZ_RAW_LINK *) arg1;
link2 = (TZ_RAW_LINK *) arg2;
assert (!IS_EMPTY_STR (link1->alias) && !IS_EMPTY_STR (link2->alias));
return strcmp (link1->alias, link2->alias);
}
/*
* comp_func_raw_offset_rules - comparison function between offset rules, used
* when optimizing TZ raw data
* Returns: -1 if arg1 < arg2, 0 if arg1 = arg2, 1 if arg1 > arg2
* arg1(in): first value to compare
* arg2(in): second value to compare
* Note: comparing two TZ_RAW_OFFSET_RULE values means comparing their ending
* datetime value.
*/
static int
comp_func_raw_offset_rules (const void *arg1, const void *arg2)
{
TZ_RAW_OFFSET_RULE *rule1, *rule2;
int r1_until, r2_until;
assert (arg1 != NULL && arg2 != NULL);
rule1 = (TZ_RAW_OFFSET_RULE *) arg1;
rule2 = (TZ_RAW_OFFSET_RULE *) arg2;
if (rule1->until_flag == UNTIL_INFINITE)
{
assert (rule2->until_flag != UNTIL_INFINITE);
return 1;
}
else if (rule2->until_flag == UNTIL_INFINITE)
{
return -1;
}
r1_until = julian_encode (rule1->until_mon, rule1->until_day, rule1->until_year);
r2_until = julian_encode (rule2->until_mon, rule2->until_day, rule2->until_year);
if (r1_until == r2_until)
{
/* both dates are equal; compare time (reuse r1_until and r2_until we should not have two offset changes in the
* same date */
assert (false);
r1_until = (rule1->until_hour * 60 + rule1->until_min) * 60 + rule1->until_sec;
r2_until = (rule2->until_hour * 60 + rule2->until_min) * 60 + rule2->until_sec;
}
if (r1_until < r2_until)
{
return -1;
}
else if (r1_until > r2_until)
{
return 1;
}
assert (false); /* can't have two time-overlapping offset rules */
return 0;
}
/*
* comp_func_raw_ds_rulesets - comparison function between daylight saving
* rulesets, used when optimizing TZ raw data.
* Returns: -1 if arg1 < arg2, 0 if arg1 = arg2, 1 if arg1 > arg2
* arg1(in): first value to compare
* arg2(in): second value to compare
* Note: comparing two TZ_RAW_DS_RULE_SET values means comparing their name
* members.
*/
static int
comp_func_raw_ds_rulesets (const void *arg1, const void *arg2)
{
TZ_RAW_DS_RULESET *rs1, *rs2;
assert (arg1 != NULL && arg2 != NULL);
rs1 = (TZ_RAW_DS_RULESET *) arg1;
rs2 = (TZ_RAW_DS_RULESET *) arg2;
assert (!IS_EMPTY_STR (rs1->name) && !IS_EMPTY_STR (rs2->name));
return strcmp (rs1->name, rs2->name);
}
/*
* get_day_of_week_for_raw_rule - Returns the day in which the ds_rule applies
*
* Returns: the day
* rule(in): daylight saving rule
* year(in): year in which to apply rule
*/
static int
get_day_of_week_for_raw_rule (const TZ_RAW_DS_RULE * rule, const int year)
{
int ds_rule_day;
int ds_rule_month = rule->in_month;
if (rule->change_on.type == TZ_DS_TYPE_FIXED)
{
ds_rule_day = rule->change_on.day_of_month;
}
else
{
int ds_rule_weekday, day_month_bound;
bool before = (rule->change_on.type == TZ_DS_TYPE_VAR_SMALLER) ? true : false;
ds_rule_weekday = rule->change_on.day_of_week;
day_month_bound = rule->change_on.day_of_month;
ds_rule_day = tz_get_first_weekday_around_date (year, ds_rule_month, ds_rule_weekday, day_month_bound, before);
}
return ds_rule_day;
}
/*
* comp_func_raw_ds_rules - comparison function between daylight saving
* rules, used when optimizing TZ raw data.
* Returns: -1 if arg1 < arg2, 0 if arg1 = arg2, 1 if arg1 > arg2
* arg1(in): first value to compare
* arg2(in): second value to compare
* Note: comparing two TZ_RAW_DS_RULE values means comparing their starting
* year.
*/
static int
comp_func_raw_ds_rules (const void *arg1, const void *arg2)
{
TZ_RAW_DS_RULE *rule1, *rule2;
int day1, day2;
assert (arg1 != NULL && arg2 != NULL);
rule1 = (TZ_RAW_DS_RULE *) arg1;
rule2 = (TZ_RAW_DS_RULE *) arg2;
if (rule1->from_year < rule2->from_year)
{
return -1;
}
else if (rule1->from_year > rule2->from_year)
{
return 1;
}
if (rule1->in_month != rule2->in_month)
{
return rule1->in_month < rule2->in_month ? -1 : 1;
}
day1 = get_day_of_week_for_raw_rule (rule1, rule1->from_year);
day2 = get_day_of_week_for_raw_rule (rule2, rule2->from_year);
return day1 < day2 ? -1 : 1;
}
/*
* comp_func_tz_names - comparison function between two TZ_NAME values
* Returns: -1 if arg1 < arg2, 0 if arg1 = arg2, 1 if arg1 > arg2
* arg1(in): first value to compare
* arg2(in): second value to compare
*/
static int
comp_func_tz_names (const void *arg1, const void *arg2)
{
TZ_NAME *name1, *name2;
assert (arg1 != NULL && arg2 != NULL);
name1 = (TZ_NAME *) arg1;
name2 = (TZ_NAME *) arg2;
assert (!IS_EMPTY_STR (name1->name) && !IS_EMPTY_STR (name2->name));
return strcmp (name1->name, name2->name);
}
/*
* print_seconds_as_time_hms_var () - takes a signed number of seconds and
* prints it to stdout in [-]hh:mm[:ss] format
* Returns:
* seconds(in): number of seconds to print as hh:mm[:ss]
*/
static void
print_seconds_as_time_hms_var (int seconds)
{
if (seconds < 0)
{
printf ("-");
seconds = -seconds;
}
printf ("%02d", (int) (seconds / 3600));
seconds %= 3600;
printf (":%02d", (int) (seconds / 60));
seconds %= 60;
if (seconds > 0)
{
printf (":%02d", seconds);
}
}
/*
* tzc_get_timezones_dot_c_filepath () - get path to timezones.c file in the CUBRID install folder
*
* size (in) : maximum name size
* timezones_dot_c_file_path (out) : output file path
*/
static void
tzc_get_timezones_dot_c_filepath (size_t size, char *timezones_dot_c_file_path)
{
char tz_cub_path[PATH_MAX] = { 0 };
envvar_cubrid_dir (tz_cub_path, sizeof (tz_cub_path));
tzc_build_filepath (timezones_dot_c_file_path, size, tz_cub_path, PATH_PARTIAL_TIMEZONES_FILE);
}
/*
* tzc_export_timezone_dot_c () - saves all timezone data into a C source
* file to be later compiled into a shared library
* Returns: always NO_ERROR
* tzd(in): timezone data
* tz_C_filepath(in): timezones.c file path
*/
static int
tzc_export_timezone_dot_c (const TZ_DATA * tzd, const char *timezones_dot_c_filepath)
{
int err_status = NO_ERROR;
TZ_OFFSET_RULE *offrule = NULL;
int i;
TZ_DS_RULE *rule = NULL;
TZ_LEAP_SEC *leap_sec = NULL;
FILE *fp;
fp = fopen_ex (timezones_dot_c_filepath, "wt");
if (fp == NULL)
{
err_status = TZC_ERR_GENERIC;
goto exit;
}
#if defined(WINDOWS)
fprintf (fp, "#include <stdio.h>\n");
#else
fprintf (fp, "#include <stddef.h>\n");
#endif
fprintf (fp, "#include \"timezone_lib_common.h\"\n\n");
/* countries */
fprintf (fp, "%s const int tz_country_count = %d;\n", SHLIB_EXPORT_PREFIX, tzd->country_count);
fprintf (fp, "%s const TZ_COUNTRY tz_countries[] = {\n", SHLIB_EXPORT_PREFIX);
for (i = 0; i < tzd->country_count; i++)
{
fprintf (fp, "\t{\"%s\", \"%s\"}%s\n", tzd->countries[i].code, tzd->countries[i].full_name,
(i == tzd->country_count - 1) ? "" : ",");
}
fprintf (fp, "};\n\n");
/* timezone names */
fprintf (fp, "%s const char *tz_timezone_names[] = {\n", SHLIB_EXPORT_PREFIX);
for (i = 0; i < tzd->timezone_count; i++)
{
fprintf (fp, "\t\"%s\"%s\n", tzd->timezone_names[i], (i == tzd->timezone_count - 1 ? "" : ","));
}
fprintf (fp, "};\n\n");
/* timezones */
fprintf (fp, "%s const int timezone_count = %d;\n", SHLIB_EXPORT_PREFIX, tzd->timezone_count);
fprintf (fp, "%s const TZ_TIMEZONE timezones[] = {\n", SHLIB_EXPORT_PREFIX);
for (i = 0; i < tzd->timezone_count; i++)
{
fprintf (fp, "\t{%d, %d, %d, %d}%s\n", tzd->timezones[i].zone_id, tzd->timezones[i].country_id,
tzd->timezones[i].gmt_off_rule_start, tzd->timezones[i].gmt_off_rule_count,
(i == tzd->timezone_count - 1) ? "" : ",");
}
fprintf (fp, "};\n\n");
/* NOTE: timezone names are not exported into the shared library, but into a separate C file, to be included into
* CUBRID */
/* offset rule array */
fprintf (fp, "%s const int offset_rule_count = %d;\n", SHLIB_EXPORT_PREFIX, tzd->offset_rule_count);
fprintf (fp, "%s const TZ_OFFSET_RULE offset_rules[] = {\n", SHLIB_EXPORT_PREFIX);
for (i = 0; i < tzd->offset_rule_count; i++)
{
int julian_date;
offrule = &(tzd->offset_rules[i]);
julian_date = julian_encode (1 + offrule->until_mon, 1 + offrule->until_day, offrule->until_year);
fprintf (fp, "\t{%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, ", offrule->gmt_off, offrule->ds_ruleset,
offrule->until_year, offrule->until_mon, offrule->until_day, offrule->until_hour, offrule->until_min,
offrule->until_sec, offrule->until_time_type, offrule->until_flag, offrule->ds_type, julian_date);
if (offrule->std_format == NULL)
{
fprintf (fp, "NULL, ");
}
else
{
fprintf (fp, "\"%s\", ", offrule->std_format);
}
if (offrule->save_format == NULL)
{
fprintf (fp, "NULL, ");
}
else
{
fprintf (fp, "\"%s\", ", offrule->save_format);
}
if (offrule->var_format == NULL)
{
fprintf (fp, "NULL }");
}
else
{
fprintf (fp, "\"%s\" }", offrule->var_format);
}
if (i < tzd->offset_rule_count - 1)
{
fprintf (fp, ",\n");
}
}
fprintf (fp, "};\n\n");
/* tz names (timezone names and aliases) */
fprintf (fp, "%s const int tz_name_count = %d;\n", SHLIB_EXPORT_PREFIX, tzd->name_count);
fprintf (fp, "%s const TZ_NAME tz_names[] = {\n", SHLIB_EXPORT_PREFIX);
for (i = 0; i < tzd->name_count; i++)
{
fprintf (fp, "\t{%d, \"%s\", %d}%s\n", tzd->names[i].zone_id, tzd->names[i].name, tzd->names[i].is_alias,
(i == tzd->name_count - 1) ? "" : ",");
}
fprintf (fp, "};\n\n");
/* daylight saving rulesets */
fprintf (fp, "%s const int ds_ruleset_count = %d;\n", SHLIB_EXPORT_PREFIX, tzd->ds_ruleset_count);
fprintf (fp, "%s const TZ_DS_RULESET ds_rulesets[] = {\n", SHLIB_EXPORT_PREFIX);
for (i = 0; i < tzd->ds_ruleset_count; i++)
{
fprintf (fp, "\t{%d, %d, \"%s\", %d, \"%s\"}%s\n", tzd->ds_rulesets[i].index_start, tzd->ds_rulesets[i].count,
tzd->ds_rulesets[i].ruleset_name, tzd->ds_rulesets[i].to_year_max, tzd->ds_rulesets[i].default_abrev,
(i == tzd->ds_ruleset_count - 1) ? "" : ",");
}
fprintf (fp, "};\n\n");
/* daylight saving rules */
fprintf (fp, "%s const int ds_rule_count = %d;\n", SHLIB_EXPORT_PREFIX, tzd->ds_rule_count);
fprintf (fp, "%s const TZ_DS_RULE ds_rules[] = {\n", SHLIB_EXPORT_PREFIX);
for (i = 0; i < tzd->ds_rule_count; i++)
{
rule = &(tzd->ds_rules[i]);
fprintf (fp, "\t{%d, %d, %d, {%d, %d, %d}, %d, %d, %d, \"%s\"}%s\n", rule->from_year, rule->to_year,
rule->in_month, rule->change_on.type, rule->change_on.day_of_month, rule->change_on.day_of_week,
rule->at_time, rule->at_time_type, rule->save_time, rule->letter_abbrev,
(i == tzd->ds_rule_count - 1) ? "" : ",");
}
fprintf (fp, "};\n\n");
/* leap seconds */
fprintf (fp, "%s const int ds_leap_sec_count = %d;\n", SHLIB_EXPORT_PREFIX, tzd->ds_leap_sec_count);
fprintf (fp, "%s const TZ_LEAP_SEC ds_leap_sec[] = {\n", SHLIB_EXPORT_PREFIX);
for (i = 0; i < tzd->ds_leap_sec_count; i++)
{
leap_sec = &(tzd->ds_leap_sec[i]);
fprintf (fp, "\t{%d, %d, %d, %d, %d}%s\n", leap_sec->year, leap_sec->month, leap_sec->day,
leap_sec->corr_negative, leap_sec->is_rolling, (i == tzd->ds_leap_sec_count - 1) ? "" : ",");
}
fprintf (fp, "};\n\n");
#if defined(WINDOWS)
/* windows iana map */
fprintf (fp, "%s const int windows_iana_map_count = %d;\n", SHLIB_EXPORT_PREFIX, tzd->windows_iana_map_count);
fprintf (fp, "%s const TZ_WINDOWS_IANA_MAP windows_iana_map[] = {\n", SHLIB_EXPORT_PREFIX);
for (i = 0; i < tzd->windows_iana_map_count; i++)
{
fprintf (fp, "\t{\"%s\", \"%s\", %d}%s\n", tzd->windows_iana_map[i].windows_zone,
tzd->windows_iana_map[i].territory, tzd->windows_iana_map[i].iana_zone_id,
(i == tzd->windows_iana_map_count - 1) ? "" : ",");
}
fprintf (fp, "};\n\n");
#endif
PRINT_STRING_VAR_TO_C_FILE (fp, "tz_timezone_checksum", tzd->checksum);
if (fp)
{
fclose (fp);
}
exit:
return err_status;
}
/*
* tzc_get_ds_ruleset_by_name() - returns the ID/index of a ruleset having the
* specified name
* Returns: ID of the ruleset with the given name, or -1 if not found
* tzd(in): time zone data where to search
* ruleset(in): ruleset name to search for
*/
static int
tzc_get_ds_ruleset_by_name (const TZ_DS_RULESET * ds_rulesets, int ds_ruleset_count, const char *ruleset)
{
int ruleset_id = -1;
int index_bot, index_top;
int cmp_res;
index_bot = 0;
index_top = ds_ruleset_count - 1;
while (index_bot <= index_top)
{
ruleset_id = (index_bot + index_top) / 2;
cmp_res = strcmp (ruleset, ds_rulesets[ruleset_id].ruleset_name);
if (cmp_res == 0)
{
return ruleset_id;
}
else if (cmp_res < 0)
{
index_top = ruleset_id - 1;
}
else
{
index_bot = ruleset_id + 1;
}
}
return -1;
}
/*
* tzc_get_timezone_aliases() - returns the list of names for a given timezone
* Returns: ID of the ruleset with the given name, or -1 if not found
* tzd(in): time zone data where to search
* zone_id(in): timezone ID for which the aliases must be fetched
* aliases(out): list of indexes of aliases/names for the given timezone ID
* alias_count(out): number of aliases for the given timezone
*/
static int
tzc_get_timezone_aliases (const TZ_DATA * tzd, const int zone_id, int **aliases, int *alias_count)
{
int i, err_status = NO_ERROR;
int *temp_array = NULL;
assert (*aliases == NULL);
*aliases = NULL;
*alias_count = 0;
for (i = 0; i < tzd->name_count; i++)
{
if (zone_id == tzd->names[i].zone_id && tzd->names[i].is_alias == 1)
{
/* name/alias found for the given timezone ID */
temp_array = (int *) realloc (*aliases, ((*alias_count) + 1) * sizeof (int));
if (temp_array == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", (*alias_count) + 1);
err_status = TZC_ERR_OUT_OF_MEMORY;
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_OUT_OF_MEMORY, err_msg, "int");
goto exit;
}
*aliases = temp_array;
(*aliases)[*alias_count] = i;
(*alias_count)++;
}
}
exit:
return err_status;
}
/*
* tzc_dump_one_offset_rule () - dump a single offset rule
* Returns:
* offset_rule(in): the offset rule to dump
*/
static void
tzc_dump_one_offset_rule (const TZ_DATA * tzd, const TZ_OFFSET_RULE * offset_rule)
{
/* print GMTOFF column */
print_seconds_as_time_hms_var (offset_rule->gmt_off);
printf ("\t");
/* print RULES column */
if (offset_rule->ds_type == DS_TYPE_RULESET_ID)
{
assert (offset_rule->ds_ruleset >= 0 && offset_rule->ds_ruleset < tzd->ds_ruleset_count);
printf ("%s", tzd->ds_rulesets[offset_rule->ds_ruleset].ruleset_name);
}
else
{
if (offset_rule->ds_ruleset == 0)
{
printf ("-");
}
else
{
print_seconds_as_time_hms_var (offset_rule->ds_ruleset);
}
}
/* print FORMAT column */
if (offset_rule->var_format != NULL)
{
assert (offset_rule->std_format == NULL && offset_rule->save_format == NULL);
printf ("\t%s", offset_rule->var_format);
}
else
{
assert (offset_rule->std_format != NULL);
printf ("\t%s", offset_rule->std_format);
if (offset_rule->save_format != NULL)
{
printf ("/%s", offset_rule->save_format);
}
}
/* print UNTIL column */
if (offset_rule->until_year != 0)
{
printf ("\t%d", offset_rule->until_year);
if (offset_rule->until_mon != TZ_MON_COUNT)
{
printf (" %s", MONTH_NAMES_ABBREV[offset_rule->until_mon]);
if (offset_rule->until_day != 0)
{
printf (" %d", offset_rule->until_day + 1);
}
}
}
printf ("\t");
if (offset_rule->until_hour + offset_rule->until_min + offset_rule->until_sec > 0)
{
printf ("\t%02d:%02d", offset_rule->until_hour, offset_rule->until_min);
if (offset_rule->until_sec > 0)
{
printf (":%02d", offset_rule->until_sec);
}
}
}
/*
* tzc_dump_ds_ruleset () - dump daylight saving rules from a specified
* ruleset
* Returns:
* tzd(in): loaded timezone data
* ruleset_id(in): ID of the ruleset to dump
*/
static void
tzc_dump_ds_ruleset (const TZ_DATA * tzd, const int ruleset_id)
{
TZ_DS_RULESET *ruleset;
TZ_DS_RULE *rule;
int i, start_index, end_index;
assert (ruleset_id >= 0 && ruleset_id < tzd->ds_ruleset_count);
ruleset = &(tzd->ds_rulesets[ruleset_id]);
printf ("\nDaylight saving ruleset : %s", ruleset->ruleset_name);
start_index = ruleset->index_start;
end_index = start_index + ruleset->count;
for (i = start_index; i < end_index; i++)
{
rule = &(tzd->ds_rules[i]);
/* print NAME and FROM columns */
printf ("\nRule\t%s\t%d\t", ruleset->ruleset_name, rule->from_year);
/* print TO column */
if (rule->to_year == TZ_MAX_YEAR)
{
printf ("max\t");
}
else if (rule->to_year == rule->from_year)
{
printf ("only\t");
}
else
{
assert (rule->to_year > rule->from_year);
printf ("%d\t", rule->to_year);
}
/* NOTE: TYPE column is '-' for all rules at this time, so we just print a '-' */
printf ("-\t");
/* print IN column */
assert (rule->in_month < 12);
printf ("%s\t", MONTH_NAMES_ABBREV[rule->in_month]);
/* print ON column */
switch (rule->change_on.type)
{
case TZ_DS_TYPE_FIXED:
printf ("%d", rule->change_on.day_of_month + 1);
break;
case TZ_DS_TYPE_VAR_GREATER:
printf ("%s>=%d", DAY_NAMES_ABBREV[rule->change_on.day_of_week], rule->change_on.day_of_month + 1);
break;
case TZ_DS_TYPE_VAR_SMALLER:
printf ("%s<=%d", DAY_NAMES_ABBREV[rule->change_on.day_of_week], rule->change_on.day_of_month + 1);
break;
default:
assert (false);
break;
}
printf ("\t");
/* print AT column */
print_seconds_as_time_hms_var (rule->at_time);
printf ("\t");
/* print SAVE column */
print_seconds_as_time_hms_var (rule->save_time);
printf ("\t");
/* print LETTERS column */
printf ("%s", rule->letter_abbrev);
}
}
/*
* tzc_dump_summary () - dump timezone general information
* Returns:
* tzd(in): timezone data
*/
void
tzc_dump_summary (const TZ_DATA * tzd)
{
assert (tzd != NULL);
printf ("\n Summary");
printf ("\n No. of countries: %d", tzd->country_count);
printf ("\n No. of timezones: %d", tzd->timezone_count);
printf ("\n No. of aliases: %d", tzd->name_count - tzd->timezone_count);
printf ("\n No. of offset rules: %d", tzd->offset_rule_count);
printf ("\n No. of daylight saving rulesets: %d", tzd->ds_ruleset_count);
printf ("\n No. of daylight saving rules: %d", tzd->ds_rule_count);
printf ("\n No. of leap seconds: %d", tzd->ds_leap_sec_count);
}
/*
* tzc_dump_countries () - dump the list of countries
* Returns:
* tzd(in): timezone data
*/
void
tzc_dump_countries (const TZ_DATA * tzd)
{
int i;
assert (tzd != NULL);
for (i = 0; i < tzd->country_count; i++)
{
printf ("%s %s\n", tzd->countries[i].code, tzd->countries[i].full_name);
}
}
/*
* tzc_dump_timezones () - dump the list of timezones
* Returns:
* tzd(in): timezone data
*/
void
tzc_dump_timezones (const TZ_DATA * tzd)
{
int i;
assert (tzd != NULL);
for (i = 0; i < tzd->timezone_count; i++)
{
printf ("%5d. %s\n", i, tzd->timezone_names[i]);
}
}
/*
* tzc_dump_one_timezone () - dump all information related to a given timezone
* Returns:
* tzd(in): timezone data
* zone_id(in): ID of the timezone for which to dump information
*/
void
tzc_dump_one_timezone (const TZ_DATA * tzd, const int zone_id)
{
int err_status = NO_ERROR;
int i, j;
int *zone_aliases = NULL;
int *ds_rulesets_used = NULL;
int count_ds_rulesets_used = 0;
int *temp_int_array;
int alias_count = 0, start_index = 0;
bool is_first = true, found;
TZ_TIMEZONE *zone = NULL;
TZ_OFFSET_RULE *offset_rule = NULL;
assert (tzd != NULL);
printf (" Zone name: %s\n", tzd->timezone_names[zone_id]);
err_status = tzc_get_timezone_aliases (tzd, zone_id, &zone_aliases, &alias_count);
if (err_status != NO_ERROR)
{
goto exit;
}
if (alias_count > 0)
{
printf (" Aliases (%d): ", alias_count);
}
for (j = 0; j < alias_count; j++)
{
TZ_NAME *tz_name = &(tzd->names[zone_aliases[j]]);
if (!is_first)
{
printf (", ");
}
else
{
is_first = false;
}
printf ("%s", tz_name->name);
}
if (alias_count > 0)
{
printf ("\n");
}
zone = &(tzd->timezones[zone_id]);
start_index = zone->gmt_off_rule_start;
/* dump offset rules, and also build the list of DS rulesets to be dumped */
printf ("\n Offset rule index: %d, count: %d", zone->gmt_off_rule_start, zone->gmt_off_rule_count);
if (zone->gmt_off_rule_count > 0)
{
printf ("\n Offset rules: \n");
}
for (i = 0; i < zone->gmt_off_rule_count; i++)
{
offset_rule = &(tzd->offset_rules[start_index + i]);
tzc_dump_one_offset_rule (tzd, offset_rule);
printf ("\n");
if (offset_rule->ds_type == DS_TYPE_FIXED)
{
continue;
}
/* search for the ruleset id */
found = false;
for (j = 0; j < count_ds_rulesets_used && !found; j++)
{
if (ds_rulesets_used[j] == offset_rule->ds_ruleset)
{
found = true;
}
}
if (!found)
{
temp_int_array =
(int *) realloc (ds_rulesets_used, (count_ds_rulesets_used + 1) * sizeof (ds_rulesets_used[0]));
if (temp_int_array == NULL)
{
printf ("\nOUT OF MEMORY!\n");
goto exit;
}
ds_rulesets_used = temp_int_array;
ds_rulesets_used[count_ds_rulesets_used] = offset_rule->ds_ruleset;
count_ds_rulesets_used++;
}
}
printf ("\n Found %d daylight saving ruleset(s) used by offset rules\n", count_ds_rulesets_used);
for (i = 0; i < count_ds_rulesets_used; i++)
{
tzc_dump_ds_ruleset (tzd, ds_rulesets_used[i]);
}
exit:
if (ds_rulesets_used != NULL)
{
free (ds_rulesets_used);
}
if (zone_aliases != NULL)
{
free (zone_aliases);
}
}
/*
* tzc_dump_leap_sec () - dump the list of leap seconds
* Returns:
* tzd(in): timezone data
*/
void
tzc_dump_leap_sec (const TZ_DATA * tzd)
{
int i;
TZ_LEAP_SEC *leap_sec;
assert (tzd != NULL);
printf ("\n# Leap\tYEAR\tMONTH\tDAY\tHH:MM:SS\tCORR\tR/S\n");
for (i = 0; i < tzd->ds_leap_sec_count; i++)
{
leap_sec = &(tzd->ds_leap_sec[i]);
printf ("Leap\t%d\t%s\t%d\t%d:%d:%d\t%s\t%s\n", leap_sec->year, MONTH_NAMES_ABBREV[leap_sec->month],
leap_sec->day, 23, 59, 60, (leap_sec->corr_negative ? "-" : "+"), (leap_sec->is_rolling ? "R" : "S"));
}
}
/*
* tzc_log_error () - log timezone compiler error
* Returns:
* context(in): timezone compiler error context
* code(in): error code
* msg1(in): first string replacement for error message
* msg2(in): second string replacement for error message
*/
static void
tzc_log_error (const TZ_RAW_CONTEXT * context, const int code, const char *msg1, const char *msg2)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
char err_msg_temp[TZC_ERR_MSG_MAX_SIZE];
assert (code <= 0 && -(code) < tzc_Err_message_count);
*err_msg = '\0';
*err_msg_temp = '\0';
if (context != NULL && !IS_EMPTY_STR (context->current_file) && context->current_line != -1)
{
snprintf_dots_truncate (err_msg_temp, sizeof (err_msg_temp) - 1, " (file %s, line %d)", context->current_file,
context->current_line);
}
strcat (err_msg, err_msg_temp);
*err_msg_temp = '\0';
snprintf_dots_truncate (err_msg_temp, sizeof (err_msg_temp), tzc_Err_messages[-code], msg1, msg2);
strcat (err_msg, err_msg_temp);
strcat (err_msg, "\n");
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_TZ_COMPILE_ERROR, 1, err_msg);
}
static void
tzc_summary (TZ_RAW_DATA * tzd_raw, TZ_DATA * tzd)
{
int i, j;
int max_len, temp_len;
int max_len2, temp_len2;
int max_len3;
printf ("\n");
printf (" COUNTRY MAX NAME LEN: ");
max_len = 0;
for (i = 0; i < tzd_raw->country_count; i++)
{
temp_len = strlen (tzd_raw->countries[i].full_name);
if (temp_len > max_len)
{
max_len = temp_len;
}
}
printf ("%d\n", max_len);
printf (" DS RULES & RULESETS\n");
max_len = 0;
max_len2 = 0;
for (i = 0; i < tzd_raw->ruleset_count; i++)
{
TZ_RAW_DS_RULESET *ds_ruleset = &(tzd_raw->ds_rulesets[i]);
temp_len = strlen (ds_ruleset->name);
if (temp_len > max_len)
{
max_len = temp_len;
}
for (j = 0; j < ds_ruleset->rule_count; j++)
{
TZ_RAW_DS_RULE *rule = &(ds_ruleset->rules[j]);
temp_len = strlen (rule->letter_abbrev);
if (temp_len > max_len2)
{
max_len2 = temp_len;
}
}
}
printf (" DS RULESET MAX NAME LEN: %d\n", max_len);
printf (" DS RULE MAX LETTER_ABBREV LEN: %d\n\n", max_len2);
printf (" TIMEZONE\n");
max_len = 0;
max_len2 = 0;
max_len3 = 0;
for (i = 0; i < tzd_raw->zone_count; i++)
{
temp_len = strlen (tzd_raw->zones[i].full_name);
if (temp_len > max_len)
{
max_len = temp_len;
}
if (tzd_raw->zones[i].comments != NULL)
{
temp_len = strlen (tzd_raw->zones[i].comments);
if (temp_len > max_len2)
{
max_len2 = temp_len;
}
}
if (tzd_raw->zones[i].coordinates != NULL)
{
temp_len = strlen (tzd_raw->zones[i].coordinates);
if (temp_len > max_len3)
{
max_len3 = temp_len;
}
}
}
printf (" MAX NAME LEN: %d", max_len);
printf (" MAX comments LEN: %d", max_len2);
printf (" MAX coordinates LEN: %d\n", max_len3);
printf (" TZ_NAMES (timezone names and aliases) MAX NAME LEN: ");
max_len = 0;
for (i = 0; i < tzd->name_count; i++)
{
temp_len = strlen (tzd->names[i].name);
if (temp_len > max_len)
{
max_len = temp_len;
}
}
printf ("%d\n", max_len);
printf (" TZ_RW_LINKS : \n");
max_len = 0;
max_len2 = 0;
for (i = 0; i < tzd_raw->link_count; i++)
{
temp_len = strlen (tzd_raw->links[i].name);
if (temp_len > max_len)
{
max_len = temp_len;
}
temp_len2 = strlen (tzd_raw->links[i].alias);
if (temp_len2 > max_len2)
{
max_len2 = temp_len2;
}
}
printf (" MAX NAME LEN: %d", max_len);
printf (" MAX ALIAS LEN: %d\n", max_len2);
printf (" TZ_RAW_OFFSET_RULES : \n");
max_len = 0;
max_len2 = 0;
for (i = 0; i < tzd_raw->zone_count; i++)
{
TZ_RAW_ZONE_INFO *zone = &(tzd_raw->zones[i]);
for (j = 0; j < zone->offset_rule_count; j++)
{
TZ_RAW_OFFSET_RULE *offrule = &(zone->offset_rules[j]);
temp_len = strlen (offrule->ds_ruleset_name);
if (temp_len > max_len)
{
max_len = temp_len;
}
temp_len = strlen (offrule->format);
if (temp_len > max_len2)
{
max_len2 = temp_len;
}
}
}
printf (" MAX rules LEN: %d", max_len);
printf (" MAX format LEN: %d\n", max_len2);
}
#if defined(WINDOWS)
/*
* comp_func_tz_windows_zones - comparison function between two
* TZ_WINDOWS_IANA_MAP values
* Returns: -1 if arg1 < arg2, 0 if arg1 = arg2, 1 if arg1 > arg2
* arg1(in): first value to compare
* arg2(in): second value to compare
*/
static int
comp_func_tz_windows_zones (const void *arg1, const void *arg2)
{
TZ_WINDOWS_IANA_MAP *map1, *map2;
int ret;
assert (arg1 != NULL && arg2 != NULL);
map1 = (TZ_WINDOWS_IANA_MAP *) arg1;
map2 = (TZ_WINDOWS_IANA_MAP *) arg2;
assert (!IS_EMPTY_STR (map1->windows_zone) && !IS_EMPTY_STR (map2->windows_zone) && !IS_EMPTY_STR (map1->territory)
&& !IS_EMPTY_STR (map2->territory));
ret = strcmp (map1->windows_zone, map2->windows_zone);
if (ret != 0)
{
return ret;
}
return strcmp (map1->territory, map2->territory);
}
/*
* xml_start_mapZone() - extracts from a mapZone tag the Windows timezone name
* and IANA timezone name
*
* Returns: 0 parser OK, non-zero value if parser NOK
* data(in): user data
* attr(in): array of pairs for XML attribute and value (strings) of current
* element
*/
static int
xml_start_mapZone (void *data, const char **attr)
{
XML_PARSER_DATA *pd = (XML_PARSER_DATA *) data;
TZ_DATA *tz = NULL;
char *windows_zone = NULL;
char *iana_zone = NULL;
char *territory = NULL;
TZ_WINDOWS_IANA_MAP *temp;
int len_windows_zone;
int len_territory;
int iana_zone_id = -1;
int i;
assert (data != NULL);
tz = (TZ_DATA *) XML_USER_DATA (pd);
if (xml_get_att_value (attr, "other", &windows_zone) == 0 && xml_get_att_value (attr, "territory", &territory) == 0
&& xml_get_att_value (attr, "type", &iana_zone) == 0)
{
assert (windows_zone != NULL && territory != NULL && iana_zone != NULL);
if (tz->windows_iana_map != NULL
&& strcmp (windows_zone, tz->windows_iana_map[tz->windows_iana_map_count - 1].windows_zone) == 0)
{
return 0;
}
temp =
(TZ_WINDOWS_IANA_MAP *) realloc (tz->windows_iana_map,
(tz->windows_iana_map_count + 1) * sizeof (TZ_WINDOWS_IANA_MAP));
if (temp == NULL)
{
char err_msg[TZC_ERR_MSG_MAX_SIZE];
sprintf (err_msg, "%d", tz->windows_iana_map_count + 1);
TZC_LOG_ERROR_2ARG (NULL, TZC_ERR_OUT_OF_MEMORY, err_msg, "TZ_WINDOWS_IANA_MAP");
return -1;
}
len_windows_zone = strlen (windows_zone);
len_territory = strlen (territory);
if (len_windows_zone > TZ_WINDOWS_ZONE_NAME_SIZE || len_territory > TZ_COUNTRY_CODE_SIZE)
{
TZC_LOG_ERROR_1ARG (NULL, TZC_ERR_INVALID_VALUE, "TZ_WINDOWS_IANA_MAP");
if (temp != nullptr)
{
free (temp);
}
return -1;
}
memmove (temp[tz->windows_iana_map_count].windows_zone, windows_zone, len_windows_zone);
temp[tz->windows_iana_map_count].windows_zone[len_windows_zone] = '\0';
memmove (temp[tz->windows_iana_map_count].territory, territory, len_territory);
temp[tz->windows_iana_map_count].territory[len_territory] = '\0';
for (i = 0; i < tz->name_count; i++)
{
if (strcmp (iana_zone, tz->names[i].name) == 0)
{
iana_zone_id = tz->names[i].zone_id;
break;
}
}
temp[tz->windows_iana_map_count].iana_zone_id = iana_zone_id;
tz->windows_iana_map = temp;
tz->windows_iana_map_count++;
return 0;
}
return 1;
}
/*
* tzc_load_windows_iana_map() - loads the data from the file marked as
* TZF_LIBC_IANA_ZONES_MAP
*
* Returns: 0 (NO_ERROR) if success, error code or -1 otherwise
* tz_data(out): timezone data structure to hold the loaded information
* input_folder(in): folder containing IANA's timezone database
*
*/
static int
tzc_load_windows_iana_map (TZ_DATA * tz_data, const char *input_folder)
{
int err_status = NO_ERROR, i;
char filepath[PATH_MAX] = { 0 };
char str[TZ_MAX_LINE_LEN] = { 0 };
XML_PARSER_DATA windows_zones_parser;
assert (tz_data != NULL);
assert (input_folder != NULL);
for (i = 0; i < tz_File_count; i++)
{
if (tz_Files[i].type == TZF_WINDOWS_IANA_ZONES_MAP)
{
break;
}
}
assert (i < tz_File_count);
tzc_build_filepath (filepath, sizeof (filepath), input_folder, tz_Files[i].name);
tz_data->windows_iana_map_count = 0;
tz_data->windows_iana_map = NULL;
windows_zones_parser.ud = tz_data;
windows_zones_parser.xml_parser =
xml_init_parser (&windows_zones_parser, filepath, "UTF-8", windows_zones_elements,
sizeof (windows_zones_elements) / sizeof (XML_ELEMENT_DEF *));
if (windows_zones_parser.xml_parser == NULL)
{
err_status = ER_TZ_COMPILE_ERROR;
goto exit;
}
xml_parser_exec (&windows_zones_parser);
/* sort windows zone names */
qsort (tz_data->windows_iana_map, tz_data->windows_iana_map_count, sizeof (TZ_WINDOWS_IANA_MAP),
comp_func_tz_windows_zones);
exit:
xml_destroy_parser (&windows_zones_parser);
return err_status;
}
#endif
/*
* tzc_find_timezone_names() - returns 1 if the timezone name was found
* or 0 otherwise
* Returns: 1 if the timezone name was found or 0 otherwise
* tzd(in): time zone data where to search
* timezone_name(in): timezone name to search for
*/
static int
tzc_find_timezone_names (const TZ_DATA * tzd, const char *timezone_name)
{
int index_bot, index_top;
int cmp_res;
index_bot = 0;
index_top = tzd->name_count - 1;
while (index_bot <= index_top)
{
int mid = index_bot + (index_top - index_bot) / 2;
cmp_res = strcmp (timezone_name, tzd->names[mid].name);
if (cmp_res == 0)
{
return tzd->names[mid].zone_id;
}
else if (cmp_res < 0)
{
index_top = mid - 1;
}
else
{
index_bot = mid + 1;
}
}
return -1;
}
/*
* tzc_find_country_names() - returns 1 if the country name was found
* or 0 otherwise
* Returns: 1 if the country name was found or 0 otherwise
* countries(in): vector of countries where to search
* country_count(in): number of elements of countries vector
* country_name (in) : name of the country to search for
*/
static int
tzc_find_country_names (const TZ_COUNTRY * countries, const int country_count, const char *country_name)
{
int index_bot, index_top;
int cmp_res;
index_bot = 0;
index_top = country_count - 1;
while (index_bot <= index_top)
{
int mid = index_bot + (index_top - index_bot) / 2;
cmp_res = strcmp (country_name, countries[mid].full_name);
if (cmp_res == 0)
{
return mid;
}
else if (cmp_res < 0)
{
index_top = mid - 1;
}
else
{
index_bot = mid + 1;
}
}
return -1;
}
/*
* comp_ds_rules() - equality function for two daylight saving rules
*
* Returns: true if the rules are identical or false otherwise
* rule1(in): first daylight saving rule
* rule2(in): second daylight saving rule
*/
static bool
comp_ds_rules (const TZ_DS_RULE * rule1, const TZ_DS_RULE * rule2)
{
if (rule1->at_time != rule2->at_time || rule1->change_on.day_of_month != rule2->change_on.day_of_month
|| rule1->change_on.day_of_week != rule2->change_on.day_of_week || rule1->change_on.type != rule2->change_on.type
|| rule1->from_year != rule2->from_year || rule1->to_year != rule2->to_year || rule1->in_month != rule2->in_month
|| rule1->save_time != rule2->save_time)
{
return false;
}
return true;
}
/*
* comp_offset_rules() - equality function for two offset rules
*
* Returns: true if the rules are identical or false otherwise
* rule1(in): first offset rule
* rule2(in): second offset rule
*/
static bool
comp_offset_rules (const TZ_OFFSET_RULE * rule1, const TZ_OFFSET_RULE * rule2)
{
if (rule1->gmt_off != rule2->gmt_off || rule1->until_sec != rule2->until_sec || rule1->until_min != rule2->until_min
|| rule1->until_hour != rule2->until_hour || rule1->until_day != rule2->until_day
|| rule1->until_mon != rule2->until_mon || rule1->until_year != rule2->until_year)
{
return false;
}
return true;
}
/*
* copy_offset_rule() - copies in dst the offset rule in tzd at position index
* in the offset rule array
*
* Returns: error or no error
* dst(in/out): destination offset rule
* tzd(in): timezone data
* index(in): index of the source offset rule in tzd
*/
static int
copy_offset_rule (TZ_OFFSET_RULE * dst, const TZ_DATA * tzd, const int index)
{
int err_status = NO_ERROR;
*dst = tzd->offset_rules[index];
dst->julian_date = julian_encode (1 + tzd->offset_rules[index].until_mon,
1 + tzd->offset_rules[index].until_day, tzd->offset_rules[index].until_year);
if (tzd->offset_rules[index].std_format != NULL)
{
DUPLICATE_STR (dst->std_format, tzd->offset_rules[index].std_format);
}
else
{
dst->std_format = NULL;
}
if (tzd->offset_rules[index].save_format != NULL)
{
DUPLICATE_STR (dst->save_format, tzd->offset_rules[index].save_format);
}
else
{
dst->save_format = NULL;
}
if (tzd->offset_rules[index].var_format != NULL)
{
DUPLICATE_STR (dst->var_format, tzd->offset_rules[index].var_format);
}
else
{
dst->var_format = NULL;
}
exit:
return err_status;
}
/*
* init_ds_ruleset() - initializes the members of dst_ruleset
*
* Returns: error or no error
* dst_ruleset(in/out): destination ds ruleset
* tzd(in): timezone data
* index(in): index of the ds ruleset in the tzd ds_ruleset array
* start(in): start of the ds ruleset
*/
static int
init_ds_ruleset (TZ_DS_RULESET * dst_ruleset, const TZ_DATA * tzd, const int index, const int start)
{
int err_status = NO_ERROR;
dst_ruleset->count = tzd->ds_rulesets[index].count;
DUPLICATE_STR (dst_ruleset->ruleset_name, tzd->ds_rulesets[index].ruleset_name);
dst_ruleset->index_start = start;
dst_ruleset->to_year_max = tzd->ds_rulesets[index].to_year_max;
DUPLICATE_STR (dst_ruleset->default_abrev, tzd->ds_rulesets[index].default_abrev);
exit:
return err_status;
}
/*
* copy_ds_rule() - copies in dst the daylight saving rule in tzd at
* position index in the daylight saving rule array
*
* Returns: error or no error
* dst(in/out): destination daylight saving rule
* tzd(in): timezone data
* index(in): index of the source daylight saving rule in tzd
*/
static int
copy_ds_rule (TZ_DS_RULE * dst, const TZ_DATA * tzd, const int index)
{
int err_status = NO_ERROR;
*dst = tzd->ds_rules[index];
if (tzd->ds_rules[index].letter_abbrev != NULL)
{
DUPLICATE_STR (dst->letter_abbrev, tzd->ds_rules[index].letter_abbrev);
}
else
{
dst->letter_abbrev = NULL;
}
exit:
return err_status;
}
/*
* tz_data_partial_clone() - copies timezone data from tzd into
* the three data structures
*
* Returns: error or no error
* timezone_names(in/out): timezone names without aliases
* timezones(in/out): timezones
* names(in/out): timezone names including aliases
* tzd(in): timezone data
*/
static int
tz_data_partial_clone (char **timezone_names, TZ_TIMEZONE * timezones, TZ_NAME * names, const TZ_DATA * tzd)
{
int i, err_status = NO_ERROR;
for (i = 0; i < tzd->timezone_count; i++)
{
DUPLICATE_STR (timezone_names[i], tzd->timezone_names[i]);
}
memcpy (timezones, tzd->timezones, tzd->timezone_count * sizeof (TZ_TIMEZONE));
memcpy (names, tzd->names, tzd->name_count * sizeof (TZ_NAME));
for (i = 0; i < tzd->name_count; i++)
{
DUPLICATE_STR (names[i].name, tzd->names[i].name);
}
exit:
return err_status;
}
/*
* init_tz_name() - copies the members of src into dst
*
* Returns: error or no error
* dst(in/out): destination tz_name
* src(in): source tz_name
*/
static int
init_tz_name (TZ_NAME * dst, TZ_NAME * src)
{
int err_status = NO_ERROR;
dst->is_alias = src->is_alias;
DUPLICATE_STR (dst->name, src->name);
dst->zone_id = src->zone_id;
exit:
return err_status;
}
#if defined (SA_MODE)
/*
* tzc_extend() - Does a merge between the new timezone data and
* the old timezone data in order to maintain backward
* compatibility with the timezone data present in the
* database. If the data could not be made backward
* compatible a message is printed
*
* Returns: error or no error
* tzd (in/out): new timezone library data that needs to be merged with
* the old timezone library data
*/
static int
tzc_extend (TZ_DATA * tzd)
{
int err_status = NO_ERROR;
TZ_DATA old_tzd;
TZ_TIMEZONE *all_timezones = NULL, *timezones, *old_timezones;
TZ_NAME *names, *all_names = NULL, *old_names;
int i, j, k, l;
int all_timezones_count = 0;
int all_timezones_and_aliases_count = 0;
char **all_timezone_names = NULL;
int start_timezones, start_names, start;
int gmt_off_rule_start, prev_gmt_off_rule_start;
char *timezone_name;
int zone_id;
TZ_OFFSET_RULE *all_offset_rules = NULL;
int all_offset_rule_count = 0;
int start_gmt_old, start_gmt_new;
int all_ds_ruleset_count = 0;
int ruleset_id;
char *mark_ruleset = NULL;
TZ_DS_RULESET *all_ds_rulesets = NULL;
int prev_start_ds_rule = 0, start_ds_rule = 0;
TZ_DS_RULE *all_ds_rules = NULL;
int all_ds_rule_count = 0;
int comp_res;
TZ_DATA *tzd_or_old_tzd = NULL;
const char *ruleset_name;
bool is_compat = true;
int start_ds_ruleset_old = 0, start_ds_ruleset_new = 0;
const TZ_DS_RULE *old_ds_rule = NULL;
const TZ_DS_RULE *new_ds_rule = NULL;
int all_country_count = 0;
TZ_COUNTRY *all_countries = NULL;
char *country_name = NULL;
int country_id;
int temp_zone_id;
bool found_duplicate = false;
OFFSET_RULE_INTERVAL *old_tzd_offset_rule_map = NULL;
OFFSET_RULE_INTERVAL *tzd_offset_rule_map = NULL;
int old_tzd_map_count = 0, tzd_map_count = 0;
int find_idx = -1;
int cnt;
char timezone_library_path[PATH_MAX] = { 0 };
/* First load the data structures from the old library and after that do the update */
envvar_libdir_file (timezone_library_path, PATH_MAX, LIB_TZ_NAME);
err_status = tz_load_with_library_path (&old_tzd, timezone_library_path);
if (err_status != NO_ERROR)
{
goto exit;
}
names = tzd->names;
for (i = 0; i < tzd->name_count; i++)
{
if (tzc_find_timezone_names (&old_tzd, names[i].name) == -1)
{
if (names[i].is_alias == 0)
{
all_timezones_count++;
}
all_timezones_and_aliases_count++;
}
}
all_timezones_count += old_tzd.timezone_count;
all_timezones_and_aliases_count += old_tzd.name_count;
/* Count the number of new added countries */
for (i = 0; i < tzd->country_count; i++)
{
int country_id = tzc_find_country_names (old_tzd.countries, old_tzd.country_count,
tzd->countries[i].full_name);
if (country_id == -1)
{
all_country_count++;
}
}
all_country_count += old_tzd.country_count;
all_countries = (TZ_COUNTRY *) calloc (all_country_count, sizeof (TZ_COUNTRY));
if (all_countries == NULL)
{
err_status = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, err_status, 1, all_country_count * sizeof (TZ_COUNTRY));
goto exit;
}
all_timezone_names = (char **) calloc (all_timezones_count, sizeof (char *));
if (all_timezone_names == NULL)
{
err_status = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, err_status, 1, all_timezones_count * sizeof (char *));
goto exit;
}
all_timezones = (TZ_TIMEZONE *) calloc (all_timezones_count, sizeof (TZ_TIMEZONE));
if (all_timezones == NULL)
{
err_status = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, err_status, 1, all_timezones_count * sizeof (TZ_TIMEZONE));
goto exit;
}
all_names = (TZ_NAME *) calloc (all_timezones_and_aliases_count, sizeof (TZ_NAME));
if (all_names == NULL)
{
err_status = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, err_status, 1, all_timezones_and_aliases_count * sizeof (TZ_NAME));
goto exit;
}
old_tzd_offset_rule_map = (OFFSET_RULE_INTERVAL *) calloc (old_tzd.timezone_count, sizeof (OFFSET_RULE_INTERVAL));
if (old_tzd_offset_rule_map == NULL)
{
err_status = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, err_status, 1, old_tzd.timezone_count * sizeof (OFFSET_RULE_INTERVAL));
goto exit;
}
tzd_offset_rule_map = (OFFSET_RULE_INTERVAL *) calloc (tzd->timezone_count, sizeof (OFFSET_RULE_INTERVAL));
if (tzd_offset_rule_map == NULL)
{
err_status = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, err_status, 1, tzd->timezone_count * sizeof (OFFSET_RULE_INTERVAL));
goto exit;
}
mark_ruleset = (char *) calloc (old_tzd.ds_ruleset_count, sizeof (char));
if (mark_ruleset == NULL)
{
err_status = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, err_status, 1, old_tzd.ds_ruleset_count * sizeof (char));
goto exit;
}
err_status = tz_data_partial_clone (all_timezone_names, all_timezones, all_names, &old_tzd);
if (err_status != NO_ERROR)
{
goto exit;
}
/* Add the new countries */
i = 0, j = 0, k = 0;
while (i < old_tzd.country_count && j < tzd->country_count)
{
comp_res = strcmp (old_tzd.countries[i].full_name, tzd->countries[j].full_name);
if (comp_res == 0)
{
INIT_COUNTRY (&all_countries[k], &tzd->countries[j]);
i++;
j++;
}
else if (comp_res < 0)
{
INIT_COUNTRY (&all_countries[k], &old_tzd.countries[i]);
i++;
}
else
{
INIT_COUNTRY (&all_countries[k], &tzd->countries[j]);
j++;
}
k++;
}
while (i < old_tzd.country_count)
{
INIT_COUNTRY (&all_countries[k], &old_tzd.countries[i]);
i++;
k++;
}
while (j < tzd->country_count)
{
INIT_COUNTRY (&all_countries[k], &tzd->countries[j]);
j++;
k++;
}
assert (k == all_country_count);
/* Add the new timezones */
timezones = tzd->timezones;
start_timezones = old_tzd.timezone_count;
start_names = old_tzd.name_count;
for (i = 0; i < tzd->name_count; i++)
{
zone_id = tzc_find_timezone_names (&old_tzd, names[i].name);
if (zone_id == -1)
{
err_status = init_tz_name (&all_names[start_names], &names[i]);
if (err_status != NO_ERROR)
{
goto exit;
}
if (names[i].is_alias == 0)
{
DUPLICATE_STR (all_timezone_names[start_timezones], names[i].name);
all_timezones[start_timezones] = timezones[names[i].zone_id];
all_timezones[start_timezones].zone_id = start_timezones;
all_names[start_names].zone_id = start_timezones;
start_timezones++;
}
start_names++;
if (is_compat == true)
{
is_compat = false;
}
}
}
/* Now fix the zone ids for the alias timezones */
for (i = old_tzd.name_count; i < all_timezones_and_aliases_count; i++)
{
if (all_names[i].is_alias == 1)
{
timezone_name = tzd->timezone_names[all_names[i].zone_id];
zone_id = tzc_find_timezone_names (&old_tzd, timezone_name);
if (zone_id != -1)
{
all_names[i].zone_id = zone_id;
}
/* We have an alias pointing to a new timezone */
else
{
for (j = old_tzd.timezone_count; j < all_timezones_count; j++)
{
if (strcmp (timezone_name, all_timezone_names[j]) == 0)
{
all_names[i].zone_id = j;
break;
}
}
}
}
}
for (i = 0; i < all_timezones_count; i++)
{
found_duplicate = false;
zone_id = tzc_find_timezone_names (tzd, all_timezone_names[i]);
if (zone_id == -1)
{
/* Go back and search for duplicate intervals in the old timezone library */
for (j = i - 1; j >= 0; j--)
{
temp_zone_id = tzc_find_timezone_names (tzd, all_timezone_names[j]);
if (temp_zone_id == -1 && all_timezones[i].gmt_off_rule_start == all_timezones[j].gmt_off_rule_start
&& all_timezones[i].gmt_off_rule_count == all_timezones[j].gmt_off_rule_count)
{
found_duplicate = true;
break;
}
}
if (found_duplicate == true)
{
continue;
}
all_offset_rule_count += all_timezones[i].gmt_off_rule_count;
}
else
{
/* Go back and search for duplicate intervals in the tzd timezone library */
for (j = i - 1; j >= 0; j--)
{
temp_zone_id = tzc_find_timezone_names (tzd, all_timezone_names[j]);
if (temp_zone_id != -1
&& timezones[zone_id].gmt_off_rule_start == timezones[temp_zone_id].gmt_off_rule_start
&& timezones[zone_id].gmt_off_rule_count == timezones[temp_zone_id].gmt_off_rule_count)
{
found_duplicate = true;
break;
}
}
if (found_duplicate == true)
{
continue;
}
all_offset_rule_count += timezones[zone_id].gmt_off_rule_count;
}
}
all_offset_rules = (TZ_OFFSET_RULE *) calloc (all_offset_rule_count, sizeof (TZ_OFFSET_RULE));
if (all_offset_rules == NULL)
{
err_status = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, err_status, 1, all_offset_rule_count * sizeof (TZ_OFFSET_RULE));
goto exit;
}
gmt_off_rule_start = 0;
/* Add the new offset rules, fix the old ones and do a check for backward compatibility */
/* Use the old_tzd_offset_rule_map and the tzd_offset_rule_map arrays to filter out duplicate offset rule intervals.
* For each timezone we check if its offset rule interval was previously found. If it was, we use the mapped start of
* the interval in the new timezone library. If not, we map the old start of the interval to the new one in the new
* timezone library. */
for (i = 0; i < all_timezones_count; i++)
{
prev_gmt_off_rule_start = gmt_off_rule_start;
all_timezones[i].gmt_off_rule_start = gmt_off_rule_start;
find_idx = -1;
country_name = NULL;
zone_id = tzc_find_timezone_names (tzd, all_timezone_names[i]);
if (zone_id == -1)
{
if (old_tzd.timezones[i].country_id != -1)
{
country_name = old_tzd.countries[old_tzd.timezones[i].country_id].full_name;
}
start = old_tzd.timezones[i].gmt_off_rule_start;
cnt = old_tzd.timezones[i].gmt_off_rule_count;
for (j = 0; j < old_tzd_map_count; j++)
{
if (old_tzd_offset_rule_map[j].original_offset_rule_start == start
&& old_tzd_offset_rule_map[j].len == cnt)
{
find_idx = j;
break;
}
}
if (find_idx == -1)
{
old_tzd_offset_rule_map[old_tzd_map_count].original_offset_rule_start = start;
old_tzd_offset_rule_map[old_tzd_map_count].len = cnt;
old_tzd_offset_rule_map[old_tzd_map_count++].final_offset_rule_start = gmt_off_rule_start;
gmt_off_rule_start += cnt;
}
else
{
all_timezones[i].gmt_off_rule_start = old_tzd_offset_rule_map[find_idx].final_offset_rule_start;
}
}
else
{
if (tzd->timezones[zone_id].country_id != -1)
{
country_name = tzd->countries[tzd->timezones[zone_id].country_id].full_name;
}
all_timezones[i].gmt_off_rule_count = timezones[zone_id].gmt_off_rule_count;
start = timezones[zone_id].gmt_off_rule_start;
cnt = timezones[zone_id].gmt_off_rule_count;
for (j = 0; j < tzd_map_count; j++)
{
if (tzd_offset_rule_map[j].original_offset_rule_start == start && tzd_offset_rule_map[j].len == cnt)
{
find_idx = j;
break;
}
}
if (find_idx == -1)
{
tzd_offset_rule_map[tzd_map_count].original_offset_rule_start = start;
tzd_offset_rule_map[tzd_map_count].len = cnt;
tzd_offset_rule_map[tzd_map_count++].final_offset_rule_start = gmt_off_rule_start;
gmt_off_rule_start += cnt;
}
else
{
all_timezones[i].gmt_off_rule_start = tzd_offset_rule_map[find_idx].final_offset_rule_start;
}
if (i < old_tzd.timezone_count)
{
if (old_tzd.timezones[i].gmt_off_rule_count != timezones[zone_id].gmt_off_rule_count)
{
is_compat = false;
}
else
{
start_gmt_old = old_tzd.timezones[i].gmt_off_rule_start;
start_gmt_new = tzd->timezones[zone_id].gmt_off_rule_start;
for (j = start_gmt_old; j < start_gmt_old + old_tzd.timezones[i].gmt_off_rule_count; j++)
{
int tzd_offset_rule_idx = start_gmt_new + j - start_gmt_old;
if (old_tzd.offset_rules[j].ds_type != tzd->offset_rules[tzd_offset_rule_idx].ds_type)
{
is_compat = false;
break;
}
if (old_tzd.offset_rules[j].ds_type != DS_TYPE_FIXED)
{
if (strcmp (old_tzd.ds_rulesets[old_tzd.offset_rules[j].ds_ruleset].ruleset_name,
tzd->ds_rulesets[tzd->offset_rules[tzd_offset_rule_idx].ds_ruleset].ruleset_name)
!= 0)
{
is_compat = false;
break;
}
}
else
{
if (old_tzd.offset_rules[j].ds_ruleset != tzd->offset_rules[tzd_offset_rule_idx].ds_ruleset)
{
is_compat = false;
break;
}
}
if (comp_offset_rules (&old_tzd.offset_rules[j], &tzd->offset_rules[tzd_offset_rule_idx]) ==
false)
{
is_compat = false;
break;
}
}
}
}
}
/* Fix the country ids in the new timezone vector */
if (country_name != NULL)
{
country_id = tzc_find_country_names (all_countries, all_country_count, country_name);
}
else
{
country_id = -1;
}
all_timezones[i].country_id = country_id;
if (find_idx == -1)
{
for (j = prev_gmt_off_rule_start; j < gmt_off_rule_start; j++)
{
int offset_rule_index = start + j - prev_gmt_off_rule_start;
if (zone_id == -1)
{
err_status = copy_offset_rule (&all_offset_rules[j], &old_tzd, offset_rule_index);
if (err_status != NO_ERROR)
{
goto exit;
}
if (old_tzd.offset_rules[offset_rule_index].ds_type != DS_TYPE_FIXED)
{
mark_ruleset[old_tzd.offset_rules[offset_rule_index].ds_ruleset] = 1;
}
}
else
{
err_status = copy_offset_rule (&all_offset_rules[j], tzd, offset_rule_index);
if (err_status != NO_ERROR)
{
goto exit;
}
}
}
}
}
for (i = 0; i < old_tzd.ds_ruleset_count; i++)
{
ruleset_id =
tzc_get_ds_ruleset_by_name (tzd->ds_rulesets, tzd->ds_ruleset_count, old_tzd.ds_rulesets[i].ruleset_name);
if (ruleset_id == -1 && mark_ruleset[i] == 1)
{
all_ds_ruleset_count++;
all_ds_rule_count += old_tzd.ds_rulesets[i].count;
}
}
all_ds_ruleset_count += tzd->ds_ruleset_count;
all_ds_rule_count += tzd->ds_rule_count;
all_ds_rulesets = (TZ_DS_RULESET *) calloc (all_ds_ruleset_count, sizeof (TZ_DS_RULESET));
if (all_ds_rulesets == NULL)
{
err_status = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, err_status, 1, all_ds_ruleset_count * sizeof (TZ_DS_RULESET));
goto exit;
}
all_ds_rules = (TZ_DS_RULE *) calloc (all_ds_rule_count, sizeof (TZ_DS_RULE));
if (all_ds_rules == NULL)
{
err_status = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, err_status, 1, all_ds_rule_count * sizeof (TZ_DS_RULE));
goto exit;
}
/* Do a merge between old timezone library ds_rulesets and current timezone library */
i = 0, j = 0, k = 0;
while (i < old_tzd.ds_ruleset_count && j < tzd->ds_ruleset_count)
{
prev_start_ds_rule = start_ds_rule;
tzd_or_old_tzd = NULL;
comp_res = strcmp (old_tzd.ds_rulesets[i].ruleset_name, tzd->ds_rulesets[j].ruleset_name);
if (comp_res == 0)
{
start_ds_rule += tzd->ds_rulesets[j].count;
err_status = init_ds_ruleset (&all_ds_rulesets[k], tzd, j, prev_start_ds_rule);
if (err_status != NO_ERROR)
{
goto exit;
}
tzd_or_old_tzd = tzd;
start = tzd->ds_rulesets[j].index_start;
if (old_tzd.ds_rulesets[i].count != tzd->ds_rulesets[j].count)
{
is_compat = false;
}
i++;
j++;
}
else if (comp_res < 0)
{
if (mark_ruleset[i] == 1)
{
start_ds_rule += old_tzd.ds_rulesets[i].count;
err_status = init_ds_ruleset (&all_ds_rulesets[k], &old_tzd, i, prev_start_ds_rule);
if (err_status != NO_ERROR)
{
goto exit;
}
tzd_or_old_tzd = &old_tzd;
start = old_tzd.ds_rulesets[i].index_start;
}
i++;
}
/* This is a new ruleset, we will set its index start later */
else
{
err_status = init_ds_ruleset (&all_ds_rulesets[k], tzd, j, -tzd->ds_rulesets[j].index_start);
if (err_status != NO_ERROR)
{
goto exit;
}
j++;
}
if (comp_res >= 0 || (comp_res < 0 && mark_ruleset[i - 1] == 1))
{
k++;
}
/* Now copy the daylight saving rules also and do backward compatibility checking */
if (tzd_or_old_tzd != NULL)
{
if (comp_res == 0 && old_tzd.ds_rulesets[i - 1].count == tzd->ds_rulesets[j - 1].count)
{
start_ds_ruleset_old = old_tzd.ds_rulesets[i - 1].index_start;
start_ds_ruleset_new = tzd->ds_rulesets[j - 1].index_start;
}
for (l = prev_start_ds_rule; l < start_ds_rule; l++)
{
err_status = copy_ds_rule (&all_ds_rules[l], tzd_or_old_tzd, start + l - prev_start_ds_rule);
if (err_status != NO_ERROR)
{
goto exit;
}
/* Do backward compatibility checking */
if (comp_res == 0 && old_tzd.ds_rulesets[i - 1].count == tzd->ds_rulesets[j - 1].count)
{
old_ds_rule = &old_tzd.ds_rules[start_ds_ruleset_old + l - prev_start_ds_rule];
new_ds_rule = &tzd->ds_rules[start_ds_ruleset_new + l - prev_start_ds_rule];
if (comp_ds_rules (old_ds_rule, new_ds_rule) == false)
{
is_compat = false;
}
}
}
}
}
/* Now copy the remaining rules */
while (i < old_tzd.ds_ruleset_count)
{
prev_start_ds_rule = start_ds_rule;
if (mark_ruleset[i] == 1)
{
start_ds_rule += old_tzd.ds_rulesets[i].count;
err_status = init_ds_ruleset (&all_ds_rulesets[k], &old_tzd, i, prev_start_ds_rule);
if (err_status != NO_ERROR)
{
goto exit;
}
start = old_tzd.ds_rulesets[i].index_start;
k++;
}
for (l = prev_start_ds_rule; l < start_ds_rule; l++)
{
err_status = copy_ds_rule (&all_ds_rules[l], &old_tzd, start + l - prev_start_ds_rule);
if (err_status != NO_ERROR)
{
goto exit;
}
}
i++;
}
while (j < tzd->ds_ruleset_count)
{
err_status = init_ds_ruleset (&all_ds_rulesets[k], tzd, j, -tzd->ds_rulesets[j].index_start);
if (err_status != NO_ERROR)
{
goto exit;
}
j++;
k++;
}
/* Now copy the new daylight saving rules that were added in the current timezone library */
assert (k == all_ds_ruleset_count);
for (i = 0; i < all_ds_ruleset_count; i++)
{
if (all_ds_rulesets[i].index_start < 0)
{
prev_start_ds_rule = start_ds_rule;
start = -all_ds_rulesets[i].index_start;
all_ds_rulesets[i].index_start = prev_start_ds_rule;
start_ds_rule += all_ds_rulesets[i].count;
/* Now copy the new daylight saving rules to the end */
for (j = prev_start_ds_rule; j < start_ds_rule; j++)
{
err_status = copy_ds_rule (&all_ds_rules[j], tzd, start + j - prev_start_ds_rule);
if (err_status != NO_ERROR)
{
goto exit;
}
}
}
}
/* Now we need to fix the ds_ruleset index in the offset rules */
for (i = 0; i < all_timezones_count; i++)
{
zone_id = tzc_find_timezone_names (tzd, all_timezone_names[i]);
if (zone_id == -1)
{
start = old_tzd.timezones[i].gmt_off_rule_start;
tzd_or_old_tzd = &old_tzd;
}
else
{
start = timezones[zone_id].gmt_off_rule_start;
tzd_or_old_tzd = tzd;
}
for (j = 0; j < all_timezones[i].gmt_off_rule_count; j++)
{
int tzd_or_old_tzd_ds_ruleset = tzd_or_old_tzd->offset_rules[start + j].ds_ruleset;
if (tzd_or_old_tzd->offset_rules[start + j].ds_type != DS_TYPE_FIXED)
{
ruleset_name = tzd_or_old_tzd->ds_rulesets[tzd_or_old_tzd_ds_ruleset].ruleset_name;
}
else
{
ruleset_name = NULL;
}
if (ruleset_name != NULL)
{
ruleset_id = tzc_get_ds_ruleset_by_name (all_ds_rulesets, all_ds_ruleset_count, ruleset_name);
all_offset_rules[all_timezones[i].gmt_off_rule_start + j].ds_ruleset = ruleset_id;
}
}
}
qsort (all_names, all_timezones_and_aliases_count, sizeof (TZ_NAME), comp_func_tz_names);
exit:
/* Free all data structures except for the windows zone map data structure and the leap seconds array */
tzc_free_tz_data (tzd, false);
if (mark_ruleset != NULL)
{
free_and_init (mark_ruleset);
}
if (old_tzd_offset_rule_map != NULL)
{
free_and_init (old_tzd_offset_rule_map);
}
if (tzd_offset_rule_map != NULL)
{
free_and_init (tzd_offset_rule_map);
}
/* Now use the new data structures */
tzd->countries = all_countries;
tzd->country_count = all_country_count;
tzd->timezone_names = all_timezone_names;
tzd->timezones = all_timezones;
tzd->timezone_count = all_timezones_count;
tzd->names = all_names;
tzd->name_count = all_timezones_and_aliases_count;
tzd->offset_rules = all_offset_rules;
tzd->offset_rule_count = all_offset_rule_count;
tzd->ds_rulesets = all_ds_rulesets;
tzd->ds_ruleset_count = all_ds_ruleset_count;
tzd->ds_rules = all_ds_rules;
tzd->ds_rule_count = all_ds_rule_count;
if (err_status == NO_ERROR)
{
old_names = old_tzd.names;
old_timezones = old_tzd.timezones;
names = tzd->names;
timezones = tzd->timezones;
for (i = 0; i < ZONE_MAX; i++)
{
tz_Is_backward_compatible_timezone[i] = true;
}
for (i = 0; i < old_tzd.name_count; i++)
{
int tzd_zone_id;
int old_start, old_count;
int new_start, new_count;
tzd_zone_id = tzc_find_timezone_names (tzd, old_names[i].name);
/* This should not ever happen */
assert (tzd_zone_id != -1);
if (old_timezones[old_names[i].zone_id].gmt_off_rule_count != timezones[tzd_zone_id].gmt_off_rule_count)
{
tz_Is_backward_compatible_timezone[i] = false;
continue;
}
old_start = old_timezones[old_names[i].zone_id].gmt_off_rule_start;
new_start = timezones[tzd_zone_id].gmt_off_rule_start;
old_count = old_timezones[old_names[i].zone_id].gmt_off_rule_count;
new_count = timezones[tzd_zone_id].gmt_off_rule_count;
for (j = old_start, k = new_start; j < old_start + old_count && k < new_start + new_count; j++, k++)
{
TZ_OFFSET_RULE rule1, rule2;
rule1 = old_tzd.offset_rules[j];
rule2 = tzd->offset_rules[k];
if (rule1.ds_type != rule2.ds_type)
{
tz_Is_backward_compatible_timezone[i] = false;
break;
}
if (comp_offset_rules (&rule1, &rule2) == false)
{
tz_Is_backward_compatible_timezone[i] = false;
break;
}
if (rule1.ds_type != DS_TYPE_FIXED)
{
int ds_rule1_count = old_tzd.ds_rulesets[rule1.ds_ruleset].count;
int ds_rule2_count = tzd->ds_rulesets[rule2.ds_ruleset].count;
int ds_rule1_start = old_tzd.ds_rulesets[rule1.ds_ruleset].index_start;
int ds_rule2_start = tzd->ds_rulesets[rule2.ds_ruleset].index_start;
int rule1_index = ds_rule1_start;
int rule2_index = ds_rule2_start;
if (strcmp (old_tzd.ds_rulesets[rule1.ds_ruleset].ruleset_name,
tzd->ds_rulesets[rule2.ds_ruleset].ruleset_name) != 0)
{
tz_Is_backward_compatible_timezone[i] = false;
break;
}
if (ds_rule1_count != ds_rule2_count)
{
tz_Is_backward_compatible_timezone[i] = false;
break;
}
while ((rule1_index < ds_rule1_start + ds_rule1_count)
&& (rule2_index < ds_rule2_start + ds_rule2_count))
{
TZ_DS_RULE ds_rule1 = old_tzd.ds_rules[rule1_index];
TZ_DS_RULE ds_rule2 = tzd->ds_rules[rule2_index];
if (comp_ds_rules (&ds_rule1, &ds_rule2) == false)
{
break;
}
rule1_index++, rule2_index++;
}
if (rule1_index < ds_rule1_start + ds_rule1_count)
{
tz_Is_backward_compatible_timezone[i] = false;
break;
}
}
else if (rule1.ds_ruleset != rule2.ds_ruleset)
{
tz_Is_backward_compatible_timezone[i] = false;
break;
}
}
}
if (is_compat == false)
{
printf ("Updating data in the tables containing timezone data types...\n");
err_status = ER_TZ_COMPILE_ERROR;
}
}
return err_status;
}
#endif
/*
* tzc_compute_timezone_checksum() - Computes an MD5 for the timezone data
* structures
* Returns:
* tzd (in/out): timezone library
* type(in): tells which make_tz mode was used
*/
static int
tzc_compute_timezone_checksum (TZ_DATA * tzd, TZ_GEN_TYPE type)
{
char *input_buf;
int size = 0;
int i;
int error = NO_ERROR;
char *buf;
for (i = 0; i < tzd->country_count; i++)
{
size += sizeof (tzd->countries[i].code);
size += sizeof (tzd->countries[i].full_name);
}
for (i = 0; i < tzd->timezone_count; i++)
{
size += sizeof (tzd->timezones[i].country_id);
size += sizeof (tzd->timezones[i].gmt_off_rule_count);
size += sizeof (tzd->timezones[i].gmt_off_rule_start);
size += sizeof (tzd->timezones[i].zone_id);
}
for (i = 0; i < tzd->timezone_count; i++)
{
size += strlen (tzd->timezone_names[i]);
}
for (i = 0; i < tzd->offset_rule_count; i++)
{
size += sizeof (tzd->offset_rules[i].gmt_off);
size += sizeof (tzd->offset_rules[i].ds_ruleset);
size += sizeof (tzd->offset_rules[i].until_year);
size += sizeof (tzd->offset_rules[i].until_mon);
size += sizeof (tzd->offset_rules[i].until_day);
size += sizeof (tzd->offset_rules[i].until_hour);
size += sizeof (tzd->offset_rules[i].until_min);
size += sizeof (tzd->offset_rules[i].until_sec);
size += sizeof (tzd->offset_rules[i].until_time_type);
size += sizeof (tzd->offset_rules[i].until_flag);
size += sizeof (tzd->offset_rules[i].ds_type);
size += sizeof (tzd->offset_rules[i].julian_date);
if (tzd->offset_rules[i].std_format != NULL)
{
size += strlen (tzd->offset_rules[i].std_format);
}
if (tzd->offset_rules[i].save_format != NULL)
{
size += strlen (tzd->offset_rules[i].save_format);
}
if (tzd->offset_rules[i].var_format != NULL)
{
size += strlen (tzd->offset_rules[i].var_format);
}
}
for (i = 0; i < tzd->name_count; i++)
{
size += sizeof (tzd->names[i].is_alias);
size += sizeof (tzd->names[i].zone_id);
size += strlen (tzd->names[i].name);
}
for (i = 0; i < tzd->ds_ruleset_count; i++)
{
size += sizeof (tzd->ds_rulesets[i].count);
size += sizeof (tzd->ds_rulesets[i].index_start);
size += strlen (tzd->ds_rulesets[i].ruleset_name);
}
for (i = 0; i < tzd->ds_rule_count; i++)
{
size += sizeof (tzd->ds_rules[i].from_year);
size += sizeof (tzd->ds_rules[i].to_year);
size += sizeof (tzd->ds_rules[i].in_month);
size += sizeof (tzd->ds_rules[i].change_on);
size += sizeof (tzd->ds_rules[i].at_time);
size += sizeof (tzd->ds_rules[i].at_time_type);
size += sizeof (tzd->ds_rules[i].save_time);
if (tzd->ds_rules[i].letter_abbrev != NULL)
{
size += strlen (tzd->ds_rules[i].letter_abbrev);
}
}
for (i = 0; i < tzd->ds_leap_sec_count; i++)
{
size += sizeof (tzd->ds_leap_sec[i].corr_negative);
size += sizeof (tzd->ds_leap_sec[i].day);
size += sizeof (tzd->ds_leap_sec[i].is_rolling);
size += sizeof (tzd->ds_leap_sec[i].month);
size += sizeof (tzd->ds_leap_sec[i].year);
}
input_buf = (char *) calloc (size, sizeof (char));
if (input_buf == NULL)
{
error = ER_OUT_OF_VIRTUAL_MEMORY;
er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
return error;
}
buf = input_buf;
for (i = 0; i < tzd->country_count; i++)
{
memcpy (buf, tzd->countries[i].code, sizeof (tzd->countries[i].code));
buf += sizeof (tzd->countries[i].code);
memcpy (buf, tzd->countries[i].full_name, sizeof (tzd->countries[i].full_name));
buf += sizeof (tzd->countries[i].full_name);
}
for (i = 0; i < tzd->timezone_count; i++)
{
BUF_PUT_INT32 (buf, tzd->timezones[i].country_id);
BUF_PUT_INT32 (buf, tzd->timezones[i].gmt_off_rule_count);
BUF_PUT_INT32 (buf, tzd->timezones[i].gmt_off_rule_start);
BUF_PUT_INT32 (buf, tzd->timezones[i].zone_id);
}
for (i = 0; i < tzd->timezone_count; i++)
{
memcpy (buf, tzd->timezone_names[i], strlen (tzd->timezone_names[i]));
buf += strlen (tzd->timezone_names[i]);
}
for (i = 0; i < tzd->offset_rule_count; i++)
{
BUF_PUT_INT32 (buf, tzd->offset_rules[i].gmt_off);
BUF_PUT_INT32 (buf, tzd->offset_rules[i].ds_ruleset);
BUF_PUT_INT16 (buf, tzd->offset_rules[i].until_year);
memcpy (buf, &tzd->offset_rules[i].until_mon, sizeof (tzd->offset_rules[i].until_mon));
buf += sizeof (tzd->offset_rules[i].until_mon);
memcpy (buf, &tzd->offset_rules[i].until_day, sizeof (tzd->offset_rules[i].until_day));
buf += sizeof (tzd->offset_rules[i].until_day);
memcpy (buf, &tzd->offset_rules[i].until_hour, sizeof (tzd->offset_rules[i].until_hour));
buf += sizeof (tzd->offset_rules[i].until_hour);
memcpy (buf, &tzd->offset_rules[i].until_min, sizeof (tzd->offset_rules[i].until_min));
buf += sizeof (tzd->offset_rules[i].until_min);
memcpy (buf, &tzd->offset_rules[i].until_sec, sizeof (tzd->offset_rules[i].until_sec));
buf += sizeof (tzd->offset_rules[i].until_sec);
BUF_PUT_INT32 (buf, tzd->offset_rules[i].until_time_type);
BUF_PUT_INT32 (buf, tzd->offset_rules[i].until_flag);
BUF_PUT_INT32 (buf, tzd->offset_rules[i].ds_type);
BUF_PUT_INT32 (buf, tzd->offset_rules[i].julian_date);
if (tzd->offset_rules[i].std_format != NULL)
{
memcpy (buf, tzd->offset_rules[i].std_format, strlen (tzd->offset_rules[i].std_format));
buf += strlen (tzd->offset_rules[i].std_format);
}
if (tzd->offset_rules[i].save_format != NULL)
{
memcpy (buf, tzd->offset_rules[i].save_format, strlen (tzd->offset_rules[i].save_format));
buf += strlen (tzd->offset_rules[i].save_format);
}
if (tzd->offset_rules[i].var_format != NULL)
{
memcpy (buf, tzd->offset_rules[i].var_format, strlen (tzd->offset_rules[i].var_format));
buf += strlen (tzd->offset_rules[i].var_format);
}
}
for (i = 0; i < tzd->name_count; i++)
{
memcpy (buf, &tzd->names[i].is_alias, sizeof (tzd->names[i].is_alias));
buf += sizeof (tzd->names[i].is_alias);
BUF_PUT_INT32 (buf, tzd->names[i].zone_id);
memcpy (buf, tzd->names[i].name, strlen (tzd->names[i].name));
buf += strlen (tzd->names[i].name);
}
for (i = 0; i < tzd->ds_ruleset_count; i++)
{
BUF_PUT_INT32 (buf, tzd->ds_rulesets[i].count);
BUF_PUT_INT32 (buf, tzd->ds_rulesets[i].index_start);
memcpy (buf, tzd->ds_rulesets[i].ruleset_name, strlen (tzd->ds_rulesets[i].ruleset_name));
buf += strlen (tzd->ds_rulesets[i].ruleset_name);
}
for (i = 0; i < tzd->ds_rule_count; i++)
{
BUF_PUT_INT16 (buf, tzd->ds_rules[i].from_year);
BUF_PUT_INT16 (buf, tzd->ds_rules[i].to_year);
memcpy (buf, &tzd->ds_rules[i].in_month, sizeof (tzd->ds_rules[i].in_month));
buf += sizeof (tzd->ds_rules[i].in_month);
BUF_PUT_INT32 (buf, tzd->ds_rules[i].change_on.type);
memcpy (buf, &tzd->ds_rules[i].change_on.day_of_month, sizeof (tzd->ds_rules[i].change_on.day_of_month));
buf += sizeof (tzd->ds_rules[i].change_on.day_of_month);
memcpy (buf, &tzd->ds_rules[i].change_on.day_of_week, sizeof (tzd->ds_rules[i].change_on.day_of_week));
buf += sizeof (tzd->ds_rules[i].change_on.day_of_week);
BUF_PUT_INT32 (buf, tzd->ds_rules[i].at_time);
BUF_PUT_INT32 (buf, tzd->ds_rules[i].at_time_type);
BUF_PUT_INT32 (buf, tzd->ds_rules[i].save_time);
if (tzd->ds_rules[i].letter_abbrev != NULL)
{
memcpy (buf, tzd->ds_rules[i].letter_abbrev, strlen (tzd->ds_rules[i].letter_abbrev));
buf += strlen (tzd->ds_rules[i].letter_abbrev);
}
}
for (i = 0; i < tzd->ds_leap_sec_count; i++)
{
memcpy (buf, &tzd->ds_leap_sec[i].corr_negative, sizeof (tzd->ds_leap_sec[i].corr_negative));
buf += sizeof (tzd->ds_leap_sec[i].corr_negative);
memcpy (buf, &tzd->ds_leap_sec[i].day, sizeof (tzd->ds_leap_sec[i].day));
buf += sizeof (tzd->ds_leap_sec[i].day);
memcpy (buf, &tzd->ds_leap_sec[i].is_rolling, sizeof (tzd->ds_leap_sec[i].is_rolling));
buf += sizeof (tzd->ds_leap_sec[i].is_rolling);
memcpy (buf, &tzd->ds_leap_sec[i].month, sizeof (tzd->ds_leap_sec[i].month));
BUF_PUT_INT16 (buf, tzd->ds_leap_sec[i].year);
}
memset (tzd->checksum, 0, sizeof (tzd->checksum));
error = crypt_md5_buffer_hex (input_buf, size, tzd->checksum);
free (input_buf);
return error;
}
#if defined (SA_MODE)
/*
* execute_query() - Execute the query given by str
*
* Return: error or no error
* str (in): query string
* result (out): query result
*
*/
static int
execute_query (const char *str, DB_QUERY_RESULT ** result)
{
DB_SESSION *session = NULL;
STATEMENT_ID stmt_id;
int error = NO_ERROR;
session = db_open_buffer (str);
if (session == NULL)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto exit;
}
stmt_id = db_compile_statement (session);
if (stmt_id < 0)
{
assert (er_errid () != NO_ERROR);
error = er_errid ();
goto exit;
}
error = db_execute_statement_local (session, stmt_id, result);
exit:
if (session != NULL)
{
db_close_session (session);
}
return error;
}
/*
* tzc_update() - Do a data migration in case that tzc_extend fails
*
* Returns: error or no error
* tzd (in): Timezone library used to the data migration
* database_name(in): Database name for which to do data migration or NULL if data migration should be done
* for all the databases
*/
static int
tzc_update (TZ_DATA * tzd, const char *database_name)
{
DB_INFO *db_info_list = NULL;
DB_INFO *db_info = NULL;
int error = NO_ERROR;
tz_set_new_timezone_data (tzd);
AU_DISABLE_PASSWORDS ();
db_set_client_type (DB_CLIENT_TYPE_ADMIN_UTILITY);
db_login ("DBA", NULL);
tz_Compare_datetimetz_tz_id = true;
tz_Compare_timestamptz_tz_id = true;
/* Read the directory with the databases names */
error = cfg_read_directory (&db_info_list, false);
if (error != NO_ERROR)
{
goto exit;
}
/* Iterate through all the databases */
for (db_info = db_info_list; db_info != NULL; db_info = db_info->next)
{
if (database_name != NULL && strcmp (database_name, db_info->name) != 0)
{
continue;
}
error = tzc_update_internal (database_name);
if (error != NO_ERROR)
{
goto exit;
}
}
exit:
cfg_free_directory (db_info_list);
tz_Compare_datetimetz_tz_id = false;
tz_Compare_timestamptz_tz_id = false;
return error;
}
/*
* tzc_update_internal() - Do a data migration in case that tzc_extend fails
*
* Returns: error or no error
* database_name(in): Database name for which to do data migration
*/
static int
tzc_update_internal (const char *database_name)
{
const char *program_name = "extend";
DB_OBJLIST *table_list = NULL, *table = NULL;
DB_QUERY_RESULT *result = NULL;
QUERY_BUF update_query;
QUERY_BUF update_set;
QUERY_BUF update_where;
bool is_first_column = true;
bool has_timezone_column = false;
int error = NO_ERROR;
assert (database_name != NULL);
printf ("Opening database %s\n", database_name);
/* Open the database */
error = db_restart (program_name, TRUE, database_name);
if (error != NO_ERROR)
{
printf ("Error while opening database %s\n", database_name);
goto exit_on_error;
}
printf ("Updating database %s...\n", database_name);
table_list = db_get_all_classes ();
if (table_list == NULL)
{
error = db_error_code ();
if (error != NO_ERROR)
{
printf ("Error while listing tables for database %s\n", database_name);
goto free_resource_1;
}
}
/* First get the names for the tables in the database */
for (table = table_list; table != NULL; table = table->next)
{
const char *table_name = NULL;
DB_ATTRIBUTE *column_list = NULL, *column = NULL;
if (db_is_system_class (table->op) == TRUE)
{
continue;
}
table_name = db_get_class_name (table->op); /* owner_name.table_name */
printf ("Updating table %s...\n", table_name);
column_list = db_get_attributes (table->op);
if (column_list == NULL)
{
error = db_error_code ();
if (error != NO_ERROR)
{
printf ("Error while listing columns for table %s\n", table_name);
goto free_resource_2;
}
}
printf ("We will update the following columns:\n");
is_first_column = true;
has_timezone_column = false;
memset (&update_query, 0, sizeof (QUERY_BUF));
memset (&update_set, 0, sizeof (QUERY_BUF));
memset (&update_where, 0, sizeof (QUERY_BUF));
for (column = column_list; column != NULL; column = db_attribute_next (column))
{
const char *column_name = db_attribute_name (column);
DB_DOMAIN *column_domain = db_attribute_domain (column);
DB_TYPE column_type = db_domain_type (column_domain);
/* Now do the update if the datatype is of timezone type */
switch (column_type)
{
case DB_TYPE_DATETIMETZ:
case DB_TYPE_DATETIMELTZ:
case DB_TYPE_TIMESTAMPTZ:
case DB_TYPE_TIMESTAMPLTZ:
has_timezone_column = true;
/* We are going to make an update query for each table which includes a timezone column like:
* UPDATE [t] SET [tzc1] = CONV_TZ([tzc1]), [tzc2] = CONV_TZ([tzc2]) ...
* WHERE [tzc1] != CONV_TZ([tzc1]) OR [tzc2] != CONV_TZ([tzc2]) ... ;
*/
if (is_first_column == true)
{
if (tz_write_query_string
(&update_set, "UPDATE [%s] SET [%s] = conv_tz ([%s])", table_name, column_name,
column_name) == NULL)
{
break;
}
if (tz_write_query_string (&update_where, " WHERE [%s] != conv_tz ([%s])", column_name, column_name)
== NULL)
{
break;
}
is_first_column = false;
}
else
{
if (tz_write_query_string (&update_set, ", [%s] = conv_tz ([%s])", column_name, column_name) == NULL)
{
break;
}
if (tz_write_query_string (&update_where, " OR [%s] != conv_tz ([%s])", column_name, column_name) ==
NULL)
{
break;
}
}
printf ("%s ", column_name);
continue;
default:
continue;
}
/* If no error occurred, "continue;" should have been executed before coming here. */
if (column != NULL)
{
printf ("Failed to create a query to update the timezone of column %s in table %s.\n", column_name,
table_name);
goto free_resource_3;
}
}
printf ("\n");
/* If we have at least a column that is of timezone data type then execute the query */
if (has_timezone_column == true)
{
update_query.buf = (char *) malloc ((update_set.len + update_where.len) * sizeof (char));
if (update_query.buf == NULL)
{
printf ("Failed to allocate memory for query buffer.\n");
goto free_resource_3;
}
strcpy (update_query.buf, update_set.buf);
strcpy (update_query.buf + update_set.len, update_where.buf);
free_and_init (update_set.buf);
free_and_init (update_where.buf);
error = execute_query (update_query.buf, &result);
if (error < 0)
{
printf ("Error while updating table %s\n", table_name);
db_abort_transaction ();
goto free_resource_4;
}
db_query_end (result);
free_and_init (update_query.buf);
}
printf ("Finished updating table %s\n", table_name);
}
db_objlist_free (table_list);
table_list = NULL;
db_commit_transaction ();
printf ("Finished updating database %s\n", database_name);
printf ("Shutting down database %s...\n", database_name);
db_shutdown ();
return NO_ERROR;
free_resource_4:
free_and_init (update_query.buf);
free_resource_3:
free_and_init (update_set.buf);
free_and_init (update_where.buf);
free_resource_2:
db_objlist_free (table_list);
table_list = NULL;
free_resource_1:
db_shutdown ();
exit_on_error:
return error;
}
/*
* tz_write_query_string() - Write a dynamic query.
*
* Returns: A pointer to the QUERY_BUF passed when calling.
* NULL If an error occurred.
* query(in): a pointer to QUERY_BUF.
* format(in): a format for writing a dynamic query.
* args(in): a va_list of arguments.
*
* Note: It is assumed that the length of the first query does not exceed 4096.
* This is because the length, including one table name and two column
* names, does not exceed 1024. In the case of realloc, there is
* an assumption of the same.If we want to use it anywhere other than
* the tzc_update_internal() function, we need to change it.
*/
static QUERY_BUF *
tz_write_query_string (QUERY_BUF * query, const char *format, ...)
{
#define QUERY_BUF_INIT_SIZE 4096
#define QUERY_BUF_UNIT_SIZE 1024
char *backup = NULL;
int len;
va_list args;
if (query->buf == NULL)
{
query->buf = (char *) malloc (QUERY_BUF_INIT_SIZE * sizeof (char));
if (query->buf == NULL)
{
printf ("\nFailed to allocate memory for query buffer.\n");
return NULL;
}
query->size = QUERY_BUF_INIT_SIZE;
va_start (args, format);
len = vsprintf (query->buf, format, args);
va_end (args);
query->last = query->buf + len;
query->len = len;
}
else
{
va_start (args, format);
len = vsnprintf (NULL, 0, format, args);
va_end (args);
if ((query->len + len) > query->size)
{
backup = query->buf;
query->buf = (char *) realloc (query->buf, (query->size + QUERY_BUF_UNIT_SIZE) * sizeof (char));
if (query->buf == NULL)
{
printf ("\nFailed to allocate memory for query buffer.\n");
query->buf = backup;
return NULL;
}
query->size += QUERY_BUF_UNIT_SIZE;
query->last = query->buf + query->len;
}
va_start (args, format);
len = vsprintf (query->last, format, args);
va_end (args);
query->last += len;
query->len += len;
}
return query;
#undef QUERY_BUF_INIT_SIZE
#undef QUERY_BUF_UNIT_SIZE
}
#endif