update-tzdata.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. #!/usr/bin/python -B
  2. # Copyright 2017 The Android Open Source Project
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. """Generates the timezone data files used by Android."""
  16. from __future__ import print_function
  17. import glob
  18. import os
  19. import re
  20. import subprocess
  21. import sys
  22. import tarfile
  23. import tempfile
  24. sys.path.append('%s/external/icu/tools' % os.environ.get('ANDROID_BUILD_TOP'))
  25. import i18nutil
  26. import icuutil
  27. import tzdatautil
  28. # Calculate the paths that are referred to by multiple functions.
  29. android_build_top = i18nutil.GetAndroidRootOrDie()
  30. timezone_dir = os.path.realpath('%s/system/timezone' % android_build_top)
  31. i18nutil.CheckDirExists(timezone_dir, 'system/timezone')
  32. android_host_out = i18nutil.GetAndroidHostOutOrDie()
  33. zone_compactor_dir = os.path.realpath('%s/system/timezone/zone_compactor' % android_build_top)
  34. i18nutil.CheckDirExists(timezone_dir, 'system/timezone/zone_zompactor')
  35. timezone_input_tools_dir = os.path.realpath('%s/input_tools' % timezone_dir)
  36. timezone_input_data_dir = os.path.realpath('%s/input_data' % timezone_dir)
  37. timezone_output_data_dir = '%s/output_data' % timezone_dir
  38. i18nutil.CheckDirExists(timezone_output_data_dir, 'output_data')
  39. tmp_dir = tempfile.mkdtemp('-tzdata')
  40. def GenerateZicInputFile(extracted_iana_data_dir):
  41. # Android APIs assume DST means "summer time" so we follow the rearguard format
  42. # introduced in 2018e.
  43. zic_input_file_name = 'rearguard.zi'
  44. # 'NDATA=' is used to remove unnecessary rules files.
  45. subprocess.check_call(['make', '-C', extracted_iana_data_dir, 'NDATA=', zic_input_file_name])
  46. zic_input_file = '%s/%s' % (extracted_iana_data_dir, zic_input_file_name)
  47. if not os.path.exists(zic_input_file):
  48. print('Could not find %s' % zic_input_file)
  49. sys.exit(1)
  50. return zic_input_file
  51. def WriteSetupFile(zic_input_file):
  52. """Writes the list of zones that ZoneCompactor should process."""
  53. links = []
  54. zones = []
  55. for line in open(zic_input_file):
  56. fields = line.split()
  57. if fields:
  58. if fields[0] == 'Link':
  59. links.append('%s %s %s' % (fields[0], fields[1], fields[2]))
  60. zones.append(fields[2])
  61. elif fields[0] == 'Zone':
  62. zones.append(fields[1])
  63. zones.sort()
  64. zone_compactor_setup_file = '%s/setup' % tmp_dir
  65. setup = open(zone_compactor_setup_file, 'w')
  66. for link in sorted(set(links)):
  67. setup.write('%s\n' % link)
  68. for zone in sorted(set(zones)):
  69. setup.write('%s\n' % zone)
  70. setup.close()
  71. return zone_compactor_setup_file
  72. def BuildIcuData(iana_data_tar_file):
  73. icu_build_dir = '%s/icu' % tmp_dir
  74. icuutil.PrepareIcuBuild(icu_build_dir)
  75. icuutil.MakeTzDataFiles(icu_build_dir, iana_data_tar_file)
  76. # Create ICU system image files.
  77. icuutil.MakeAndCopyIcuDataFiles(icu_build_dir)
  78. icu_overlay_dir = '%s/icu_overlay' % timezone_output_data_dir
  79. # Create the ICU overlay time zone file.
  80. icu_overlay_dat_file = '%s/icu_tzdata.dat' % icu_overlay_dir
  81. icuutil.MakeAndCopyOverlayTzIcuData(icu_build_dir, icu_overlay_dat_file)
  82. # Copy ICU license file(s)
  83. icuutil.CopyLicenseFiles(icu_overlay_dir)
  84. def GetIanaVersion(iana_tar_file):
  85. iana_tar_filename = os.path.basename(iana_tar_file)
  86. iana_version = re.search('tz(?:data|code)(.+)\\.tar\\.gz', iana_tar_filename).group(1)
  87. return iana_version
  88. def ExtractTarFile(tar_file, dir):
  89. print('Extracting %s...' % tar_file)
  90. if not os.path.exists(dir):
  91. os.mkdir(dir)
  92. tar = tarfile.open(tar_file, 'r')
  93. tar.extractall(dir)
  94. def BuildZic(iana_tools_dir):
  95. iana_zic_code_tar_file = tzdatautil.GetIanaTarFile(iana_tools_dir, 'code')
  96. iana_zic_code_version = GetIanaVersion(iana_zic_code_tar_file)
  97. iana_zic_data_tar_file = tzdatautil.GetIanaTarFile(iana_tools_dir, 'data')
  98. iana_zic_data_version = GetIanaVersion(iana_zic_data_tar_file)
  99. print('Found IANA zic release %s/%s in %s/%s ...' \
  100. % (iana_zic_code_version, iana_zic_data_version, iana_zic_code_tar_file, iana_zic_data_tar_file))
  101. zic_build_dir = '%s/zic' % tmp_dir
  102. ExtractTarFile(iana_zic_code_tar_file, zic_build_dir)
  103. ExtractTarFile(iana_zic_data_tar_file, zic_build_dir)
  104. # zic
  105. print('Building zic...')
  106. # VERSION_DEPS= is to stop the build process looking for files that might not
  107. # be present across different versions.
  108. subprocess.check_call(['make', '-C', zic_build_dir, 'zic'])
  109. zic_binary_file = '%s/zic' % zic_build_dir
  110. if not os.path.exists(zic_binary_file):
  111. print('Could not find %s' % zic_binary_file)
  112. sys.exit(1)
  113. return zic_binary_file
  114. def BuildTzdata(zic_binary_file, extracted_iana_data_dir, iana_data_version):
  115. print('Generating zic input file...')
  116. zic_input_file = GenerateZicInputFile(extracted_iana_data_dir)
  117. print('Calling zic...')
  118. zic_output_dir = '%s/data' % tmp_dir
  119. os.mkdir(zic_output_dir)
  120. zic_cmd = [zic_binary_file, '-d', zic_output_dir, zic_input_file]
  121. subprocess.check_call(zic_cmd)
  122. # ZoneCompactor
  123. zone_compactor_setup_file = WriteSetupFile(zic_input_file)
  124. print('Calling ZoneCompactor to update tzdata to %s...' % iana_data_version)
  125. subprocess.check_call(['make', '-C', android_build_top, '-j30', 'zone_compactor'])
  126. # Create args for ZoneCompactor
  127. zone_tab_file = '%s/zone.tab' % extracted_iana_data_dir
  128. jar_file = '%s/framework/zone_compactor.jar' % android_host_out
  129. header_string = 'tzdata%s' % iana_data_version
  130. print('Executing ZoneCompactor...')
  131. iana_output_data_dir = '%s/iana' % timezone_output_data_dir
  132. subprocess.check_call(['java', '-jar', jar_file,
  133. zone_compactor_setup_file, zic_output_dir, zone_tab_file,
  134. iana_output_data_dir, header_string])
  135. def BuildTzlookup(iana_data_dir):
  136. countryzones_source_file = '%s/android/countryzones.txt' % timezone_input_data_dir
  137. tzlookup_dest_file = '%s/android/tzlookup.xml' % timezone_output_data_dir
  138. print('Calling TzLookupGenerator to create tzlookup.xml...')
  139. subprocess.check_call(['make', '-C', android_build_top, '-j30', 'tzlookup_generator'])
  140. jar_file = '%s/framework/tzlookup_generator.jar' % android_host_out
  141. zone_tab_file = '%s/zone.tab' % iana_data_dir
  142. subprocess.check_call(['java', '-jar', jar_file,
  143. countryzones_source_file, zone_tab_file, tzlookup_dest_file])
  144. def CreateDistroFiles(iana_data_version, output_distro_dir, output_version_file):
  145. create_distro_script = '%s/distro/tools/create-distro.py' % timezone_dir
  146. tzdata_file = '%s/iana/tzdata' % timezone_output_data_dir
  147. icu_file = '%s/icu_overlay/icu_tzdata.dat' % timezone_output_data_dir
  148. tzlookup_file = '%s/android/tzlookup.xml' % timezone_output_data_dir
  149. distro_file_pattern = '%s/*.zip' % output_distro_dir
  150. existing_files = glob.glob(distro_file_pattern)
  151. print('Removing %s' % existing_files)
  152. for existing_file in existing_files:
  153. os.remove(existing_file)
  154. subprocess.check_call([create_distro_script,
  155. '-iana_version', iana_data_version,
  156. '-tzdata', tzdata_file,
  157. '-icu', icu_file,
  158. '-tzlookup', tzlookup_file,
  159. '-output_distro_dir', output_distro_dir,
  160. '-output_version_file', output_version_file])
  161. def UpdateTestFiles():
  162. testing_data_dir = '%s/testing/data' % timezone_dir
  163. update_test_files_script = '%s/create-test-data.sh' % testing_data_dir
  164. subprocess.check_call([update_test_files_script], cwd=testing_data_dir)
  165. # Run with no arguments from any directory, with no special setup required.
  166. # See http://www.iana.org/time-zones/ for more about the source of this data.
  167. def main():
  168. print('Source data file structure: %s' % timezone_input_data_dir)
  169. print('Source tools file structure: %s' % timezone_input_tools_dir)
  170. print('Output data file structure: %s' % timezone_output_data_dir)
  171. iana_input_data_dir = '%s/iana' % timezone_input_data_dir
  172. iana_data_tar_file = tzdatautil.GetIanaTarFile(iana_input_data_dir, 'data')
  173. iana_data_version = GetIanaVersion(iana_data_tar_file)
  174. print('IANA time zone data release %s in %s ...' % (iana_data_version, iana_data_tar_file))
  175. icu_dir = icuutil.icuDir()
  176. print('Found icu in %s ...' % icu_dir)
  177. BuildIcuData(iana_data_tar_file)
  178. iana_tools_dir = '%s/iana' % timezone_input_tools_dir
  179. zic_binary_file = BuildZic(iana_tools_dir)
  180. iana_data_dir = '%s/iana_data' % tmp_dir
  181. ExtractTarFile(iana_data_tar_file, iana_data_dir)
  182. BuildTzdata(zic_binary_file, iana_data_dir, iana_data_version)
  183. BuildTzlookup(iana_data_dir)
  184. # Create a distro file and version file from the output from prior stages.
  185. output_distro_dir = '%s/distro' % timezone_output_data_dir
  186. output_version_file = '%s/version/tz_version' % timezone_output_data_dir
  187. CreateDistroFiles(iana_data_version, output_distro_dir, output_version_file)
  188. # Update test versions of distro files too.
  189. UpdateTestFiles()
  190. print('Look in %s and %s for new files' % (timezone_output_data_dir, icu_dir))
  191. sys.exit(0)
  192. if __name__ == '__main__':
  193. main()