diff --git a/src/env.cpp b/src/env.cpp index 606a094f8..8373efe0e 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -13,6 +13,7 @@ #endif #include #include +#include #include #include #include @@ -159,6 +160,24 @@ static mode_t get_umask() { return res; } +/// Check if the specified variable is a locale variable. +static bool var_is_timezone(const wcstring &key) { return key == L"TZ"; } + +/// Properly sets all locale information. +static void handle_timezone(const wchar_t *env_var_name) { + debug(2, L"handle_timezone() called in response to '%ls' changing", env_var_name); + const env_var_t val = env_get_string(env_var_name, ENV_EXPORT); + const std::string &value = wcs2string(val); + const std::string &name = wcs2string(env_var_name); + debug(2, L"timezone var %s='%s'", name.c_str(), value.c_str()); + if (val.empty()) { + unsetenv(name.c_str()); + } else { + setenv(name.c_str(), value.c_str(), 1); + } + tzset(); +} + /// Check if the specified variable is a locale variable. static bool var_is_locale(const wcstring &key) { for (size_t i = 0; locale_variable[i]; i++) { @@ -234,6 +253,8 @@ static void react_to_variable_change(const wcstring &key) { handle_locale(key.c_str()); } else if (var_is_curses(key)) { handle_curses(key.c_str()); + } else if (var_is_timezone(key)) { + handle_timezone(key.c_str()); } else if (key == L"fish_term256" || key == L"fish_term24bit") { update_fish_color_support(); reader_react_to_color_change(); diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index f3dd53523..d1dcf65ba 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -3780,6 +3780,42 @@ static void test_string(void) { } } +/// Helper for test_timezone_env_vars(). +long return_timezone_hour(time_t tstamp, const wchar_t *timezone) { + struct tm ltime; + char ltime_str[3]; + char *str_ptr; + int n; + + env_set(L"TZ", timezone, ENV_EXPORT); + localtime_r(&tstamp, <ime); + n = strftime(ltime_str, 3, "%H", <ime); + if (n != 2) { + err(L"strftime() returned %d, expected 2", n); + return 0; + } + return strtol(ltime_str, &str_ptr, 10); +} + +/// Verify that setting special env vars have the expected effect on the current shell process. +static void test_timezone_env_vars(void) { + // Confirm changing the timezone affects fish's idea of the local time. + time_t tstamp = time(NULL); + + long first_tstamp = return_timezone_hour(tstamp, L"UTC-1"); + long second_tstamp = return_timezone_hour(tstamp, L"UTC-2"); + long delta = second_tstamp - first_tstamp; + if (delta != 1 && delta != -23) { + err(L"expected a one hour timezone delta got %ld", delta); + } +} + +/// Verify that setting special env vars have the expected effect on the current shell process. +static void test_env_vars(void) { + test_timezone_env_vars(); + // TODO: Add tests for the locale and ncurses vars. +} + /// Main test. int main(int argc, char **argv) { // Look for the file tests/test.fish. We expect to run in a directory containing that file. @@ -3869,6 +3905,7 @@ int main(int argc, char **argv) { if (should_test_function("history_races")) history_tests_t::test_history_races(); if (should_test_function("history_formats")) history_tests_t::test_history_formats(); if (should_test_function("string")) test_string(); + if (should_test_function("env_vars")) test_env_vars(); // history_tests_t::test_history_speed(); say(L"Encountered %d errors in low-level tests", err_count);