summaryrefslogtreecommitdiff
path: root/time.c
diff options
context:
space:
mode:
authorNobuyoshi Nakada <[email protected]>2025-02-17 21:46:47 +0900
committerNobuyoshi Nakada <[email protected]>2025-02-19 18:27:32 +0900
commit3f07bc76ff6a11232d9f18e5eaa31835c195e8f0 (patch)
treef94ee52c712179414e6295338af79f57b30e20c5 /time.c
parentbd84c75a013042f4feefa4616ab119394b3bbf24 (diff)
[Bug #21144] Win32: Use Windows time zone ID if TZ is not set
If the TZ environment variable is not set, the time zone names retrieved from the system are localized for UI display and may vary across editions and language packs for the same time zone. Use the time zone IDs that are invariant across environments instead.
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/12765
Diffstat (limited to 'time.c')
-rw-r--r--time.c57
1 files changed, 52 insertions, 5 deletions
diff --git a/time.c b/time.c
index d19d1873f8..56f93ec58d 100644
--- a/time.c
+++ b/time.c
@@ -45,6 +45,10 @@
#include "ruby/util.h"
#include "timev.h"
+#if defined(_WIN32)
+# include "timezoneapi.h" /* DYNAMIC_TIME_ZONE_INFORMATION */
+#endif
+
#include "builtin.h"
static ID id_submicro, id_nano_num, id_nano_den, id_offset, id_zone;
@@ -703,10 +707,51 @@ static VALUE tm_from_time(VALUE klass, VALUE time);
bool ruby_tz_uptodate_p;
+#ifdef _WIN32
+enum {tzkey_max = numberof(((DYNAMIC_TIME_ZONE_INFORMATION *)NULL)->TimeZoneKeyName)};
+static struct {
+ char use_tzkey;
+ char name[tzkey_max * 4 + 1];
+} w32_tz;
+
+static char *
+get_tzname(int dst)
+{
+ if (w32_tz.use_tzkey) {
+ if (w32_tz.name[0]) {
+ return w32_tz.name;
+ }
+ else {
+ /*
+ * Use GetDynamicTimeZoneInformation::TimeZoneKeyName, Windows
+ * time zone ID, which is not localized because it is the key
+ * for "Dynamic DST" keys under the "Time Zones" registry.
+ * Available since Windows Vista and Windows Server 2008.
+ */
+ DYNAMIC_TIME_ZONE_INFORMATION tzi;
+ WCHAR *const wtzkey = tzi.TimeZoneKeyName;
+ DWORD tzret = GetDynamicTimeZoneInformation(&tzi);
+ if (tzret != TIME_ZONE_ID_INVALID && *wtzkey) {
+ int wlen = (int)wcsnlen(wtzkey, tzkey_max);
+ int clen = WideCharToMultiByte(CP_UTF8, 0, wtzkey, wlen,
+ w32_tz.name, sizeof(w32_tz.name) - 1,
+ NULL, NULL);
+ w32_tz.name[clen] = '\0';
+ return w32_tz.name;
+ }
+ }
+ }
+ return _tzname[_daylight && dst];
+}
+#endif
+
void
-ruby_reset_timezone(void)
+ruby_reset_timezone(const char *val)
{
ruby_tz_uptodate_p = false;
+#ifdef _WIN32
+ w32_tz.use_tzkey = !val || !*val;
+#endif
ruby_reset_leap_second_info();
}
@@ -1653,11 +1698,9 @@ localtime_with_gmtoff_zone(const time_t *t, struct tm *result, long *gmtoff, VAL
if (zone) {
#if defined(HAVE_TM_ZONE)
*zone = zone_str(tm.tm_zone);
+#elif defined(_WIN32)
+ *zone = zone_str(get_tzname(tm.tm_isdst));
#elif defined(HAVE_TZNAME) && defined(HAVE_DAYLIGHT)
-# if defined(RUBY_MSVCRT_VERSION) && RUBY_MSVCRT_VERSION >= 140
-# define tzname _tzname
-# define daylight _daylight
-# endif
/* this needs tzset or localtime, instead of localtime_r */
*zone = zone_str(tzname[daylight && tm.tm_isdst]);
#else
@@ -5863,6 +5906,10 @@ rb_time_zone_abbreviation(VALUE zone, VALUE time)
void
Init_Time(void)
{
+#ifdef _WIN32
+ ruby_reset_timezone(getenv("TZ"));
+#endif
+
id_submicro = rb_intern_const("submicro");
id_nano_num = rb_intern_const("nano_num");
id_nano_den = rb_intern_const("nano_den");