Merge branch 'feature/timezone' into 'master'

esp_rmaker_time_sync: Add support for POSIX timezones

See merge request app-frameworks/esp-rainmaker!155
This commit is contained in:
Piyush Shah
2020-09-04 22:51:44 +08:00
11 changed files with 919 additions and 9 deletions

View File

@@ -6,6 +6,7 @@ set(core_srcs "src/core/esp_rmaker_core.c"
"src/core/esp_rmaker_node_config.c"
"src/core/esp_rmaker_client_data.c"
"src/core/esp_rmaker_time_sync.c"
"src/core/esp_rmaker_timezone.c"
"src/core/esp_rmaker_storage.c"
"src/core/esp_rmaker_user_mapping.pb-c.c"
"src/core/esp_rmaker_utils.c"

View File

@@ -85,6 +85,14 @@ menu "ESP RainMaker Config"
default 1 if ESP_RMAKER_MQTT_PORT_443
default 2 if ESP_RMAKER_MQTT_PORT_8883
config ESP_RMAKER_DEF_TIMEZONE
string "Default Timezone"
default ""
help
Default Timezone to use. Eg. "Asia/Shanghai", "America/Los_Angeles".
Check documentation for complete list of valid values. This value
will be used only if no timezone is set using the C APIs.
config ESP_RMAKER_SNTP_SERVER_NAME
string "ESP RainMaker SNTP Server Name"
default "pool.ntp.org"

View File

@@ -41,6 +41,8 @@ extern "C"
#define ESP_RMAKER_DEF_OTA_STATUS_NAME "status"
#define ESP_RMAKER_DEF_OTA_INFO_NAME "info"
#define ESP_RMAKER_DEF_OTA_URL_NAME "url"
#define ESP_RMAKER_DEF_TIMEZONE_NAME "tz"
#define ESP_RMAKER_DEF_TIMEZONE_POSIX_NAME "tz_posix"
/**
* Create standard name param
@@ -216,6 +218,31 @@ esp_rmaker_param_t *esp_rmaker_ota_info_param_create(const char *param_name);
*/
esp_rmaker_param_t *esp_rmaker_ota_url_param_create(const char *param_name);
/**
* Create standard Timezone param
*
* This will create the standard timezone parameter.
*
* @param[in] param_name Name of the parameter
* @param[in] val Default Value of the parameter (Eg. "Asia/Shanghai"). Can be kept NULL.
*
* @return Parameter handle on success.
* @return NULL in case of failures.
*/
esp_rmaker_param_t *esp_rmaker_timezone_param_create(const char *param_name, const char *val);
/**
* Create standard POSIX Timezone param
*
* This will create the standard posix timezone parameter.
*
* @param[in] param_name Name of the parameter
* @param[in] val Default Value of the parameter (Eg. "CST-8"). Can be kept NULL.
*
* @return Parameter handle on success.
* @return NULL in case of failures.
*/
esp_rmaker_param_t *esp_rmaker_timezone_posix_param_create(const char *param_name, const char *val);
#ifdef __cplusplus
}
#endif

View File

@@ -37,6 +37,23 @@ extern "C"
*/
esp_rmaker_device_t *esp_rmaker_ota_service_create(const char *serv_name, void *priv_data);
/** Create a standard OTA service
*
* This creates an OTA service with the mandatory parameters. The default parameter names will be used.
* Refer \ref esp_rmaker_standard_params.h for default names.
*
* @param[in] serv_name The unique service name
* @param[in] timezone Default value of timezone string (Eg. "Asia/Shanghai"). Can be kept NULL.
* @param[in] timezone_posix Default value of posix timezone string (Eg. "CST-8"). Can be kept NULL.
* @param[in] priv_data (Optional) Private data associated with the service. This should stay
* allocated throughout the lifetime of the service.
*
* @return service_handle on success.
* @return NULL in case of any error.
*/
esp_rmaker_device_t *esp_rmaker_time_service_create(const char *serv_name, const char *timezone,
const char *timezone_posix, void *priv_data);
#ifdef __cplusplus
}
#endif

View File

@@ -41,7 +41,8 @@ extern "C"
#define ESP_RMAKER_PARAM_OTA_STATUS "esp.param.ota_status"
#define ESP_RMAKER_PARAM_OTA_INFO "esp.param.ota_info"
#define ESP_RMAKER_PARAM_OTA_URL "esp.param.ota_url"
#define ESP_RMAKER_PARAM_TIMEZONE "esp.param.tz"
#define ESP_RMAKER_PARAM_TIMEZONE_POSIX "esp.param.tz_posix"
/********** STANDARD DEVICE TYPES **********/
@@ -52,6 +53,7 @@ extern "C"
/********** STANDARD SERVICE TYPES **********/
#define ESP_RMAKER_SERVICE_OTA "esp.service.ota"
#define ESP_RMAKER_SERVICE_TIME "esp.service.time"
#ifdef __cplusplus
}

View File

@@ -13,6 +13,7 @@
// limitations under the License.
#pragma once
#include <stdint.h>
#include <sntp.h>
#include <esp_err.h>
#ifdef __cplusplus
@@ -23,6 +24,12 @@ extern "C"
typedef struct esp_rmaker_time_config {
/** If not specified, then 'CONFIG_ESP_RMAKER_SNTP_SERVER_NAME' is used as the SNTP server. */
char *sntp_server_name;
/** Optional callback to invoke, whenever time is synchronised. This will be called
* periodically as per the SNTP polling interval (which is 60min by default).
* If kept NULL, the default callback will be invoked, which will just print the
* current local time.
*/
sntp_sync_time_cb_t sync_time_cb;
} esp_rmaker_time_config_t;
/** Reboot the chip after a delay
@@ -104,6 +111,64 @@ bool esp_rmaker_time_check(void);
*/
esp_err_t esp_rmaker_time_wait_for_sync(uint32_t ticks_to_wait);
/** Set POSIX timezone
*
* Set the timezone (TZ environment variable) as per the POSIX format
* specified in the [GNU libc documentation](https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html).
* Eg. For China: "CST-8"
* For US Pacific Time (including daylight saving information): "PST8PDT,M3.2.0,M11.1.0"
*
* @param[in] tz_posix NULL terminated TZ POSIX string
*
* @return ESP_OK on success
* @return error on failure
*/
esp_err_t esp_rmaker_time_set_timezone_posix(const char *tz_posix);
/** Set timezone location string
*
* Set the timezone as a user friendly location string.
* Check [here](https://rainmaker.espressif.com/docs/time-service.html) for a list of valid values.
*
* Eg. For China: "Asia/Shanghai"
* For US Pacific Time: "America/Los_Angeles"
*
* @note Setting timezone using this API internally also sets the POSIX timezone string.
*
* @param[in] tz NULL terminated Timezone location string
*
* @return ESP_OK on success
* @return error on failure
*/
esp_err_t esp_rmaker_time_set_timezone(const char *tz);
/** Enable Timezone Service
*
* This enables the ESP RainMaker standard timezone service which can be used to set
* timezone, either in POSIX or location string format. Please refer the specifications
* for additional details.
*
* @return ESP_OK on success
* @return error on failure
*/
esp_err_t esp_rmaker_timezone_service_enable(void);
/** Get printable local time string
*
* Get a printable local time string, with information of timezone and Daylight Saving.
* Eg. "Tue Sep 1 09:04:38 2020 -0400[EDT], DST: Yes"
* "Tue Sep 1 21:04:04 2020 +0800[CST], DST: No"
*
*
* @param[out] buf Pointer to a pre-allocated buffer into which the time string will
* be populated.
* @param[in] buf_len Length of the above buffer.
*
* @return ESP_OK on success
* @return error on failure
*/
esp_err_t esp_rmaker_get_local_time_str(char *buf, size_t buf_len);
#ifdef __cplusplus
}
#endif

View File

@@ -33,6 +33,7 @@
#include <esp_rmaker_core.h>
#include <esp_rmaker_user_mapping.h>
#include <esp_rmaker_utils.h>
#include <esp_rmaker_console_internal.h>
@@ -352,6 +353,55 @@ static void register_wifi_prov()
esp_console_cmd_register(&cmd);
}
static int local_time_cli_handler(int argc, char *argv[])
{
char local_time[64];
if (esp_rmaker_get_local_time_str(local_time, sizeof(local_time)) == ESP_OK) {
printf("%s: Current local time: %s\n", TAG, local_time);
} else {
printf("%s: Current local time (truncated): %s\n", TAG, local_time);
}
return ESP_OK;
}
static int tz_set_cli_handler(int argc, char *argv[])
{
if (argc < 2) {
printf("%s: Invalid Usage.\n", TAG);
return ESP_ERR_INVALID_ARG;
}
if (strcmp(argv[1], "posix") == 0) {
if (argv[2]) {
esp_rmaker_time_set_timezone_posix(argv[2]);
} else {
printf("%s: Invalid Usage.\n", TAG);
return ESP_ERR_INVALID_ARG;
}
} else {
esp_rmaker_time_set_timezone(argv[1]);
}
return ESP_OK;
}
static void register_time_commands()
{
const esp_console_cmd_t local_time_cmd = {
.command = "local-time",
.help = "Get the local time of device.",
.func = &local_time_cli_handler,
};
ESP_LOGI(TAG, "Registering command: %s", local_time_cmd.command);
esp_console_cmd_register(&local_time_cmd);
const esp_console_cmd_t tz_set_cmd = {
.command = "tz-set",
.help = "Set Timezone. Usage: tz-set [posix] <tz_string>.",
.func = &tz_set_cli_handler,
};
ESP_LOGI(TAG, "Registering command: %s", tz_set_cmd.command);
esp_console_cmd_register(&tz_set_cmd);
}
void register_commands()
{
register_generic_debug_commands();
@@ -359,4 +409,5 @@ void register_commands()
register_user_node_mapping();
register_get_node_id();
register_wifi_prov();
register_time_commands();
}

View File

@@ -11,14 +11,154 @@
// 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.
#include <string.h>
#include <esp_log.h>
#include "lwip/apps/sntp.h"
#include <nvs.h>
#include <lwip/apps/sntp.h>
#include <esp_rmaker_core.h>
#include <esp_rmaker_standard_types.h>
#include <esp_rmaker_standard_services.h>
#include <esp_rmaker_utils.h>
static const char *TAG = "esp_rmaker_time_sync";
#define ESP_RMAKER_NVS_PART_NAME "nvs"
#define ESP_RMAKER_TIME_SERV_NAME "time"
#define ESP_RMAKER_NVS_TIME_NAMESPACE "rmaker_time"
#define ESP_RMAKER_TZ_POSIX_NVS_NAME "tz_posix"
#define ESP_RMAKER_TZ_NVS_NAME "tz"
#define REF_TIME 1546300800 /* 01-Jan-2019 00:00:00 */
static bool init_done = false;
extern const char *esp_rmaker_tz_db_get_posix_str(const char *name);
#define ESP_RMAKER_DEF_TZ CONFIG_ESP_RMAKER_DEF_TIMEZONE
esp_err_t esp_rmaker_get_local_time_str(char *buf, size_t buf_len)
{
struct tm timeinfo;
char strftime_buf[64];
time_t now;
time(&now);
localtime_r(&now, &timeinfo);
strftime(strftime_buf, sizeof(strftime_buf), "%c %z[%Z]", &timeinfo);
size_t print_size = snprintf(buf, buf_len, "%s, DST: %s", strftime_buf, timeinfo.tm_isdst ? "Yes" : "No");
if (print_size >= buf_len) {
ESP_LOGE(TAG, "Buffer size %d insufficient for localtime string. REquired size: %d", buf_len, print_size);
return ESP_ERR_INVALID_ARG;
}
return ESP_OK;
}
static esp_err_t esp_rmaker_print_current_time(void)
{
char local_time[64];
if (esp_rmaker_get_local_time_str(local_time, sizeof(local_time)) == ESP_OK) {
if (esp_rmaker_time_check() == false) {
ESP_LOGI(TAG, "Time not synchronised yet.");
}
ESP_LOGI(TAG, "The current time is: %s.", local_time);
return ESP_OK;
}
return ESP_FAIL;
}
static char *__esp_rmaker_time_get_nvs(const char *key)
{
char *val = NULL;
nvs_handle handle;
esp_err_t err = nvs_open_from_partition(ESP_RMAKER_NVS_PART_NAME, ESP_RMAKER_NVS_TIME_NAMESPACE, NVS_READONLY, &handle);
if (err != ESP_OK) {
return NULL;
}
size_t len = 0;
if ((err = nvs_get_blob(handle, key, NULL, &len)) == ESP_OK) {
val = calloc(1, len + 1); /* +1 for NULL termination */
if (val) {
nvs_get_blob(handle, key, val, &len);
}
}
nvs_close(handle);
return val;
}
static esp_err_t __esp_rmaker_time_set_nvs(const char *key, const char *val)
{
nvs_handle handle;
esp_err_t err = nvs_open_from_partition(ESP_RMAKER_NVS_PART_NAME, ESP_RMAKER_NVS_TIME_NAMESPACE, NVS_READWRITE, &handle);
if (err != ESP_OK) {
return err;
}
err = nvs_set_blob(handle, key, val, strlen(val));
nvs_commit(handle);
nvs_close(handle);
return err;
}
static char *esp_rmaker_time_get_timezone_posix(void)
{
return __esp_rmaker_time_get_nvs(ESP_RMAKER_TZ_POSIX_NVS_NAME);
}
static char *esp_rmaker_time_get_timezone(void)
{
return __esp_rmaker_time_get_nvs(ESP_RMAKER_TZ_NVS_NAME);
}
esp_err_t esp_rmaker_time_set_timezone_posix(const char *tz_posix)
{
esp_err_t err = __esp_rmaker_time_set_nvs(ESP_RMAKER_TZ_POSIX_NVS_NAME, tz_posix);
if (err == ESP_OK) {
setenv("TZ", tz_posix, 1);
tzset();
esp_rmaker_print_current_time();
}
return err;
}
esp_err_t esp_rmaker_time_set_timezone(const char *tz)
{
const char *tz_posix = esp_rmaker_tz_db_get_posix_str(tz);
if (!tz_posix) {
return ESP_ERR_INVALID_ARG;
}
esp_err_t err = esp_rmaker_time_set_timezone_posix(tz_posix);
if (err == ESP_OK) {
err = __esp_rmaker_time_set_nvs(ESP_RMAKER_TZ_NVS_NAME, tz);
}
return err;
}
esp_err_t esp_rmaker_timezone_enable(void)
{
char *tz_posix = esp_rmaker_time_get_timezone_posix();
if (tz_posix) {
setenv("TZ", tz_posix, 1);
tzset();
free(tz_posix);
} else {
if (strlen(ESP_RMAKER_DEF_TZ) > 0) {
const char *tz_def = esp_rmaker_tz_db_get_posix_str(ESP_RMAKER_DEF_TZ);
if (tz_def) {
setenv("TZ", tz_def, 1);
tzset();
return ESP_OK;
} else {
ESP_LOGE(TAG, "Invalid Timezone %s specified.", ESP_RMAKER_DEF_TZ);
return ESP_ERR_INVALID_ARG;
}
}
}
return ESP_OK;
}
static void esp_rmaker_time_sync_cb(struct timeval *tv)
{
ESP_LOGI(TAG, "SNTP Synchronised.");
esp_rmaker_print_current_time();
}
esp_err_t esp_rmaker_time_sync_init(esp_rmaker_time_config_t *config)
{
@@ -37,6 +177,12 @@ esp_err_t esp_rmaker_time_sync_init(esp_rmaker_time_config_t *config)
sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_setservername(0, sntp_server_name);
sntp_init();
if (config->sync_time_cb) {
sntp_set_time_sync_notification_cb(config->sync_time_cb);
} else {
sntp_set_time_sync_notification_cb(esp_rmaker_time_sync_cb);
}
esp_rmaker_timezone_enable();
init_done = true;
return ESP_OK;
}
@@ -52,6 +198,7 @@ bool esp_rmaker_time_check(void)
}
#define DEFAULT_TICKS (2000 / portTICK_PERIOD_MS) /* 2 seconds in ticks */
esp_err_t esp_rmaker_time_wait_for_sync(uint32_t ticks_to_wait)
{
if (!init_done) {
@@ -77,12 +224,63 @@ esp_err_t esp_rmaker_time_wait_for_sync(uint32_t ticks_to_wait)
}
/* Get current time */
struct tm timeinfo;
char strftime_buf[64];
time_t now;
time(&now);
localtime_r(&now, &timeinfo);
strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
ESP_LOGI(TAG, "The current UTC time is: %s", strftime_buf);
esp_rmaker_print_current_time();
return ESP_OK;
}
static esp_err_t esp_rmaker_time_service_cb(const esp_rmaker_device_t *device, const esp_rmaker_param_t *param,
const esp_rmaker_param_val_t val, void *priv_data, esp_rmaker_write_ctx_t *ctx)
{
esp_err_t err = ESP_FAIL;
if (strcmp(esp_rmaker_param_get_type(param), ESP_RMAKER_PARAM_TIMEZONE) == 0) {
ESP_LOGI(TAG, "Received value = %s for %s - %s",
val.val.s, esp_rmaker_device_get_name(device), esp_rmaker_param_get_name(param));
err = esp_rmaker_time_set_timezone(val.val.s);
if (err == ESP_OK) {
char *tz_posix = esp_rmaker_time_get_timezone_posix();
if (tz_posix) {
esp_rmaker_param_t *tz_posix_param = esp_rmaker_device_get_param_by_type(
device, ESP_RMAKER_PARAM_TIMEZONE_POSIX);
esp_rmaker_param_update_and_report(tz_posix_param, esp_rmaker_str(tz_posix));
free(tz_posix);
}
}
} else if (strcmp(esp_rmaker_param_get_type(param), ESP_RMAKER_PARAM_TIMEZONE_POSIX) == 0) {
ESP_LOGI(TAG, "Received value = %s for %s - %s",
val.val.s, esp_rmaker_device_get_name(device), esp_rmaker_param_get_name(param));
err = esp_rmaker_time_set_timezone_posix(val.val.s);
}
if (err == ESP_OK) {
esp_rmaker_param_update_and_report(param, val);
}
return err;
}
static esp_err_t esp_rmaker_time_add_service(const char *tz, const char *tz_posix)
{
esp_rmaker_device_t *service = esp_rmaker_time_service_create(ESP_RMAKER_TIME_SERV_NAME, tz, tz_posix, NULL);
if (!service) {
ESP_LOGE(TAG, "Failed to create Time Service");
return ESP_FAIL;
}
esp_rmaker_device_add_cb(service, esp_rmaker_time_service_cb, NULL);
esp_err_t err = esp_rmaker_node_add_device(esp_rmaker_get_node(), service);
if (err == ESP_OK) {
ESP_LOGI(TAG, "Time service enabled");
}
return err;
}
esp_err_t esp_rmaker_timezone_service_enable(void)
{
char *tz_posix = esp_rmaker_time_get_timezone_posix();
char *tz = esp_rmaker_time_get_timezone();
esp_err_t err = esp_rmaker_time_add_service(tz, tz_posix);
if (tz_posix) {
free(tz_posix);
}
if (tz) {
free(tz);
}
return err;
}

View File

@@ -0,0 +1,515 @@
// MIT License
//
// Copyright (c) 2020 Nayar Systems
// Copyright (c) 2020 Jacob Lambert
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
// Original code taken from: https://github.com/jdlambert/micro_tz_db
// which was forked from https://github.com/nayarsystems/posix_tz_db
#include <stdio.h>
typedef struct {
const char *name;
const char *posix_str;
} esp_rmaker_tz_db_pair_t;
static const esp_rmaker_tz_db_pair_t esp_rmaker_tz_db_tzs[] = {
{"Africa/Abidjan", "GMT0"},
{"Africa/Accra", "GMT0"},
{"Africa/Addis_Ababa", "EAT-3"},
{"Africa/Algiers", "CET-1"},
{"Africa/Asmara", "EAT-3"},
{"Africa/Bamako", "GMT0"},
{"Africa/Bangui", "WAT-1"},
{"Africa/Banjul", "GMT0"},
{"Africa/Bissau", "GMT0"},
{"Africa/Blantyre", "CAT-2"},
{"Africa/Brazzaville", "WAT-1"},
{"Africa/Bujumbura", "CAT-2"},
{"Africa/Cairo", "EET-2"},
{"Africa/Casablanca", "<+01>-1"},
{"Africa/Ceuta", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Africa/Conakry", "GMT0"},
{"Africa/Dakar", "GMT0"},
{"Africa/Dar_es_Salaam", "EAT-3"},
{"Africa/Djibouti", "EAT-3"},
{"Africa/Douala", "WAT-1"},
{"Africa/El_Aaiun", "<+01>-1"},
{"Africa/Freetown", "GMT0"},
{"Africa/Gaborone", "CAT-2"},
{"Africa/Harare", "CAT-2"},
{"Africa/Johannesburg", "SAST-2"},
{"Africa/Juba", "EAT-3"},
{"Africa/Kampala", "EAT-3"},
{"Africa/Khartoum", "CAT-2"},
{"Africa/Kigali", "CAT-2"},
{"Africa/Kinshasa", "WAT-1"},
{"Africa/Lagos", "WAT-1"},
{"Africa/Libreville", "WAT-1"},
{"Africa/Lome", "GMT0"},
{"Africa/Luanda", "WAT-1"},
{"Africa/Lubumbashi", "CAT-2"},
{"Africa/Lusaka", "CAT-2"},
{"Africa/Malabo", "WAT-1"},
{"Africa/Maputo", "CAT-2"},
{"Africa/Maseru", "SAST-2"},
{"Africa/Mbabane", "SAST-2"},
{"Africa/Mogadishu", "EAT-3"},
{"Africa/Monrovia", "GMT0"},
{"Africa/Nairobi", "EAT-3"},
{"Africa/Ndjamena", "WAT-1"},
{"Africa/Niamey", "WAT-1"},
{"Africa/Nouakchott", "GMT0"},
{"Africa/Ouagadougou", "GMT0"},
{"Africa/Porto-Novo", "WAT-1"},
{"Africa/Sao_Tome", "GMT0"},
{"Africa/Tripoli", "EET-2"},
{"Africa/Tunis", "CET-1"},
{"Africa/Windhoek", "CAT-2"},
{"America/Adak", "HST10HDT,M3.2.0,M11.1.0"},
{"America/Anchorage", "AKST9AKDT,M3.2.0,M11.1.0"},
{"America/Anguilla", "AST4"},
{"America/Antigua", "AST4"},
{"America/Araguaina", "<-03>3"},
{"America/Argentina/Buenos_Aires", "<-03>3"},
{"America/Argentina/Catamarca", "<-03>3"},
{"America/Argentina/Cordoba", "<-03>3"},
{"America/Argentina/Jujuy", "<-03>3"},
{"America/Argentina/La_Rioja", "<-03>3"},
{"America/Argentina/Mendoza", "<-03>3"},
{"America/Argentina/Rio_Gallegos", "<-03>3"},
{"America/Argentina/Salta", "<-03>3"},
{"America/Argentina/San_Juan", "<-03>3"},
{"America/Argentina/San_Luis", "<-03>3"},
{"America/Argentina/Tucuman", "<-03>3"},
{"America/Argentina/Ushuaia", "<-03>3"},
{"America/Aruba", "AST4"},
{"America/Asuncion", "<-04>4<-03>,M10.1.0/0,M3.4.0/0"},
{"America/Atikokan", "EST5"},
{"America/Bahia", "<-03>3"},
{"America/Bahia_Banderas", "CST6CDT,M4.1.0,M10.5.0"},
{"America/Barbados", "AST4"},
{"America/Belem", "<-03>3"},
{"America/Belize", "CST6"},
{"America/Blanc-Sablon", "AST4"},
{"America/Boa_Vista", "<-04>4"},
{"America/Bogota", "<-05>5"},
{"America/Boise", "MST7MDT,M3.2.0,M11.1.0"},
{"America/Cambridge_Bay", "MST7MDT,M3.2.0,M11.1.0"},
{"America/Campo_Grande", "<-04>4"},
{"America/Cancun", "EST5"},
{"America/Caracas", "<-04>4"},
{"America/Cayenne", "<-03>3"},
{"America/Cayman", "EST5"},
{"America/Chicago", "CST6CDT,M3.2.0,M11.1.0"},
{"America/Chihuahua", "MST7MDT,M4.1.0,M10.5.0"},
{"America/Costa_Rica", "CST6"},
{"America/Creston", "MST7"},
{"America/Cuiaba", "<-04>4"},
{"America/Curacao", "AST4"},
{"America/Danmarkshavn", "GMT0"},
{"America/Dawson", "MST7"},
{"America/Dawson_Creek", "MST7"},
{"America/Denver", "MST7MDT,M3.2.0,M11.1.0"},
{"America/Detroit", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Dominica", "AST4"},
{"America/Edmonton", "MST7MDT,M3.2.0,M11.1.0"},
{"America/Eirunepe", "<-05>5"},
{"America/El_Salvador", "CST6"},
{"America/Fortaleza", "<-03>3"},
{"America/Fort_Nelson", "MST7"},
{"America/Glace_Bay", "AST4ADT,M3.2.0,M11.1.0"},
{"America/Godthab", "<-03>3<-02>,M3.5.0/-2,M10.5.0/-1"},
{"America/Goose_Bay", "AST4ADT,M3.2.0,M11.1.0"},
{"America/Grand_Turk", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Grenada", "AST4"},
{"America/Guadeloupe", "AST4"},
{"America/Guatemala", "CST6"},
{"America/Guayaquil", "<-05>5"},
{"America/Guyana", "<-04>4"},
{"America/Halifax", "AST4ADT,M3.2.0,M11.1.0"},
{"America/Havana", "CST5CDT,M3.2.0/0,M11.1.0/1"},
{"America/Hermosillo", "MST7"},
{"America/Indiana/Indianapolis", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Indiana/Knox", "CST6CDT,M3.2.0,M11.1.0"},
{"America/Indiana/Marengo", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Indiana/Petersburg", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Indiana/Tell_City", "CST6CDT,M3.2.0,M11.1.0"},
{"America/Indiana/Vevay", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Indiana/Vincennes", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Indiana/Winamac", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Inuvik", "MST7MDT,M3.2.0,M11.1.0"},
{"America/Iqaluit", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Jamaica", "EST5"},
{"America/Juneau", "AKST9AKDT,M3.2.0,M11.1.0"},
{"America/Kentucky/Louisville", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Kentucky/Monticello", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Kralendijk", "AST4"},
{"America/La_Paz", "<-04>4"},
{"America/Lima", "<-05>5"},
{"America/Los_Angeles", "PST8PDT,M3.2.0,M11.1.0"},
{"America/Lower_Princes", "AST4"},
{"America/Maceio", "<-03>3"},
{"America/Managua", "CST6"},
{"America/Manaus", "<-04>4"},
{"America/Marigot", "AST4"},
{"America/Martinique", "AST4"},
{"America/Matamoros", "CST6CDT,M3.2.0,M11.1.0"},
{"America/Mazatlan", "MST7MDT,M4.1.0,M10.5.0"},
{"America/Menominee", "CST6CDT,M3.2.0,M11.1.0"},
{"America/Merida", "CST6CDT,M4.1.0,M10.5.0"},
{"America/Metlakatla", "AKST9AKDT,M3.2.0,M11.1.0"},
{"America/Mexico_City", "CST6CDT,M4.1.0,M10.5.0"},
{"America/Miquelon", "<-03>3<-02>,M3.2.0,M11.1.0"},
{"America/Moncton", "AST4ADT,M3.2.0,M11.1.0"},
{"America/Monterrey", "CST6CDT,M4.1.0,M10.5.0"},
{"America/Montevideo", "<-03>3"},
{"America/Montreal", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Montserrat", "AST4"},
{"America/Nassau", "EST5EDT,M3.2.0,M11.1.0"},
{"America/New_York", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Nipigon", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Nome", "AKST9AKDT,M3.2.0,M11.1.0"},
{"America/Noronha", "<-02>2"},
{"America/North_Dakota/Beulah", "CST6CDT,M3.2.0,M11.1.0"},
{"America/North_Dakota/Center", "CST6CDT,M3.2.0,M11.1.0"},
{"America/North_Dakota/New_Salem", "CST6CDT,M3.2.0,M11.1.0"},
{"America/Ojinaga", "MST7MDT,M3.2.0,M11.1.0"},
{"America/Panama", "EST5"},
{"America/Pangnirtung", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Paramaribo", "<-03>3"},
{"America/Phoenix", "MST7"},
{"America/Port-au-Prince", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Port_of_Spain", "AST4"},
{"America/Porto_Velho", "<-04>4"},
{"America/Puerto_Rico", "AST4"},
{"America/Punta_Arenas", "<-03>3"},
{"America/Rainy_River", "CST6CDT,M3.2.0,M11.1.0"},
{"America/Rankin_Inlet", "CST6CDT,M3.2.0,M11.1.0"},
{"America/Recife", "<-03>3"},
{"America/Regina", "CST6"},
{"America/Resolute", "CST6CDT,M3.2.0,M11.1.0"},
{"America/Rio_Branco", "<-05>5"},
{"America/Santarem", "<-03>3"},
{"America/Santiago", "<-04>4<-03>,M9.1.6/24,M4.1.6/24"},
{"America/Santo_Domingo", "AST4"},
{"America/Sao_Paulo", "<-03>3"},
{"America/Scoresbysund", "<-01>1<+00>,M3.5.0/0,M10.5.0/1"},
{"America/Sitka", "AKST9AKDT,M3.2.0,M11.1.0"},
{"America/St_Barthelemy", "AST4"},
{"America/St_Johns", "NST3:30NDT,M3.2.0,M11.1.0"},
{"America/St_Kitts", "AST4"},
{"America/St_Lucia", "AST4"},
{"America/St_Thomas", "AST4"},
{"America/St_Vincent", "AST4"},
{"America/Swift_Current", "CST6"},
{"America/Tegucigalpa", "CST6"},
{"America/Thule", "AST4ADT,M3.2.0,M11.1.0"},
{"America/Thunder_Bay", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Tijuana", "PST8PDT,M3.2.0,M11.1.0"},
{"America/Toronto", "EST5EDT,M3.2.0,M11.1.0"},
{"America/Tortola", "AST4"},
{"America/Vancouver", "PST8PDT,M3.2.0,M11.1.0"},
{"America/Whitehorse", "MST7"},
{"America/Winnipeg", "CST6CDT,M3.2.0,M11.1.0"},
{"America/Yakutat", "AKST9AKDT,M3.2.0,M11.1.0"},
{"America/Yellowknife", "MST7MDT,M3.2.0,M11.1.0"},
{"Antarctica/Casey", "<+08>-8"},
{"Antarctica/Davis", "<+07>-7"},
{"Antarctica/DumontDUrville", "<+10>-10"},
{"Antarctica/Macquarie", "<+11>-11"},
{"Antarctica/Mawson", "<+05>-5"},
{"Antarctica/McMurdo", "NZST-12NZDT,M9.5.0,M4.1.0/3"},
{"Antarctica/Palmer", "<-03>3"},
{"Antarctica/Rothera", "<-03>3"},
{"Antarctica/Syowa", "<+03>-3"},
{"Antarctica/Troll", "<+00>0<+02>-2,M3.5.0/1,M10.5.0/3"},
{"Antarctica/Vostok", "<+06>-6"},
{"Arctic/Longyearbyen", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Asia/Aden", "<+03>-3"},
{"Asia/Almaty", "<+06>-6"},
{"Asia/Amman", "EET-2EEST,M3.5.4/24,M10.5.5/1"},
{"Asia/Anadyr", "<+12>-12"},
{"Asia/Aqtau", "<+05>-5"},
{"Asia/Aqtobe", "<+05>-5"},
{"Asia/Ashgabat", "<+05>-5"},
{"Asia/Atyrau", "<+05>-5"},
{"Asia/Baghdad", "<+03>-3"},
{"Asia/Bahrain", "<+03>-3"},
{"Asia/Baku", "<+04>-4"},
{"Asia/Bangkok", "<+07>-7"},
{"Asia/Barnaul", "<+07>-7"},
{"Asia/Beirut", "EET-2EEST,M3.5.0/0,M10.5.0/0"},
{"Asia/Bishkek", "<+06>-6"},
{"Asia/Brunei", "<+08>-8"},
{"Asia/Chita", "<+09>-9"},
{"Asia/Choibalsan", "<+08>-8"},
{"Asia/Colombo", "<+0530>-5:30"},
{"Asia/Damascus", "EET-2EEST,M3.5.5/0,M10.5.5/0"},
{"Asia/Dhaka", "<+06>-6"},
{"Asia/Dili", "<+09>-9"},
{"Asia/Dubai", "<+04>-4"},
{"Asia/Dushanbe", "<+05>-5"},
{"Asia/Famagusta", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
{"Asia/Gaza", "EET-2EEST,M3.5.5/0,M10.5.6/1"},
{"Asia/Hebron", "EET-2EEST,M3.5.5/0,M10.5.6/1"},
{"Asia/Ho_Chi_Minh", "<+07>-7"},
{"Asia/Hong_Kong", "HKT-8"},
{"Asia/Hovd", "<+07>-7"},
{"Asia/Irkutsk", "<+08>-8"},
{"Asia/Jakarta", "WIB-7"},
{"Asia/Jayapura", "WIT-9"},
{"Asia/Jerusalem", "IST-2IDT,M3.4.4/26,M10.5.0"},
{"Asia/Kabul", "<+0430>-4:30"},
{"Asia/Kamchatka", "<+12>-12"},
{"Asia/Karachi", "PKT-5"},
{"Asia/Kathmandu", "<+0545>-5:45"},
{"Asia/Khandyga", "<+09>-9"},
{"Asia/Kolkata", "IST-5:30"},
{"Asia/Krasnoyarsk", "<+07>-7"},
{"Asia/Kuala_Lumpur", "<+08>-8"},
{"Asia/Kuching", "<+08>-8"},
{"Asia/Kuwait", "<+03>-3"},
{"Asia/Macau", "CST-8"},
{"Asia/Magadan", "<+11>-11"},
{"Asia/Makassar", "WITA-8"},
{"Asia/Manila", "PST-8"},
{"Asia/Muscat", "<+04>-4"},
{"Asia/Nicosia", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
{"Asia/Novokuznetsk", "<+07>-7"},
{"Asia/Novosibirsk", "<+07>-7"},
{"Asia/Omsk", "<+06>-6"},
{"Asia/Oral", "<+05>-5"},
{"Asia/Phnom_Penh", "<+07>-7"},
{"Asia/Pontianak", "WIB-7"},
{"Asia/Pyongyang", "KST-9"},
{"Asia/Qatar", "<+03>-3"},
{"Asia/Qyzylorda", "<+05>-5"},
{"Asia/Riyadh", "<+03>-3"},
{"Asia/Sakhalin", "<+11>-11"},
{"Asia/Samarkand", "<+05>-5"},
{"Asia/Seoul", "KST-9"},
{"Asia/Shanghai", "CST-8"},
{"Asia/Singapore", "<+08>-8"},
{"Asia/Srednekolymsk", "<+11>-11"},
{"Asia/Taipei", "CST-8"},
{"Asia/Tashkent", "<+05>-5"},
{"Asia/Tbilisi", "<+04>-4"},
{"Asia/Tehran", "<+0330>-3:30<+0430>,J79/24,J263/24"},
{"Asia/Thimphu", "<+06>-6"},
{"Asia/Tokyo", "JST-9"},
{"Asia/Tomsk", "<+07>-7"},
{"Asia/Ulaanbaatar", "<+08>-8"},
{"Asia/Urumqi", "<+06>-6"},
{"Asia/Ust-Nera", "<+10>-10"},
{"Asia/Vientiane", "<+07>-7"},
{"Asia/Vladivostok", "<+10>-10"},
{"Asia/Yakutsk", "<+09>-9"},
{"Asia/Yangon", "<+0630>-6:30"},
{"Asia/Yekaterinburg", "<+05>-5"},
{"Asia/Yerevan", "<+04>-4"},
{"Atlantic/Azores", "<-01>1<+00>,M3.5.0/0,M10.5.0/1"},
{"Atlantic/Bermuda", "AST4ADT,M3.2.0,M11.1.0"},
{"Atlantic/Canary", "WET0WEST,M3.5.0/1,M10.5.0"},
{"Atlantic/Cape_Verde", "<-01>1"},
{"Atlantic/Faroe", "WET0WEST,M3.5.0/1,M10.5.0"},
{"Atlantic/Madeira", "WET0WEST,M3.5.0/1,M10.5.0"},
{"Atlantic/Reykjavik", "GMT0"},
{"Atlantic/South_Georgia", "<-02>2"},
{"Atlantic/Stanley", "<-03>3"},
{"Atlantic/St_Helena", "GMT0"},
{"Australia/Adelaide", "ACST-9:30ACDT,M10.1.0,M4.1.0/3"},
{"Australia/Brisbane", "AEST-10"},
{"Australia/Broken_Hill", "ACST-9:30ACDT,M10.1.0,M4.1.0/3"},
{"Australia/Currie", "AEST-10AEDT,M10.1.0,M4.1.0/3"},
{"Australia/Darwin", "ACST-9:30"},
{"Australia/Eucla", "<+0845>-8:45"},
{"Australia/Hobart", "AEST-10AEDT,M10.1.0,M4.1.0/3"},
{"Australia/Lindeman", "AEST-10"},
{"Australia/Lord_Howe", "<+1030>-10:30<+11>-11,M10.1.0,M4.1.0"},
{"Australia/Melbourne", "AEST-10AEDT,M10.1.0,M4.1.0/3"},
{"Australia/Perth", "AWST-8"},
{"Australia/Sydney", "AEST-10AEDT,M10.1.0,M4.1.0/3"},
{"Europe/Amsterdam", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Andorra", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Astrakhan", "<+04>-4"},
{"Europe/Athens", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
{"Europe/Belgrade", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Berlin", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Bratislava", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Brussels", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Bucharest", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
{"Europe/Budapest", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Busingen", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Chisinau", "EET-2EEST,M3.5.0,M10.5.0/3"},
{"Europe/Copenhagen", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Dublin", "IST-1GMT0,M10.5.0,M3.5.0/1"},
{"Europe/Gibraltar", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Guernsey", "GMT0BST,M3.5.0/1,M10.5.0"},
{"Europe/Helsinki", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
{"Europe/Isle_of_Man", "GMT0BST,M3.5.0/1,M10.5.0"},
{"Europe/Istanbul", "<+03>-3"},
{"Europe/Jersey", "GMT0BST,M3.5.0/1,M10.5.0"},
{"Europe/Kaliningrad", "EET-2"},
{"Europe/Kiev", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
{"Europe/Kirov", "<+03>-3"},
{"Europe/Lisbon", "WET0WEST,M3.5.0/1,M10.5.0"},
{"Europe/Ljubljana", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/London", "GMT0BST,M3.5.0/1,M10.5.0"},
{"Europe/Luxembourg", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Madrid", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Malta", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Mariehamn", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
{"Europe/Minsk", "<+03>-3"},
{"Europe/Monaco", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Moscow", "MSK-3"},
{"Europe/Oslo", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Paris", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Podgorica", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Prague", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Riga", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
{"Europe/Rome", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Samara", "<+04>-4"},
{"Europe/San_Marino", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Sarajevo", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Saratov", "<+04>-4"},
{"Europe/Simferopol", "MSK-3"},
{"Europe/Skopje", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Sofia", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
{"Europe/Stockholm", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Tallinn", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
{"Europe/Tirane", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Ulyanovsk", "<+04>-4"},
{"Europe/Uzhgorod", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
{"Europe/Vaduz", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Vatican", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Vienna", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Vilnius", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
{"Europe/Volgograd", "<+04>-4"},
{"Europe/Warsaw", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Zagreb", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Europe/Zaporozhye", "EET-2EEST,M3.5.0/3,M10.5.0/4"},
{"Europe/Zurich", "CET-1CEST,M3.5.0,M10.5.0/3"},
{"Indian/Antananarivo", "EAT-3"},
{"Indian/Chagos", "<+06>-6"},
{"Indian/Christmas", "<+07>-7"},
{"Indian/Cocos", "<+0630>-6:30"},
{"Indian/Comoro", "EAT-3"},
{"Indian/Kerguelen", "<+05>-5"},
{"Indian/Mahe", "<+04>-4"},
{"Indian/Maldives", "<+05>-5"},
{"Indian/Mauritius", "<+04>-4"},
{"Indian/Mayotte", "EAT-3"},
{"Indian/Reunion", "<+04>-4"},
{"Pacific/Apia", "<+13>-13<+14>,M9.5.0/3,M4.1.0/4"},
{"Pacific/Auckland", "NZST-12NZDT,M9.5.0,M4.1.0/3"},
{"Pacific/Bougainville", "<+11>-11"},
{"Pacific/Chatham", "<+1245>-12:45<+1345>,M9.5.0/2:45,M4.1.0/3:45"},
{"Pacific/Chuuk", "<+10>-10"},
{"Pacific/Easter", "<-06>6<-05>,M9.1.6/22,M4.1.6/22"},
{"Pacific/Efate", "<+11>-11"},
{"Pacific/Enderbury", "<+13>-13"},
{"Pacific/Fakaofo", "<+13>-13"},
{"Pacific/Fiji", "<+12>-12<+13>,M11.2.0,M1.2.3/99"},
{"Pacific/Funafuti", "<+12>-12"},
{"Pacific/Galapagos", "<-06>6"},
{"Pacific/Gambier", "<-09>9"},
{"Pacific/Guadalcanal", "<+11>-11"},
{"Pacific/Guam", "ChST-10"},
{"Pacific/Honolulu", "HST10"},
{"Pacific/Kiritimati", "<+14>-14"},
{"Pacific/Kosrae", "<+11>-11"},
{"Pacific/Kwajalein", "<+12>-12"},
{"Pacific/Majuro", "<+12>-12"},
{"Pacific/Marquesas", "<-0930>9:30"},
{"Pacific/Midway", "SST11"},
{"Pacific/Nauru", "<+12>-12"},
{"Pacific/Niue", "<-11>11"},
{"Pacific/Norfolk", "<+11>-11<+12>,M10.1.0,M4.1.0/3"},
{"Pacific/Noumea", "<+11>-11"},
{"Pacific/Pago_Pago", "SST11"},
{"Pacific/Palau", "<+09>-9"},
{"Pacific/Pitcairn", "<-08>8"},
{"Pacific/Pohnpei", "<+11>-11"},
{"Pacific/Port_Moresby", "<+10>-10"},
{"Pacific/Rarotonga", "<-10>10"},
{"Pacific/Saipan", "ChST-10"},
{"Pacific/Tahiti", "<-10>10"},
{"Pacific/Tarawa", "<+12>-12"},
{"Pacific/Tongatapu", "<+13>-13"},
{"Pacific/Wake", "<+12>-12"},
{"Pacific/Wallis", "<+12>-12"}
};
static char lower(char start) {
if ('A' <= start && start <= 'Z') {
return start - 'A' + 'a';
}
return start;
}
/**
* Basically strcmp, but accounting for spaces that have become underscores
* @param[in] target - the 0-terminated string on the left hand side of the comparison
* @param[in] other - the 0-terminated string on the right hand side of the comparison
* @return > 0 if target comes before other alphabetically,
* ==0 if they're the same,
* < 0 if other comes before target alphabetically
* (we don't expect NULL arguments, but, -1 if either is NULL)
**/
static int tz_name_cmp(const char * target, const char * other)
{
if (!target || !other) {
return -1;
}
while (*target) {
if (lower(*target) != lower(*other)) {
break;
}
do {
target++;
} while (*target == '_');
do {
other++;
} while (*other == '_');
}
return lower(*target) - lower(*other);
}
const char *esp_rmaker_tz_db_get_posix_str(const char *name)
{
int lo = 0, hi = sizeof(esp_rmaker_tz_db_tzs) / sizeof(esp_rmaker_tz_db_pair_t);
while (lo < hi) {
int mid = (lo + hi) / 2;
esp_rmaker_tz_db_pair_t mid_pair = esp_rmaker_tz_db_tzs[mid];
int comparison = tz_name_cmp(name, mid_pair.name);
if (comparison == 0) {
return mid_pair.posix_str;
} else if (comparison < 0) {
hi = mid;
} else {
lo = mid + 1;
}
}
return NULL;
}

View File

@@ -138,3 +138,17 @@ esp_rmaker_param_t *esp_rmaker_ota_url_param_create(const char *param_name)
esp_rmaker_str(""), PROP_FLAG_WRITE);
return param;
}
esp_rmaker_param_t *esp_rmaker_timezone_param_create(const char *param_name, const char *val)
{
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_TIMEZONE,
esp_rmaker_str(val), PROP_FLAG_READ | PROP_FLAG_WRITE);
return param;
}
esp_rmaker_param_t *esp_rmaker_timezone_posix_param_create(const char *param_name, const char *val)
{
esp_rmaker_param_t *param = esp_rmaker_param_create(param_name, ESP_RMAKER_PARAM_TIMEZONE_POSIX,
esp_rmaker_str(val), PROP_FLAG_READ | PROP_FLAG_WRITE);
return param;
}

View File

@@ -27,3 +27,15 @@ esp_rmaker_device_t *esp_rmaker_ota_service_create(const char *serv_name, void *
return service;
}
esp_rmaker_device_t *esp_rmaker_time_service_create(const char *serv_name, const char *timezone,
const char *timezone_posix, void *priv_data)
{
esp_rmaker_device_t *service = esp_rmaker_service_create(serv_name, ESP_RMAKER_SERVICE_TIME, priv_data);
if (service) {
esp_rmaker_device_add_param(service, esp_rmaker_timezone_param_create(
ESP_RMAKER_DEF_TIMEZONE_NAME, timezone));
esp_rmaker_device_add_param(service, esp_rmaker_timezone_posix_param_create(
ESP_RMAKER_DEF_TIMEZONE_POSIX_NAME, timezone_posix));
}
return service;
}