112 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			112 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
#!/usr/bin/env python3
 | 
						|
 | 
						|
# Retrieves the time (and time difference) at a location (can be an address,
 | 
						|
# postal code, country name, etc). Uses Google Maps APIs to map locations to
 | 
						|
# coordinates, and coordinates to timezones.
 | 
						|
#
 | 
						|
# Requires: pytz. tzlocal
 | 
						|
# Usage: time-in Copenhagen "Toronto, ON" Vancouver
 | 
						|
 | 
						|
import sys
 | 
						|
import os
 | 
						|
import json
 | 
						|
from base64 import b64encode
 | 
						|
from datetime import datetime
 | 
						|
from urllib import request
 | 
						|
 | 
						|
from pytz import timezone
 | 
						|
from tzlocal import get_localzone
 | 
						|
 | 
						|
URL_BASE = "https://maps.googleapis.com/maps/api/"
 | 
						|
URL_TZ = URL_BASE + "timezone/json?location={},{}×tamp={}"
 | 
						|
URL_GEOCODE = URL_BASE + "geocode/json?address={}"
 | 
						|
 | 
						|
CACHE_FILE = "/tmp/time-in"
 | 
						|
CACHE = {}
 | 
						|
 | 
						|
 | 
						|
#
 | 
						|
def _generate_url(url, *args):
 | 
						|
    return url.format(*map(request.quote, map(str, args)))
 | 
						|
 | 
						|
def _request(url, *args):
 | 
						|
    try:
 | 
						|
        real_url = url.format(*map(request.quote, map(str, args)))
 | 
						|
        resp = request.urlopen(real_url).read()
 | 
						|
        result = json.loads(resp.decode('utf-8'))
 | 
						|
        if result['status'] == 'OK':
 | 
						|
            return result
 | 
						|
        elif result['status'] != 'UNKNOWN_ERROR':
 | 
						|
            raise Exception(result['error_message'])
 | 
						|
    except IOError:
 | 
						|
        print("IOError")  # FIXME Better error handling
 | 
						|
 | 
						|
def get_timezone(lat, lng, timestamp):
 | 
						|
    return _request(URL_TZ, lat, lng, timestamp)['timeZoneId']
 | 
						|
 | 
						|
def get_coords(desc):
 | 
						|
    result = _request(URL_GEOCODE, desc)
 | 
						|
    if result['results']:
 | 
						|
        return {
 | 
						|
            "lat": result['results'][0]['geometry']['location']['lat'],
 | 
						|
            "lng": result['results'][0]['geometry']['location']['lng'],
 | 
						|
            "name": result['results'][0]['formatted_address']
 | 
						|
        }
 | 
						|
 | 
						|
def cache(key, fn, ttl):
 | 
						|
    if key in CACHE and CACHE[key]['time'] < CACHE[key]['time'] + ttl:
 | 
						|
        return CACHE[key]['value']
 | 
						|
    value = fn()
 | 
						|
    if value is not None:
 | 
						|
        CACHE[key] = {
 | 
						|
            'time': datetime.now().timestamp(),
 | 
						|
            'value': fn()
 | 
						|
        }
 | 
						|
    return value
 | 
						|
 | 
						|
def diff(a, b):
 | 
						|
    a = a.replace(tzinfo=None)
 | 
						|
    b = b.replace(tzinfo=None)
 | 
						|
    return max(a, b) - min(a, b)
 | 
						|
 | 
						|
def encode(s):
 | 
						|
    return str(b64encode(s.encode('ascii')))
 | 
						|
 | 
						|
 | 
						|
#
 | 
						|
if __name__ == '__main__':
 | 
						|
    if not sys.argv[1:]:
 | 
						|
        print("Usage: {} [LOCATION]".format(sys.argv[0]))
 | 
						|
        sys.exit(1)
 | 
						|
 | 
						|
    if os.path.isfile(CACHE_FILE):
 | 
						|
        with open(CACHE_FILE, 'r') as f:
 | 
						|
            CACHE = json.load(f)
 | 
						|
 | 
						|
    here = get_localzone().localize(datetime.now())
 | 
						|
    for location in sys.argv[1:]:
 | 
						|
        d_coords = cache("coords-" + encode(location), lambda: get_coords(location), 86400)
 | 
						|
        if d_coords:
 | 
						|
            d_tzname = cache(
 | 
						|
                "tz-" + encode(location),
 | 
						|
                lambda: get_timezone(d_coords['lat'], d_coords['lng'], here.timestamp()),
 | 
						|
                1800
 | 
						|
            )
 | 
						|
 | 
						|
            then = here.astimezone(timezone(d_tzname))
 | 
						|
            diff = diff(here, then)
 | 
						|
 | 
						|
            print("{:24} {}\t{} {}".format(
 | 
						|
                d_coords['name'],
 | 
						|
                then,
 | 
						|
                diff,
 | 
						|
                "ago" if then < here else "ahead"
 | 
						|
            ))
 | 
						|
        else:
 | 
						|
            print("Couldn't find {}".format(location))
 | 
						|
 | 
						|
    with open(CACHE_FILE, 'w') as f:
 | 
						|
        json.dump(CACHE, f)
 | 
						|
 | 
						|
    sys.exit(0)
 |