11
11
#include < fcntl.h>
12
12
#include < stdint.h>
13
13
14
- #include < fuchsia/deprecatedtimezone /cpp/fidl.h>
14
+ #include < fuchsia/intl /cpp/fidl.h>
15
15
#include < lib/inspect/cpp/inspect.h>
16
16
#include < lib/sys/cpp/component_context.h>
17
17
#include < lib/sys/cpp/service_directory.h>
21
21
#include < zircon/syscalls/object.h>
22
22
#include < zircon/types.h>
23
23
24
+ #include " third_party/icu/source/common/unicode/errorcode.h"
25
+ #include " third_party/icu/source/i18n/unicode/timezone.h"
26
+
24
27
#include " platform/assert.h"
28
+ #include " platform/syslog.h"
25
29
#include " platform/utils.h"
26
30
#include " vm/zone.h"
27
31
28
32
namespace {
29
33
34
+ static constexpr int32_t kMsPerSec = 1000 ;
35
+
30
36
// The data directory containing ICU timezone data files.
31
37
static constexpr char kICUTZDataDir [] = " /config/data/tzdata/icu/44/le" ;
32
38
39
+ // This is the general OK status.
40
+ static constexpr int32_t kOk = 0 ;
41
+
42
+ // This status means that the error code is not initialized yet ("set" was not
43
+ // yet called). Error codes are usually either 0 (kOk), or negative.
44
+ static constexpr int32_t kUninitialized = 1 ;
45
+
33
46
// The status codes for tzdata file open and read.
34
47
enum class TZDataStatus {
48
+ // The operation completed without error.
35
49
OK = 0 ,
36
50
// The open call for the tzdata file did not succeed.
37
51
COULD_NOT_OPEN = -1 ,
@@ -41,16 +55,23 @@ enum class TZDataStatus {
41
55
42
56
// Adds a facility for introspecting timezone data errors. Allows insight into
43
57
// the internal state of the VM even if error reporting facilities fail.
58
+ //
59
+ // Under normal operation, all metric values below should be zero.
44
60
class InspectMetrics {
45
61
public:
46
62
// Does not take ownership of inspector.
47
63
explicit InspectMetrics (inspect::Inspector* inspector)
48
64
: inspector_(inspector),
49
65
root_(inspector_->GetRoot ()),
50
66
metrics_(root_.CreateChild(" os" )),
51
- dst_status_(metrics_.CreateInt(" dst_status" , 0 )),
52
- tz_data_status_(metrics_.CreateInt(" tz_data_status" , 0 )),
53
- tz_data_close_status_(metrics_.CreateInt(" tz_data_close_status" , 0 )) {}
67
+ dst_status_(metrics_.CreateInt(" dst_status" , kUninitialized )),
68
+ tz_data_status_(metrics_.CreateInt(" tz_data_status" , kUninitialized )),
69
+ tz_data_close_status_(
70
+ metrics_.CreateInt(" tz_data_close_status" , kUninitialized )),
71
+ get_profile_status_(
72
+ metrics_.CreateInt(" get_profile_status" , kUninitialized )),
73
+ profiles_timezone_content_status_(
74
+ metrics_.CreateInt(" timezone_content_status" , kOk )) {}
54
75
55
76
// Sets the last status code for DST offset calls.
56
77
void SetDSTOffsetStatus (zx_status_t status) {
@@ -64,6 +85,17 @@ class InspectMetrics {
64
85
tz_data_close_status_.Set (status);
65
86
}
66
87
88
+ // Sets the last status code for the call to PropertyProvider::GetProfile.
89
+ void SetProfileStatus (zx_status_t status) {
90
+ get_profile_status_.Set (static_cast <int32_t >(status));
91
+ }
92
+
93
+ // Sets the last status seen while examining timezones returned from
94
+ // PropertyProvider::GetProfile.
95
+ void SetTimeZoneContentStatus (zx_status_t status) {
96
+ profiles_timezone_content_status_.Set (static_cast <int32_t >(status));
97
+ }
98
+
67
99
private:
68
100
// The inspector that all metrics are being reported into.
69
101
inspect::Inspector* inspector_;
@@ -82,6 +114,15 @@ class InspectMetrics {
82
114
83
115
// The return code for the close() call for tzdata files.
84
116
inspect::IntProperty tz_data_close_status_;
117
+
118
+ // The return code of the GetProfile call in GetTimeZoneName. If this is
119
+ // nonzero, then os_fuchsia.cc reported a default timezone as a fallback.
120
+ inspect::IntProperty get_profile_status_;
121
+
122
+ // U_ILLEGAL_ARGUMENT_ERROR(=1) if timezones read from ProfileProvider were
123
+ // incorrect. Otherwise 0. If this metric reports U_ILLEGAL_ARGUMENT_ERROR,
124
+ // the os_fuchsia.cc module reported a default timezone as a fallback.
125
+ inspect::IntProperty profiles_timezone_content_status_;
85
126
};
86
127
87
128
// Initialized on OS:Init(), deinitialized on OS::Cleanup.
@@ -132,50 +173,85 @@ intptr_t OS::ProcessId() {
132
173
return static_cast <intptr_t >(getpid ());
133
174
}
134
175
176
+ // This is the default timezone returned if it could not be obtained. For
177
+ // Fuchsia, the default device timezone is always UTC.
178
+ static const char kDefaultTimezone [] = " UTC" ;
179
+
135
180
// TODO(FL-98): Change this to talk to fuchsia.dart to get timezone service to
136
181
// directly get timezone.
137
182
//
138
183
// Putting this hack right now due to CP-120 as I need to remove
139
184
// component:ConnectToEnvironmentServices and this is the only thing that is
140
185
// blocking it and FL-98 will take time.
141
- static fuchsia::deprecatedtimezone::TimezoneSyncPtr tz ;
186
+ static fuchsia::intl::PropertyProviderSyncPtr property_provider ;
142
187
143
188
static zx_status_t GetLocalAndDstOffsetInSeconds (int64_t seconds_since_epoch,
144
189
int32_t * local_offset,
145
190
int32_t * dst_offset) {
146
- zx_status_t status = tz->GetTimezoneOffsetMinutes (seconds_since_epoch * 1000 ,
147
- local_offset, dst_offset);
148
- metrics->SetDSTOffsetStatus (status);
149
- if (status != ZX_OK) {
150
- return status;
191
+ const char * timezone_id = OS::GetTimeZoneName (seconds_since_epoch);
192
+ std::unique_ptr<icu::TimeZone> timezone (
193
+ icu::TimeZone::createTimeZone (timezone_id));
194
+ UErrorCode error = U_ZERO_ERROR;
195
+ const auto ms_since_epoch =
196
+ static_cast <UDate>(kMsPerSec * seconds_since_epoch);
197
+ // The units of time that local_offset and dst_offset are returned from this
198
+ // function is, usefully, not documented, but it seems that the units are
199
+ // milliseconds. Add these variables here for clarity.
200
+ int32_t local_offset_ms = 0 ;
201
+ int32_t dst_offset_ms = 0 ;
202
+ timezone ->getOffset (ms_since_epoch, /* local_time=*/ false , local_offset_ms,
203
+ dst_offset_ms, error);
204
+ metrics->SetDSTOffsetStatus (error);
205
+ if (error != U_ZERO_ERROR) {
206
+ icu::ErrorCode icu_error;
207
+ icu_error.set (error);
208
+ Syslog::PrintErr (" could not get DST offset: %s\n " , icu_error.errorName ());
209
+ return ZX_ERR_INTERNAL;
151
210
}
152
- *local_offset *= 60 ;
153
- *dst_offset *= 60 ;
211
+ // We must return offset in seconds, so convert.
212
+ *local_offset = local_offset_ms / kMsPerSec ;
213
+ *dst_offset = dst_offset_ms / kMsPerSec ;
154
214
return ZX_OK;
155
215
}
156
216
157
217
const char * OS::GetTimeZoneName (int64_t seconds_since_epoch) {
158
218
// TODO(abarth): Handle time zone changes.
159
- static const auto * tz_name = new std::string ([] {
160
- std::string result;
161
- tz->GetTimezoneId (&result);
162
- return result;
163
- }());
219
+ static const std::unique_ptr<std::string> tz_name =
220
+ std::make_unique<std::string>([]() -> std::string {
221
+ fuchsia::intl::Profile profile;
222
+ const zx_status_t status = property_provider->GetProfile (&profile);
223
+ metrics->SetProfileStatus (status);
224
+ if (status != ZX_OK) {
225
+ return kDefaultTimezone ;
226
+ }
227
+ const std::vector<fuchsia::intl::TimeZoneId>& timezones =
228
+ profile.time_zones ();
229
+ if (timezones.empty ()) {
230
+ metrics->SetTimeZoneContentStatus (U_ILLEGAL_ARGUMENT_ERROR);
231
+ // Empty timezone array is not up to fuchsia::intl spec. The serving
232
+ // endpoint is broken and should be fixed.
233
+ Syslog::PrintErr (" got empty timezone value\n " );
234
+ return kDefaultTimezone ;
235
+ }
236
+ return timezones[0 ].id ;
237
+ }());
164
238
return tz_name->c_str ();
165
239
}
166
240
167
241
int OS::GetTimeZoneOffsetInSeconds (int64_t seconds_since_epoch) {
168
- int32_t local_offset, dst_offset;
169
- zx_status_t status = GetLocalAndDstOffsetInSeconds (
242
+ int32_t local_offset = 0 ;
243
+ int32_t dst_offset = 0 ;
244
+ const zx_status_t status = GetLocalAndDstOffsetInSeconds (
170
245
seconds_since_epoch, &local_offset, &dst_offset);
171
246
return status == ZX_OK ? local_offset + dst_offset : 0 ;
172
247
}
173
248
174
249
int OS::GetLocalTimeZoneAdjustmentInSeconds () {
175
- int32_t local_offset, dst_offset;
176
250
zx_time_t now = 0 ;
177
251
zx_clock_get (ZX_CLOCK_UTC, &now);
178
- zx_status_t status = GetLocalAndDstOffsetInSeconds (
252
+ int32_t local_offset = 0 ;
253
+ int32_t dst_offset = 0 ;
254
+ const zx_status_t status = GetLocalAndDstOffsetInSeconds (
179
255
now / ZX_SEC (1 ), &local_offset, &dst_offset);
180
256
return status == ZX_OK ? local_offset : 0 ;
181
257
}
@@ -199,7 +275,7 @@ int64_t OS::GetCurrentMonotonicFrequency() {
199
275
}
200
276
201
277
int64_t OS::GetCurrentMonotonicMicros () {
202
- int64_t ticks = GetCurrentMonotonicTicks ();
278
+ const int64_t ticks = GetCurrentMonotonicTicks ();
203
279
ASSERT (GetCurrentMonotonicFrequency () == kNanosecondsPerSecond );
204
280
return ticks / kNanosecondsPerMicrosecond ;
205
281
}
@@ -347,7 +423,8 @@ void OS::Init() {
347
423
metrics = std::make_unique<InspectMetrics>(component_inspector->inspector ());
348
424
349
425
InitializeTZData ();
350
- context->svc ()->Connect (tz.NewRequest ());
426
+ auto services = sys::ServiceDirectory::CreateFromNamespace ();
427
+ services->Connect (property_provider.NewRequest ());
351
428
}
352
429
353
430
void OS::Cleanup () {
0 commit comments