396 lines
16 KiB
Python
Executable File
396 lines
16 KiB
Python
Executable File
#!/usr/bin/env python2
|
|
#############################################################################
|
|
##
|
|
## Copyright (C) 2020 The Qt Company Ltd.
|
|
## Contact: https://www.qt.io/licensing/
|
|
##
|
|
## This file is part of the test suite of the Qt Toolkit.
|
|
##
|
|
## $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
|
## Commercial License Usage
|
|
## Licensees holding valid commercial Qt licenses may use this file in
|
|
## accordance with the commercial license agreement provided with the
|
|
## Software or, alternatively, in accordance with the terms contained in
|
|
## a written agreement between you and The Qt Company. For licensing terms
|
|
## and conditions see https://www.qt.io/terms-conditions. For further
|
|
## information use the contact form at https://www.qt.io/contact-us.
|
|
##
|
|
## GNU General Public License Usage
|
|
## Alternatively, this file may be used under the terms of the GNU
|
|
## General Public License version 3 as published by the Free Software
|
|
## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
## included in the packaging of this file. Please review the following
|
|
## information to ensure the GNU General Public License requirements will
|
|
## be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
|
##
|
|
## $QT_END_LICENSE$
|
|
##
|
|
#############################################################################
|
|
"""Parse CLDR data for QTimeZone use with MS-Windows
|
|
|
|
Script to parse the CLDR supplemental/windowsZones.xml file and encode
|
|
for use in QTimeZone. See ``./cldr2qlocalexml.py`` for where to get
|
|
the CLDR data. Pass its common/ directory as first parameter to this
|
|
script and the qtbase root directory as second parameter. It shall
|
|
update qtbase's src/corelib/time/qtimezoneprivate_data_p.h ready for
|
|
use.
|
|
"""
|
|
|
|
import os
|
|
import re
|
|
import datetime
|
|
import textwrap
|
|
|
|
from localetools import unicode2hex, wrap_list, Error, SourceFileEditor
|
|
from cldr import CldrAccess
|
|
|
|
### Data that may need updates in response to new entries in the CLDR file ###
|
|
|
|
# This script shall report the update you need, if this arises.
|
|
# However, you may need to research the relevant zone's standard offset.
|
|
|
|
# List of currently known Windows IDs.
|
|
# If this script reports missing IDs, please add them here.
|
|
# Look up the offset using (google and) timeanddate.com.
|
|
# Not public so may safely be changed. Please keep in alphabetic order by ID.
|
|
# ( Windows Id, Offset Seconds )
|
|
windowsIdList = (
|
|
(u'Afghanistan Standard Time', 16200),
|
|
(u'Alaskan Standard Time', -32400),
|
|
(u'Aleutian Standard Time', -36000),
|
|
(u'Altai Standard Time', 25200),
|
|
(u'Arab Standard Time', 10800),
|
|
(u'Arabian Standard Time', 14400),
|
|
(u'Arabic Standard Time', 10800),
|
|
(u'Argentina Standard Time', -10800),
|
|
(u'Astrakhan Standard Time', 14400),
|
|
(u'Atlantic Standard Time', -14400),
|
|
(u'AUS Central Standard Time', 34200),
|
|
(u'Aus Central W. Standard Time', 31500),
|
|
(u'AUS Eastern Standard Time', 36000),
|
|
(u'Azerbaijan Standard Time', 14400),
|
|
(u'Azores Standard Time', -3600),
|
|
(u'Bahia Standard Time', -10800),
|
|
(u'Bangladesh Standard Time', 21600),
|
|
(u'Belarus Standard Time', 10800),
|
|
(u'Bougainville Standard Time', 39600),
|
|
(u'Canada Central Standard Time', -21600),
|
|
(u'Cape Verde Standard Time', -3600),
|
|
(u'Caucasus Standard Time', 14400),
|
|
(u'Cen. Australia Standard Time', 34200),
|
|
(u'Central America Standard Time', -21600),
|
|
(u'Central Asia Standard Time', 21600),
|
|
(u'Central Brazilian Standard Time', -14400),
|
|
(u'Central Europe Standard Time', 3600),
|
|
(u'Central European Standard Time', 3600),
|
|
(u'Central Pacific Standard Time', 39600),
|
|
(u'Central Standard Time (Mexico)', -21600),
|
|
(u'Central Standard Time', -21600),
|
|
(u'China Standard Time', 28800),
|
|
(u'Chatham Islands Standard Time', 45900),
|
|
(u'Cuba Standard Time', -18000),
|
|
(u'Dateline Standard Time', -43200),
|
|
(u'E. Africa Standard Time', 10800),
|
|
(u'E. Australia Standard Time', 36000),
|
|
(u'E. Europe Standard Time', 7200),
|
|
(u'E. South America Standard Time', -10800),
|
|
(u'Easter Island Standard Time', -21600),
|
|
(u'Eastern Standard Time', -18000),
|
|
(u'Eastern Standard Time (Mexico)', -18000),
|
|
(u'Egypt Standard Time', 7200),
|
|
(u'Ekaterinburg Standard Time', 18000),
|
|
(u'Fiji Standard Time', 43200),
|
|
(u'FLE Standard Time', 7200),
|
|
(u'Georgian Standard Time', 14400),
|
|
(u'GMT Standard Time', 0),
|
|
(u'Greenland Standard Time', -10800),
|
|
(u'Greenwich Standard Time', 0),
|
|
(u'GTB Standard Time', 7200),
|
|
(u'Haiti Standard Time', -18000),
|
|
(u'Hawaiian Standard Time', -36000),
|
|
(u'India Standard Time', 19800),
|
|
(u'Iran Standard Time', 12600),
|
|
(u'Israel Standard Time', 7200),
|
|
(u'Jordan Standard Time', 7200),
|
|
(u'Kaliningrad Standard Time', 7200),
|
|
(u'Korea Standard Time', 32400),
|
|
(u'Libya Standard Time', 7200),
|
|
(u'Line Islands Standard Time', 50400),
|
|
(u'Lord Howe Standard Time', 37800),
|
|
(u'Magadan Standard Time', 36000),
|
|
(u'Magallanes Standard Time', -10800), # permanent DST
|
|
(u'Marquesas Standard Time', -34200),
|
|
(u'Mauritius Standard Time', 14400),
|
|
(u'Middle East Standard Time', 7200),
|
|
(u'Montevideo Standard Time', -10800),
|
|
(u'Morocco Standard Time', 0),
|
|
(u'Mountain Standard Time (Mexico)', -25200),
|
|
(u'Mountain Standard Time', -25200),
|
|
(u'Myanmar Standard Time', 23400),
|
|
(u'N. Central Asia Standard Time', 21600),
|
|
(u'Namibia Standard Time', 3600),
|
|
(u'Nepal Standard Time', 20700),
|
|
(u'New Zealand Standard Time', 43200),
|
|
(u'Newfoundland Standard Time', -12600),
|
|
(u'Norfolk Standard Time', 39600),
|
|
(u'North Asia East Standard Time', 28800),
|
|
(u'North Asia Standard Time', 25200),
|
|
(u'North Korea Standard Time', 30600),
|
|
(u'Omsk Standard Time', 21600),
|
|
(u'Pacific SA Standard Time', -10800),
|
|
(u'Pacific Standard Time', -28800),
|
|
(u'Pacific Standard Time (Mexico)', -28800),
|
|
(u'Pakistan Standard Time', 18000),
|
|
(u'Paraguay Standard Time', -14400),
|
|
(u'Qyzylorda Standard Time', 18000), # a.k.a. Kyzylorda, in Kazakhstan
|
|
(u'Romance Standard Time', 3600),
|
|
(u'Russia Time Zone 3', 14400),
|
|
(u'Russia Time Zone 10', 39600),
|
|
(u'Russia Time Zone 11', 43200),
|
|
(u'Russian Standard Time', 10800),
|
|
(u'SA Eastern Standard Time', -10800),
|
|
(u'SA Pacific Standard Time', -18000),
|
|
(u'SA Western Standard Time', -14400),
|
|
(u'Saint Pierre Standard Time', -10800), # New France
|
|
(u'Sakhalin Standard Time', 39600),
|
|
(u'Samoa Standard Time', 46800),
|
|
(u'Sao Tome Standard Time', 0),
|
|
(u'Saratov Standard Time', 14400),
|
|
(u'SE Asia Standard Time', 25200),
|
|
(u'Singapore Standard Time', 28800),
|
|
(u'South Africa Standard Time', 7200),
|
|
(u'Sri Lanka Standard Time', 19800),
|
|
(u'Sudan Standard Time', 7200), # unless they mean South Sudan, +03:00
|
|
(u'Syria Standard Time', 7200),
|
|
(u'Taipei Standard Time', 28800),
|
|
(u'Tasmania Standard Time', 36000),
|
|
(u'Tocantins Standard Time', -10800),
|
|
(u'Tokyo Standard Time', 32400),
|
|
(u'Tomsk Standard Time', 25200),
|
|
(u'Tonga Standard Time', 46800),
|
|
(u'Transbaikal Standard Time', 32400), # Yakutsk
|
|
(u'Turkey Standard Time', 7200),
|
|
(u'Turks And Caicos Standard Time', -14400),
|
|
(u'Ulaanbaatar Standard Time', 28800),
|
|
(u'US Eastern Standard Time', -18000),
|
|
(u'US Mountain Standard Time', -25200),
|
|
(u'UTC-11', -39600),
|
|
(u'UTC-09', -32400),
|
|
(u'UTC-08', -28800),
|
|
(u'UTC-02', -7200),
|
|
(u'UTC', 0),
|
|
(u'UTC+12', 43200),
|
|
(u'UTC+13', 46800),
|
|
(u'Venezuela Standard Time', -16200),
|
|
(u'Vladivostok Standard Time', 36000),
|
|
(u'Volgograd Standard Time', 14400),
|
|
(u'W. Australia Standard Time', 28800),
|
|
(u'W. Central Africa Standard Time', 3600),
|
|
(u'W. Europe Standard Time', 3600),
|
|
(u'W. Mongolia Standard Time', 25200), # Hovd
|
|
(u'West Asia Standard Time', 18000),
|
|
(u'West Bank Standard Time', 7200),
|
|
(u'West Pacific Standard Time', 36000),
|
|
(u'Yakutsk Standard Time', 32400),
|
|
(u'Yukon Standard Time', -25200), # Non-DST Mountain Standard Time since 2020-11-01
|
|
)
|
|
|
|
# List of standard UTC IDs to use. Not public so may be safely changed.
|
|
# Do not remove IDs, as each entry is part of the API/behavior guarantee.
|
|
# ( UTC Id, Offset Seconds )
|
|
utcIdList = (
|
|
(u'UTC', 0), # Goes first so is default
|
|
(u'UTC-14:00', -50400),
|
|
(u'UTC-13:00', -46800),
|
|
(u'UTC-12:00', -43200),
|
|
(u'UTC-11:00', -39600),
|
|
(u'UTC-10:00', -36000),
|
|
(u'UTC-09:00', -32400),
|
|
(u'UTC-08:00', -28800),
|
|
(u'UTC-07:00', -25200),
|
|
(u'UTC-06:00', -21600),
|
|
(u'UTC-05:00', -18000),
|
|
(u'UTC-04:30', -16200),
|
|
(u'UTC-04:00', -14400),
|
|
(u'UTC-03:30', -12600),
|
|
(u'UTC-03:00', -10800),
|
|
(u'UTC-02:00', -7200),
|
|
(u'UTC-01:00', -3600),
|
|
(u'UTC-00:00', 0),
|
|
(u'UTC+00:00', 0),
|
|
(u'UTC+01:00', 3600),
|
|
(u'UTC+02:00', 7200),
|
|
(u'UTC+03:00', 10800),
|
|
(u'UTC+03:30', 12600),
|
|
(u'UTC+04:00', 14400),
|
|
(u'UTC+04:30', 16200),
|
|
(u'UTC+05:00', 18000),
|
|
(u'UTC+05:30', 19800),
|
|
(u'UTC+05:45', 20700),
|
|
(u'UTC+06:00', 21600),
|
|
(u'UTC+06:30', 23400),
|
|
(u'UTC+07:00', 25200),
|
|
(u'UTC+08:00', 28800),
|
|
(u'UTC+08:30', 30600),
|
|
(u'UTC+09:00', 32400),
|
|
(u'UTC+09:30', 34200),
|
|
(u'UTC+10:00', 36000),
|
|
(u'UTC+11:00', 39600),
|
|
(u'UTC+12:00', 43200),
|
|
(u'UTC+13:00', 46800),
|
|
(u'UTC+14:00', 50400),
|
|
)
|
|
|
|
### End of data that may need updates in response to CLDR ###
|
|
|
|
class ByteArrayData:
|
|
def __init__(self):
|
|
self.data = []
|
|
self.hash = {}
|
|
|
|
def append(self, s):
|
|
s = s + '\0'
|
|
if s in self.hash:
|
|
return self.hash[s]
|
|
|
|
lst = unicode2hex(s)
|
|
index = len(self.data)
|
|
if index > 0xffff:
|
|
raise Error('Index ({}) outside the uint16 range !'.format(index))
|
|
self.hash[s] = index
|
|
self.data += lst
|
|
return index
|
|
|
|
def write(self, out, name):
|
|
out('\nstatic const char {}[] = {{\n'.format(name))
|
|
out(wrap_list(self.data))
|
|
out('\n};\n')
|
|
|
|
class ZoneIdWriter (SourceFileEditor):
|
|
def write(self, version, defaults, windowsIds):
|
|
self.__writeWarning(version)
|
|
windows, iana = self.__writeTables(self.writer.write, defaults, windowsIds)
|
|
windows.write(self.writer.write, 'windowsIdData')
|
|
iana.write(self.writer.write, 'ianaIdData')
|
|
|
|
def __writeWarning(self, version):
|
|
self.writer.write("""
|
|
/*
|
|
This part of the file was generated on {} from the
|
|
Common Locale Data Repository v{} file supplemental/windowsZones.xml
|
|
|
|
http://www.unicode.org/cldr/
|
|
|
|
Do not edit this code: run cldr2qtimezone.py on updated (or
|
|
edited) CLDR data; see qtbase/util/locale_database/.
|
|
*/
|
|
|
|
""".format(str(datetime.date.today()), version))
|
|
|
|
@staticmethod
|
|
def __writeTables(out, defaults, windowsIds):
|
|
windowsIdData, ianaIdData = ByteArrayData(), ByteArrayData()
|
|
|
|
# Write Windows/IANA table
|
|
out('// Windows ID Key, Country Enum, IANA ID Index\n')
|
|
out('static const QZoneData zoneDataTable[] = {\n')
|
|
for index, data in sorted(windowsIds.items()):
|
|
out(' {{ {:6d},{:6d},{:6d} }}, // {} / {}\n'.format(
|
|
data['windowsKey'], data['countryId'],
|
|
ianaIdData.append(data['ianaList']),
|
|
data['windowsId'], data['country']))
|
|
out(' { 0, 0, 0 } // Trailing zeroes\n')
|
|
out('};\n\n')
|
|
|
|
# Write Windows ID key table
|
|
out('// Windows ID Key, Windows ID Index, IANA ID Index, UTC Offset\n')
|
|
out('static const QWindowsData windowsDataTable[] = {\n')
|
|
for index, pair in enumerate(windowsIdList, 1):
|
|
out(' {{ {:6d},{:6d},{:6d},{:6d} }}, // {}\n'.format(
|
|
index,
|
|
windowsIdData.append(pair[0]),
|
|
ianaIdData.append(defaults[index]),
|
|
pair[1], pair[0]))
|
|
out(' { 0, 0, 0, 0 } // Trailing zeroes\n')
|
|
out('};\n\n')
|
|
|
|
# Write UTC ID key table
|
|
out('// IANA ID Index, UTC Offset\n')
|
|
out('static const QUtcData utcDataTable[] = {\n')
|
|
for pair in utcIdList:
|
|
out(' {{ {:6d},{:6d} }}, // {}\n'.format(
|
|
ianaIdData.append(pair[0]), pair[1], pair[0]))
|
|
out(' { 0, 0 } // Trailing zeroes\n')
|
|
out('};\n')
|
|
|
|
return windowsIdData, ianaIdData
|
|
|
|
def usage(err, name, message=''):
|
|
err.write("""Usage: {} path/to/cldr/core/common path/to/qtbase
|
|
""".format(name)) # TODO: more interesting message
|
|
if message:
|
|
err.write('\n' + message + '\n')
|
|
|
|
def main(args, out, err):
|
|
"""Parses CLDR's data and updates Qt's representation of it.
|
|
|
|
Takes sys.argv, sys.stdout, sys.stderr (or equivalents) as
|
|
arguments. Expects two command-line options: the root of the
|
|
unpacked CLDR data-file tree and the root of the qtbase module's
|
|
checkout. Updates QTimeZone's private data about Windows time-zone
|
|
IDs."""
|
|
name = args.pop(0)
|
|
if len(args) != 2:
|
|
usage(err, name, "Expected two arguments")
|
|
return 1
|
|
|
|
cldrPath = args.pop(0)
|
|
qtPath = args.pop(0)
|
|
|
|
if not os.path.isdir(qtPath):
|
|
usage(err, name, "No such Qt directory: " + qtPath)
|
|
return 1
|
|
if not os.path.isdir(cldrPath):
|
|
usage(err, name, "No such CLDR directory: " + cldrPath)
|
|
return 1
|
|
|
|
dataFilePath = os.path.join(qtPath, 'src', 'corelib', 'time', 'qtimezoneprivate_data_p.h')
|
|
if not os.path.isfile(dataFilePath):
|
|
usage(err, name, 'No such file: ' + dataFilePath)
|
|
return 1
|
|
|
|
try:
|
|
version, defaults, winIds = CldrAccess(cldrPath).readWindowsTimeZones(
|
|
dict((name, ind) for ind, name in enumerate((x[0] for x in windowsIdList), 1)))
|
|
except IOError as e:
|
|
usage(err, name,
|
|
'Failed to open common/supplemental/windowsZones.xml: ' + (e.message or e.args[1]))
|
|
return 1
|
|
except Error as e:
|
|
err.write('\n'.join(textwrap.wrap(
|
|
'Failed to read windowsZones.xml: ' + (e.message or e.args[1]),
|
|
subsequent_indent=' ', width=80)) + '\n')
|
|
return 1
|
|
|
|
out.write('Input file parsed, now writing data\n')
|
|
try:
|
|
writer = ZoneIdWriter(dataFilePath, qtPath)
|
|
except IOError as e:
|
|
err.write('Failed to open files to transcribe: {}'.format(e.message or e.args[1]))
|
|
return 1
|
|
|
|
try:
|
|
writer.write(version, defaults, winIds)
|
|
except Error as e:
|
|
writer.cleanup()
|
|
err.write('\nError in Windows ID data: ' + e.message + '\n')
|
|
return 1
|
|
|
|
writer.close()
|
|
out.write('Data generation completed, please check the new file at ' + dataFilePath + '\n')
|
|
return 0
|
|
|
|
if __name__ == '__main__':
|
|
import sys
|
|
sys.exit(main(sys.argv, sys.stdout, sys.stderr))
|