├── .gitignore ├── Gemfile ├── Gemfile.lock ├── README.md ├── generate.rb └── netlify.toml /.gitignore: -------------------------------------------------------------------------------- 1 | _site 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | gem 'timezone_finder' 6 | 7 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | timezone_finder (1.5.7) 5 | 6 | PLATFORMS 7 | ruby 8 | 9 | DEPENDENCIES 10 | timezone_finder 11 | 12 | BUNDLED WITH 13 | 1.17.3 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TimeZones - lots of cities and their timezones 2 | 3 | This small project generates a CSV (and JSON!) of many cities and their IANA timezones. 4 | It was inspired by Dato by @sindresorhus which didn't have San Francisco! 5 | 6 | The output is deployed to Netlify. It downloads a CC-BY list of about 25k cities and their time zones from [Geonames](http://www.geonames.org/) and writes it into simplified CSV and JSON files. 7 | For US cities, the JSON also contains `adminRegion` property with the state. Unfortunately, localized admin region data isn't readily available for the rest. 8 | 9 | The output can be downloaded at (https://timezones.netlify.com/cities_time_zones.csv) or (https://timezones.netlify.com/cities_time_zones.json). 10 | -------------------------------------------------------------------------------- /generate.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require 'timezone_finder' 5 | require 'fileutils' 6 | require 'csv' 7 | require 'json' 8 | 9 | def download_and_unzip(url) 10 | Dir.chdir('_site') do 11 | system("curl -o output.zip \"#{url}\"") 12 | system('unzip output.zip') 13 | FileUtils.rm_rf('output.zip') 14 | end 15 | end 16 | 17 | FileUtils.rm_rf('_site') 18 | FileUtils.mkdir_p('_site') 19 | 20 | download_and_unzip 'https://download.geonames.org/export/dump/cities15000.zip' 21 | 22 | tf = TimezoneFinder.create 23 | 24 | csv = CSV.open("_site/cities15000.txt", "r", { :col_sep => "\t" }) 25 | 26 | cities = csv.map do |city| 27 | _, name, _, keywords, lat, lon, _, _, countryCode, _, admin_region = city 28 | time_zone = city[-2] 29 | 30 | # no need to lookup, Geonames data is super! 31 | # just for validation 32 | time_zone_calc = tf.timezone_at(lng: lon.to_f, lat: lat.to_f) 33 | STDERR.puts "#{name} - #{time_zone_calc} != #{time_zone} (#{city[-1]})" if time_zone != time_zone_calc 34 | 35 | # the keyowords are comma separated 36 | keywords = CSV.parse(keywords || '') 37 | 38 | city_hash = { 39 | name: name, 40 | countryCode: countryCode, 41 | keywords: keywords, 42 | timeZone: time_zone, 43 | } 44 | city_hash[:adminRegion] = admin_region if countryCode == "US" 45 | city_hash 46 | end 47 | 48 | File.open('_site/cities_time_zones.json','w') do |output| 49 | output.write JSON.pretty_generate(cities) 50 | end 51 | 52 | 53 | CSV.open('_site/cities_time_zones.csv', 'w') do |output| 54 | cities.each do |city| 55 | output << [city[:name], city[:countryCode], city[:adminRegion], city[:keywords].join(','), city[:timeZone]] 56 | end 57 | end 58 | 59 | File.open('_site/_redirects', 'w') do |f| 60 | f.puts '/ https://github.com/igor-makarov/timezones 301' 61 | end 62 | 63 | File.open('_site/_headers', 'w') do |f| 64 | f.puts '/cities_time_zones.json' 65 | f.puts ' Content-type: application/json; charset=utf-8' 66 | end 67 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | command = "bundle exec generate.rb" 3 | publish = "_site" 4 | --------------------------------------------------------------------------------