OSDN Git Service

am ad17523d: add warning for Windows users and fix typos
[android-x86/frameworks-base.git] / core / jni / android_text_format_Time.cpp
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
30 namespace android {
31
32 static jfieldID g_allDayField = 0;
33 static jfieldID g_secField = 0;
34 static jfieldID g_minField = 0;
35 static jfieldID g_hourField = 0;
36 static jfieldID g_mdayField = 0;
37 static jfieldID g_monField = 0;
38 static jfieldID g_yearField = 0;
39 static jfieldID g_wdayField = 0;
40 static jfieldID g_ydayField = 0;
41 static jfieldID g_isdstField = 0;
42 static jfieldID g_gmtoffField = 0;
43 static jfieldID g_timezoneField = 0;
44
45 static jfieldID g_shortMonthsField = 0;
46 static jfieldID g_longMonthsField = 0;
47 static jfieldID g_longStandaloneMonthsField = 0;
48 static jfieldID g_shortWeekdaysField = 0;
49 static jfieldID g_longWeekdaysField = 0;
50 static jfieldID g_timeOnlyFormatField = 0;
51 static jfieldID g_dateOnlyFormatField = 0;
52 static jfieldID g_dateTimeFormatField = 0;
53 static jfieldID g_amField = 0;
54 static jfieldID g_pmField = 0;
55 static jfieldID g_dateCommandField = 0;
56 static jfieldID g_localeField = 0;
57
58 static jclass g_timeClass = NULL;
59
60 static 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->GetBooleanField(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
83 static 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
108 static 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
123 static 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
142 static 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
161 static 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
187 static 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
313 static 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
326 static 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
338 static 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
352 static 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
368 static int get_char(JNIEnv* env, const jchar *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             char msg[100];
377             sprintf(msg, "Parse error at pos=%d", spos);
378             jniThrowException(env, "android/util/TimeFormatException", msg);
379             *thrown = true;
380         }
381         return 0;
382     }
383 }
384
385 static bool check_char(JNIEnv* env, const jchar *s, int spos, jchar expected)
386 {
387     jchar c = s[spos];
388     if (c != expected) {
389         char msg[100];
390         sprintf(msg, "Unexpected character 0x%02x at pos=%d.  Expected %c.", c, spos,
391                 expected);
392         jniThrowException(env, "android/util/TimeFormatException", msg);
393         return false;
394     }
395     return true;
396 }
397
398
399 static jboolean android_text_format_Time_parse(JNIEnv* env, jobject This, jstring strObj)
400 {
401     jsize len = env->GetStringLength(strObj);
402     const jchar *s = env->GetStringChars(strObj, NULL);
403
404     bool thrown = false;
405     int n;
406     jboolean inUtc = false;
407
408     if (len < 8) {
409         char msg[100];
410         sprintf(msg, "String too short -- expected at least 8 characters.");
411         jniThrowException(env, "android/util/TimeFormatException", msg);
412         return false;
413     }
414
415     // year
416     n = get_char(env, s, 0, 1000, &thrown);
417     n += get_char(env, s, 1, 100, &thrown);
418     n += get_char(env, s, 2, 10, &thrown);
419     n += get_char(env, s, 3, 1, &thrown);
420     if (thrown) return false;
421     env->SetIntField(This, g_yearField, n);
422
423     // month
424     n = get_char(env, s, 4, 10, &thrown);
425     n += get_char(env, s, 5, 1, &thrown);
426     n--;
427     if (thrown) return false;
428     env->SetIntField(This, g_monField, n);
429
430     // day of month
431     n = get_char(env, s, 6, 10, &thrown);
432     n += get_char(env, s, 7, 1, &thrown);
433     if (thrown) return false;
434     env->SetIntField(This, g_mdayField, n);
435
436     if (len > 8) {
437         // T
438         if (!check_char(env, s, 8, 'T')) return false;
439         env->SetBooleanField(This, g_allDayField, JNI_FALSE);
440
441         // hour
442         n = get_char(env, s, 9, 10, &thrown);
443         n += get_char(env, s, 10, 1, &thrown);
444         if (thrown) return false;
445         env->SetIntField(This, g_hourField, n);
446
447         // min
448         n = get_char(env, s, 11, 10, &thrown);
449         n += get_char(env, s, 12, 1, &thrown);
450         if (thrown) return false;
451         env->SetIntField(This, g_minField, n);
452
453         // sec
454         n = get_char(env, s, 13, 10, &thrown);
455         n += get_char(env, s, 14, 1, &thrown);
456         if (thrown) return false;
457         env->SetIntField(This, g_secField, n);
458
459         if (len > 15) {
460             // Z
461             if (!check_char(env, s, 15, 'Z')) return false;
462             inUtc = true;
463         }
464     } else {
465         env->SetBooleanField(This, g_allDayField, JNI_TRUE);
466         env->SetIntField(This, g_hourField, 0);
467         env->SetIntField(This, g_minField, 0);
468         env->SetIntField(This, g_secField, 0);
469     }
470
471     env->SetIntField(This, g_wdayField, 0);
472     env->SetIntField(This, g_ydayField, 0);
473     env->SetIntField(This, g_isdstField, -1);
474     env->SetLongField(This, g_gmtoffField, 0);
475     
476     env->ReleaseStringChars(strObj, s);
477     return inUtc;
478 }
479
480 static jboolean android_text_format_Time_parse3339(JNIEnv* env, 
481                                            jobject This, 
482                                            jstring strObj)
483 {
484     jsize len = env->GetStringLength(strObj);
485     const jchar *s = env->GetStringChars(strObj, NULL);
486
487     bool thrown = false;
488     int n;
489     jboolean inUtc = false;
490
491     if (len < 10) {
492         jniThrowException(env, "android/util/TimeFormatException",
493                 "Time input is too short; must be at least 10 characters");
494         return false;
495     }
496
497     // year
498     n = get_char(env, s, 0, 1000, &thrown);    
499     n += get_char(env, s, 1, 100, &thrown);
500     n += get_char(env, s, 2, 10, &thrown);
501     n += get_char(env, s, 3, 1, &thrown);
502     if (thrown) return false;
503     env->SetIntField(This, g_yearField, n);
504     
505     // -
506     if (!check_char(env, s, 4, '-')) return false;
507     
508     // month
509     n = get_char(env, s, 5, 10, &thrown);
510     n += get_char(env, s, 6, 1, &thrown);
511     --n;
512     if (thrown) return false;
513     env->SetIntField(This, g_monField, n);
514
515     // -
516     if (!check_char(env, s, 7, '-')) return false;
517
518     // day
519     n = get_char(env, s, 8, 10, &thrown);
520     n += get_char(env, s, 9, 1, &thrown);
521     if (thrown) return false;
522     env->SetIntField(This, g_mdayField, n);
523
524     if (len >= 19) {
525         // T
526         if (!check_char(env, s, 10, 'T')) return false;
527
528         env->SetBooleanField(This, g_allDayField, JNI_FALSE);
529         // hour
530         n = get_char(env, s, 11, 10, &thrown);
531         n += get_char(env, s, 12, 1, &thrown);
532         if (thrown) return false;
533         int hour = n;
534         // env->SetIntField(This, g_hourField, n);
535         
536         // :
537         if (!check_char(env, s, 13, ':')) return false;
538
539         // minute
540         n = get_char(env, s, 14, 10, &thrown);
541         n += get_char(env, s, 15, 1, &thrown);
542         if (thrown) return false;
543         int minute = n;
544         // env->SetIntField(This, g_minField, n);
545
546         // :
547         if (!check_char(env, s, 16, ':')) return false;
548
549         // second
550         n = get_char(env, s, 17, 10, &thrown);
551         n += get_char(env, s, 18, 1, &thrown);
552         if (thrown) return false;
553         env->SetIntField(This, g_secField, n);
554
555         // skip the '.XYZ' -- we don't care about subsecond precision.
556         int tz_index = 19;
557         if (tz_index < len && s[tz_index] == '.') {
558             do {
559                 tz_index++;
560             } while (tz_index < len
561                 && s[tz_index] >= '0'
562                 && s[tz_index] <= '9');
563         }
564
565         int offset = 0;
566         if (len > tz_index) {
567             char c = s[tz_index];
568
569             // NOTE: the offset is meant to be subtracted to get from local time
570             // to UTC.  we therefore use 1 for '-' and -1 for '+'.
571             switch (c) {
572             case 'Z':
573                 // Zulu time -- UTC
574                 offset = 0;
575                 break;
576             case '-': 
577                 offset = 1;
578                 break;
579             case '+': 
580                 offset = -1;
581                 break;
582             default:
583                 char msg[100];
584                 sprintf(msg, "Unexpected character 0x%02x at position %d.  Expected + or -",
585                         c, tz_index);
586                 jniThrowException(env, "android/util/TimeFormatException", msg);
587                 return false;
588             }
589             inUtc = true;
590
591             if (offset != 0) {
592                 if (len < tz_index + 6) {
593                     char msg[100];
594                     sprintf(msg, "Unexpected length; should be %d characters", tz_index + 6);
595                     jniThrowException(env, "android/util/TimeFormatException", msg);
596                     return false;
597                 }
598
599                 // hour
600                 n = get_char(env, s, tz_index + 1, 10, &thrown);
601                 n += get_char(env, s, tz_index + 2, 1, &thrown);
602                 if (thrown) return false;
603                 n *= offset;
604                 hour += n;
605
606                 // :
607                 if (!check_char(env, s, tz_index + 3, ':')) return false;
608             
609                 // minute
610                 n = get_char(env, s, tz_index + 4, 10, &thrown);
611                 n += get_char(env, s, tz_index + 5, 1, &thrown);
612                 if (thrown) return false;
613                 n *= offset;
614                 minute += n;
615             }
616         }
617         env->SetIntField(This, g_hourField, hour);
618         env->SetIntField(This, g_minField, minute);
619
620         if (offset != 0) {
621             // we need to normalize after applying the hour and minute offsets
622             android_text_format_Time_normalize(env, This, false /* use isdst */);
623             // The timezone is set to UTC in the calling Java code.
624         }
625     } else {
626         env->SetBooleanField(This, g_allDayField, JNI_TRUE);
627         env->SetIntField(This, g_hourField, 0);
628         env->SetIntField(This, g_minField, 0);
629         env->SetIntField(This, g_secField, 0);
630     }
631
632     env->SetIntField(This, g_wdayField, 0);
633     env->SetIntField(This, g_ydayField, 0);
634     env->SetIntField(This, g_isdstField, -1);
635     env->SetLongField(This, g_gmtoffField, 0);
636     
637     env->ReleaseStringChars(strObj, s);
638     return inUtc;
639 }
640
641 // ============================================================================
642 /*
643  * JNI registration.
644  */
645 static JNINativeMethod gMethods[] = {
646     /* name, signature, funcPtr */
647     { "normalize",               "(Z)J",                                        (void*)android_text_format_Time_normalize },
648     { "switchTimezone",          "(Ljava/lang/String;)V",                       (void*)android_text_format_Time_switchTimezone },
649     { "nativeCompare",           "(Landroid/text/format/Time;Landroid/text/format/Time;)I",     (void*)android_text_format_Time_compare },
650     { "format1",                 "(Ljava/lang/String;)Ljava/lang/String;",      (void*)android_text_format_Time_format },
651     { "format2445",              "()Ljava/lang/String;",                        (void*)android_text_format_Time_format2445 },
652     { "toString",                "()Ljava/lang/String;",                        (void*)android_text_format_Time_toString },
653     { "nativeParse",             "(Ljava/lang/String;)Z",                       (void*)android_text_format_Time_parse },
654     { "nativeParse3339",         "(Ljava/lang/String;)Z",                       (void*)android_text_format_Time_parse3339 },
655     { "setToNow",                "()V",                                         (void*)android_text_format_Time_setToNow },
656     { "toMillis",                "(Z)J",                                        (void*)android_text_format_Time_toMillis },
657     { "set",                     "(J)V",                                        (void*)android_text_format_Time_set }
658 };
659
660 int register_android_text_format_Time(JNIEnv* env)
661 {
662     jclass timeClass = env->FindClass("android/text/format/Time");
663
664     g_timeClass = (jclass) env->NewGlobalRef(timeClass);
665
666     g_allDayField = env->GetFieldID(timeClass, "allDay", "Z");
667     g_secField = env->GetFieldID(timeClass, "second", "I");
668     g_minField = env->GetFieldID(timeClass, "minute", "I");
669     g_hourField = env->GetFieldID(timeClass, "hour", "I");
670     g_mdayField = env->GetFieldID(timeClass, "monthDay", "I");
671     g_monField = env->GetFieldID(timeClass, "month", "I");
672     g_yearField = env->GetFieldID(timeClass, "year", "I");
673     g_wdayField = env->GetFieldID(timeClass, "weekDay", "I");
674     g_ydayField = env->GetFieldID(timeClass, "yearDay", "I");
675     g_isdstField = env->GetFieldID(timeClass, "isDst", "I");
676     g_gmtoffField = env->GetFieldID(timeClass, "gmtoff", "J");
677     g_timezoneField = env->GetFieldID(timeClass, "timezone", "Ljava/lang/String;");
678
679     g_shortMonthsField = env->GetStaticFieldID(timeClass, "sShortMonths", "[Ljava/lang/String;");
680     g_longMonthsField = env->GetStaticFieldID(timeClass, "sLongMonths", "[Ljava/lang/String;");
681     g_longStandaloneMonthsField = env->GetStaticFieldID(timeClass, "sLongStandaloneMonths", "[Ljava/lang/String;");
682     g_shortWeekdaysField = env->GetStaticFieldID(timeClass, "sShortWeekdays", "[Ljava/lang/String;");
683     g_longWeekdaysField = env->GetStaticFieldID(timeClass, "sLongWeekdays", "[Ljava/lang/String;");
684     g_timeOnlyFormatField = env->GetStaticFieldID(timeClass, "sTimeOnlyFormat", "Ljava/lang/String;");
685     g_dateOnlyFormatField = env->GetStaticFieldID(timeClass, "sDateOnlyFormat", "Ljava/lang/String;");
686     g_dateTimeFormatField = env->GetStaticFieldID(timeClass, "sDateTimeFormat", "Ljava/lang/String;");
687     g_amField = env->GetStaticFieldID(timeClass, "sAm", "Ljava/lang/String;");
688     g_pmField = env->GetStaticFieldID(timeClass, "sPm", "Ljava/lang/String;");
689     g_dateCommandField = env->GetStaticFieldID(timeClass, "sDateCommand", "Ljava/lang/String;");
690     g_localeField = env->GetStaticFieldID(timeClass, "sLocale", "Ljava/util/Locale;");
691
692     return AndroidRuntime::registerNativeMethods(env, "android/text/format/Time", gMethods, NELEM(gMethods));
693 }
694
695 }; // namespace android