diff options
-rw-r--r-- | Meta/CMake/time_zone_data.cmake | 6 | ||||
-rw-r--r-- | Meta/Lagom/Tools/CodeGenerators/LibTimeZone/GenerateTimeZoneData.cpp | 110 | ||||
-rw-r--r-- | Userland/Libraries/LibTimeZone/TimeZone.cpp | 9 | ||||
-rw-r--r-- | Userland/Libraries/LibTimeZone/TimeZone.h | 19 |
4 files changed, 141 insertions, 3 deletions
diff --git a/Meta/CMake/time_zone_data.cmake b/Meta/CMake/time_zone_data.cmake index 9740cfb8c4..e252d96466 100644 --- a/Meta/CMake/time_zone_data.cmake +++ b/Meta/CMake/time_zone_data.cmake @@ -35,6 +35,9 @@ set(TZDB_NORTH_AMERICA_PATH "${TZDB_PATH}/${TZDB_NORTH_AMERICA_SOURCE}") set(TZDB_SOUTH_AMERICA_SOURCE southamerica) set(TZDB_SOUTH_AMERICA_PATH "${TZDB_PATH}/${TZDB_SOUTH_AMERICA_SOURCE}") +set(TZDB_ZONE_1970_SOURCE zone1970.tab) +set(TZDB_ZONE_1970_PATH "${TZDB_PATH}/${TZDB_ZONE_1970_SOURCE}") + function(extract_tzdb_file source path) if(EXISTS "${TZDB_ZIP_PATH}" AND NOT EXISTS "${path}") message(STATUS "Extracting TZDB ${source} from ${TZDB_ZIP_PATH}...") @@ -58,6 +61,7 @@ if (ENABLE_TIME_ZONE_DATABASE_DOWNLOAD) extract_tzdb_file("${TZDB_EUROPE_SOURCE}" "${TZDB_EUROPE_PATH}") extract_tzdb_file("${TZDB_NORTH_AMERICA_SOURCE}" "${TZDB_NORTH_AMERICA_PATH}") extract_tzdb_file("${TZDB_SOUTH_AMERICA_SOURCE}" "${TZDB_SOUTH_AMERICA_PATH}") + extract_tzdb_file("${TZDB_ZONE_1970_SOURCE}" "${TZDB_ZONE_1970_PATH}") set(TIME_ZONE_DATA_HEADER LibTimeZone/TimeZoneData.h) set(TIME_ZONE_DATA_IMPLEMENTATION LibTimeZone/TimeZoneData.cpp) @@ -78,7 +82,7 @@ if (ENABLE_TIME_ZONE_DATABASE_DOWNLOAD) "${TIME_ZONE_META_TARGET_PREFIX}" "${TIME_ZONE_DATA_HEADER}" "${TIME_ZONE_DATA_IMPLEMENTATION}" - arguments "${TZDB_AFRICA_PATH}" "${TZDB_ANTARCTICA_PATH}" "${TZDB_ASIA_PATH}" "${TZDB_AUSTRALASIA_PATH}" "${TZDB_BACKWARD_PATH}" "${TZDB_ETCETERA_PATH}" "${TZDB_EUROPE_PATH}" "${TZDB_NORTH_AMERICA_PATH}" "${TZDB_SOUTH_AMERICA_PATH}" + arguments -z "${TZDB_ZONE_1970_PATH}" "${TZDB_AFRICA_PATH}" "${TZDB_ANTARCTICA_PATH}" "${TZDB_ASIA_PATH}" "${TZDB_AUSTRALASIA_PATH}" "${TZDB_BACKWARD_PATH}" "${TZDB_ETCETERA_PATH}" "${TZDB_EUROPE_PATH}" "${TZDB_NORTH_AMERICA_PATH}" "${TZDB_SOUTH_AMERICA_PATH}" ) set(TIME_ZONE_DATA_SOURCES diff --git a/Meta/Lagom/Tools/CodeGenerators/LibTimeZone/GenerateTimeZoneData.cpp b/Meta/Lagom/Tools/CodeGenerators/LibTimeZone/GenerateTimeZoneData.cpp index c9f913b734..49dad7287a 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibTimeZone/GenerateTimeZoneData.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibTimeZone/GenerateTimeZoneData.cpp @@ -13,6 +13,7 @@ #include <AK/Vector.h> #include <LibCore/ArgsParser.h> #include <LibCore/File.h> +#include <LibTimeZone/TimeZone.h> namespace { @@ -63,6 +64,8 @@ struct TimeZoneData { HashMap<String, Vector<DaylightSavingsOffset>> dst_offsets; Vector<String> dst_offset_names; + + HashMap<String, TimeZone::Location> time_zone_coordinates; }; } @@ -115,6 +118,29 @@ struct AK::Formatter<DaylightSavingsOffset> : Formatter<FormatString> { } }; +template<> +struct AK::Formatter<TimeZone::Coordinate> : Formatter<FormatString> { + ErrorOr<void> format(FormatBuilder& builder, TimeZone::Coordinate const& coordinate) + { + return Formatter<FormatString>::format(builder, + "{{ {}, {}, {} }}", + coordinate.degrees, + coordinate.minutes, + coordinate.seconds); + } +}; + +template<> +struct AK::Formatter<TimeZone::Location> : Formatter<FormatString> { + ErrorOr<void> format(FormatBuilder& builder, TimeZone::Location const& location) + { + return Formatter<FormatString>::format(builder, + "{{ {}, {} }}", + location.latitude, + location.longitude); + } +}; + static Optional<DateTime> parse_date_time(Span<StringView const> segments) { constexpr auto months = Array { "Jan"sv, "Feb"sv, "Mar"sv, "Apr"sv, "May"sv, "Jun"sv, "Jul"sv, "Aug"sv, "Sep"sv, "Oct"sv, "Nov"sv, "Dec"sv }; @@ -312,6 +338,56 @@ static ErrorOr<void> parse_time_zones(StringView time_zone_path, TimeZoneData& t return {}; } +static void parse_time_zone_coordinates(Core::File& file, TimeZoneData& time_zone_data) +{ + auto parse_coordinate = [](auto coordinate) { + VERIFY(coordinate.substring_view(0, 1).is_one_of("+"sv, "-"sv)); + TimeZone::Coordinate parsed {}; + + if (coordinate.length() == 5) { + // ±DDMM + parsed.degrees = coordinate.substring_view(0, 3).to_int().value(); + parsed.minutes = coordinate.substring_view(3).to_int().value(); + } else if (coordinate.length() == 6) { + // ±DDDMM + parsed.degrees = coordinate.substring_view(0, 4).to_int().value(); + parsed.minutes = coordinate.substring_view(4).to_int().value(); + } else if (coordinate.length() == 7) { + // ±DDMMSS + parsed.degrees = coordinate.substring_view(0, 3).to_int().value(); + parsed.minutes = coordinate.substring_view(3, 2).to_int().value(); + parsed.seconds = coordinate.substring_view(5).to_int().value(); + } else if (coordinate.length() == 8) { + // ±DDDDMMSS + parsed.degrees = coordinate.substring_view(0, 4).to_int().value(); + parsed.minutes = coordinate.substring_view(4, 2).to_int().value(); + parsed.seconds = coordinate.substring_view(6).to_int().value(); + } else { + VERIFY_NOT_REACHED(); + } + + return parsed; + }; + + while (file.can_read_line()) { + auto line = file.read_line(); + if (line.is_empty() || line.trim_whitespace(TrimMode::Left).starts_with('#')) + continue; + + auto segments = line.split_view('\t'); + auto coordinates = segments[1]; + auto zone = segments[2]; + + VERIFY(time_zone_data.time_zones.contains(zone)); + + auto index = coordinates.find_any_of("+-"sv, StringView::SearchDirection::Backward).value(); + auto latitude = parse_coordinate(coordinates.substring_view(0, index)); + auto longitude = parse_coordinate(coordinates.substring_view(index)); + + time_zone_data.time_zone_coordinates.set(zone, { latitude, longitude }); + } +} + static void set_dst_rule_indices(TimeZoneData& time_zone_data) { for (auto& time_zone : time_zone_data.time_zones) { @@ -473,6 +549,18 @@ static constexpr Array<@type@, @size@> @name@ { { append_offsets(name, "DaylightSavingsOffset"sv, dst_offsets); }); + generator.set("size", String::number(time_zone_data.time_zone_names.size())); + generator.append(R"~~~( +static constexpr Array<Location, @size@> s_time_zone_locations { { +)~~~"); + + for (auto const& time_zone : time_zone_data.time_zone_names) { + auto location = time_zone_data.time_zone_coordinates.get(time_zone).value_or({}); + + generator.append(String::formatted(" {},\n", location)); + } + generator.append("} };\n"); + auto append_string_conversions = [&](StringView enum_title, StringView enum_snake, auto const& values, Vector<Alias> const& aliases = {}) { HashValueMap<String> hashes; hashes.ensure_capacity(values.size()); @@ -619,6 +707,19 @@ Optional<Array<NamedOffset, 2>> get_named_time_zone_offsets(TimeZone time_zone, return named_offsets; } + +Optional<Location> get_time_zone_location(TimeZone time_zone) +{ + auto is_valid_coordinate = [](auto const& coordinate) { + return (coordinate.degrees != 0) || (coordinate.minutes != 0) || (coordinate.seconds != 0); + }; + + auto const& location = s_time_zone_locations[to_underlying(time_zone)]; + + if (is_valid_coordinate(location.latitude) && is_valid_coordinate(location.longitude)) + return location; + return {}; +} )~~~"); generate_available_values(generator, "all_time_zones"sv, time_zone_data.time_zone_names); @@ -635,30 +736,35 @@ ErrorOr<int> serenity_main(Main::Arguments arguments) { StringView generated_header_path; StringView generated_implementation_path; + StringView time_zone_coordinates_path; Vector<StringView> time_zone_paths; Core::ArgsParser args_parser; args_parser.add_option(generated_header_path, "Path to the time zone data header file to generate", "generated-header-path", 'h', "generated-header-path"); args_parser.add_option(generated_implementation_path, "Path to the time zone data implementation file to generate", "generated-implementation-path", 'c', "generated-implementation-path"); + args_parser.add_option(time_zone_coordinates_path, "Path to the time zone data coordinates file", "time-zone-coordinates-path", 'z', "time-zone-coordinates-path"); args_parser.add_positional_argument(time_zone_paths, "Paths to the time zone database files", "time-zone-paths"); args_parser.parse(arguments); - auto open_file = [&](StringView path) -> ErrorOr<NonnullRefPtr<Core::File>> { + auto open_file = [&](StringView path, Core::OpenMode mode = Core::OpenMode::ReadWrite) -> ErrorOr<NonnullRefPtr<Core::File>> { if (path.is_empty()) { args_parser.print_usage(stderr, arguments.argv[0]); return Error::from_string_literal("Must provide all command line options"sv); } - return Core::File::open(path, Core::OpenMode::ReadWrite); + return Core::File::open(path, mode); }; auto generated_header_file = TRY(open_file(generated_header_path)); auto generated_implementation_file = TRY(open_file(generated_implementation_path)); + auto time_zone_coordinates_file = TRY(open_file(time_zone_coordinates_path, Core::OpenMode::ReadOnly)); TimeZoneData time_zone_data {}; for (auto time_zone_path : time_zone_paths) TRY(parse_time_zones(time_zone_path, time_zone_data)); + parse_time_zone_coordinates(time_zone_coordinates_file, time_zone_data); + generate_time_zone_data_header(generated_header_file, time_zone_data); generate_time_zone_data_implementation(generated_implementation_file, time_zone_data); diff --git a/Userland/Libraries/LibTimeZone/TimeZone.cpp b/Userland/Libraries/LibTimeZone/TimeZone.cpp index 9602acd708..253cad783f 100644 --- a/Userland/Libraries/LibTimeZone/TimeZone.cpp +++ b/Userland/Libraries/LibTimeZone/TimeZone.cpp @@ -183,4 +183,13 @@ Optional<Array<NamedOffset, 2>> get_named_time_zone_offsets(StringView time_zone return {}; } +Optional<Location> __attribute__((weak)) get_time_zone_location(TimeZone) { return {}; } + +Optional<Location> get_time_zone_location(StringView time_zone) +{ + if (auto maybe_time_zone = time_zone_from_string(time_zone); maybe_time_zone.has_value()) + return get_time_zone_location(*maybe_time_zone); + return {}; +} + } diff --git a/Userland/Libraries/LibTimeZone/TimeZone.h b/Userland/Libraries/LibTimeZone/TimeZone.h index b862bef6dd..905b6d6cc4 100644 --- a/Userland/Libraries/LibTimeZone/TimeZone.h +++ b/Userland/Libraries/LibTimeZone/TimeZone.h @@ -31,6 +31,22 @@ struct NamedOffset : public Offset { String name; }; +struct Coordinate { + constexpr float decimal_coordinate() const + { + return static_cast<float>(degrees) + (static_cast<float>(minutes) / 60.0f) + (static_cast<float>(seconds) / 3'600.0f); + } + + i16 degrees { 0 }; + u8 minutes { 0 }; + u8 seconds { 0 }; +}; + +struct Location { + Coordinate latitude; + Coordinate longitude; +}; + StringView system_time_zone(); StringView current_time_zone(); ErrorOr<void> change_time_zone(StringView time_zone); @@ -49,4 +65,7 @@ Optional<Offset> get_time_zone_offset(StringView time_zone, AK::Time time); Optional<Array<NamedOffset, 2>> get_named_time_zone_offsets(TimeZone time_zone, AK::Time time); Optional<Array<NamedOffset, 2>> get_named_time_zone_offsets(StringView time_zone, AK::Time time); +Optional<Location> get_time_zone_location(TimeZone time_zone); +Optional<Location> get_time_zone_location(StringView time_zone); + } |