Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
03d999d
Handle time zone environment variable having international characters.
jonsimantov May 24, 2023
95c473e
Fix environment variable string.
jonsimantov May 25, 2023
3610b85
Fix build error.
jonsimantov May 25, 2023
81fbcae
Merge branch 'main' into remote-config-international-characters
jonsimantov May 25, 2023
a715c63
Try a French time zone.
jonsimantov May 25, 2023
d935bba
Merge branch 'remote-config-international-characters' of https://gith…
jonsimantov May 25, 2023
d7a0249
Fix workflow.
jonsimantov May 25, 2023
697df16
Escape characters.
jonsimantov May 25, 2023
c76087f
Use GITHUB_ENV
jonsimantov May 25, 2023
11cd846
Fix shell command.
jonsimantov May 25, 2023
e327cfd
Change the approach, using tzset() instead.
jonsimantov May 25, 2023
86ae732
Logging.
jonsimantov May 25, 2023
bda40f0
Format code.
jonsimantov May 25, 2023
8536968
Set the time zone via tzutil.
jonsimantov May 25, 2023
a93ad48
List time zones available.
jonsimantov May 26, 2023
02102d0
Remove comment.
jonsimantov May 26, 2023
4845994
Set to Paris time.
jonsimantov May 26, 2023
9187cbc
Fix test timezone.
jonsimantov May 26, 2023
f7b6d66
New method using ICL.
jonsimantov May 26, 2023
26c381a
Clean up string conversion.
jonsimantov May 27, 2023
95e1a03
Clean up log.
jonsimantov May 27, 2023
75b3209
Fix compile error.
jonsimantov May 27, 2023
ace40f0
Add ICU DLL.
jonsimantov May 27, 2023
bc81b27
Fix log; test France.
jonsimantov May 27, 2023
bde33b0
Initialize variables.
jonsimantov May 28, 2023
48aed59
Cast
jonsimantov May 28, 2023
dd2aafa
Don't get the daylight zone.
jonsimantov May 28, 2023
8a7c7fe
Fix
jonsimantov May 28, 2023
e5da424
Fix 2
jonsimantov May 29, 2023
097466b
Fix daylight mode fallback.
jonsimantov May 29, 2023
f52f75d
Remove French language test code.
jonsimantov May 29, 2023
149c9c5
Change log messages to debug.
jonsimantov May 31, 2023
58c6ac2
Change errors.
jonsimantov May 31, 2023
14bfe83
Merge branch 'main' into remote-config-international-characters
jonsimantov May 31, 2023
55792c5
Add icu.dll to library dependencies.
jonsimantov May 31, 2023
df16b68
Update release notes.
jonsimantov May 31, 2023
decdce8
Remove version number
jonsimantov May 31, 2023
23f9bd7
Added icu.dll dep to locale unit test.
jonsimantov Jun 1, 2023
bc1bb5f
Update release note.
jonsimantov Jun 1, 2023
66ca4a1
Remove extraneous printfs.
jonsimantov Jun 1, 2023
ebb0510
Add comment to test cmake.
jonsimantov Jun 1, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 89 additions & 16 deletions app/src/locale.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,20 @@
#elif FIREBASE_PLATFORM_WINDOWS
#include <time.h>
#include <windows.h>
// To convert Windows time zone names to IANA time zone names:
#define UCHAR_TYPE wchar_t
#include <icu.h>
#elif FIREBASE_PLATFORM_LINUX
#include <clocale>
#include <ctime>
#include <locale>
#else
#error "Unknown platform."
#endif // platform selector

#include <algorithm>
#include <codecvt>
#include <locale>
#include <string>
#include <vector>

namespace firebase {
Expand Down Expand Up @@ -90,22 +95,90 @@ std::string GetLocale() {
// Get the current time zone, e.g. "US/Pacific"
std::string GetTimezone() {
#if FIREBASE_PLATFORM_WINDOWS
// If "TZ" environment variable is defined, use it, else use _get_tzname.
int tz_bytes = GetEnvironmentVariable("TZ", nullptr, 0);
if (tz_bytes > 0) {
std::vector<char> buffer(tz_bytes);
GetEnvironmentVariable("TZ", &buffer[0], tz_bytes);
return std::string(&buffer[0]);
static bool tz_was_set = false;
if (!tz_was_set) {
_tzset(); // Set the time zone used by the below functions, based on OS
// settings or the TZ variable, as appropriate.
tz_was_set = true;
}
int daylight; // daylight savings time?
if (_get_daylight(&daylight) != 0) return "";
size_t length = 0; // get the needed string length
if (_get_tzname(&length, nullptr, 0, daylight ? 1 : 0) != 0) return "";
std::vector<char> namebuf(length);
if (_get_tzname(&length, &namebuf[0], length, daylight ? 1 : 0) != 0)
return "";
std::string name_str(&namebuf[0]);
return name_str;
// Get the standard time zone name.
std::string windows_tz_utf8;
{
size_t length = 0; // get the needed string length
if (_get_tzname(&length, nullptr, 0, 0) != 0) return "";
std::vector<char> namebuf(length);
if (_get_tzname(&length, &namebuf[0], length, 0) != 0) return "";
windows_tz_utf8 = std::string(&namebuf[0]);
}

// Convert time zone name to wide string
std::wstring_convert<std::codecvt_utf8<wchar_t>> to_utf16;
std::wstring windows_tz_utf16 = to_utf16.from_bytes(windows_tz_utf8);

std::string locale_name = GetLocale();
wchar_t iana_time_zone_buffer[128];
bool got_time_zone = false;
if (locale_name.size() >= 5) {
wcscpy(iana_time_zone_buffer, L"");
UErrorCode error_code = (UErrorCode)0;
int32_t size = 0;
// Try time zone first with the region code returned above, assuming it's at
// least 5 characters. For example, "en_US" -> "US"
std::string region_code = std::string(&locale_name[3], 2);
size = ucal_getTimeZoneIDForWindowsID(
windows_tz_utf16.c_str(), -1, region_code.c_str(),
iana_time_zone_buffer,
sizeof(iana_time_zone_buffer) / sizeof(iana_time_zone_buffer[0]),
&error_code);
got_time_zone = (U_SUCCESS(error_code) && size > 0);
if (!got_time_zone) {
LogDebug(
"Couldn't convert Windows time zone '%s' with region '%s' to IANA: "
"%s (%x). Falling back to non-region time zone conversion.",
windows_tz_utf8.c_str(), region_code.c_str(), u_errorName(error_code),
error_code);
}
}
if (!got_time_zone) {
wcscpy(iana_time_zone_buffer, L"");
UErrorCode error_code = (UErrorCode)0;
int32_t size = 0;
// Try without specifying a region
size = ucal_getTimeZoneIDForWindowsID(
windows_tz_utf16.c_str(), -1, nullptr, iana_time_zone_buffer,
sizeof(iana_time_zone_buffer) / sizeof(iana_time_zone_buffer[0]),
&error_code);
got_time_zone = (U_SUCCESS(error_code) && size > 0);
if (!got_time_zone) {
// Couldn't convert to IANA
LogDebug(
"Couldn't convert time zone '%s' to IANA: %s (%x). Falling back to "
"Windows time zone name.",
windows_tz_utf8.c_str(), u_errorName(error_code), error_code);
}
}
if (!got_time_zone) {
// Return the Windows time zone ID as a backup.
// In this case, we need to get the correct daylight savings time
// setting to get the right name.
int daylight = 0; // daylight savings time?
if (_get_daylight(&daylight) != 0) return windows_tz_utf8;
if (daylight) {
size_t length = 0; // get the needed string length
if (_get_tzname(&length, nullptr, 0, 1) != 0) return windows_tz_utf8;
std::vector<char> namebuf(length);
if (_get_tzname(&length, &namebuf[0], length, 1) != 0)
return windows_tz_utf8;
windows_tz_utf8 = std::string(&namebuf[0]);
}
return windows_tz_utf8;
}

std::wstring iana_tz_utf16(iana_time_zone_buffer);
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> to_utf8;
std::string iana_tz_utf8 = to_utf8.to_bytes(iana_tz_utf16);
return iana_tz_utf8;

#elif FIREBASE_PLATFORM_LINUX
// If TZ environment variable is defined and not empty, use it, else use
// tzname.
Expand Down
10 changes: 9 additions & 1 deletion app/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,19 @@ firebase_cpp_cc_test(firebase_app_path_test
firebase_app
)

if (MSVC)
# On Windows, our locale library's GetTimezone() function requires the icu.dll
# system library for converting from Windows time zones to IANA time zones.
set(LOCALE_TEST_DEPS icu)
else()
set(LOCALE_TEST_DEPS)
endif()

firebase_cpp_cc_test(firebase_app_locale_test
SOURCES
locale_test.cc
DEPENDS
firebase_app
firebase_app ${LOCALE_TEST_DEPS}
)

firebase_cpp_cc_test(firebase_app_callback_test
Expand Down
9 changes: 8 additions & 1 deletion release_build_files/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ App Check | `advapi32, ws2_32, crypt32`
Firestore | `advapi32, ws2_32, crypt32, rpcrt4, ole32, shell32, dbghelp, bcrypt`
Functions | `advapi32, ws2_32, crypt32, rpcrt4, ole32`
Realtime Database | `advapi32, ws2_32, crypt32, iphlpapi, psapi, userenv, shell32`
Remote Config | `advapi32, ws2_32, crypt32, rpcrt4, ole32`
Remote Config | `advapi32, ws2_32, crypt32, rpcrt4, ole32, icu`
Storage | `advapi32, ws2_32, crypt32`

## Getting Started
Expand Down Expand Up @@ -627,6 +627,13 @@ workflow use only during the development of your app, not for publicly shipping
code.

## Release Notes
### Upcoming Release
- Changes
- Remote Config (Desktop): Fixed handling of time zones on Windows when the
time zone name in the current system language contains an accented
character or apostrophe. This adds a requirement for applications using
Remote Config on Windows desktop to link the "icu.dll" system library.

### 11.1.0
- Changes
- General (Android): Update to Firebase Android BoM version 32.1.0.
Expand Down
5 changes: 5 additions & 0 deletions remote_config/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ else()
${PROJECT_BINARY_DIR}/..)
set(additional_link_LIB
firebase_rest_lib)
if(MSVC)
# Remote Config on Windows requires the icu.dll system library for time zone
# handling.
set(additional_link_LIB ${additional_link_LIB} icu)
endif()
endif()

add_library(firebase_remote_config STATIC
Expand Down
2 changes: 1 addition & 1 deletion remote_config/integration_test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ else()
"-framework Security"
)
elseif(MSVC)
set(ADDITIONAL_LIBS advapi32 ws2_32 crypt32)
set(ADDITIONAL_LIBS advapi32 ws2_32 crypt32 icu)
else()
set(ADDITIONAL_LIBS pthread)
endif()
Expand Down