Skip to content

Commit

Permalink
[reland] Add basic support for Fuchsia (google#202)
Browse files Browse the repository at this point in the history
- Implement `FuchsiaZoneInfoSource` to read tzif2 files from their designated locations on Fuchsia systems.
- Implement calls to Fuchsia APIs in `local_time_zone()`.
- Disable a time conversion edge case that is unsupported on Fuchsia.

This reverts commit 63b7130.
  • Loading branch information
kpozin authored Oct 13, 2021
1 parent 769622e commit 913019e
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 0 deletions.
64 changes: 64 additions & 0 deletions src/time_zone_info.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <functional>
#include <memory>
#include <sstream>
Expand Down Expand Up @@ -722,6 +723,68 @@ std::unique_ptr<ZoneInfoSource> AndroidZoneInfoSource::Open(
return nullptr;
}

// A zoneinfo source for use inside Fuchsia components. This attempts to
// read zoneinfo files from one of several known paths in a component's
// incoming namespace. [Config data][1] is preferred, but package-specific
// resources are also supported.
//
// Fuchsia's implementation supports `FileZoneInfoSource::Version()`.
//
// [1]: https://fuchsia.dev/fuchsia-src/development/components/data#using_config_data_in_your_component
class FuchsiaZoneInfoSource : public FileZoneInfoSource {
public:
static std::unique_ptr<ZoneInfoSource> Open(const std::string& name);
std::string Version() const override { return version_; }

private:
explicit FuchsiaZoneInfoSource(FilePtr fp, std::string version)
: FileZoneInfoSource(std::move(fp)), version_(std::move(version)) {}
std::string version_;
};

std::unique_ptr<ZoneInfoSource> FuchsiaZoneInfoSource::Open(
const std::string& name) {
// Use of the "file:" prefix is intended for testing purposes only.
const std::size_t pos = (name.compare(0, 5, "file:") == 0) ? 5 : 0;

// Prefixes where a Fuchsia component might find zoneinfo files,
// in descending order of preference.
const auto kTzdataPrefixes = {
"/config/data/tzdata/",
"/pkg/data/tzdata/",
"/data/tzdata/",
};
const auto kEmptyPrefix = {""};
const bool name_absolute = (pos != name.size() && name[pos] == '/');
const auto prefixes = name_absolute ? kEmptyPrefix : kTzdataPrefixes;

// Fuchsia builds place zoneinfo files at "<prefix><format><name>".
for (const std::string prefix : prefixes) {
std::string path = prefix;
if (!prefix.empty()) path += "zoneinfo/tzif2/"; // format
path.append(name, pos, std::string::npos);

auto fp = FOpen(path.c_str(), "rb");
if (fp.get() == nullptr) continue;

std::string version;
if (!prefix.empty()) {
// Fuchsia builds place the version in "<prefix>revision.txt".
std::ifstream version_stream(prefix + "revision.txt");
if (version_stream.is_open()) {
// revision.txt should contain no newlines, but to be
// defensive we read just the first line.
std::getline(version_stream, version);
}
}

return std::unique_ptr<ZoneInfoSource>(
new FuchsiaZoneInfoSource(std::move(fp), std::move(version)));
}

return nullptr;
}

} // namespace

bool TimeZoneInfo::Load(const std::string& name) {
Expand All @@ -739,6 +802,7 @@ bool TimeZoneInfo::Load(const std::string& name) {
name, [](const std::string& n) -> std::unique_ptr<ZoneInfoSource> {
if (auto z = FileZoneInfoSource::Open(n)) return z;
if (auto z = AndroidZoneInfoSource::Open(n)) return z;
if (auto z = FuchsiaZoneInfoSource::Open(n)) return z;
return nullptr;
});
return zip != nullptr && Load(zip.get());
Expand Down
34 changes: 34 additions & 0 deletions src/time_zone_lookup.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@
#include <vector>
#endif

#if defined(__Fuchsia__)
#include <fuchsia/intl/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/sys/cpp/component_context.h>
#include <zircon/types.h>
#endif

#include <cstdlib>
#include <cstring>
#include <string>
Expand Down Expand Up @@ -139,6 +147,32 @@ time_zone local_time_zone() {
}
CFRelease(tz_default);
#endif
#if defined(__Fuchsia__)
std::string primary_tz;
{
const zx::duration kTimeout = zx::msec(500);

async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
std::unique_ptr<sys::ComponentContext> context =
sys::ComponentContext::Create();
// Note: We can't use the synchronous FIDL API here because it doesn't
// allow timeouts; if the FIDL call failed, local_time_zone() would never
// return.
auto intl_provider = context->svc()->Connect<fuchsia::intl::PropertyProvider>();
intl_provider->GetProfile(
[&loop, &primary_tz](fuchsia::intl::Profile profile) {
if (!profile.time_zones().empty()) {
primary_tz = profile.time_zones()[0].id;
}
loop.Quit();
});
loop.Run(zx::deadline_after(kTimeout));

if (!primary_tz.empty()) {
zone = primary_tz.c_str();
}
}
#endif

// Allow ${TZ} to override to default zone.
char* tz_env = nullptr;
Expand Down
4 changes: 4 additions & 0 deletions src/time_zone_lookup_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1023,7 +1023,11 @@ TEST(MakeTime, SysSecondsLimits) {
#endif
const year_t min_tm_year = year_t{std::numeric_limits<int>::min()} + 1900;
tp = convert(civil_second(min_tm_year, 1, 1, 0, 0, 0), cut);
#if defined(__Fuchsia__)
// Fuchsia's gmtime_r() fails on extreme negative values (fxbug.dev/78527).
#else
EXPECT_EQ("-2147481748-01-01T00:00:00+00:00", format(RFC3339, tp, cut));
#endif
#endif
}
}
Expand Down

0 comments on commit 913019e

Please sign in to comment.