1/*
2** Copyright 2006, The Android Open Source Project
3**
4** Licensed under the Apache License, Version 2.0 (the "License");
5** you may not use this file except in compliance with the License.
6** You may obtain a copy of the License at
7**
8**     http://www.apache.org/licenses/LICENSE-2.0
9**
10** Unless required by applicable law or agreed to in writing, software
11** distributed under the License is distributed on an "AS IS" BASIS,
12** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13** See the License for the specific language governing permissions and
14** limitations under the License.
15*/
16
17#define LOG_TAG "Log_println"
18
19#include <utils/Log.h>
20#include <utils/String8.h>
21#include <assert.h>
22
23#include "jni.h"
24#include "utils/misc.h"
25#include "android_runtime/AndroidRuntime.h"
26#include "ScopedStringChars.h"
27#include "TimeUtils.h"
28#include <nativehelper/JNIHelp.h>
29#include <cutils/tztime.h>
30
31namespace android {
32
33static jfieldID g_allDayField = 0;
34static jfieldID g_secField = 0;
35static jfieldID g_minField = 0;
36static jfieldID g_hourField = 0;
37static jfieldID g_mdayField = 0;
38static jfieldID g_monField = 0;
39static jfieldID g_yearField = 0;
40static jfieldID g_wdayField = 0;
41static jfieldID g_ydayField = 0;
42static jfieldID g_isdstField = 0;
43static jfieldID g_gmtoffField = 0;
44static jfieldID g_timezoneField = 0;
45
46static jfieldID g_shortMonthsField = 0;
47static jfieldID g_longMonthsField = 0;
48static jfieldID g_longStandaloneMonthsField = 0;
49static jfieldID g_shortWeekdaysField = 0;
50static jfieldID g_longWeekdaysField = 0;
51static jfieldID g_timeOnlyFormatField = 0;
52static jfieldID g_dateOnlyFormatField = 0;
53static jfieldID g_dateTimeFormatField = 0;
54static jfieldID g_amField = 0;
55static jfieldID g_pmField = 0;
56static jfieldID g_dateCommandField = 0;
57static jfieldID g_localeField = 0;
58
59static jclass g_timeClass = NULL;
60
61static inline bool java2time(JNIEnv* env, Time* t, jobject o)
62{
63    t->t.tm_sec = env->GetIntField(o, g_secField);
64    t->t.tm_min = env->GetIntField(o, g_minField);
65    t->t.tm_hour = env->GetIntField(o, g_hourField);
66    t->t.tm_mday = env->GetIntField(o, g_mdayField);
67    t->t.tm_mon = env->GetIntField(o, g_monField);
68    t->t.tm_year = (env->GetIntField(o, g_yearField))-1900;
69    t->t.tm_wday = env->GetIntField(o, g_wdayField);
70    t->t.tm_yday = env->GetIntField(o, g_ydayField);
71    t->t.tm_isdst = env->GetIntField(o, g_isdstField);
72    t->t.tm_gmtoff = env->GetLongField(o, g_gmtoffField);
73    bool allDay = env->GetBooleanField(o, g_allDayField);
74    if (allDay &&
75       ((t->t.tm_sec !=0) || (t->t.tm_min != 0) || (t->t.tm_hour != 0))) {
76        jniThrowException(env, "java/lang/IllegalArgumentException",
77                          "allDay is true but sec, min, hour are not 0.");
78        return false;
79    }
80    return true;
81}
82
83static inline void time2java(JNIEnv* env, jobject o, const Time &t)
84{
85    env->SetIntField(o, g_secField, t.t.tm_sec);
86    env->SetIntField(o, g_minField, t.t.tm_min);
87    env->SetIntField(o, g_hourField, t.t.tm_hour);
88    env->SetIntField(o, g_mdayField, t.t.tm_mday);
89    env->SetIntField(o, g_monField, t.t.tm_mon);
90    env->SetIntField(o, g_yearField, t.t.tm_year+1900);
91    env->SetIntField(o, g_wdayField, t.t.tm_wday);
92    env->SetIntField(o, g_ydayField, t.t.tm_yday);
93    env->SetIntField(o, g_isdstField, t.t.tm_isdst);
94    env->SetLongField(o, g_gmtoffField, t.t.tm_gmtoff);
95}
96
97#define ACQUIRE_TIMEZONE(This, t) \
98    jstring timezoneString_##This \
99            = (jstring) env->GetObjectField(This, g_timezoneField); \
100    t.timezone = env->GetStringUTFChars(timezoneString_##This, NULL);
101
102#define RELEASE_TIMEZONE(This, t) \
103    env->ReleaseStringUTFChars(timezoneString_##This, t.timezone);
104
105
106// ============================================================================
107
108static jlong android_text_format_Time_normalize(JNIEnv* env, jobject This,
109                                           jboolean ignoreDst)
110{
111    Time t;
112    if (!java2time(env, &t, This)) return 0L;
113    ACQUIRE_TIMEZONE(This, t)
114
115    int64_t result = t.toMillis(ignoreDst != 0);
116
117    time2java(env, This, t);
118    RELEASE_TIMEZONE(This, t)
119
120    return result;
121}
122
123static void android_text_format_Time_switchTimezone(JNIEnv* env, jobject This,
124                            jstring timezoneObject)
125{
126    Time t;
127    if (!java2time(env, &t, This)) return;
128    ACQUIRE_TIMEZONE(This, t)
129
130    const char* timezone = env->GetStringUTFChars(timezoneObject, NULL);
131
132    t.switchTimezone(timezone);
133
134    time2java(env, This, t);
135    env->ReleaseStringUTFChars(timezoneObject, timezone);
136    RELEASE_TIMEZONE(This, t)
137
138    // we do this here because there's no point in reallocating the string
139    env->SetObjectField(This, g_timezoneField, timezoneObject);
140}
141
142static jint android_text_format_Time_compare(JNIEnv* env, jobject clazz,
143                            jobject aObject, jobject bObject)
144{
145    Time a, b;
146
147    if (!java2time(env, &a, aObject)) return 0;
148    ACQUIRE_TIMEZONE(aObject, a)
149
150    if (!java2time(env, &b, bObject)) return 0;
151    ACQUIRE_TIMEZONE(bObject, b)
152
153    int result = Time::compare(a, b);
154
155    RELEASE_TIMEZONE(aObject, a)
156    RELEASE_TIMEZONE(bObject, b)
157
158    return result;
159}
160
161static jstring android_text_format_Time_format2445(JNIEnv* env, jobject This)
162{
163    Time t;
164    if (!java2time(env, &t, This)) return env->NewStringUTF("");
165    bool allDay = env->GetBooleanField(This, g_allDayField);
166
167    if (!allDay) {
168        ACQUIRE_TIMEZONE(This, t)
169        bool inUtc = strcmp("UTC", t.timezone) == 0;
170        short buf[16];
171        t.format2445(buf, true);
172        RELEASE_TIMEZONE(This, t)
173        if (inUtc) {
174            // The letter 'Z' is appended to the end so allow for one
175            // more character in the buffer.
176            return env->NewString((jchar*)buf, 16);
177        } else {
178            return env->NewString((jchar*)buf, 15);
179        }
180    } else {
181        short buf[8];
182        t.format2445(buf, false);
183        return env->NewString((jchar*)buf, 8);
184    }
185}
186
187static jstring android_text_format_Time_format(JNIEnv* env, jobject This,
188                            jstring formatObject)
189{
190    // We only teardown and setup our 'locale' struct and other state
191    // when the Java-side locale changed.  This is safe to do here
192    // without locking because we're always called from Java code
193    // synchronized on the class instance.
194    static jobject js_locale_previous = NULL;
195    static struct strftime_locale locale;
196    static jstring js_mon[12], js_month[12], js_wday[7], js_weekday[7];
197    static jstring js_standalone_month[12];
198    static jstring js_X_fmt, js_x_fmt, js_c_fmt, js_am, js_pm, js_date_fmt;
199
200    Time t;
201    if (!java2time(env, &t, This)) return env->NewStringUTF("");
202
203    jclass timeClass = g_timeClass;
204    jobject js_locale = (jobject) env->GetStaticObjectField(timeClass, g_localeField);
205    if (js_locale_previous != js_locale) {
206        if (js_locale_previous != NULL) {
207            // Free the old one.
208            for (int i = 0; i < 12; i++) {
209                env->ReleaseStringUTFChars(js_mon[i], locale.mon[i]);
210                env->ReleaseStringUTFChars(js_month[i], locale.month[i]);
211                env->ReleaseStringUTFChars(js_standalone_month[i], locale.standalone_month[i]);
212                env->DeleteGlobalRef(js_mon[i]);
213                env->DeleteGlobalRef(js_month[i]);
214                env->DeleteGlobalRef(js_standalone_month[i]);
215            }
216
217            for (int i = 0; i < 7; i++) {
218                env->ReleaseStringUTFChars(js_wday[i], locale.wday[i]);
219                env->ReleaseStringUTFChars(js_weekday[i], locale.weekday[i]);
220                env->DeleteGlobalRef(js_wday[i]);
221                env->DeleteGlobalRef(js_weekday[i]);
222            }
223
224            env->ReleaseStringUTFChars(js_X_fmt, locale.X_fmt);
225            env->ReleaseStringUTFChars(js_x_fmt, locale.x_fmt);
226            env->ReleaseStringUTFChars(js_c_fmt, locale.c_fmt);
227            env->ReleaseStringUTFChars(js_am, locale.am);
228            env->ReleaseStringUTFChars(js_pm, locale.pm);
229            env->ReleaseStringUTFChars(js_date_fmt, locale.date_fmt);
230            env->DeleteGlobalRef(js_X_fmt);
231            env->DeleteGlobalRef(js_x_fmt);
232            env->DeleteGlobalRef(js_c_fmt);
233            env->DeleteGlobalRef(js_am);
234            env->DeleteGlobalRef(js_pm);
235            env->DeleteGlobalRef(js_date_fmt);
236        }
237        js_locale_previous = js_locale;
238
239        jobjectArray ja;
240        ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_shortMonthsField);
241        for (int i = 0; i < 12; i++) {
242            // Calendar.JANUARY == 0.
243            js_mon[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i));
244            locale.mon[i] = env->GetStringUTFChars(js_mon[i], NULL);
245        }
246
247        ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longMonthsField);
248        for (int i = 0; i < 12; i++) {
249            // Calendar.JANUARY == 0.
250            js_month[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i));
251            locale.month[i] = env->GetStringUTFChars(js_month[i], NULL);
252        }
253
254        ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longStandaloneMonthsField);
255        for (int i = 0; i < 12; i++) {
256            // Calendar.JANUARY == 0.
257            js_standalone_month[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i));
258            locale.standalone_month[i] = env->GetStringUTFChars(js_standalone_month[i], NULL);
259        }
260
261        ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_shortWeekdaysField);
262        for (int i = 0; i < 7; i++) {
263            // Calendar.SUNDAY == 1, and there's an empty string in element 0.
264            js_wday[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i + 1));
265            locale.wday[i] = env->GetStringUTFChars(js_wday[i], NULL);
266        }
267
268        ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longWeekdaysField);
269        for (int i = 0; i < 7; i++) {
270            // Calendar.SUNDAY == 1, and there's an empty string in element 0.
271            js_weekday[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i + 1));
272            locale.weekday[i] = env->GetStringUTFChars(js_weekday[i], NULL);
273        }
274
275        js_X_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
276                                                       timeClass, g_timeOnlyFormatField));
277        locale.X_fmt = env->GetStringUTFChars(js_X_fmt, NULL);
278
279        js_x_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
280                                                       timeClass, g_dateOnlyFormatField));
281        locale.x_fmt = env->GetStringUTFChars(js_x_fmt, NULL);
282
283        js_c_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
284                                                       timeClass, g_dateTimeFormatField));
285        locale.c_fmt = env->GetStringUTFChars(js_c_fmt, NULL);
286
287        js_am = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
288                                                    timeClass, g_amField));
289        locale.am = env->GetStringUTFChars(js_am, NULL);
290
291        js_pm = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
292                                                    timeClass, g_pmField));
293        locale.pm = env->GetStringUTFChars(js_pm, NULL);
294
295        js_date_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
296                                                          timeClass, g_dateCommandField));
297        locale.date_fmt = env->GetStringUTFChars(js_date_fmt, NULL);
298    }
299
300    ACQUIRE_TIMEZONE(This, t)
301
302    const char* format = env->GetStringUTFChars(formatObject, NULL);
303
304    String8 r = t.format(format, &locale);
305
306    env->ReleaseStringUTFChars(formatObject, format);
307    RELEASE_TIMEZONE(This, t)
308
309    return env->NewStringUTF(r.string());
310}
311
312
313static jstring android_text_format_Time_toString(JNIEnv* env, jobject This)
314{
315    Time t;
316    if (!java2time(env, &t, This)) return env->NewStringUTF("");
317    ACQUIRE_TIMEZONE(This, t)
318
319    String8 r = t.toString();
320
321    RELEASE_TIMEZONE(This, t)
322
323    return env->NewStringUTF(r.string());
324}
325
326static void android_text_format_Time_setToNow(JNIEnv* env, jobject This)
327{
328    env->SetBooleanField(This, g_allDayField, JNI_FALSE);
329    Time t;
330    ACQUIRE_TIMEZONE(This, t)
331
332    t.setToNow();
333
334    time2java(env, This, t);
335    RELEASE_TIMEZONE(This, t)
336}
337
338static jlong android_text_format_Time_toMillis(JNIEnv* env, jobject This,
339                                        jboolean ignoreDst)
340{
341    Time t;
342    if (!java2time(env, &t, This)) return 0L;
343    ACQUIRE_TIMEZONE(This, t)
344
345    int64_t result = t.toMillis(ignoreDst != 0);
346
347    RELEASE_TIMEZONE(This, t)
348
349    return result;
350}
351
352static void android_text_format_Time_set(JNIEnv* env, jobject This, jlong millis)
353{
354    env->SetBooleanField(This, g_allDayField, JNI_FALSE);
355    Time t;
356    ACQUIRE_TIMEZONE(This, t)
357
358    t.set(millis);
359
360    time2java(env, This, t);
361    RELEASE_TIMEZONE(This, t)
362}
363
364
365// ============================================================================
366// Just do this here because it's not worth recreating the strings
367
368static int get_char(JNIEnv* env, const ScopedStringChars& s, int spos, int mul,
369                    bool* thrown)
370{
371    jchar c = s[spos];
372    if (c >= '0' && c <= '9') {
373        return (c - '0') * mul;
374    } else {
375        if (!*thrown) {
376            jniThrowExceptionFmt(env, "android/util/TimeFormatException",
377                                 "Parse error at pos=%d", spos);
378            *thrown = true;
379        }
380        return 0;
381    }
382}
383
384static bool check_char(JNIEnv* env, const ScopedStringChars& s, int spos, jchar expected)
385{
386    jchar c = s[spos];
387    if (c != expected) {
388        jniThrowExceptionFmt(env, "android/util/TimeFormatException",
389                             "Unexpected character 0x%02x at pos=%d.  Expected %c.",
390                             c, spos, expected);
391        return false;
392    }
393    return true;
394}
395
396
397static jboolean android_text_format_Time_parse(JNIEnv* env, jobject This, jstring strObj)
398{
399    jsize len = env->GetStringLength(strObj);
400    if (len < 8) {
401        jniThrowException(env, "android/util/TimeFormatException",
402                          "String too short -- expected at least 8 characters.");
403        return false;
404    }
405
406    jboolean inUtc = false;
407
408    ScopedStringChars s(env, strObj);
409
410    // year
411    int n;
412    bool thrown = false;
413    n = get_char(env, s, 0, 1000, &thrown);
414    n += get_char(env, s, 1, 100, &thrown);
415    n += get_char(env, s, 2, 10, &thrown);
416    n += get_char(env, s, 3, 1, &thrown);
417    if (thrown) return false;
418    env->SetIntField(This, g_yearField, n);
419
420    // month
421    n = get_char(env, s, 4, 10, &thrown);
422    n += get_char(env, s, 5, 1, &thrown);
423    n--;
424    if (thrown) return false;
425    env->SetIntField(This, g_monField, n);
426
427    // day of month
428    n = get_char(env, s, 6, 10, &thrown);
429    n += get_char(env, s, 7, 1, &thrown);
430    if (thrown) return false;
431    env->SetIntField(This, g_mdayField, n);
432
433    if (len > 8) {
434        // T
435        if (!check_char(env, s, 8, 'T')) return false;
436        env->SetBooleanField(This, g_allDayField, JNI_FALSE);
437
438        // hour
439        n = get_char(env, s, 9, 10, &thrown);
440        n += get_char(env, s, 10, 1, &thrown);
441        if (thrown) return false;
442        env->SetIntField(This, g_hourField, n);
443
444        // min
445        n = get_char(env, s, 11, 10, &thrown);
446        n += get_char(env, s, 12, 1, &thrown);
447        if (thrown) return false;
448        env->SetIntField(This, g_minField, n);
449
450        // sec
451        n = get_char(env, s, 13, 10, &thrown);
452        n += get_char(env, s, 14, 1, &thrown);
453        if (thrown) return false;
454        env->SetIntField(This, g_secField, n);
455
456        if (len > 15) {
457            // Z
458            if (!check_char(env, s, 15, 'Z')) return false;
459            inUtc = true;
460        }
461    } else {
462        env->SetBooleanField(This, g_allDayField, JNI_TRUE);
463        env->SetIntField(This, g_hourField, 0);
464        env->SetIntField(This, g_minField, 0);
465        env->SetIntField(This, g_secField, 0);
466    }
467
468    env->SetIntField(This, g_wdayField, 0);
469    env->SetIntField(This, g_ydayField, 0);
470    env->SetIntField(This, g_isdstField, -1);
471    env->SetLongField(This, g_gmtoffField, 0);
472
473    return inUtc;
474}
475
476static jboolean android_text_format_Time_parse3339(JNIEnv* env,
477                                           jobject This,
478                                           jstring strObj)
479{
480    jsize len = env->GetStringLength(strObj);
481    if (len < 10) {
482        jniThrowException(env, "android/util/TimeFormatException",
483                          "String too short --- expected at least 10 characters.");
484        return false;
485    }
486
487    jboolean inUtc = false;
488
489    ScopedStringChars s(env, strObj);
490
491    // year
492    int n;
493    bool thrown = false;
494    n = get_char(env, s, 0, 1000, &thrown);
495    n += get_char(env, s, 1, 100, &thrown);
496    n += get_char(env, s, 2, 10, &thrown);
497    n += get_char(env, s, 3, 1, &thrown);
498    if (thrown) return false;
499    env->SetIntField(This, g_yearField, n);
500
501    // -
502    if (!check_char(env, s, 4, '-')) return false;
503
504    // month
505    n = get_char(env, s, 5, 10, &thrown);
506    n += get_char(env, s, 6, 1, &thrown);
507    --n;
508    if (thrown) return false;
509    env->SetIntField(This, g_monField, n);
510
511    // -
512    if (!check_char(env, s, 7, '-')) return false;
513
514    // day
515    n = get_char(env, s, 8, 10, &thrown);
516    n += get_char(env, s, 9, 1, &thrown);
517    if (thrown) return false;
518    env->SetIntField(This, g_mdayField, n);
519
520    if (len >= 19) {
521        // T
522        if (!check_char(env, s, 10, 'T')) return false;
523
524        env->SetBooleanField(This, g_allDayField, JNI_FALSE);
525        // hour
526        n = get_char(env, s, 11, 10, &thrown);
527        n += get_char(env, s, 12, 1, &thrown);
528        if (thrown) return false;
529        int hour = n;
530        // env->SetIntField(This, g_hourField, n);
531
532        // :
533        if (!check_char(env, s, 13, ':')) return false;
534
535        // minute
536        n = get_char(env, s, 14, 10, &thrown);
537        n += get_char(env, s, 15, 1, &thrown);
538        if (thrown) return false;
539        int minute = n;
540        // env->SetIntField(This, g_minField, n);
541
542        // :
543        if (!check_char(env, s, 16, ':')) return false;
544
545        // second
546        n = get_char(env, s, 17, 10, &thrown);
547        n += get_char(env, s, 18, 1, &thrown);
548        if (thrown) return false;
549        env->SetIntField(This, g_secField, n);
550
551        // skip the '.XYZ' -- we don't care about subsecond precision.
552        int tz_index = 19;
553        if (tz_index < len && s[tz_index] == '.') {
554            do {
555                tz_index++;
556            } while (tz_index < len
557                && s[tz_index] >= '0'
558                && s[tz_index] <= '9');
559        }
560
561        int offset = 0;
562        if (len > tz_index) {
563            char c = s[tz_index];
564
565            // NOTE: the offset is meant to be subtracted to get from local time
566            // to UTC.  we therefore use 1 for '-' and -1 for '+'.
567            switch (c) {
568            case 'Z':
569                // Zulu time -- UTC
570                offset = 0;
571                break;
572            case '-':
573                offset = 1;
574                break;
575            case '+':
576                offset = -1;
577                break;
578            default:
579                jniThrowExceptionFmt(env, "android/util/TimeFormatException",
580                                     "Unexpected character 0x%02x at position %d.  Expected + or -",
581                                     c, tz_index);
582                return false;
583            }
584            inUtc = true;
585
586            if (offset != 0) {
587                if (len < tz_index + 6) {
588                    jniThrowExceptionFmt(env, "android/util/TimeFormatException",
589                                         "Unexpected length; should be %d characters",
590                                         tz_index + 6);
591                    return false;
592                }
593
594                // hour
595                n = get_char(env, s, tz_index + 1, 10, &thrown);
596                n += get_char(env, s, tz_index + 2, 1, &thrown);
597                if (thrown) return false;
598                n *= offset;
599                hour += n;
600
601                // :
602                if (!check_char(env, s, tz_index + 3, ':')) return false;
603
604                // minute
605                n = get_char(env, s, tz_index + 4, 10, &thrown);
606                n += get_char(env, s, tz_index + 5, 1, &thrown);
607                if (thrown) return false;
608                n *= offset;
609                minute += n;
610            }
611        }
612        env->SetIntField(This, g_hourField, hour);
613        env->SetIntField(This, g_minField, minute);
614
615        if (offset != 0) {
616            // we need to normalize after applying the hour and minute offsets
617            android_text_format_Time_normalize(env, This, false /* use isdst */);
618            // The timezone is set to UTC in the calling Java code.
619        }
620    } else {
621        env->SetBooleanField(This, g_allDayField, JNI_TRUE);
622        env->SetIntField(This, g_hourField, 0);
623        env->SetIntField(This, g_minField, 0);
624        env->SetIntField(This, g_secField, 0);
625    }
626
627    env->SetIntField(This, g_wdayField, 0);
628    env->SetIntField(This, g_ydayField, 0);
629    env->SetIntField(This, g_isdstField, -1);
630    env->SetLongField(This, g_gmtoffField, 0);
631
632    return inUtc;
633}
634
635// ============================================================================
636/*
637 * JNI registration.
638 */
639static JNINativeMethod gMethods[] = {
640    /* name, signature, funcPtr */
641    { "normalize",               "(Z)J",                                        (void*)android_text_format_Time_normalize },
642    { "switchTimezone",          "(Ljava/lang/String;)V",                       (void*)android_text_format_Time_switchTimezone },
643    { "nativeCompare",           "(Landroid/text/format/Time;Landroid/text/format/Time;)I",     (void*)android_text_format_Time_compare },
644    { "format1",                 "(Ljava/lang/String;)Ljava/lang/String;",      (void*)android_text_format_Time_format },
645    { "format2445",              "()Ljava/lang/String;",                        (void*)android_text_format_Time_format2445 },
646    { "toString",                "()Ljava/lang/String;",                        (void*)android_text_format_Time_toString },
647    { "nativeParse",             "(Ljava/lang/String;)Z",                       (void*)android_text_format_Time_parse },
648    { "nativeParse3339",         "(Ljava/lang/String;)Z",                       (void*)android_text_format_Time_parse3339 },
649    { "setToNow",                "()V",                                         (void*)android_text_format_Time_setToNow },
650    { "toMillis",                "(Z)J",                                        (void*)android_text_format_Time_toMillis },
651    { "set",                     "(J)V",                                        (void*)android_text_format_Time_set }
652};
653
654int register_android_text_format_Time(JNIEnv* env)
655{
656    jclass timeClass = env->FindClass("android/text/format/Time");
657
658    g_timeClass = (jclass) env->NewGlobalRef(timeClass);
659
660    g_allDayField = env->GetFieldID(timeClass, "allDay", "Z");
661    g_secField = env->GetFieldID(timeClass, "second", "I");
662    g_minField = env->GetFieldID(timeClass, "minute", "I");
663    g_hourField = env->GetFieldID(timeClass, "hour", "I");
664    g_mdayField = env->GetFieldID(timeClass, "monthDay", "I");
665    g_monField = env->GetFieldID(timeClass, "month", "I");
666    g_yearField = env->GetFieldID(timeClass, "year", "I");
667    g_wdayField = env->GetFieldID(timeClass, "weekDay", "I");
668    g_ydayField = env->GetFieldID(timeClass, "yearDay", "I");
669    g_isdstField = env->GetFieldID(timeClass, "isDst", "I");
670    g_gmtoffField = env->GetFieldID(timeClass, "gmtoff", "J");
671    g_timezoneField = env->GetFieldID(timeClass, "timezone", "Ljava/lang/String;");
672
673    g_shortMonthsField = env->GetStaticFieldID(timeClass, "sShortMonths", "[Ljava/lang/String;");
674    g_longMonthsField = env->GetStaticFieldID(timeClass, "sLongMonths", "[Ljava/lang/String;");
675    g_longStandaloneMonthsField = env->GetStaticFieldID(timeClass, "sLongStandaloneMonths", "[Ljava/lang/String;");
676    g_shortWeekdaysField = env->GetStaticFieldID(timeClass, "sShortWeekdays", "[Ljava/lang/String;");
677    g_longWeekdaysField = env->GetStaticFieldID(timeClass, "sLongWeekdays", "[Ljava/lang/String;");
678    g_timeOnlyFormatField = env->GetStaticFieldID(timeClass, "sTimeOnlyFormat", "Ljava/lang/String;");
679    g_dateOnlyFormatField = env->GetStaticFieldID(timeClass, "sDateOnlyFormat", "Ljava/lang/String;");
680    g_dateTimeFormatField = env->GetStaticFieldID(timeClass, "sDateTimeFormat", "Ljava/lang/String;");
681    g_amField = env->GetStaticFieldID(timeClass, "sAm", "Ljava/lang/String;");
682    g_pmField = env->GetStaticFieldID(timeClass, "sPm", "Ljava/lang/String;");
683    g_dateCommandField = env->GetStaticFieldID(timeClass, "sDateCommand", "Ljava/lang/String;");
684    g_localeField = env->GetStaticFieldID(timeClass, "sLocale", "Ljava/util/Locale;");
685
686    return AndroidRuntime::registerNativeMethods(env, "android/text/format/Time", gMethods, NELEM(gMethods));
687}
688
689}; // namespace android
690