Update timezone and locale codes

* Use ICU function for reading default system timezone
* Update default locale reading logic(remove POSIX postfix & ignore 'C' locale)
* Refactor global timezone, vzone, tzname codes

Signed-off-by: Seonghyun Kim <sh8281.kim@samsung.com>
This commit is contained in:
Seonghyun Kim 2023-04-20 12:04:45 +09:00 committed by Hyukwoo Park
commit 7a98a9ba52
11 changed files with 158 additions and 34 deletions

View file

@ -64,6 +64,7 @@ jobs:
strategy:
matrix:
arch: [x86, x86_64]
api: [28]
steps:
- uses: actions/checkout@v3
with:
@ -86,12 +87,12 @@ jobs:
path: |
~/.android/avd/*
~/.android/adb*
key: avd-29-${{ matrix.arch }}
key: avd-${{ matrix.api }}-${{ matrix.arch }}
- name: Create AVD and generate snapshot for caching
if: steps.avd-cache.outputs.cache-hit != 'true'
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 29
api-level: ${{ matrix.api }}
arch: ${{ matrix.arch }}
force-avd-creation: false
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
@ -100,7 +101,7 @@ jobs:
- name: Run tests
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 29
api-level: ${{ matrix.api }}
arch: ${{ matrix.arch }}
force-avd-creation: false
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none

View file

@ -12,6 +12,7 @@
android:supportsRtl="true"
android:theme="@style/Theme.EscargotAndroidTestShell"
tools:targetApi="31">
<profileable android:shell="true" />
<activity
android:name=".MainActivity"
android:exported="true">

View file

@ -111,7 +111,6 @@ static Value temporalInstantFromEpoch(ExecutionState& state, size_t argc, Value*
static Value builtinTemporalNowTimeZone(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
{
state.context()->vmInstance()->ensureTimezone();
return Value(String::fromUTF8(state.context()->vmInstance()->timezoneID().c_str(), state.context()->vmInstance()->timezoneID().length(), true));
}

View file

@ -182,9 +182,6 @@ static void toDateTimeOptionsTest(ExecutionState& state, Value options, AtomicSt
static String* defaultTimeZone(ExecutionState& state)
{
#if !defined(OS_WINDOWS_UWP)
state.context()->vmInstance()->ensureTimezone();
#endif
return String::fromUTF8(state.context()->vmInstance()->timezoneID().data(), state.context()->vmInstance()->timezoneID().length());
}

View file

@ -281,7 +281,7 @@ time64_t DateObject::applyLocalTimezoneOffset(ExecutionState& state, time64_t t)
// roughly check range before calling yearFromTime function
#if defined(ENABLE_ICU) && !defined(OS_WINDOWS_UWP)
stdOffset = vzone_getRawOffset(state.context()->vmInstance()->timezone());
stdOffset = vzone_getRawOffset(state.context()->vmInstance()->vzone());
#else
stdOffset = 0;
#endif
@ -301,7 +301,7 @@ time64_t DateObject::applyLocalTimezoneOffset(ExecutionState& state, time64_t t)
t += msBetweenYears;
#if defined(ENABLE_ICU) && !defined(OS_WINDOWS_UWP)
vzone_getOffset3(state.context()->vmInstance()->timezone(), t, true, stdOffset, dstOffset, succ);
vzone_getOffset3(state.context()->vmInstance()->vzone(), t, true, stdOffset, dstOffset, succ);
#else
dstOffset = 0;
#endif
@ -1243,7 +1243,7 @@ void DateObject::resolveCache(ExecutionState& state)
int32_t stdOffset = 0, dstOffset = 0;
#if defined(ENABLE_ICU) && !defined(OS_WINDOWS_UWP)
UErrorCode succ = U_ZERO_ERROR;
vzone_getOffset3(state.context()->vmInstance()->timezone(), t, true, stdOffset, dstOffset, succ);
vzone_getOffset3(state.context()->vmInstance()->vzone(), t, true, stdOffset, dstOffset, succ);
#endif
m_cachedLocal.isdst = dstOffset == 0 ? 0 : 1;
@ -1300,24 +1300,23 @@ String* DateObject::toTimeString(ExecutionState& state)
{
RESOLVECACHE(state);
if (IS_VALID_TIME(m_primitiveValue)) {
char buffer[32];
char buffer[256];
int tzOffsetAsMin = -getTimezoneOffset(state); // 540
int tzOffsetHour = (tzOffsetAsMin / const_Date_minutesPerHour);
int tzOffsetMin = ((tzOffsetAsMin / (double)const_Date_minutesPerHour) - tzOffsetHour) * 60;
tzOffsetHour *= 100;
const std::string& timeZoneName = state.context()->vmInstance()->tzname(m_cachedLocal.isdst ? 1 : 0);
#if defined(OS_WINDOWS)
const char* timeZoneName = _tzname[m_cachedLocal.isdst ? 1 : 0];
snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT%s%04d", getHours(state), getMinutes(state), getSeconds(state), (tzOffsetAsMin < 0) ? "-" : "+", std::abs(tzOffsetHour + tzOffsetMin));
StringBuilder sb;
sb.appendString(buffer);
sb.appendChar('(');
sb.appendString(String::fromUTF8(timeZoneName, strlen(timeZoneName)));
sb.appendString(String::fromUTF8(timeZoneName.data(), timeZoneName.length()));
sb.appendChar(')');
return sb.finalize();
#else
const char* timeZoneName = m_cachedLocal.isdst ? tzname[1] : tzname[0];
snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT%s%04d (%s)", getHours(state), getMinutes(state), getSeconds(state), (tzOffsetAsMin < 0) ? "-" : "+", std::abs(tzOffsetHour + tzOffsetMin), timeZoneName);
return new ASCIIString(buffer);
snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT%s%04d (%s)", getHours(state), getMinutes(state), getSeconds(state), (tzOffsetAsMin < 0) ? "-" : "+", std::abs(tzOffsetHour + tzOffsetMin), timeZoneName.data());
return String::fromUTF8(buffer, strnlen(buffer, sizeof(buffer)));
#endif
} else {
return new ASCIIString(invalidDate);

View file

@ -52,6 +52,8 @@ NT_TIB* getTIB()
#include <pthread.h>
#endif
#include <time.h> // for tzname
namespace Escargot {
#if defined(ENABLE_WASM)
@ -436,8 +438,14 @@ VMInstance::VMInstance(const char* locale, const char* timezone, const char* bas
m_locale = uloc_getDefault();
#endif
}
// remove posix postfix(A_POSIX -> A)
if (m_locale.find("_POSIX") == m_locale.length() - 6) {
m_locale = m_locale.substr(0, m_locale.length() - 6);
}
#endif
// add gc event callback
GCEventListenerSet& list = ThreadLocal::gcEventListenerSet();
list.ensureMarkStartListeners()->push_back(std::make_pair(vmMarkStartCallback, this));
@ -566,19 +574,89 @@ static std::string findTimezone()
#endif
void VMInstance::ensureTimezone()
void VMInstance::ensureVZone()
{
ensureTimezoneID();
#if !defined(OS_WINDOWS_UWP)
auto u16 = utf8StringToUTF16String(timezoneID().data(), timezoneID().size());
m_timezone = vzone_openID(u16.data(), u16.size());
#endif
}
void VMInstance::ensureTimezoneID()
{
if (m_timezoneID == "") {
m_timezoneID = findTimezone();
} else {
#if !defined(OS_WINDOWS_UWP)
tzset();
#endif
}
}
void VMInstance::ensureTzname()
{
if (m_tzname[0].size()) {
return;
}
#if !defined(OS_WINDOWS_UWP)
auto u16 = utf8StringToUTF16String(m_timezoneID.data(), m_timezoneID.size());
m_timezone = vzone_openID(u16.data(), u16.size());
auto u16timezoneID = String::fromUTF8(timezoneID().data(), timezoneID().size())->toUTF16StringData();
UErrorCode status = U_ZERO_ERROR;
UCalendar* cal = ucal_open(u16timezoneID.data(), u16timezoneID.length(), "en", UCAL_DEFAULT, &status);
if (U_SUCCESS(status)) {
UChar name[128];
int32_t nameLength = ucal_getTimeZoneDisplayName(cal, UCalendarDisplayNameType::UCAL_STANDARD, locale().data(), name, sizeof(name) / sizeof(UChar), &status);
if (U_SUCCESS(status)) {
m_tzname[0] = (new UTF16String(name, nameLength))->toNonGCUTF8StringData();
nameLength = ucal_getTimeZoneDisplayName(cal, UCalendarDisplayNameType::UCAL_DST, locale().data(), name, sizeof(name) / sizeof(UChar), &status);
if (U_SUCCESS(status)) {
m_tzname[1] = (new UTF16String(name, nameLength))->toNonGCUTF8StringData();
}
}
ucal_close(cal);
if (m_tzname[0].size() && m_tzname[1].size()) {
return;
}
}
// Fallback route
#if defined(OS_WINDOWS)
size_t s;
char tznameResult[100];
_get_tzname(&s, tznameResult, sizeof(tznameResult), 0);
m_tzname[0] = tznameResult;
_get_tzname(&s, tznameResult, sizeof(tznameResult), 1);
m_tzname[1] = tznameResult;
#else
// FIXME tzset function is MT-safe but changing TZ value and read tzname is not MT-safe
#if defined(ENABLE_THREADING)
static std::mutex s_mutex;
std::lock_guard<std::mutex> guard(s_mutex);
#endif
char* oldTZ = getenv("TZ");
setenv("TZ", timezoneID().data(), 1);
tzset();
m_tzname[0] = ::tzname[0];
m_tzname[1] = ::tzname[1];
if (oldTZ) {
setenv("TZ", oldTZ, 1);
} else {
unsetenv("TZ");
}
tzset();
#endif
}
#else
void VMInstance::ensureTzname()
{
if (m_tzname[0].size()) {
return;
}
tzset();
#if defined(OS_WINDOWS)
m_tzname[0] = ::_tzname[0];
m_tzname[1] = ::_tzname[1];
#else
m_tzname[0] = ::tzname[0];
m_tzname[1] = ::tzname[1];
#endif
}
#endif

View file

@ -123,24 +123,29 @@ public:
return m_locale;
}
const std::string& timezoneID()
{
return m_timezoneID;
}
void ensureTimezone();
#if !defined(OS_WINDOWS_UWP)
VZone* timezone()
VZone* vzone()
{
if (m_timezone == nullptr) {
ensureTimezone();
ensureVZone();
}
return m_timezone;
}
#endif
const std::string& timezoneID()
{
ensureTimezoneID();
return m_timezoneID;
}
#endif
const std::string& tzname(size_t i)
{
ensureTzname();
return m_tzname[i];
}
DateObject* cachedUTC(ExecutionState& state);
// object
@ -461,7 +466,11 @@ private:
VZone* m_timezone;
#endif
std::string m_timezoneID;
void ensureTimezoneID();
void ensureVZone();
#endif
void ensureTzname();
std::string m_tzname[2];
DateObject* m_cachedUTC;
// promise job queue

View file

@ -114,6 +114,8 @@
#define ucal_open RuntimeICUBinder::ICU::instance().ucal_open
#define ucal_close RuntimeICUBinder::ICU::instance().ucal_close
#define ucal_getDefaultTimeZone RuntimeICUBinder::ICU::instance().ucal_getDefaultTimeZone
#define ucal_getTimeZoneDisplayName RuntimeICUBinder::ICU::instance().ucal_getTimeZoneDisplayName
#define ucal_getKeywordValuesForLocale RuntimeICUBinder::ICU::instance().ucal_getKeywordValuesForLocale
#define ucal_openTimeZoneIDEnumeration RuntimeICUBinder::ICU::instance().ucal_openTimeZoneIDEnumeration
#define ucal_openTimeZones RuntimeICUBinder::ICU::instance().ucal_openTimeZones
@ -181,6 +183,8 @@
#define ures_getNextResource RuntimeICUBinder::ICU::instance().ures_getNextResource
#define ures_hasNext RuntimeICUBinder::ICU::instance().ures_hasNext
#define ures_resetIterator RuntimeICUBinder::ICU::instance().ures_resetIterator
#define ures_getSize RuntimeICUBinder::ICU::instance().ures_getSize
#define ures_getStringByIndex RuntimeICUBinder::ICU::instance().ures_getStringByIndex
#define unumf_openForSkeletonAndLocale RuntimeICUBinder::ICU::instance().unumf_openForSkeletonAndLocale
#define unumf_openForSkeletonAndLocaleWithError RuntimeICUBinder::ICU::instance().unumf_openForSkeletonAndLocaleWithError

View file

@ -5781,6 +5781,24 @@ enum UCalendarDateFields {
UCAL_DAY_OF_MONTH = UCAL_DATE
};
/**
* Possible formats for a UCalendar's display name
* @stable ICU 2.0
*/
enum UCalendarDisplayNameType {
/** Standard display name */
UCAL_STANDARD,
/** Short standard display name */
UCAL_SHORT_STANDARD,
/** Daylight savings display name */
UCAL_DST,
/** Short daylight savings display name */
UCAL_SHORT_DST
};
/** @stable ICU 2.0 */
typedef enum UCalendarDisplayNameType UCalendarDisplayNameType;
/** @stable ICU 2.0 */
typedef enum UCalendarDateFields UCalendarDateFields;
/**

View file

@ -242,7 +242,10 @@ std::string ICU::findSystemLocale()
}
c = setlocale(LC_CTYPE, "");
if (c && strlen(c)) {
return extractLocaleName(c);
auto locale = extractLocaleName(c);
if (locale != "C") {
return locale;
}
}
return ICU::instance().uloc_getDefault();
}
@ -304,8 +307,19 @@ std::string ICU::findSystemTimezoneName()
} else if (timeValueFromFile("/etc/TIMEZONE", "TZ", tz, tzSize)) { // Solaris.
return tz;
}
#endif
UChar result[256];
UErrorCode status = U_ZERO_ERROR;
int32_t len = ICU::instance().ucal_getDefaultTimeZone(result, sizeof(result) / sizeof(UChar), &status);
if (U_SUCCESS(status)) {
std::string u8Result;
for (int32_t i = 0; i < len; i ++) {
u8Result.push_back(result[i]);
}
return u8Result;
}
// fallback
time_t t;
tm lt;
t = time(NULL);

View file

@ -126,6 +126,8 @@ namespace RuntimeICUBinder {
F(unumsys_getName, const char* (*)(const UNumberingSystem* unumsys), const char*) \
F(unumsys_open, UNumberingSystem* (*)(const char* locale, UErrorCode* status), UNumberingSystem*) \
F(ucal_open, UCalendar* (*)(const UChar* zoneID, int32_t len, const char* locale, UCalendarType type, UErrorCode* status), UCalendar*) \
F(ucal_getDefaultTimeZone, int32_t (*)(UChar* result, int32_t resultCapacity, UErrorCode* ec), int32_t) \
F(ucal_getTimeZoneDisplayName, int32_t (*)(const UCalendar* cal, UCalendarDisplayNameType type, const char* locale, UChar* result, int32_t resultLength, UErrorCode* status), int32_t) \
F(ucal_getKeywordValuesForLocale, UEnumeration* (*)(const char* key, const char* locale, UBool commonlyUsed, UErrorCode* status), UEnumeration*) \
F(ucal_openTimeZoneIDEnumeration, UEnumeration* (*)(USystemTimeZoneType zoneType, const char* region, const int32_t* rawOffset, UErrorCode* ec), UEnumeration*) \
F(ucal_openTimeZones, UEnumeration* (*)(UErrorCode * ec), UEnumeration*) \
@ -156,6 +158,8 @@ namespace RuntimeICUBinder {
F(ures_getKey, const char* (*)(const UResourceBundle* resourceBundle), const char*) \
F(ures_getNextResource, UResourceBundle* (*)(UResourceBundle * resourceBundle, UResourceBundle * fillIn, UErrorCode * status), UResourceBundle*) \
F(ures_hasNext, UBool (*)(const UResourceBundle* resourceBundle), UBool) \
F(ures_getSize, int32_t (*)(const UResourceBundle* resourceBundle), int32_t) \
F(ures_getStringByIndex, const UChar* (*)(const UResourceBundle *resourceBundle, int32_t indexS, int32_t* len, UErrorCode *status), const UChar*) \
F(uplrules_select, int32_t (*)(const UPluralRules* uplrules, double number, UChar* keyword, int32_t capacity, UErrorCode* status), int32_t) \
F(uplrules_getKeywords, UEnumeration* (*)(const UPluralRules* uplrules, UErrorCode* status), UEnumeration*) \
F(uplrules_open, UPluralRules* (*)(const char* locale, UErrorCode* status), UPluralRules*) \