如何用Python结合IANA zdump,通过经纬度与时间获取含/不含DST的时区偏移?
Got it, let's break this down step by step. Since you already have the tz2020a database and want to skip pytz/timezone-finder, we'll rely on two core components from the IANA tzdata: the zone.tab file (to map coordinates to a timezone name) and the zdump tool (to fetch the exact UTC offset for a given time in that timezone).
Step 1: Map Coordinates to IANA Timezone Name
First, we need to turn your latitude/longitude into a valid IANA timezone identifier. The zone.tab file in your tz2020a directory lists every timezone alongside its representative geographic coordinates—we'll parse this file and find the closest timezone to your target coordinates using the haversine formula for great-circle distance.
Code to Parse zone.tab & Find Nearest Timezone
import math def parse_zone_tab(zone_tab_path): """Parse zone.tab into a list of (timezone_name, lat_decimal, lon_decimal) entries""" timezone_entries = [] with open(zone_tab_path, 'r', encoding='utf-8') as f: for line in f: line = line.strip() # Skip comments and empty lines if not line or line.startswith('#'): continue # Split columns: country code, coordinates, timezone name, optional comments parts = line.split('\t') if len(parts) < 3: continue coord_str = parts[1] tz_name = parts[2] # Convert coordinate string (e.g., +394606-1045253) to decimal degrees lat_part, lon_part = '', '' for c in coord_str: if c in '+-' and lat_part: lon_part += c else: lat_part += c if not lat_part else lon_part # Parse latitude lat_sign = 1 if lat_part.startswith('+') else -1 lat_deg = int(lat_part[1:3]) lat_min = int(lat_part[3:5]) lat_sec = int(lat_part[5:]) if len(lat_part) > 5 else 0 lat_decimal = lat_sign * (lat_deg + lat_min/60 + lat_sec/3600) # Parse longitude lon_sign = 1 if lon_part.startswith('+') else -1 lon_deg = int(lon_part[1:4]) lon_min = int(lon_part[4:6]) lon_sec = int(lon_part[6:]) if len(lon_part) > 6 else 0 lon_decimal = lon_sign * (lon_deg + lon_min/60 + lon_sec/3600) timezone_entries.append((tz_name, lat_decimal, lon_decimal)) return timezone_entries def find_nearest_timezone(target_lat, target_lon, zone_entries): """Find the closest timezone using haversine distance calculation""" closest_tz = None min_distance = float('inf') for tz_name, lat, lon in zone_entries: # Haversine formula to compute great-circle distance (in km) dlat = math.radians(target_lat - lat) dlon = math.radians(target_lon - lon) a = math.sin(dlat/2)**2 + math.cos(math.radians(target_lat)) * math.cos(math.radians(lat)) * math.sin(dlon/2)**2 c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a)) distance = 6371 * c # Earth radius in kilometers if distance < min_distance: min_distance = distance closest_tz = tz_name return closest_tz
Step 2: Use zdump to Fetch Timezone Offset
Once we have the timezone name, we'll use zdump (a command-line tool included with IANA tzdata) to get the UTC offset (including DST if active) for your target time. zdump outputs verbose timezone data for a specific timestamp, which we'll parse to extract the offset in hours.
Code to Run zdump & Parse Offset
import subprocess from datetime import datetime def get_timezone_offset(tz_name, target_time, tzdata_path=None): """ Get UTC offset (e.g., +5.5, +6.0) using zdump. target_time should be a UTC datetime object (convert local times to UTC first!) """ # Convert target UTC time to epoch timestamp (seconds since 1970-01-01 UTC) utc_timestamp = int(target_time.timestamp()) # Point zdump to the correct timezone file in your tz2020a database tz_file_path = f"{tzdata_path}/{tz_name}" if tzdata_path else tz_name # Construct zdump command: verbose output for the target timestamp cmd = ['zdump', '-v', '-t', str(utc_timestamp), tz_file_path] try: result = subprocess.run(cmd, capture_output=True, text=True, check=True) output_line = result.stdout.strip() # Example output: "America/Denver Sat Jan 1 00:00:00 2000 UTC = Fri Dec 31 17:00:00 1999 MST isdst=0 gmtoff=-21600" # Extract the gmtoff value (seconds offset from UTC) parts = output_line.split() gmtoff_idx = parts.index('gmtoff=') + 1 gmtoff_seconds = int(parts[gmtoff_idx]) # Convert seconds to hours (handle half-hour offsets like +5.5) offset_hours = gmtoff_seconds / 3600 return round(offset_hours, 1) except subprocess.CalledProcessError as e: print(f"Error running zdump: {e.stderr}") return None
Putting It All Together
Here's how to use these functions with your tz2020a database:
# Configuration (replace with your actual paths) TZ2020A_PATH = "/path/to/your/tz2020a" ZONE_TAB_PATH = f"{TZ2020A_PATH}/zone.tab" # Example input: target coordinates and UTC time target_lat = 19.0760 # Mumbai latitude target_lon = 72.8777 # Mumbai longitude target_time = datetime(2024, 8, 15, 12, 0, 0) # UTC time: August 15, 2024 noon # Step 1: Find nearest timezone zone_entries = parse_zone_tab(ZONE_TAB_PATH) nearest_tz = find_nearest_timezone(target_lat, target_lon, zone_entries) print(f"Nearest IANA timezone: {nearest_tz}") # Step 2: Get UTC offset offset = get_timezone_offset(nearest_tz, target_time, tzdata_path=TZ2020A_PATH) print(f"UTC offset at target time: {offset}") # Should output +5.5 for Mumbai in August
Key Notes
- Time Handling: Always pass a UTC datetime to
get_timezone_offset—if your input time is local, convert it to UTC first usingdatetime.astimezone(timezone.utc). - zdump Path: If
zdumpisn't in your system PATH, replace'zdump'in the command with the full path to the zdump binary (usually included with the IANA tzdata toolset). - Accuracy: The
zone.tabuses representative coordinates for each timezone, so this method works for most cases. For edge cases near timezone boundaries, you might need more precise geographic data, but this covers standard use cases.
内容的提问来源于stack exchange,提问作者Aravindh PyDev

