Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716//// tzset.cpp//// Copyright (c) Microsoft Corporation. All rights reserved.//// Defines the _tzset() function which updates the global time zone state, and// the _isindst() function, which tests whether a time is in Daylight Savings// Time or not.//#include <corecrt_internal_time.h>#include <locale.h>
_DEFINE_SET_FUNCTION(_set_daylight, int, _daylight)_DEFINE_SET_FUNCTION(_set_dstbias, long, _dstbias )_DEFINE_SET_FUNCTION(_set_timezone, long, _timezone)
// Pointer to a saved copy of the TZ value obtained in the previous call to the// tzset functions, if one is available:static wchar_t* last_wide_tz = nullptr;
// If the time zone was last updated by calling the system API, then the tz_info// variable contains the time zone information and tz_api_used is set to true.static int tz_api_used;static TIME_ZONE_INFORMATION tz_info;
static __crt_state_management::dual_state_global<long> tzset_init_state;
namespace{ // Structure used to represent DST transition date/times: struct transitiondate { int yr; // year of interest int yd; // day of year int ms; // milli-seconds in the day };
enum class date_type { absolute_date, day_in_month };
enum class transition_type { start_of_dst, end_of_dst };
size_t const local_env_buffer_size = 256; int const milliseconds_per_day = 24 * 60 * 60 * 1000;}
// DST start and end structures:static transitiondate dststart = { -1, 0, 0 };static transitiondate dstend = { -1, 0, 0 };
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+//// The _tzset() family of functions////-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+// Gets the value of the TZ environment variable. If there is no TZ environment// variable or if we do not have access to the environment, nullptr is returned.// If the value of the TZ variable fits into the local_buffer, it is stored there// and a pointer to the local_buffer is returned. Otherwise, a buffer is// dynamically allocated, the value is stored into that buffer, and a pointer to// that buffer is returned. In this case, the caller is responsible for freeing// the buffer.static wchar_t* get_tz_environment_variable(wchar_t (&local_buffer)[local_env_buffer_size]) throw(){ size_t required_length; errno_t const status = _wgetenv_s(&required_length, local_buffer, local_env_buffer_size, L"TZ"); if (status == 0) { return local_buffer; }
if (status != ERANGE) { return nullptr; }
__crt_unique_heap_ptr<wchar_t> dynamic_buffer(_malloc_crt_t(wchar_t, required_length)); if (dynamic_buffer.get() == nullptr) { return nullptr; }
size_t actual_length; if (_wgetenv_s(&actual_length, dynamic_buffer.get(), required_length, L"TZ") != 0) { return nullptr; }
return dynamic_buffer.detach();}
static void __cdecl tzset_os_copy_to_tzname(const wchar_t * const timezone_name, wchar_t * const wide_tzname, char * const narrow_tzname, unsigned int const code_page){ // Maximum time zone name from OS is 32 characters long // (see https://docs.microsoft.com/en-us/windows/desktop/api/timezoneapi/ns-timezoneapi-_time_zone_information) _ERRCHECK(wcsncpy_s(wide_tzname, _TZ_STRINGS_SIZE, timezone_name, 32));
// Invalid characters are replaced by closest approximation or default character. // On other failure, leave narrow tzname blank. __acrt_WideCharToMultiByte( code_page, 0, timezone_name, -1, narrow_tzname, _TZ_STRINGS_SIZE, // Passing -1 as source size, so null terminator included. nullptr, nullptr );}
// Handles the _tzset if and only if there is no TZ environment variable. In// this case, we attempt to use the time zone information from the system.static void __cdecl tzset_from_system_nolock() throw(){ _BEGIN_SECURE_CRT_DEPRECATION_DISABLE char** tzname = _tzname; wchar_t** wide_tzname = __wide_tzname(); _END_SECURE_CRT_DEPRECATION_DISABLE
long timezone = 0; int daylight = 0; long dstbias = 0; _ERRCHECK(_get_timezone(&timezone)); _ERRCHECK(_get_daylight(&daylight)); _ERRCHECK(_get_dstbias (&dstbias ));
// If there is a last_wide_tz already, discard it: _free_crt(last_wide_tz); last_wide_tz = nullptr;
if (GetTimeZoneInformation(&tz_info) != 0xFFFFFFFF) { // Record that the API was used: tz_api_used = 1;
// Derive _timezone value from Bias and StandardBias fields. timezone = tz_info.Bias * 60;
if (tz_info.StandardDate.wMonth != 0) timezone += tz_info.StandardBias * 60;
// Check to see if there is a daylight time bias. Since the StandardBias // has been added into _timezone, it must be compensated for in the // value computed for _dstbias: if (tz_info.DaylightDate.wMonth != 0 && tz_info.DaylightBias != 0) { daylight = 1; dstbias = (tz_info.DaylightBias - tz_info.StandardBias) * 60; } else { daylight = 0;
// Set the bias to 0 because GetTimeZoneInformation may return // TIME_ZONE_ID_DAYLIGHT even though there is no DST (e.g., in NT // 3.51, this can happen if automatic DST adjustment is disabled // in the Control Panel. dstbias = 0; }
memset(wide_tzname[0], 0, _TZ_STRINGS_SIZE * sizeof(wchar_t)); memset(wide_tzname[1], 0, _TZ_STRINGS_SIZE * sizeof(wchar_t)); memset(tzname[0], 0, _TZ_STRINGS_SIZE); memset(tzname[1], 0, _TZ_STRINGS_SIZE);
// Try to grab the name strings for both the time zone and the daylight // zone. Note the wide character strings in tz_info must be converted // to multibyte character strings. The locale code page must be used // for this. Note that if setlocale() has not yet been called with // LC_ALL or LC_CTYPE, then the code page will be 0, which is CP_ACP, // so we will use the host's default ANSI code page. // // CRT_REFACTOR TODO We use the current locale for this transformation. // If per-thread locale has been enabled for this thread, then we'll be // using this thread's locale to update a global variable that is // accessed from multiple threads. Does the time zone information also // need to be stored per-thread? unsigned const code_page = ___lc_codepage_func();
tzset_os_copy_to_tzname(tz_info.StandardName, wide_tzname[0], tzname[0], code_page); tzset_os_copy_to_tzname(tz_info.DaylightName, wide_tzname[1], tzname[1], code_page); }
_set_timezone(timezone); _set_daylight(daylight); _set_dstbias(dstbias);}
static void __cdecl tzset_env_copy_to_tzname(const wchar_t * const tz_env, wchar_t * const wide_tzname, char * const narrow_tzname, rsize_t const tzname_length){ _ERRCHECK(wcsncpy_s(wide_tzname, _TZ_STRINGS_SIZE, tz_env, tzname_length));
// Historically when getting _tzname via TZ, the narrow environment was used to populate _tzname when getting _tzname. // The narrow environment is always encoded in the ACP (so _tzname was encoded in the ACP when coming from TZ), but // when getting _tzname from the OS, the current active code page (set via setlocale()) was used instead. // To maintain behavior compatibility, we remain intentionally inconsistent with // how _tzname is generated when getting time zone information from the OS by explicitly encoding with the ACP. // UTF-8 mode is opt-in, so we can correct this inconsistency when the current code page is UTF-8.
// Invalid characters are replaced by closest approximation or default character. // On other failure, simply leave _tzname blank. __acrt_WideCharToMultiByte( __acrt_get_utf8_acp_compatibility_codepage(), 0, wide_tzname, static_cast<int>(tzname_length), narrow_tzname, _TZ_STRINGS_SIZE - 1, // Leave room for null terminator nullptr, nullptr);}
static void __cdecl tzset_from_environment_nolock(_In_z_ wchar_t* tz_env) throw(){ _BEGIN_SECURE_CRT_DEPRECATION_DISABLE char** tzname = _tzname; wchar_t** wide_tzname = __wide_tzname(); _END_SECURE_CRT_DEPRECATION_DISABLE
long timezone = 0; int daylight = 0; _ERRCHECK(_get_timezone(&timezone)); _ERRCHECK(_get_daylight(&daylight));
// Check to see if the TZ value is unchanged from an earlier call to this // function. If it hasn't changed, we have no work to do: if (last_wide_tz != nullptr && wcscmp(tz_env, last_wide_tz) == 0) { return; }
// Update the global last_wide_tz variable: auto new_wide_tz = _malloc_crt_t(wchar_t, wcslen(tz_env) + 1); if (!new_wide_tz) { return; }
_free_crt(last_wide_tz); last_wide_tz = new_wide_tz.detach();
_ERRCHECK(wcscpy_s(last_wide_tz, wcslen(tz_env) + 1, tz_env));
// Process TZ value and update _tzname, _timezone and _daylight. memset(wide_tzname[0], 0, _TZ_STRINGS_SIZE * sizeof(wchar_t)); memset(wide_tzname[1], 0, _TZ_STRINGS_SIZE * sizeof(wchar_t)); memset(tzname[0], 0, _TZ_STRINGS_SIZE); memset(tzname[1], 0, _TZ_STRINGS_SIZE);
rsize_t const tzname_length = 3;
// Copy standard time zone name (index 0) tzset_env_copy_to_tzname(tz_env, wide_tzname[0], tzname[0], tzname_length);
// Skip first few characters if present. for (rsize_t i = 0; i < tzname_length; ++i) { if (*tz_env) { ++tz_env; } }
// The time difference is of the form: // [+|-]hh[:mm[:ss]] // Check for the minus sign first: bool const is_negative_difference = *tz_env == L'-'; if (is_negative_difference) { ++tz_env; }
wchar_t * dummy; int const decimal_base = 10;
// process, then skip over, the hours timezone = wcstol(tz_env, &dummy, decimal_base) * 3600; while (*tz_env == '+' || (*tz_env >= L'0' && *tz_env <= L'9')) { ++tz_env; }
// Check if minutes were specified: if (*tz_env == L':') { // Process, then skip over, the minutes timezone += wcstol(++tz_env, &dummy, decimal_base) * 60; while (*tz_env >= L'0' && *tz_env <= L'9') { ++tz_env; }
// Check if seconds were specified: if (*tz_env == L':') { // Process, then skip over, the seconds: timezone += wcstol(++tz_env, &dummy, decimal_base); while (*tz_env >= L'0' && *tz_env <= L'9') { ++tz_env; } } }
if (is_negative_difference) { timezone = -timezone; }
// Finally, check for a DST zone suffix: daylight = *tz_env ? 1 : 0;
if (daylight) { // Copy daylight time zone name (index 1) tzset_env_copy_to_tzname(tz_env, wide_tzname[1], tzname[1], tzname_length); }
_set_timezone(timezone); _set_daylight(daylight);}
static void __cdecl tzset_nolock() throw(){ // Clear the flag indicated whether GetTimeZoneInformation was used. tz_api_used = 0;
// Set year fields of dststart and dstend structures to -1 to ensure // they are recomputed as after this dststart.yr = dstend.yr = -1;
// Get the value of the TZ environment variable: wchar_t local_env_buffer[local_env_buffer_size]; wchar_t* const tz_env = get_tz_environment_variable(local_env_buffer);
// If the buffer ended up being dynamically allocated, make sure we // clean it up before we return: __crt_unique_heap_ptr<wchar_t> tz_env_cleanup(tz_env == local_env_buffer ? nullptr : tz_env);
// If the environment variable is not available for whatever reason, update // without using the environment (note that unless the Desktop CRT is loaded // and we have access to non-MSDK APIs, we will always tak this path). if (tz_env == nullptr || tz_env[0] == '\0') return tzset_from_system_nolock();
return tzset_from_environment_nolock(tz_env);}
// Sets the time zone information and calculates whether we are currently in// Daylight Savings Time. This reads the TZ environment variable, if that// variable exists and can be read by the process; otherwise, the system is// queried for the current time zone state. The _daylight, _timezone, and// _tzname global variables are updated accordingly.extern "C" void __cdecl _tzset(){ __acrt_lock(__acrt_time_lock); __try { tzset_nolock(); } __finally { __acrt_unlock(__acrt_time_lock); } __endtry}
// This function may be called to ensure that the time zone information ha sbeen// set at least once. If the time zone information has not yet been set, this// function sets it.extern "C" void __cdecl __tzset(){ auto const first_time = tzset_init_state.dangerous_get_state_array() + __crt_state_management::get_current_state_index();
if (__crt_interlocked_read(first_time) != 0) { return; }
__acrt_lock(__acrt_time_lock); __try { if (__crt_interlocked_read(first_time) != 0) { __leave; }
tzset_nolock();
_InterlockedIncrement(first_time); } __finally { __acrt_unlock(__acrt_time_lock); } __endtry}
//-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+//// The _isindst() family of functions////-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+// Converts the format of a transition date specification to a value of a// transitiondate structure. The dststart and dstend global variables are// filled in with the converted date.static void __cdecl cvtdate( transition_type const trantype, // start or end of DST date_type const datetype, // Day-in-month or absolute date int const year, // Year, as an offset from 1900 int const month, // Month, where 0 is January int const week, // Week of month, if datetype is day-in-month int const dayofweek, // Day of week, if datetype is day-in-month int const date, // Date of month (1 - 31) int const hour, // Hours (0 - 23) int const min, // Minutes (0 - 59) int const sec, // Seconds (0 - 59) int const msec // Milliseconds (0 - 999) ) throw(){ int yearday; int monthdow; long dstbias = 0;
if (datetype == date_type::day_in_month) { // Figure out the year-day of the start of the month: yearday = 1 + (__crt_time_is_leap_year(year) ? _lpdays[month - 1] : _days[month - 1]);
// Figureo ut the day of the week of the start of the month: monthdow = (yearday + ((year - 70) * 365) + __crt_time_elapsed_leap_years(year) + _BASE_DOW) % 7;
// Figure out the year-day of the transition date: if (monthdow <= dayofweek) yearday += (dayofweek - monthdow) + (week - 1) * 7; else yearday += (dayofweek - monthdow) + week * 7;
// We may have to adjust the calculation above if week == 5 (meaning the // last instance of the day in the month). Check if the year falls // beyond after month and adjust accordingly: int const days_to_compare = __crt_time_is_leap_year(year) ? _lpdays[month] : _days[month];
if (week == 5 && yearday > days_to_compare) { yearday -= 7; } } else { yearday = __crt_time_is_leap_year(year) ? _lpdays[month - 1] : _days[month - 1];
yearday += date; }
if (trantype == transition_type::start_of_dst) { dststart.yd = yearday; dststart.ms = msec + (1000 * (sec + 60 * (min + 60 * hour)));
// Set the year field of dststart so that unnecessary calls to cvtdate() // may be avoided: dststart.yr = year; } else // end_of_dst { dstend.yd = yearday; dstend.ms = msec + (1000 * (sec + 60 * (min + 60 * hour)));
// The converted date is still a DST date. We must convert to a standard // (local) date while being careful the millisecond field does not // overflow or underflow _ERRCHECK(_get_dstbias(&dstbias)); dstend.ms += (dstbias * 1000); if (dstend.ms < 0) { dstend.ms += milliseconds_per_day; dstend.yd--; } else if (dstend.ms >= milliseconds_per_day) { dstend.ms -= milliseconds_per_day; dstend.yd++; }
// Set the year field of dstend so that unnecessary calls to cvtdate() // may be avoided: dstend.yr = year; }
return;}
// Implementation Details: Note that there are two ways that the Daylight// Savings Time transition data may be returned by GetTimeZoneInformation. The// first is a day-in-month format, which is similar to what is used in the USA.// The transition date is given as the n'th occurrence of a specified day of the// week in a specified month. The second is as an absolute date. The two cases// are distinguished by the value of the wYear field of the SYSTEMTIME structure// (zero denotes a day-in-month format).static int __cdecl _isindst_nolock(tm* const tb) throw(){ int daylight = 0; _ERRCHECK(_get_daylight(&daylight)); if (daylight == 0) return 0;
// Compute (or recompute) the transition dates for Daylight Savings Time // if necessary. The yr fields of dststart and dstend are compared to the // year of interest to determine necessity. if (tb->tm_year != dststart.yr || tb->tm_year != dstend.yr) { if (tz_api_used) { // Convert the start of daylight savings time to dststart: if (tz_info.DaylightDate.wYear == 0) { cvtdate( transition_type::start_of_dst, date_type::day_in_month, tb->tm_year, tz_info.DaylightDate.wMonth, tz_info.DaylightDate.wDay, tz_info.DaylightDate.wDayOfWeek, 0, tz_info.DaylightDate.wHour, tz_info.DaylightDate.wMinute, tz_info.DaylightDate.wSecond, tz_info.DaylightDate.wMilliseconds); } else { cvtdate( transition_type::start_of_dst, date_type::absolute_date, tb->tm_year, tz_info.DaylightDate.wMonth, 0, 0, tz_info.DaylightDate.wDay, tz_info.DaylightDate.wHour, tz_info.DaylightDate.wMinute, tz_info.DaylightDate.wSecond, tz_info.DaylightDate.wMilliseconds); }
// Convert start of standard time to dstend: if (tz_info.StandardDate.wYear == 0) { cvtdate( transition_type::end_of_dst, date_type::day_in_month, tb->tm_year, tz_info.StandardDate.wMonth, tz_info.StandardDate.wDay, tz_info.StandardDate.wDayOfWeek, 0, tz_info.StandardDate.wHour, tz_info.StandardDate.wMinute, tz_info.StandardDate.wSecond, tz_info.StandardDate.wMilliseconds); } else { cvtdate( transition_type::end_of_dst, date_type::absolute_date, tb->tm_year, tz_info.StandardDate.wMonth, 0, 0, tz_info.StandardDate.wDay, tz_info.StandardDate.wHour, tz_info.StandardDate.wMinute, tz_info.StandardDate.wSecond, tz_info.StandardDate.wMilliseconds); } } else { // The GetTimeZoneInformation API was not used, or failed. We use // the USA Daylight Savings Time rules as a fallback. int startmonth = 3; // March int startweek = 2; // Second week int endmonth = 11;// November int endweek = 1; // First week
// The rules changed in 2007: if (107 > tb->tm_year) { startmonth = 4; // April startweek = 1; // first week endmonth = 10;// October endweek = 5; // last week }
cvtdate( transition_type::start_of_dst, date_type::day_in_month, tb->tm_year, startmonth, startweek, 0, // Sunday 0, 2, // 02:00 (2 AM) 0, 0, 0);
cvtdate( transition_type::end_of_dst, date_type::day_in_month, tb->tm_year, endmonth, endweek, 0, // Sunday 0, 2, // 02:00 (2 AM) 0, 0, 0); } }
// Handle simple cases first: if (dststart.yd < dstend.yd) { // Northern hemisphere ordering: if (tb->tm_yday < dststart.yd || tb->tm_yday > dstend.yd) return 0;
if (tb->tm_yday > dststart.yd && tb->tm_yday < dstend.yd) return 1; } else { // Southern hemisphere ordering: if (tb->tm_yday < dstend.yd || tb->tm_yday > dststart.yd) return 1;
if (tb->tm_yday > dstend.yd && tb->tm_yday < dststart.yd) return 0; }
long const ms = 1000 * (tb->tm_sec + 60 * tb->tm_min + 3600 * tb->tm_hour);
if (tb->tm_yday == dststart.yd) {
return ms >= dststart.ms ? 1 : 0; } else { return ms < dstend.ms ? 1 : 0; }}
// Tests if the time represented by the tm structure falls in Daylight Savings// Time or not. The Daylight Savings Time rules are obtained from the operating// system if GetTimeZoneInformation was used by _tzset() to obtain the time zone// information; otherwise, the USA Daylight Savings Time rules (post-1986) are// used.//// Returns 1 if the time is in Daylight Savings Time; returns 0 otherwise.extern "C" int __cdecl _isindst(tm* const tb){ int retval = 0;
__acrt_lock(__acrt_time_lock); __try { retval = _isindst_nolock(tb); } __finally { __acrt_unlock(__acrt_time_lock); } __endtry
return retval;}