From e35992cf0bc346da2f833d1a35c7326f00c2b1a2 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Sun, 9 Aug 2020 00:05:40 +1000 Subject: [PATCH] [libcalamares] Add spot-patches to timezone data - for the purposes of Calamares's nearest-location selection algorithm for timezone selection, introduce spot patches: alternate markers on the map to indicate "things close to here belong in this timezone". - hide the implementation detail in the find() methods. --- src/libcalamares/locale/TimeZone.cpp | 64 +++++++++++++++++++++------- 1 file changed, 49 insertions(+), 15 deletions(-) diff --git a/src/libcalamares/locale/TimeZone.cpp b/src/libcalamares/locale/TimeZone.cpp index 4cd2bfb9a..ddc705548 100644 --- a/src/libcalamares/locale/TimeZone.cpp +++ b/src/libcalamares/locale/TimeZone.cpp @@ -183,17 +183,30 @@ loadTZData( RegionVector& regions, ZoneVector& zones, QTextStream& in ) } } -static void -loadTZData( RegionVector& regions, ZoneVector& zones ) -{ - QFile file( TZ_DATA_FILE ); - if ( file.open( QIODevice::ReadOnly | QIODevice::Text ) ) - { - QTextStream in( &file ); - loadTZData( regions, zones, in ); - } -} - +/** @brief Extra, fake, timezones + * + * The timezone locations in zone.tab are not always very useful, + * given Calamares's standard "nearest zone" algorithm: for instance, + * in most locations physically in the country of South Africa, + * Maseru (the capital of Lesotho, and location for timezone Africa/Maseru) + * is closer than Johannesburg (the location for timezone Africa/Johannesburg). + * + * The algorithm picks the wrong place. This is for instance annoying + * when clicking on Cape Town, you get Maseru, and to get Johannesburg + * you need to click somewhere very carefully north of Maserru. + * + * These alternate zones are used to introduce "extra locations" + * into the timezone database, in order to influence the closest-location + * algorithm. Lines are formatted just like in zone.tab: remember the \n + */ +static const char altZones[] = + /* This extra zone is north-east of Karoo National park, + * and means that Western Cape province and a good chunk of + * Northern- and Eastern- Cape provinces get pulled in to Johannesburg. + * Bloemfontein is still closer to Maseru than either correct zone, + * but this is a definite improvement. + */ + "ZA -3230+02259 Africa/Johannesburg\n"; class Private : public QObject { @@ -201,13 +214,27 @@ class Private : public QObject public: RegionVector m_regions; ZoneVector m_zones; + ZoneVector m_altZones; //< Extra locations for zones Private() { m_regions.reserve( 12 ); // reasonable guess m_zones.reserve( 452 ); // wc -l /usr/share/zoneinfo/zone.tab - loadTZData( m_regions, m_zones ); + // Load the official timezones + { + QFile file( TZ_DATA_FILE ); + if ( file.open( QIODevice::ReadOnly | QIODevice::Text ) ) + { + QTextStream in( &file ); + loadTZData( m_regions, m_zones, in ); + } + } + // Load the alternate zones (see documentation at altZones) + { + QTextStream in( altZones ); + loadTZData( m_regions, m_altZones, in ); + } std::sort( m_regions.begin(), m_regions.end(), []( const RegionData* lhs, const RegionData* rhs ) { return lhs->key() < rhs->key(); @@ -343,9 +370,9 @@ ZonesModel::find( const QString& region, const QString& zone ) const } STATICTEST const TimeZoneData* -find( const ZoneVector& zones, const std::function< double( const TimeZoneData* ) >& distanceFunc ) +find( double startingDistance, const ZoneVector& zones, const std::function< double( const TimeZoneData* ) >& distanceFunc ) { - double smallestDistance = 1000000.0; + double smallestDistance = startingDistance; const TimeZoneData* closest = nullptr; for ( const auto* zone : zones ) @@ -363,7 +390,14 @@ find( const ZoneVector& zones, const std::function< double( const TimeZoneData* const TimeZoneData* ZonesModel::find( const std::function< double( const TimeZoneData* ) >& distanceFunc ) const { - return CalamaresUtils::Locale::find( m_private->m_zones, distanceFunc ); + const auto* officialZone = CalamaresUtils::Locale::find( 1000000.0, m_private->m_zones, distanceFunc ); + const auto* altZone = CalamaresUtils::Locale::find( distanceFunc( officialZone ), m_private->m_altZones, distanceFunc ); + + // If nothing was closer than the official zone already was, altZone is + // nullptr; but if there is a spot-patch, then we need to re-find + // the zone by name, since we want to always return pointers into + // m_zones, not into the alternative spots. + return altZone ? find( altZone->region(), altZone->zone() ) : officialZone; } const TimeZoneData*