From d7f0849b8c053ccc6abf0dc7d5bc07da502782a4 Mon Sep 17 00:00:00 2001 From: Neil Fuller Date: Wed, 25 Jun 2014 11:13:25 +0100 Subject: Rewriting android.text.format.Time without the native _tz functions Bug: 15765976 Change-Id: I666b72ecf9da8a9dcfb97cc503006b415909a558 --- core/jni/Android.mk | 2 - core/jni/AndroidRuntime.cpp | 2 - core/jni/Time.cpp | 199 ---------- core/jni/TimeUtils.h | 89 ----- core/jni/android_text_format_Time.cpp | 689 ---------------------------------- 5 files changed, 981 deletions(-) delete mode 100644 core/jni/Time.cpp delete mode 100644 core/jni/TimeUtils.h delete mode 100644 core/jni/android_text_format_Time.cpp (limited to 'core/jni') diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 8894c18..e4fad09 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -22,7 +22,6 @@ LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES LOCAL_SRC_FILES:= \ AndroidRuntime.cpp \ - Time.cpp \ com_android_internal_content_NativeLibraryHelper.cpp \ com_google_android_gles_jni_EGLImpl.cpp \ com_google_android_gles_jni_GLImpl.cpp.arm \ @@ -76,7 +75,6 @@ LOCAL_SRC_FILES:= \ android_net_TrafficStats.cpp \ android_net_wifi_WifiNative.cpp \ android_nio_utils.cpp \ - android_text_format_Time.cpp \ android_util_AssetManager.cpp \ android_util_Binder.cpp \ android_util_EventLog.cpp \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 4f80704..a65c351 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -132,7 +132,6 @@ extern int register_android_database_SQLiteConnection(JNIEnv* env); extern int register_android_database_SQLiteGlobal(JNIEnv* env); extern int register_android_database_SQLiteDebug(JNIEnv* env); extern int register_android_nio_utils(JNIEnv* env); -extern int register_android_text_format_Time(JNIEnv* env); extern int register_android_os_Debug(JNIEnv* env); extern int register_android_os_MessageQueue(JNIEnv* env); extern int register_android_os_Parcel(JNIEnv* env); @@ -1216,7 +1215,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_util_EventLog), REG_JNI(register_android_util_Log), REG_JNI(register_android_util_FloatMath), - REG_JNI(register_android_text_format_Time), REG_JNI(register_android_content_AssetManager), REG_JNI(register_android_content_StringBlock), REG_JNI(register_android_content_XmlBlock), diff --git a/core/jni/Time.cpp b/core/jni/Time.cpp deleted file mode 100644 index f3037f3..0000000 --- a/core/jni/Time.cpp +++ /dev/null @@ -1,199 +0,0 @@ -#include "TimeUtils.h" -#include -#include - -namespace android { - -static void -dump(const Time& t) -{ - #ifdef HAVE_TM_GMTOFF - long tm_gmtoff = t.t.tm_gmtoff; - #else - long tm_gmtoff = 0; - #endif - printf("%04d-%02d-%02d %02d:%02d:%02d (%d,%ld,%d,%d)\n", - t.t.tm_year+1900, t.t.tm_mon+1, t.t.tm_mday, - t.t.tm_hour, t.t.tm_min, t.t.tm_sec, - t.t.tm_isdst, tm_gmtoff, t.t.tm_wday, t.t.tm_yday); -} - -Time::Time() -{ - t.tm_sec = 0; - t.tm_min = 0; - t.tm_hour = 0; - t.tm_mday = 0; - t.tm_mon = 0; - t.tm_year = 0; - t.tm_wday = 0; - t.tm_yday = 0; - t.tm_isdst = -1; // we don't know, so let the C library determine - #ifdef HAVE_TM_GMTOFF - t.tm_gmtoff = 0; - #endif -} - - -#define COMPARE_FIELD(field) do { \ - int diff = a.t.field - b.t.field; \ - if (diff != 0) return diff; \ - } while(0) - -int -Time::compare(Time& a, Time& b) -{ - if (0 == strcmp(a.timezone, b.timezone)) { - // if the timezones are the same, we can easily compare the two - // times. Otherwise, convert to milliseconds and compare that. - // This requires that object be normalized. - COMPARE_FIELD(tm_year); - COMPARE_FIELD(tm_mon); - COMPARE_FIELD(tm_mday); - COMPARE_FIELD(tm_hour); - COMPARE_FIELD(tm_min); - COMPARE_FIELD(tm_sec); - return 0; - } else { - int64_t am = a.toMillis(false /* use isDst */); - int64_t bm = b.toMillis(false /* use isDst */); - int64_t diff = am-bm; - return (diff < 0) ? -1 : ((diff > 0) ? 1 : 0); - } -} - -static const int DAYS_PER_MONTH[] = { - 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 - }; - -static inline int days_this_month(int year, int month) -{ - int n = DAYS_PER_MONTH[month]; - if (n != 28) { - return n; - } else { - int y = year; - return ((y%4)==0&&((y%100)!=0||(y%400)==0)) ? 29 : 28; - } -} - -void -Time::switchTimezone(const char* timezone) -{ - time_t seconds = mktime_tz(&(this->t), this->timezone); - localtime_tz(&seconds, &(this->t), timezone); -} - -String8 -Time::format(const char *format, const struct strftime_locale *locale) const -{ - char buf[257]; - int n = strftime_tz(buf, 257, format, &(this->t), locale); - if (n > 0) { - return String8(buf); - } else { - return String8(); - } -} - -static inline short -tochar(int n) -{ - return (n >= 0 && n <= 9) ? ('0'+n) : ' '; -} - -static inline short -next_char(int *m, int k) -{ - int n = *m / k; - *m = *m % k; - return tochar(n); -} - -void -Time::format2445(short* buf, bool hasTime) const -{ - int n; - - n = t.tm_year+1900; - buf[0] = next_char(&n, 1000); - buf[1] = next_char(&n, 100); - buf[2] = next_char(&n, 10); - buf[3] = tochar(n); - - n = t.tm_mon+1; - buf[4] = next_char(&n, 10); - buf[5] = tochar(n); - - n = t.tm_mday; - buf[6] = next_char(&n, 10); - buf[7] = tochar(n); - - if (hasTime) { - buf[8] = 'T'; - - n = t.tm_hour; - buf[9] = next_char(&n, 10); - buf[10] = tochar(n); - - n = t.tm_min; - buf[11] = next_char(&n, 10); - buf[12] = tochar(n); - - n = t.tm_sec; - buf[13] = next_char(&n, 10); - buf[14] = tochar(n); - bool inUtc = strcmp("UTC", timezone) == 0; - if (inUtc) { - buf[15] = 'Z'; - } - } -} - -String8 -Time::toString() const -{ - String8 str; - char* s = str.lockBuffer(150); - #ifdef HAVE_TM_GMTOFF - long tm_gmtoff = t.tm_gmtoff; - #else - long tm_gmtoff = 0; - #endif - sprintf(s, "%04d%02d%02dT%02d%02d%02d%s(%d,%d,%ld,%d,%d)", - t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, - t.tm_sec, timezone, t.tm_wday, t.tm_yday, tm_gmtoff, t.tm_isdst, - (int)(((Time*)this)->toMillis(false /* use isDst */)/1000)); - str.unlockBuffer(); - return str; -} - -void -Time::setToNow() -{ - time_t seconds; - time(&seconds); - localtime_tz(&seconds, &(this->t), this->timezone); -} - -int64_t -Time::toMillis(bool ignoreDst) -{ - if (ignoreDst) { - this->t.tm_isdst = -1; - } - int64_t r = mktime_tz(&(this->t), this->timezone); - if (r == -1) - return -1; - return r * 1000; -} - -void -Time::set(int64_t millis) -{ - time_t seconds = millis / 1000; - localtime_tz(&seconds, &(this->t), this->timezone); -} - -}; // namespace android - diff --git a/core/jni/TimeUtils.h b/core/jni/TimeUtils.h deleted file mode 100644 index b19e021..0000000 --- a/core/jni/TimeUtils.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2005 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. - */ - -#ifndef ANDROID_TIME_H -#define ANDROID_TIME_H - -#include -#include -#include -#include -#include -#include -#include - -namespace android { - -/* - * This class is the core implementation of the android.util.Time java - * class. It doesn't implement some of the methods that are implemented - * in Java. They could be done here, but it's not expected that this class - * will be used. If that assumption is incorrect, feel free to update this - * file. The reason to do it here is to not mix the implementation of this - * class and the jni glue code. - */ -class Time -{ -public: - struct tm t; - - // this object doesn't own this string - const char *timezone; - - enum { - SEC = 1, - MIN = 2, - HOUR = 3, - MDAY = 4, - MON = 5, - YEAR = 6, - WDAY = 7, - YDAY = 8 - }; - - static int compare(Time& a, Time& b); - - Time(); - - void switchTimezone(const char *timezone); - String8 format(const char *format, const struct strftime_locale *locale) const; - void format2445(short* buf, bool hasTime) const; - String8 toString() const; - void setToNow(); - int64_t toMillis(bool ignoreDst); - void set(int64_t millis); - - inline void set(int sec, int min, int hour, int mday, int mon, int year, - int isdst) - { - this->t.tm_sec = sec; - this->t.tm_min = min; - this->t.tm_hour = hour; - this->t.tm_mday = mday; - this->t.tm_mon = mon; - this->t.tm_year = year; - this->t.tm_isdst = isdst; -#ifdef HAVE_TM_GMTOFF - this->t.tm_gmtoff = 0; -#endif - this->t.tm_wday = 0; - this->t.tm_yday = 0; - } -}; - -}; // namespace android - -#endif // ANDROID_TIME_H diff --git a/core/jni/android_text_format_Time.cpp b/core/jni/android_text_format_Time.cpp deleted file mode 100644 index 28a8a5d..0000000 --- a/core/jni/android_text_format_Time.cpp +++ /dev/null @@ -1,689 +0,0 @@ -/* -** Copyright 2006, 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. -*/ - -#define LOG_TAG "Log_println" - -#include -#include -#include - -#include "jni.h" -#include "utils/misc.h" -#include "android_runtime/AndroidRuntime.h" -#include "ScopedStringChars.h" -#include "TimeUtils.h" -#include -#include - -namespace android { - -static jfieldID g_allDayField = 0; -static jfieldID g_secField = 0; -static jfieldID g_minField = 0; -static jfieldID g_hourField = 0; -static jfieldID g_mdayField = 0; -static jfieldID g_monField = 0; -static jfieldID g_yearField = 0; -static jfieldID g_wdayField = 0; -static jfieldID g_ydayField = 0; -static jfieldID g_isdstField = 0; -static jfieldID g_gmtoffField = 0; -static jfieldID g_timezoneField = 0; - -static jfieldID g_shortMonthsField = 0; -static jfieldID g_longMonthsField = 0; -static jfieldID g_longStandaloneMonthsField = 0; -static jfieldID g_shortWeekdaysField = 0; -static jfieldID g_longWeekdaysField = 0; -static jfieldID g_timeOnlyFormatField = 0; -static jfieldID g_dateOnlyFormatField = 0; -static jfieldID g_dateTimeFormatField = 0; -static jfieldID g_amField = 0; -static jfieldID g_pmField = 0; -static jfieldID g_dateCommandField = 0; -static jfieldID g_localeField = 0; - -static jclass g_timeClass = NULL; - -static inline bool java2time(JNIEnv* env, Time* t, jobject o) -{ - t->t.tm_sec = env->GetIntField(o, g_secField); - t->t.tm_min = env->GetIntField(o, g_minField); - t->t.tm_hour = env->GetIntField(o, g_hourField); - t->t.tm_mday = env->GetIntField(o, g_mdayField); - t->t.tm_mon = env->GetIntField(o, g_monField); - t->t.tm_year = (env->GetIntField(o, g_yearField))-1900; - t->t.tm_wday = env->GetIntField(o, g_wdayField); - t->t.tm_yday = env->GetIntField(o, g_ydayField); - t->t.tm_isdst = env->GetIntField(o, g_isdstField); - t->t.tm_gmtoff = env->GetLongField(o, g_gmtoffField); - bool allDay = env->GetBooleanField(o, g_allDayField); - if (allDay && - ((t->t.tm_sec !=0) || (t->t.tm_min != 0) || (t->t.tm_hour != 0))) { - jniThrowException(env, "java/lang/IllegalArgumentException", - "allDay is true but sec, min, hour are not 0."); - return false; - } - return true; -} - -static inline void time2java(JNIEnv* env, jobject o, const Time &t) -{ - env->SetIntField(o, g_secField, t.t.tm_sec); - env->SetIntField(o, g_minField, t.t.tm_min); - env->SetIntField(o, g_hourField, t.t.tm_hour); - env->SetIntField(o, g_mdayField, t.t.tm_mday); - env->SetIntField(o, g_monField, t.t.tm_mon); - env->SetIntField(o, g_yearField, t.t.tm_year+1900); - env->SetIntField(o, g_wdayField, t.t.tm_wday); - env->SetIntField(o, g_ydayField, t.t.tm_yday); - env->SetIntField(o, g_isdstField, t.t.tm_isdst); - env->SetLongField(o, g_gmtoffField, t.t.tm_gmtoff); -} - -#define ACQUIRE_TIMEZONE(This, t) \ - jstring timezoneString_##This \ - = (jstring) env->GetObjectField(This, g_timezoneField); \ - t.timezone = env->GetStringUTFChars(timezoneString_##This, NULL); - -#define RELEASE_TIMEZONE(This, t) \ - env->ReleaseStringUTFChars(timezoneString_##This, t.timezone); - - -// ============================================================================ - -static jlong android_text_format_Time_normalize(JNIEnv* env, jobject This, - jboolean ignoreDst) -{ - Time t; - if (!java2time(env, &t, This)) return 0L; - ACQUIRE_TIMEZONE(This, t) - - int64_t result = t.toMillis(ignoreDst != 0); - - time2java(env, This, t); - RELEASE_TIMEZONE(This, t) - - return static_cast(result); -} - -static void android_text_format_Time_switchTimezone(JNIEnv* env, jobject This, - jstring timezoneObject) -{ - Time t; - if (!java2time(env, &t, This)) return; - ACQUIRE_TIMEZONE(This, t) - - const char* timezone = env->GetStringUTFChars(timezoneObject, NULL); - - t.switchTimezone(timezone); - - time2java(env, This, t); - env->ReleaseStringUTFChars(timezoneObject, timezone); - RELEASE_TIMEZONE(This, t) - - // we do this here because there's no point in reallocating the string - env->SetObjectField(This, g_timezoneField, timezoneObject); -} - -static jint android_text_format_Time_compare(JNIEnv* env, jobject clazz, - jobject aObject, jobject bObject) -{ - Time a, b; - - if (!java2time(env, &a, aObject)) return 0; - ACQUIRE_TIMEZONE(aObject, a) - - if (!java2time(env, &b, bObject)) return 0; - ACQUIRE_TIMEZONE(bObject, b) - - int result = Time::compare(a, b); - - RELEASE_TIMEZONE(aObject, a) - RELEASE_TIMEZONE(bObject, b) - - return static_cast(result); -} - -static jstring android_text_format_Time_format2445(JNIEnv* env, jobject This) -{ - Time t; - if (!java2time(env, &t, This)) return env->NewStringUTF(""); - bool allDay = env->GetBooleanField(This, g_allDayField); - - if (!allDay) { - ACQUIRE_TIMEZONE(This, t) - bool inUtc = strcmp("UTC", t.timezone) == 0; - short buf[16]; - t.format2445(buf, true); - RELEASE_TIMEZONE(This, t) - if (inUtc) { - // The letter 'Z' is appended to the end so allow for one - // more character in the buffer. - return env->NewString((jchar*)buf, 16); - } else { - return env->NewString((jchar*)buf, 15); - } - } else { - short buf[8]; - t.format2445(buf, false); - return env->NewString((jchar*)buf, 8); - } -} - -static jstring android_text_format_Time_format(JNIEnv* env, jobject This, - jstring formatObject) -{ - // We only teardown and setup our 'locale' struct and other state - // when the Java-side locale changed. This is safe to do here - // without locking because we're always called from Java code - // synchronized on the class instance. - static jobject js_locale_previous = NULL; - static struct strftime_locale locale; - static jstring js_mon[12], js_month[12], js_wday[7], js_weekday[7]; - static jstring js_standalone_month[12]; - static jstring js_X_fmt, js_x_fmt, js_c_fmt, js_am, js_pm, js_date_fmt; - - Time t; - if (!java2time(env, &t, This)) return env->NewStringUTF(""); - - jclass timeClass = g_timeClass; - jobject js_locale = (jobject) env->GetStaticObjectField(timeClass, g_localeField); - if (js_locale_previous != js_locale) { - if (js_locale_previous != NULL) { - // Free the old one. - for (int i = 0; i < 12; i++) { - env->ReleaseStringUTFChars(js_mon[i], locale.mon[i]); - env->ReleaseStringUTFChars(js_month[i], locale.month[i]); - env->ReleaseStringUTFChars(js_standalone_month[i], locale.standalone_month[i]); - env->DeleteGlobalRef(js_mon[i]); - env->DeleteGlobalRef(js_month[i]); - env->DeleteGlobalRef(js_standalone_month[i]); - } - - for (int i = 0; i < 7; i++) { - env->ReleaseStringUTFChars(js_wday[i], locale.wday[i]); - env->ReleaseStringUTFChars(js_weekday[i], locale.weekday[i]); - env->DeleteGlobalRef(js_wday[i]); - env->DeleteGlobalRef(js_weekday[i]); - } - - env->ReleaseStringUTFChars(js_X_fmt, locale.X_fmt); - env->ReleaseStringUTFChars(js_x_fmt, locale.x_fmt); - env->ReleaseStringUTFChars(js_c_fmt, locale.c_fmt); - env->ReleaseStringUTFChars(js_am, locale.am); - env->ReleaseStringUTFChars(js_pm, locale.pm); - env->ReleaseStringUTFChars(js_date_fmt, locale.date_fmt); - env->DeleteGlobalRef(js_X_fmt); - env->DeleteGlobalRef(js_x_fmt); - env->DeleteGlobalRef(js_c_fmt); - env->DeleteGlobalRef(js_am); - env->DeleteGlobalRef(js_pm); - env->DeleteGlobalRef(js_date_fmt); - } - js_locale_previous = js_locale; - - jobjectArray ja; - ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_shortMonthsField); - for (int i = 0; i < 12; i++) { - // Calendar.JANUARY == 0. - js_mon[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i)); - locale.mon[i] = env->GetStringUTFChars(js_mon[i], NULL); - } - - ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longMonthsField); - for (int i = 0; i < 12; i++) { - // Calendar.JANUARY == 0. - js_month[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i)); - locale.month[i] = env->GetStringUTFChars(js_month[i], NULL); - } - - ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longStandaloneMonthsField); - for (int i = 0; i < 12; i++) { - // Calendar.JANUARY == 0. - js_standalone_month[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i)); - locale.standalone_month[i] = env->GetStringUTFChars(js_standalone_month[i], NULL); - } - - ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_shortWeekdaysField); - for (int i = 0; i < 7; i++) { - // Calendar.SUNDAY == 1, and there's an empty string in element 0. - js_wday[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i + 1)); - locale.wday[i] = env->GetStringUTFChars(js_wday[i], NULL); - } - - ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longWeekdaysField); - for (int i = 0; i < 7; i++) { - // Calendar.SUNDAY == 1, and there's an empty string in element 0. - js_weekday[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i + 1)); - locale.weekday[i] = env->GetStringUTFChars(js_weekday[i], NULL); - } - - js_X_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField( - timeClass, g_timeOnlyFormatField)); - locale.X_fmt = env->GetStringUTFChars(js_X_fmt, NULL); - - js_x_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField( - timeClass, g_dateOnlyFormatField)); - locale.x_fmt = env->GetStringUTFChars(js_x_fmt, NULL); - - js_c_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField( - timeClass, g_dateTimeFormatField)); - locale.c_fmt = env->GetStringUTFChars(js_c_fmt, NULL); - - js_am = (jstring) env->NewGlobalRef(env->GetStaticObjectField( - timeClass, g_amField)); - locale.am = env->GetStringUTFChars(js_am, NULL); - - js_pm = (jstring) env->NewGlobalRef(env->GetStaticObjectField( - timeClass, g_pmField)); - locale.pm = env->GetStringUTFChars(js_pm, NULL); - - js_date_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField( - timeClass, g_dateCommandField)); - locale.date_fmt = env->GetStringUTFChars(js_date_fmt, NULL); - } - - ACQUIRE_TIMEZONE(This, t) - - const char* format = env->GetStringUTFChars(formatObject, NULL); - - String8 r = t.format(format, &locale); - - env->ReleaseStringUTFChars(formatObject, format); - RELEASE_TIMEZONE(This, t) - - return env->NewStringUTF(r.string()); -} - - -static jstring android_text_format_Time_toString(JNIEnv* env, jobject This) -{ - Time t; - if (!java2time(env, &t, This)) return env->NewStringUTF(""); - ACQUIRE_TIMEZONE(This, t) - - String8 r = t.toString(); - - RELEASE_TIMEZONE(This, t) - - return env->NewStringUTF(r.string()); -} - -static void android_text_format_Time_setToNow(JNIEnv* env, jobject This) -{ - env->SetBooleanField(This, g_allDayField, JNI_FALSE); - Time t; - ACQUIRE_TIMEZONE(This, t) - - t.setToNow(); - - time2java(env, This, t); - RELEASE_TIMEZONE(This, t) -} - -static jlong android_text_format_Time_toMillis(JNIEnv* env, jobject This, - jboolean ignoreDst) -{ - Time t; - if (!java2time(env, &t, This)) return 0L; - ACQUIRE_TIMEZONE(This, t) - - int64_t result = t.toMillis(ignoreDst != 0); - - RELEASE_TIMEZONE(This, t) - - return static_cast(result); -} - -static void android_text_format_Time_set(JNIEnv* env, jobject This, jlong millis) -{ - env->SetBooleanField(This, g_allDayField, JNI_FALSE); - Time t; - ACQUIRE_TIMEZONE(This, t) - - t.set(millis); - - time2java(env, This, t); - RELEASE_TIMEZONE(This, t) -} - - -// ============================================================================ -// Just do this here because it's not worth recreating the strings - -static int get_char(JNIEnv* env, const ScopedStringChars& s, int spos, int mul, - bool* thrown) -{ - jchar c = s[spos]; - if (c >= '0' && c <= '9') { - return (c - '0') * mul; - } else { - if (!*thrown) { - jniThrowExceptionFmt(env, "android/util/TimeFormatException", - "Parse error at pos=%d", spos); - *thrown = true; - } - return 0; - } -} - -static bool check_char(JNIEnv* env, const ScopedStringChars& s, int spos, jchar expected) -{ - jchar c = s[spos]; - if (c != expected) { - jniThrowExceptionFmt(env, "android/util/TimeFormatException", - "Unexpected character 0x%02x at pos=%d. Expected %c.", - c, spos, expected); - return false; - } - return true; -} - - -static jboolean android_text_format_Time_parse(JNIEnv* env, jobject This, jstring strObj) -{ - jsize len = env->GetStringLength(strObj); - if (len < 8) { - jniThrowException(env, "android/util/TimeFormatException", - "String too short -- expected at least 8 characters."); - return JNI_FALSE; - } - - jboolean inUtc = JNI_FALSE; - - ScopedStringChars s(env, strObj); - - // year - int n; - bool thrown = false; - n = get_char(env, s, 0, 1000, &thrown); - n += get_char(env, s, 1, 100, &thrown); - n += get_char(env, s, 2, 10, &thrown); - n += get_char(env, s, 3, 1, &thrown); - if (thrown) return JNI_FALSE; - env->SetIntField(This, g_yearField, n); - - // month - n = get_char(env, s, 4, 10, &thrown); - n += get_char(env, s, 5, 1, &thrown); - n--; - if (thrown) return JNI_FALSE; - env->SetIntField(This, g_monField, n); - - // day of month - n = get_char(env, s, 6, 10, &thrown); - n += get_char(env, s, 7, 1, &thrown); - if (thrown) return JNI_FALSE; - env->SetIntField(This, g_mdayField, n); - - if (len > 8) { - // T - if (!check_char(env, s, 8, 'T')) return JNI_FALSE; - env->SetBooleanField(This, g_allDayField, JNI_FALSE); - - // hour - n = get_char(env, s, 9, 10, &thrown); - n += get_char(env, s, 10, 1, &thrown); - if (thrown) return JNI_FALSE; - env->SetIntField(This, g_hourField, n); - - // min - n = get_char(env, s, 11, 10, &thrown); - n += get_char(env, s, 12, 1, &thrown); - if (thrown) return JNI_FALSE; - env->SetIntField(This, g_minField, n); - - // sec - n = get_char(env, s, 13, 10, &thrown); - n += get_char(env, s, 14, 1, &thrown); - if (thrown) return JNI_FALSE; - env->SetIntField(This, g_secField, n); - - if (len > 15) { - // Z - if (!check_char(env, s, 15, 'Z')) return JNI_FALSE; - inUtc = JNI_TRUE; - } - } else { - env->SetBooleanField(This, g_allDayField, JNI_TRUE); - env->SetIntField(This, g_hourField, 0); - env->SetIntField(This, g_minField, 0); - env->SetIntField(This, g_secField, 0); - } - - env->SetIntField(This, g_wdayField, 0); - env->SetIntField(This, g_ydayField, 0); - env->SetIntField(This, g_isdstField, -1); - env->SetLongField(This, g_gmtoffField, 0); - - return inUtc; -} - -static jboolean android_text_format_Time_parse3339(JNIEnv* env, - jobject This, - jstring strObj) -{ - jsize len = env->GetStringLength(strObj); - if (len < 10) { - jniThrowException(env, "android/util/TimeFormatException", - "String too short --- expected at least 10 characters."); - return JNI_FALSE; - } - - jboolean inUtc = JNI_FALSE; - - ScopedStringChars s(env, strObj); - - // year - int n; - bool thrown = false; - n = get_char(env, s, 0, 1000, &thrown); - n += get_char(env, s, 1, 100, &thrown); - n += get_char(env, s, 2, 10, &thrown); - n += get_char(env, s, 3, 1, &thrown); - if (thrown) return JNI_FALSE; - env->SetIntField(This, g_yearField, n); - - // - - if (!check_char(env, s, 4, '-')) return JNI_FALSE; - - // month - n = get_char(env, s, 5, 10, &thrown); - n += get_char(env, s, 6, 1, &thrown); - --n; - if (thrown) return JNI_FALSE; - env->SetIntField(This, g_monField, n); - - // - - if (!check_char(env, s, 7, '-')) return JNI_FALSE; - - // day - n = get_char(env, s, 8, 10, &thrown); - n += get_char(env, s, 9, 1, &thrown); - if (thrown) return JNI_FALSE; - env->SetIntField(This, g_mdayField, n); - - if (len >= 19) { - // T - if (!check_char(env, s, 10, 'T')) return JNI_FALSE; - - env->SetBooleanField(This, g_allDayField, JNI_FALSE); - // hour - n = get_char(env, s, 11, 10, &thrown); - n += get_char(env, s, 12, 1, &thrown); - if (thrown) return JNI_FALSE; - int hour = n; - // env->SetIntField(This, g_hourField, n); - - // : - if (!check_char(env, s, 13, ':')) return JNI_FALSE; - - // minute - n = get_char(env, s, 14, 10, &thrown); - n += get_char(env, s, 15, 1, &thrown); - if (thrown) return JNI_FALSE; - int minute = n; - // env->SetIntField(This, g_minField, n); - - // : - if (!check_char(env, s, 16, ':')) return JNI_FALSE; - - // second - n = get_char(env, s, 17, 10, &thrown); - n += get_char(env, s, 18, 1, &thrown); - if (thrown) return JNI_FALSE; - env->SetIntField(This, g_secField, n); - - // skip the '.XYZ' -- we don't care about subsecond precision. - int tz_index = 19; - if (tz_index < len && s[tz_index] == '.') { - do { - tz_index++; - } while (tz_index < len - && s[tz_index] >= '0' - && s[tz_index] <= '9'); - } - - int offset = 0; - if (len > tz_index) { - char c = s[tz_index]; - - // NOTE: the offset is meant to be subtracted to get from local time - // to UTC. we therefore use 1 for '-' and -1 for '+'. - switch (c) { - case 'Z': - // Zulu time -- UTC - offset = 0; - break; - case '-': - offset = 1; - break; - case '+': - offset = -1; - break; - default: - jniThrowExceptionFmt(env, "android/util/TimeFormatException", - "Unexpected character 0x%02x at position %d. Expected + or -", - c, tz_index); - return JNI_FALSE; - } - inUtc = JNI_TRUE; - - if (offset != 0) { - if (len < tz_index + 6) { - jniThrowExceptionFmt(env, "android/util/TimeFormatException", - "Unexpected length; should be %d characters", - tz_index + 6); - return JNI_FALSE; - } - - // hour - n = get_char(env, s, tz_index + 1, 10, &thrown); - n += get_char(env, s, tz_index + 2, 1, &thrown); - if (thrown) return JNI_FALSE; - n *= offset; - hour += n; - - // : - if (!check_char(env, s, tz_index + 3, ':')) return JNI_FALSE; - - // minute - n = get_char(env, s, tz_index + 4, 10, &thrown); - n += get_char(env, s, tz_index + 5, 1, &thrown); - if (thrown) return JNI_FALSE; - n *= offset; - minute += n; - } - } - env->SetIntField(This, g_hourField, hour); - env->SetIntField(This, g_minField, minute); - - if (offset != 0) { - // we need to normalize after applying the hour and minute offsets - android_text_format_Time_normalize(env, This, false /* use isdst */); - // The timezone is set to UTC in the calling Java code. - } - } else { - env->SetBooleanField(This, g_allDayField, JNI_TRUE); - env->SetIntField(This, g_hourField, 0); - env->SetIntField(This, g_minField, 0); - env->SetIntField(This, g_secField, 0); - } - - env->SetIntField(This, g_wdayField, 0); - env->SetIntField(This, g_ydayField, 0); - env->SetIntField(This, g_isdstField, -1); - env->SetLongField(This, g_gmtoffField, 0); - - return inUtc; -} - -// ============================================================================ -/* - * JNI registration. - */ -static JNINativeMethod gMethods[] = { - /* name, signature, funcPtr */ - { "normalize", "(Z)J", (void*)android_text_format_Time_normalize }, - { "switchTimezone", "(Ljava/lang/String;)V", (void*)android_text_format_Time_switchTimezone }, - { "nativeCompare", "(Landroid/text/format/Time;Landroid/text/format/Time;)I", (void*)android_text_format_Time_compare }, - { "format1", "(Ljava/lang/String;)Ljava/lang/String;", (void*)android_text_format_Time_format }, - { "format2445", "()Ljava/lang/String;", (void*)android_text_format_Time_format2445 }, - { "toString", "()Ljava/lang/String;", (void*)android_text_format_Time_toString }, - { "nativeParse", "(Ljava/lang/String;)Z", (void*)android_text_format_Time_parse }, - { "nativeParse3339", "(Ljava/lang/String;)Z", (void*)android_text_format_Time_parse3339 }, - { "setToNow", "()V", (void*)android_text_format_Time_setToNow }, - { "toMillis", "(Z)J", (void*)android_text_format_Time_toMillis }, - { "set", "(J)V", (void*)android_text_format_Time_set } -}; - -int register_android_text_format_Time(JNIEnv* env) -{ - jclass timeClass = env->FindClass("android/text/format/Time"); - - g_timeClass = (jclass) env->NewGlobalRef(timeClass); - - g_allDayField = env->GetFieldID(timeClass, "allDay", "Z"); - g_secField = env->GetFieldID(timeClass, "second", "I"); - g_minField = env->GetFieldID(timeClass, "minute", "I"); - g_hourField = env->GetFieldID(timeClass, "hour", "I"); - g_mdayField = env->GetFieldID(timeClass, "monthDay", "I"); - g_monField = env->GetFieldID(timeClass, "month", "I"); - g_yearField = env->GetFieldID(timeClass, "year", "I"); - g_wdayField = env->GetFieldID(timeClass, "weekDay", "I"); - g_ydayField = env->GetFieldID(timeClass, "yearDay", "I"); - g_isdstField = env->GetFieldID(timeClass, "isDst", "I"); - g_gmtoffField = env->GetFieldID(timeClass, "gmtoff", "J"); - g_timezoneField = env->GetFieldID(timeClass, "timezone", "Ljava/lang/String;"); - - g_shortMonthsField = env->GetStaticFieldID(timeClass, "sShortMonths", "[Ljava/lang/String;"); - g_longMonthsField = env->GetStaticFieldID(timeClass, "sLongMonths", "[Ljava/lang/String;"); - g_longStandaloneMonthsField = env->GetStaticFieldID(timeClass, "sLongStandaloneMonths", "[Ljava/lang/String;"); - g_shortWeekdaysField = env->GetStaticFieldID(timeClass, "sShortWeekdays", "[Ljava/lang/String;"); - g_longWeekdaysField = env->GetStaticFieldID(timeClass, "sLongWeekdays", "[Ljava/lang/String;"); - g_timeOnlyFormatField = env->GetStaticFieldID(timeClass, "sTimeOnlyFormat", "Ljava/lang/String;"); - g_dateOnlyFormatField = env->GetStaticFieldID(timeClass, "sDateOnlyFormat", "Ljava/lang/String;"); - g_dateTimeFormatField = env->GetStaticFieldID(timeClass, "sDateTimeFormat", "Ljava/lang/String;"); - g_amField = env->GetStaticFieldID(timeClass, "sAm", "Ljava/lang/String;"); - g_pmField = env->GetStaticFieldID(timeClass, "sPm", "Ljava/lang/String;"); - g_dateCommandField = env->GetStaticFieldID(timeClass, "sDateCommand", "Ljava/lang/String;"); - g_localeField = env->GetStaticFieldID(timeClass, "sLocale", "Ljava/util/Locale;"); - - return AndroidRuntime::registerNativeMethods(env, "android/text/format/Time", gMethods, NELEM(gMethods)); -} - -}; // namespace android -- cgit v1.1