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 "TimeUtils.h"
27#include <nativehelper/JNIHelp.h>
28#include <cutils/tztime.h>
29
30namespace android {
31
32static jfieldID g_allDayField = 0;
33static jfieldID g_secField = 0;
34static jfieldID g_minField = 0;
35static jfieldID g_hourField = 0;
36static jfieldID g_mdayField = 0;
37static jfieldID g_monField = 0;
38static jfieldID g_yearField = 0;
39static jfieldID g_wdayField = 0;
40static jfieldID g_ydayField = 0;
41static jfieldID g_isdstField = 0;
42static jfieldID g_gmtoffField = 0;
43static jfieldID g_timezoneField = 0;
44
45static jfieldID g_shortMonthsField = 0;
46static jfieldID g_longMonthsField = 0;
47static jfieldID g_longStandaloneMonthsField = 0;
48static jfieldID g_shortWeekdaysField = 0;
49static jfieldID g_longWeekdaysField = 0;
50static jfieldID g_timeOnlyFormatField = 0;
51static jfieldID g_dateOnlyFormatField = 0;
52static jfieldID g_dateTimeFormatField = 0;
53static jfieldID g_amField = 0;
54static jfieldID g_pmField = 0;
55static jfieldID g_dateCommandField = 0;
56static jfieldID g_localeField = 0;
57
58static jclass g_timeClass = NULL;
59
60static inline bool java2time(JNIEnv* env, Time* t, jobject o)
61{
62    t->t.tm_sec = env->GetIntField(o, g_secField);
63    t->t.tm_min = env->GetIntField(o, g_minField);
64    t->t.tm_hour = env->GetIntField(o, g_hourField);
65    t->t.tm_mday = env->GetIntField(o, g_mdayField);
66    t->t.tm_mon = env->GetIntField(o, g_monField);
67    t->t.tm_year = (env->GetIntField(o, g_yearField))-1900;
68    t->t.tm_wday = env->GetIntField(o, g_wdayField);
69    t->t.tm_yday = env->GetIntField(o, g_ydayField);
70    t->t.tm_isdst = env->GetIntField(o, g_isdstField);
71    t->t.tm_gmtoff = env->GetLongField(o, g_gmtoffField);
72    bool allDay = env->GetIntField(o, g_allDayField);
73    if (allDay &&
74	((t->t.tm_sec !=0) || (t->t.tm_min != 0) || (t->t.tm_hour != 0))) {
75        char msg[100];
76	sprintf(msg, "allDay is true but sec, min, hour are not 0.");
77	jniThrowException(env, "java/lang/IllegalArgumentException", msg);
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->GetIntField(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            js_mon[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i));
243            locale.mon[i] = env->GetStringUTFChars(js_mon[i], NULL);
244        }
245
246        ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longMonthsField);
247        for (int i = 0; i < 12; i++) {
248            js_month[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i));
249            locale.month[i] = env->GetStringUTFChars(js_month[i], NULL);
250        }
251
252        ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longStandaloneMonthsField);
253        for (int i = 0; i < 12; i++) {
254            js_standalone_month[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i));
255            locale.standalone_month[i] = env->GetStringUTFChars(js_standalone_month[i], NULL);
256        }
257
258        ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_shortWeekdaysField);
259        for (int i = 0; i < 7; i++) {
260            js_wday[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i));
261            locale.wday[i] = env->GetStringUTFChars(js_wday[i], NULL);
262        }
263
264        ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longWeekdaysField);
265        for (int i = 0; i < 7; i++) {
266            js_weekday[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i));
267            locale.weekday[i] = env->GetStringUTFChars(js_weekday[i], NULL);
268        }
269
270        js_X_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
271                                                       timeClass, g_timeOnlyFormatField));
272        locale.X_fmt = env->GetStringUTFChars(js_X_fmt, NULL);
273
274        js_x_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
275                                                       timeClass, g_dateOnlyFormatField));
276        locale.x_fmt = env->GetStringUTFChars(js_x_fmt, NULL);
277
278        js_c_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
279                                                       timeClass, g_dateTimeFormatField));
280        locale.c_fmt = env->GetStringUTFChars(js_c_fmt, NULL);
281
282        js_am = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
283                                                    timeClass, g_amField));
284        locale.am = env->GetStringUTFChars(js_am, NULL);
285
286        js_pm = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
287                                                    timeClass, g_pmField));
288        locale.pm = env->GetStringUTFChars(js_pm, NULL);
289
290        js_date_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
291                                                          timeClass, g_dateCommandField));
292        locale.date_fmt = env->GetStringUTFChars(js_date_fmt, NULL);
293    }
294
295    ACQUIRE_TIMEZONE(This, t)
296
297    const char* format = env->GetStringUTFChars(formatObject, NULL);
298
299    String8 r = t.format(format, &locale);
300
301    env->ReleaseStringUTFChars(formatObject, format);
302    RELEASE_TIMEZONE(This, t)
303
304    return env->NewStringUTF(r.string());
305}
306
307
308static jstring android_text_format_Time_toString(JNIEnv* env, jobject This)
309{
310    Time t;
311    if (!java2time(env, &t, This)) return env->NewStringUTF("");;
312    ACQUIRE_TIMEZONE(This, t)
313
314    String8 r = t.toString();
315
316    RELEASE_TIMEZONE(This, t)
317
318    return env->NewStringUTF(r.string());
319}
320
321static void android_text_format_Time_setToNow(JNIEnv* env, jobject This)
322{
323    env->SetBooleanField(This, g_allDayField, JNI_FALSE);
324    Time t;
325    ACQUIRE_TIMEZONE(This, t)
326
327    t.setToNow();
328
329    time2java(env, This, t);
330    RELEASE_TIMEZONE(This, t)
331}
332
333static jlong android_text_format_Time_toMillis(JNIEnv* env, jobject This,
334                                        jboolean ignoreDst)
335{
336    Time t;
337    if (!java2time(env, &t, This)) return 0L;
338    ACQUIRE_TIMEZONE(This, t)
339
340    int64_t result = t.toMillis(ignoreDst != 0);
341
342    RELEASE_TIMEZONE(This, t)
343
344    return result;
345}
346
347static void android_text_format_Time_set(JNIEnv* env, jobject This, jlong millis)
348{
349    env->SetBooleanField(This, g_allDayField, JNI_FALSE);
350    Time t;
351    ACQUIRE_TIMEZONE(This, t)
352
353    t.set(millis);
354
355    time2java(env, This, t);
356    RELEASE_TIMEZONE(This, t)
357}
358
359
360// ============================================================================
361// Just do this here because it's not worth recreating the strings
362
363static int get_char(JNIEnv* env, const jchar *s, int spos, int mul,
364                    bool *thrown)
365{
366    jchar c = s[spos];
367    if (c >= '0' && c <= '9') {
368        return (c - '0') * mul;
369    } else {
370        if (!*thrown) {
371            char msg[100];
372            sprintf(msg, "Parse error at pos=%d", spos);
373            jniThrowException(env, "android/util/TimeFormatException", msg);
374            *thrown = true;
375        }
376        return 0;
377    }
378}
379
380static bool check_char(JNIEnv* env, const jchar *s, int spos, jchar expected)
381{
382    jchar c = s[spos];
383    if (c != expected) {
384        char msg[100];
385	sprintf(msg, "Unexpected character 0x%02x at pos=%d.  Expected %c.", c, spos,
386		expected);
387	jniThrowException(env, "android/util/TimeFormatException", msg);
388	return false;
389    }
390    return true;
391}
392
393
394static jboolean android_text_format_Time_parse(JNIEnv* env, jobject This, jstring strObj)
395{
396    jsize len = env->GetStringLength(strObj);
397    const jchar *s = env->GetStringChars(strObj, NULL);
398
399    bool thrown = false;
400    int n;
401    jboolean inUtc = false;
402
403    if (len < 8) {
404        char msg[100];
405        sprintf(msg, "String too short -- expected at least 8 characters.");
406	jniThrowException(env, "android/util/TimeFormatException", msg);
407	return false;
408    }
409
410    // year
411    n = get_char(env, s, 0, 1000, &thrown);
412    n += get_char(env, s, 1, 100, &thrown);
413    n += get_char(env, s, 2, 10, &thrown);
414    n += get_char(env, s, 3, 1, &thrown);
415    if (thrown) return false;
416    env->SetIntField(This, g_yearField, n);
417
418    // month
419    n = get_char(env, s, 4, 10, &thrown);
420    n += get_char(env, s, 5, 1, &thrown);
421    n--;
422    if (thrown) return false;
423    env->SetIntField(This, g_monField, n);
424
425    // day of month
426    n = get_char(env, s, 6, 10, &thrown);
427    n += get_char(env, s, 7, 1, &thrown);
428    if (thrown) return false;
429    env->SetIntField(This, g_mdayField, n);
430
431    if (len > 8) {
432        // T
433        if (!check_char(env, s, 8, 'T')) return false;
434        env->SetBooleanField(This, g_allDayField, JNI_FALSE);
435
436        // hour
437        n = get_char(env, s, 9, 10, &thrown);
438        n += get_char(env, s, 10, 1, &thrown);
439        if (thrown) return false;
440        env->SetIntField(This, g_hourField, n);
441
442        // min
443        n = get_char(env, s, 11, 10, &thrown);
444        n += get_char(env, s, 12, 1, &thrown);
445        if (thrown) return false;
446        env->SetIntField(This, g_minField, n);
447
448        // sec
449        n = get_char(env, s, 13, 10, &thrown);
450        n += get_char(env, s, 14, 1, &thrown);
451        if (thrown) return false;
452        env->SetIntField(This, g_secField, n);
453
454        if (len > 15) {
455            // Z
456            if (!check_char(env, s, 15, 'Z')) return false;
457	    inUtc = true;
458        }
459    } else {
460        env->SetBooleanField(This, g_allDayField, JNI_TRUE);
461        env->SetIntField(This, g_hourField, 0);
462        env->SetIntField(This, g_minField, 0);
463        env->SetIntField(This, g_secField, 0);
464    }
465
466    env->SetIntField(This, g_wdayField, 0);
467    env->SetIntField(This, g_ydayField, 0);
468    env->SetIntField(This, g_isdstField, -1);
469    env->SetLongField(This, g_gmtoffField, 0);
470
471    env->ReleaseStringChars(strObj, s);
472    return inUtc;
473}
474
475static jboolean android_text_format_Time_parse3339(JNIEnv* env,
476                                           jobject This,
477                                           jstring strObj)
478{
479    jsize len = env->GetStringLength(strObj);
480    const jchar *s = env->GetStringChars(strObj, NULL);
481
482    bool thrown = false;
483    int n;
484    jboolean inUtc = false;
485
486    if (len < 10) {
487        jniThrowException(env, "android/util/TimeFormatException",
488                "Time input is too short; must be at least 10 characters");
489        return false;
490    }
491
492    // year
493    n = get_char(env, s, 0, 1000, &thrown);
494    n += get_char(env, s, 1, 100, &thrown);
495    n += get_char(env, s, 2, 10, &thrown);
496    n += get_char(env, s, 3, 1, &thrown);
497    if (thrown) return false;
498    env->SetIntField(This, g_yearField, n);
499
500    // -
501    if (!check_char(env, s, 4, '-')) return false;
502
503    // month
504    n = get_char(env, s, 5, 10, &thrown);
505    n += get_char(env, s, 6, 1, &thrown);
506    --n;
507    if (thrown) return false;
508    env->SetIntField(This, g_monField, n);
509
510    // -
511    if (!check_char(env, s, 7, '-')) return false;
512
513    // day
514    n = get_char(env, s, 8, 10, &thrown);
515    n += get_char(env, s, 9, 1, &thrown);
516    if (thrown) return false;
517    env->SetIntField(This, g_mdayField, n);
518
519    if (len >= 19) {
520        // T
521        if (!check_char(env, s, 10, 'T')) return false;
522
523	env->SetBooleanField(This, g_allDayField, JNI_FALSE);
524        // hour
525        n = get_char(env, s, 11, 10, &thrown);
526        n += get_char(env, s, 12, 1, &thrown);
527        if (thrown) return false;
528	int hour = n;
529        // env->SetIntField(This, g_hourField, n);
530
531	// :
532	if (!check_char(env, s, 13, ':')) return false;
533
534	// minute
535        n = get_char(env, s, 14, 10, &thrown);
536        n += get_char(env, s, 15, 1, &thrown);
537        if (thrown) return false;
538	int minute = n;
539        // env->SetIntField(This, g_minField, n);
540
541	// :
542	if (!check_char(env, s, 16, ':')) return false;
543
544	// second
545        n = get_char(env, s, 17, 10, &thrown);
546        n += get_char(env, s, 18, 1, &thrown);
547        if (thrown) return false;
548        env->SetIntField(This, g_secField, n);
549
550        // skip the '.XYZ' -- we don't care about subsecond precision.
551        int tz_index = 19;
552        if (tz_index < len && s[tz_index] == '.') {
553            do {
554                tz_index++;
555            } while (tz_index < len
556                && s[tz_index] >= '0'
557                && s[tz_index] <= '9');
558        }
559
560        int offset = 0;
561        if (len > tz_index) {
562            char c = s[tz_index];
563
564	    // NOTE: the offset is meant to be subtracted to get from local time
565	    // to UTC.  we therefore use 1 for '-' and -1 for '+'.
566	    switch (c) {
567	    case 'Z':
568	        // Zulu time -- UTC
569	        offset = 0;
570		break;
571	    case '-':
572                offset = 1;
573	        break;
574	    case '+':
575                offset = -1;
576	        break;
577	    default:
578	        char msg[100];
579	        sprintf(msg, "Unexpected character 0x%02x at position %d.  Expected + or -",
580			c, tz_index);
581	        jniThrowException(env, "android/util/TimeFormatException", msg);
582	        return false;
583	    }
584            inUtc = true;
585
586	    if (offset != 0) {
587	        if (len < tz_index + 6) {
588	            char msg[100];
589	            sprintf(msg, "Unexpected length; should be %d characters", tz_index + 6);
590	            jniThrowException(env, "android/util/TimeFormatException", msg);
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    env->ReleaseStringChars(strObj, s);
633    return inUtc;
634}
635
636// ============================================================================
637/*
638 * JNI registration.
639 */
640static JNINativeMethod gMethods[] = {
641    /* name, signature, funcPtr */
642    { "normalize",               "(Z)J",                                        (void*)android_text_format_Time_normalize },
643    { "switchTimezone",          "(Ljava/lang/String;)V",                       (void*)android_text_format_Time_switchTimezone },
644    { "compare",                 "(Landroid/text/format/Time;Landroid/text/format/Time;)I",     (void*)android_text_format_Time_compare },
645    { "format1",                 "(Ljava/lang/String;)Ljava/lang/String;",      (void*)android_text_format_Time_format },
646    { "format2445",              "()Ljava/lang/String;",                        (void*)android_text_format_Time_format2445 },
647    { "toString",                "()Ljava/lang/String;",                        (void*)android_text_format_Time_toString },
648    { "nativeParse",             "(Ljava/lang/String;)Z",                       (void*)android_text_format_Time_parse },
649    { "nativeParse3339",         "(Ljava/lang/String;)Z",                       (void*)android_text_format_Time_parse3339 },
650    { "setToNow",                "()V",                                         (void*)android_text_format_Time_setToNow },
651    { "toMillis",                "(Z)J",                                        (void*)android_text_format_Time_toMillis },
652    { "set",                     "(J)V",                                        (void*)android_text_format_Time_set }
653};
654
655int register_android_text_format_Time(JNIEnv* env)
656{
657    jclass timeClass = env->FindClass("android/text/format/Time");
658
659    g_timeClass = (jclass) env->NewGlobalRef(timeClass);
660
661    g_allDayField = env->GetFieldID(timeClass, "allDay", "Z");
662    g_secField = env->GetFieldID(timeClass, "second", "I");
663    g_minField = env->GetFieldID(timeClass, "minute", "I");
664    g_hourField = env->GetFieldID(timeClass, "hour", "I");
665    g_mdayField = env->GetFieldID(timeClass, "monthDay", "I");
666    g_monField = env->GetFieldID(timeClass, "month", "I");
667    g_yearField = env->GetFieldID(timeClass, "year", "I");
668    g_wdayField = env->GetFieldID(timeClass, "weekDay", "I");
669    g_ydayField = env->GetFieldID(timeClass, "yearDay", "I");
670    g_isdstField = env->GetFieldID(timeClass, "isDst", "I");
671    g_gmtoffField = env->GetFieldID(timeClass, "gmtoff", "J");
672    g_timezoneField = env->GetFieldID(timeClass, "timezone", "Ljava/lang/String;");
673
674    g_shortMonthsField = env->GetStaticFieldID(timeClass, "sShortMonths", "[Ljava/lang/String;");
675    g_longMonthsField = env->GetStaticFieldID(timeClass, "sLongMonths", "[Ljava/lang/String;");
676    g_longStandaloneMonthsField = env->GetStaticFieldID(timeClass, "sLongStandaloneMonths", "[Ljava/lang/String;");
677    g_shortWeekdaysField = env->GetStaticFieldID(timeClass, "sShortWeekdays", "[Ljava/lang/String;");
678    g_longWeekdaysField = env->GetStaticFieldID(timeClass, "sLongWeekdays", "[Ljava/lang/String;");
679    g_timeOnlyFormatField = env->GetStaticFieldID(timeClass, "sTimeOnlyFormat", "Ljava/lang/String;");
680    g_dateOnlyFormatField = env->GetStaticFieldID(timeClass, "sDateOnlyFormat", "Ljava/lang/String;");
681    g_dateTimeFormatField = env->GetStaticFieldID(timeClass, "sDateTimeFormat", "Ljava/lang/String;");
682    g_amField = env->GetStaticFieldID(timeClass, "sAm", "Ljava/lang/String;");
683    g_pmField = env->GetStaticFieldID(timeClass, "sPm", "Ljava/lang/String;");
684    g_dateCommandField = env->GetStaticFieldID(timeClass, "sDateCommand", "Ljava/lang/String;");
685    g_localeField = env->GetStaticFieldID(timeClass, "sLocale", "Ljava/util/Locale;");
686
687    return AndroidRuntime::registerNativeMethods(env, "android/text/format/Time", gMethods, NELEM(gMethods));
688}
689
690}; // namespace android
691