| /// A tool to create the .tzf databases from a zoneinfo directory. | |
| /// | |
| /// Usage example: | |
| /// | |
| /// ```sh | |
| /// dart run tool/encode_tzf --zoneinfo path/to/zoneinfo | |
| /// ``` | |
| library; | |
| import 'dart:io'; | |
| import 'package:args/args.dart'; | |
| import 'package:file/file.dart' as pkg_file; | |
| import 'package:glob/glob.dart'; | |
| import 'package:glob/list_local_fs.dart'; | |
| import 'package:logging/logging.dart'; | |
| import 'package:path/path.dart' as p; | |
| import 'package:timezone/src/common_locations.dart' show commonLocations; | |
| import 'package:timezone/src/tools.dart'; | |
| import 'package:timezone/src/tzdb.dart'; | |
| import 'package:timezone/timezone.dart'; | |
| import 'package:timezone/tzdata.dart' as tzfile; | |
| Future<void> main(List<String> arguments) async { | |
| // Initialize logger | |
| Logger.root.level = Level.ALL; | |
| Logger.root.onRecord.listen((LogRecord rec) { | |
| print('${rec.level.name}: ${rec.time}: ${rec.message}'); | |
| }); | |
| final log = Logger('main'); | |
| // Parse CLI arguments | |
| final parser = ArgParser() | |
| ..addOption('output-all', defaultsTo: 'lib/data/latest_all.tzf') | |
| ..addOption('output-common', defaultsTo: 'lib/data/latest.tzf') | |
| ..addOption('output-10y', defaultsTo: 'lib/data/latest_10y.tzf') | |
| ..addOption('zoneinfo'); | |
| final args = parser.parse(arguments); | |
| final zoneinfoPath = args['zoneinfo'] as String?; | |
| if (zoneinfoPath == null) { | |
| print('Usage:\n${parser.usage}'); | |
| exit(1); | |
| } | |
| final db = LocationDatabase(); | |
| log.info('Importing zoneinfo files'); | |
| final files = await Glob('**').list(root: zoneinfoPath).toList(); | |
| for (final f in files) { | |
| if (f is pkg_file.File) { | |
| final name = p.relative(f.path, from: zoneinfoPath).replaceAll('\\', '/'); | |
| log.info('- $name:$f'); | |
| db.add( | |
| tzfileLocationToNativeLocation( | |
| tzfile.Location.fromBytes(name, await f.readAsBytes()), | |
| ), | |
| ); | |
| } | |
| } | |
| void logReport(FilterReport r) { | |
| log.info( | |
| ' + locations: ${r.originalLocationsCount} => ' | |
| '${r.newLocationsCount}', | |
| ); | |
| log.info( | |
| ' + transitions: ${r.originalTransitionsCount} => ' | |
| '${r.newTransitionsCount}', | |
| ); | |
| } | |
| log.info('Building location databases:'); | |
| log.info('- all locations'); | |
| final allDb = filterTimeZoneData(db); | |
| logReport(allDb.report); | |
| log.info('- common locations from all locations'); | |
| final commonDb = filterTimeZoneData(allDb.db, locations: commonLocations); | |
| logReport(commonDb.report); | |
| log.info('- [+- 5 years] from common locations'); | |
| final common10yDb = filterTimeZoneData( | |
| commonDb.db, | |
| dateFrom: DateTime(DateTime.now().year - 5, 1, 1).millisecondsSinceEpoch, | |
| dateTo: DateTime(DateTime.now().year + 5, 1, 1).millisecondsSinceEpoch, | |
| locations: commonLocations, | |
| ); | |
| logReport(common10yDb.report); | |
| log.info('Serializing location databases'); | |
| Future<void> write(String file, LocationDatabase db) => | |
| File(file).writeAsBytes(tzdbSerialize(db), flush: true); | |
| await write(args['output-all'] as String, allDb.db); | |
| await write(args['output-common'] as String, commonDb.db); | |
| await write(args['output-10y'] as String, common10yDb.db); | |
| } |