blob: 0c85c1ee71c5dd9c611ccc61647b09e73a838bab [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <jni.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "nativehelper/scoped_utf_chars.h"
// Information about the Java StructTm class.
static struct {
jclass clazz;
jmethodID ctor;
jfieldID tm_sec;
jfieldID tm_min;
jfieldID tm_hour;
jfieldID tm_mday;
jfieldID tm_mon;
jfieldID tm_year;
jfieldID tm_wday;
jfieldID tm_yday;
jfieldID tm_isdst;
jfieldID tm_gmtoff;
jfieldID tm_zone;
} gStructTmClassInfo;
// The following is not threadsafe but that's a problem with getenv(), setenv(), mktime() it
// would be impractical to get around. Luckily, tests are single threaded.
#define SET_TZ(jni_env, javaTzId) \
char _originalTz[64]; \
char* _tzEnv = getenv("TZ"); \
if (_tzEnv != nullptr) { \
strncpy(_originalTz, _tzEnv, sizeof(_originalTz)); \
_originalTz[sizeof(_originalTz) - 1] = 0; \
} \
ScopedUtfChars _tzId(jni_env, javaTzId); \
setenv("TZ", _tzId.c_str(), 1);
#define UNSET_TZ() \
if (_tzEnv != nullptr) { \
setenv("TZ", _originalTz, 1); \
}
static jobject android_text_format_cts_NativeTimeFunctions_localtime_tz(
JNIEnv* env, jclass, jint javaTimep, jstring javaTzId)
{
// Call localtime_r
struct tm out_tm;
{
SET_TZ(env, javaTzId);
const time_t timep = javaTimep;
localtime_r(&timep, &out_tm);
UNSET_TZ();
}
// Copy the results to a StructTm object.
jobject javaTm = env->NewObject(gStructTmClassInfo.clazz, gStructTmClassInfo.ctor);
env->SetIntField(javaTm, gStructTmClassInfo.tm_sec, out_tm.tm_sec);
env->SetIntField(javaTm, gStructTmClassInfo.tm_min, out_tm.tm_min);
env->SetIntField(javaTm, gStructTmClassInfo.tm_hour, out_tm.tm_hour);
env->SetIntField(javaTm, gStructTmClassInfo.tm_mday, out_tm.tm_mday);
env->SetIntField(javaTm, gStructTmClassInfo.tm_mon, out_tm.tm_mon);
env->SetIntField(javaTm, gStructTmClassInfo.tm_year, out_tm.tm_year);
env->SetIntField(javaTm, gStructTmClassInfo.tm_wday, out_tm.tm_wday);
env->SetIntField(javaTm, gStructTmClassInfo.tm_yday, out_tm.tm_yday);
env->SetIntField(javaTm, gStructTmClassInfo.tm_isdst, out_tm.tm_isdst);
env->SetLongField(javaTm, gStructTmClassInfo.tm_gmtoff, out_tm.tm_gmtoff);
env->SetObjectField(javaTm, gStructTmClassInfo.tm_zone, env->NewStringUTF(out_tm.tm_zone));
return javaTm;
}
static jint android_text_format_cts_NativeTimeFunctions_mktime_tz(
JNIEnv* env, jclass, jobject javaTm, jstring javaTzId)
{
// Populate a tm struct from the Java StructTm object.
struct tm in_tm;
in_tm.tm_sec = env->GetIntField(javaTm, gStructTmClassInfo.tm_sec);
in_tm.tm_min = env->GetIntField(javaTm, gStructTmClassInfo.tm_min);
in_tm.tm_hour = env->GetIntField(javaTm, gStructTmClassInfo.tm_hour);
in_tm.tm_mday = env->GetIntField(javaTm, gStructTmClassInfo.tm_mday);
in_tm.tm_mon = env->GetIntField(javaTm, gStructTmClassInfo.tm_mon);
in_tm.tm_year = env->GetIntField(javaTm, gStructTmClassInfo.tm_year);
in_tm.tm_wday = env->GetIntField(javaTm, gStructTmClassInfo.tm_wday);
in_tm.tm_yday = env->GetIntField(javaTm, gStructTmClassInfo.tm_yday);
in_tm.tm_isdst = env->GetIntField(javaTm, gStructTmClassInfo.tm_isdst);
in_tm.tm_gmtoff = env->GetLongField(javaTm, gStructTmClassInfo.tm_gmtoff);
jstring javaTmZone =
static_cast<jstring>(env->GetObjectField(javaTm, gStructTmClassInfo.tm_zone));
const char* tmZone = nullptr;
if (javaTmZone != nullptr) {
tmZone = env->GetStringUTFChars(javaTmZone, nullptr);
in_tm.tm_zone = tmZone;
}
// Call mktime
jint result = -1;
{
SET_TZ(env, javaTzId);
result = mktime(&in_tm);
UNSET_TZ();
}
if (javaTmZone != nullptr) {
env->ReleaseStringUTFChars(javaTmZone, tmZone);
}
return result;
}
static JNINativeMethod gMethods[] = {
{ "localtime_tz", "(ILjava/lang/String;)Landroid/text/format/cts/NativeTimeFunctions$StructTm;",
(void *) android_text_format_cts_NativeTimeFunctions_localtime_tz },
{ "mktime_tz", "(Landroid/text/format/cts/NativeTimeFunctions$StructTm;Ljava/lang/String;)I",
(void *) android_text_format_cts_NativeTimeFunctions_mktime_tz },
};
int register_android_text_format_cts_NativeTimeFunctions(JNIEnv* env)
{
jclass javaTmClass = env->FindClass("android/text/format/cts/NativeTimeFunctions$StructTm");
gStructTmClassInfo.clazz = (jclass) env->NewGlobalRef(javaTmClass);
gStructTmClassInfo.ctor = env->GetMethodID(javaTmClass, "<init>", "()V");
gStructTmClassInfo.tm_sec = env->GetFieldID(javaTmClass, "tm_sec", "I");
gStructTmClassInfo.tm_min = env->GetFieldID(javaTmClass, "tm_min", "I");
gStructTmClassInfo.tm_hour = env->GetFieldID(javaTmClass, "tm_hour", "I");
gStructTmClassInfo.tm_mday = env->GetFieldID(javaTmClass, "tm_mday", "I");
gStructTmClassInfo.tm_mon = env->GetFieldID(javaTmClass, "tm_mon", "I");
gStructTmClassInfo.tm_year = env->GetFieldID(javaTmClass, "tm_year", "I");
gStructTmClassInfo.tm_wday = env->GetFieldID(javaTmClass, "tm_wday", "I");
gStructTmClassInfo.tm_yday = env->GetFieldID(javaTmClass, "tm_yday", "I");
gStructTmClassInfo.tm_isdst = env->GetFieldID(javaTmClass, "tm_isdst", "I");
gStructTmClassInfo.tm_gmtoff = env->GetFieldID(javaTmClass, "tm_gmtoff", "J");
gStructTmClassInfo.tm_zone = env->GetFieldID(javaTmClass, "tm_zone", "Ljava/lang/String;");
jclass clazz = env->FindClass("android/text/format/cts/NativeTimeFunctions");
return env->RegisterNatives(clazz, gMethods,
sizeof(gMethods) / sizeof(JNINativeMethod));
}