summaryrefslogtreecommitdiffstats
path: root/luni/src/main/native/libcore_icu_TimeZoneNames.cpp
blob: a7c90982d36d930742b420c7d712b0a33aaa81ed (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG "TimeZoneNames"

#include "IcuUtilities.h"
#include "JNIHelp.h"
#include "JniConstants.h"
#include "JniException.h"
#include "ScopedIcuLocale.h"
#include "ScopedJavaUnicodeString.h"
#include "ScopedLocalRef.h"
#include "ScopedUtfChars.h"
#include "UniquePtr.h"
#include "unicode/calendar.h"
#include "unicode/timezone.h"
#include "unicode/tznames.h"

static bool isUtc(const UnicodeString& id) {
  static const UnicodeString kEtcUct("Etc/UCT", 7, US_INV);
  static const UnicodeString kEtcUtc("Etc/UTC", 7, US_INV);
  static const UnicodeString kEtcUniversal("Etc/Universal", 13, US_INV);
  static const UnicodeString kEtcZulu("Etc/Zulu", 8, US_INV);

  static const UnicodeString kUct("UCT", 3, US_INV);
  static const UnicodeString kUtc("UTC", 3, US_INV);
  static const UnicodeString kUniversal("Universal", 9, US_INV);
  static const UnicodeString kZulu("Zulu", 4, US_INV);

  return id == kEtcUct || id == kEtcUtc || id == kEtcUniversal || id == kEtcZulu ||
      id == kUct || id == kUtc || id == kUniversal || id == kZulu;
}

static bool setStringArrayElement(JNIEnv* env, jobjectArray array, int i, const UnicodeString& s) {
  // Fill in whatever we got. We don't use the display names if they're "GMT[+-]xx:xx"
  // because icu4c doesn't use the up-to-date time zone transition data, so it gets these
  // wrong. TimeZone.getDisplayName creates accurate names on demand.
  // TODO: investigate whether it's worth doing that work once in the Java wrapper instead of on-demand.
  static const UnicodeString kGmt("GMT", 3, US_INV);
  if (!s.isBogus() && !s.startsWith(kGmt)) {
    ScopedLocalRef<jstring> javaString(env, env->NewString(s.getBuffer(), s.length()));
    if (javaString.get() == NULL) {
      return false;
    }
    env->SetObjectArrayElement(array, i, javaString.get());
  }
  return true;
}

static void TimeZoneNames_fillZoneStrings(JNIEnv* env, jclass, jstring javaLocaleName, jobjectArray result) {
  ScopedIcuLocale icuLocale(env, javaLocaleName);
  if (!icuLocale.valid()) {
    return;
  }

  UErrorCode status = U_ZERO_ERROR;
  UniquePtr<TimeZoneNames> names(TimeZoneNames::createInstance(icuLocale.locale(), status));
  if (maybeThrowIcuException(env, "TimeZoneNames::createInstance", status)) {
    return;
  }

  const UDate now(Calendar::getNow());

  static const UnicodeString kUtc("UTC", 3, US_INV);

  size_t id_count = env->GetArrayLength(result);
  for (size_t i = 0; i < id_count; ++i) {
    ScopedLocalRef<jobjectArray> java_row(env,
                                          reinterpret_cast<jobjectArray>(env->GetObjectArrayElement(result, i)));
    ScopedLocalRef<jstring> java_zone_id(env,
                                         reinterpret_cast<jstring>(env->GetObjectArrayElement(java_row.get(), 0)));
    ScopedJavaUnicodeString zone_id(env, java_zone_id.get());
    if (!zone_id.valid()) {
      return;
    }

    UnicodeString long_std;
    names->getDisplayName(zone_id.unicodeString(), UTZNM_LONG_STANDARD, now, long_std);
    UnicodeString short_std;
    names->getDisplayName(zone_id.unicodeString(), UTZNM_SHORT_STANDARD, now, short_std);
    UnicodeString long_dst;
    names->getDisplayName(zone_id.unicodeString(), UTZNM_LONG_DAYLIGHT, now, long_dst);
    UnicodeString short_dst;
    names->getDisplayName(zone_id.unicodeString(), UTZNM_SHORT_DAYLIGHT, now, short_dst);

    if (isUtc(zone_id.unicodeString())) {
      // ICU doesn't have names for the UTC zones; it just says "GMT+00:00" for both
      // long and short names. We don't want this. The best we can do is use "UTC"
      // for everything (since we don't know how to say "Universal Coordinated Time" in
      // every language).
      // TODO: check CLDR doesn't actually have this somewhere.
      long_std = short_std = long_dst = short_dst = kUtc;
    }

    bool okay =
        setStringArrayElement(env, java_row.get(), 1, long_std) &&
        setStringArrayElement(env, java_row.get(), 2, short_std) &&
        setStringArrayElement(env, java_row.get(), 3, long_dst) &&
        setStringArrayElement(env, java_row.get(), 4, short_dst);
    if (!okay) {
      return;
    }
  }
}

static jstring TimeZoneNames_getExemplarLocation(JNIEnv* env, jclass, jstring javaLocaleName, jstring javaTz) {
  ScopedIcuLocale icuLocale(env, javaLocaleName);
  if (!icuLocale.valid()) {
    return NULL;
  }

  UErrorCode status = U_ZERO_ERROR;
  UniquePtr<TimeZoneNames> names(TimeZoneNames::createInstance(icuLocale.locale(), status));
  if (maybeThrowIcuException(env, "TimeZoneNames::createInstance", status)) {
    return NULL;
  }

  ScopedJavaUnicodeString tz(env, javaTz);
  if (!tz.valid()) {
    return NULL;
  }

  UnicodeString s;
  const UDate now(Calendar::getNow());
  names->getDisplayName(tz.unicodeString(), UTZNM_EXEMPLAR_LOCATION, now, s);
  return env->NewString(s.getBuffer(), s.length());
}

static JNINativeMethod gMethods[] = {
  NATIVE_METHOD(TimeZoneNames, fillZoneStrings, "(Ljava/lang/String;[[Ljava/lang/String;)V"),
  NATIVE_METHOD(TimeZoneNames, getExemplarLocation, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
};
void register_libcore_icu_TimeZoneNames(JNIEnv* env) {
  jniRegisterNativeMethods(env, "libcore/icu/TimeZoneNames", gMethods, NELEM(gMethods));
}