blob: 776733caf50d358478af08a3c51da594df6e0108 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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"
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -070026#include "TimeUtils.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027#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;
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -070047static jfieldID g_longStandaloneMonthsField = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048static 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;
The Android Open Source Project4df24232009-03-05 14:34:35 -080056static jfieldID g_localeField = 0;
57
58static jclass g_timeClass = NULL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059
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);
Elliott Hughesbe414332011-08-14 13:31:03 -070072 bool allDay = env->GetBooleanField(o, g_allDayField);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073 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("");
Elliott Hughesbe414332011-08-14 13:31:03 -0700165 bool allDay = env->GetBooleanField(This, g_allDayField);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166
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{
The Android Open Source Project4df24232009-03-05 14:34:35 -0800190 // 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];
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -0700197 static jstring js_standalone_month[12];
The Android Open Source Project4df24232009-03-05 14:34:35 -0800198 static jstring js_X_fmt, js_x_fmt, js_c_fmt, js_am, js_pm, js_date_fmt;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199
The Android Open Source Project4df24232009-03-05 14:34:35 -0800200 Time t;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201 if (!java2time(env, &t, This)) return env->NewStringUTF("");
202
The Android Open Source Project4df24232009-03-05 14:34:35 -0800203 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]);
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -0700211 env->ReleaseStringUTFChars(js_standalone_month[i], locale.standalone_month[i]);
The Android Open Source Project4df24232009-03-05 14:34:35 -0800212 env->DeleteGlobalRef(js_mon[i]);
213 env->DeleteGlobalRef(js_month[i]);
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -0700214 env->DeleteGlobalRef(js_standalone_month[i]);
The Android Open Source Project4df24232009-03-05 14:34:35 -0800215 }
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
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -0700252 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
The Android Open Source Project4df24232009-03-05 14:34:35 -0800258 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);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293 }
294
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800304 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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351 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 {
Jean-Baptiste Queru9db3d072009-11-12 18:45:53 -0800370 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 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800376 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];
Kenny Root6c2d4022009-07-20 01:45:48 -0500385 sprintf(msg, "Unexpected character 0x%02x at pos=%d. Expected %c.", c, spos,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 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
Kenny Root6c2d4022009-07-20 01:45:48 -0500486 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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800492 // 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
Kenny Root6c2d4022009-07-20 01:45:48 -0500519 if (len >= 19) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800520 // 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
Kenny Root6c2d4022009-07-20 01:45:48 -0500550 // 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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800560 int offset = 0;
Kenny Root6c2d4022009-07-20 01:45:48 -0500561 if (len > tz_index) {
562 char c = s[tz_index];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800563
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];
Kenny Root6c2d4022009-07-20 01:45:48 -0500579 sprintf(msg, "Unexpected character 0x%02x at position %d. Expected + or -",
580 c, tz_index);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800581 jniThrowException(env, "android/util/TimeFormatException", msg);
582 return false;
583 }
584 inUtc = true;
585
586 if (offset != 0) {
Kenny Root412dc7d2010-02-16 12:03:50 -0800587 if (len < tz_index + 6) {
Kenny Root6c2d4022009-07-20 01:45:48 -0500588 char msg[100];
Kenny Root412dc7d2010-02-16 12:03:50 -0800589 sprintf(msg, "Unexpected length; should be %d characters", tz_index + 6);
Kenny Root6c2d4022009-07-20 01:45:48 -0500590 jniThrowException(env, "android/util/TimeFormatException", msg);
591 return false;
592 }
593
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800594 // hour
Kenny Root6c2d4022009-07-20 01:45:48 -0500595 n = get_char(env, s, tz_index + 1, 10, &thrown);
596 n += get_char(env, s, tz_index + 2, 1, &thrown);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800597 if (thrown) return false;
598 n *= offset;
599 hour += n;
600
601 // :
Kenny Root6c2d4022009-07-20 01:45:48 -0500602 if (!check_char(env, s, tz_index + 3, ':')) return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800603
604 // minute
Kenny Root6c2d4022009-07-20 01:45:48 -0500605 n = get_char(env, s, tz_index + 4, 10, &thrown);
606 n += get_char(env, s, tz_index + 5, 1, &thrown);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800607 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 },
Kenny Root467cabe2011-07-25 15:58:31 -0700644 { "nativeCompare", "(Landroid/text/format/Time;Landroid/text/format/Time;)I", (void*)android_text_format_Time_compare },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800645 { "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
The Android Open Source Project4df24232009-03-05 14:34:35 -0800659 g_timeClass = (jclass) env->NewGlobalRef(timeClass);
660
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800661 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;");
Jean-Baptiste Querucf4550c2009-07-21 11:16:54 -0700676 g_longStandaloneMonthsField = env->GetStaticFieldID(timeClass, "sLongStandaloneMonths", "[Ljava/lang/String;");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800677 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;");
The Android Open Source Project4df24232009-03-05 14:34:35 -0800685 g_localeField = env->GetStaticFieldID(timeClass, "sLocale", "Ljava/util/Locale;");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800686
687 return AndroidRuntime::registerNativeMethods(env, "android/text/format/Time", gMethods, NELEM(gMethods));
688}
689
690}; // namespace android