Implement time zone aware expiration comparisons
[weather.git] / weather.py
index 493be0a..90f155c 100644 (file)
@@ -336,19 +336,26 @@ def get_alert(
     if alert:
         if verbose: return alert
         else:
-            if alert.find("\nNATIONAL WEATHER SERVICE") == -1:
-                muted = False
-            else:
+            import re
+            if re.search(r"\nNational Weather Service", alert):
                 muted = True
+            else:
+                muted = False
+            expirycheck = re.search(r"Expires:([0-9]{12})", alert)
+            if expirycheck:
+                # only report alerts and forecasts that expired less than
+                # offset ago
+                import datetime, zoneinfo
+                expiration = datetime.datetime.fromisoformat(
+                    "%sT%sZ" % (expirycheck[1][:8], expirycheck[1][-4:]))
+                now = datetime.datetime.now(tz=zoneinfo.ZoneInfo("UTC"))
+                # TODO: make this offset configurable
+                if now - expiration > datetime.timedelta(hours=1):
+                    return ""
             lines = alert.split("\n")
-            import time
-            valid_time = time.strftime("%Y%m%d%H%M")
             output = []
             for line in lines:
-                if line.startswith("Expires:") \
-                    and "Expires:" + valid_time > line:
-                    return ""
-                if muted and line.startswith("NATIONAL WEATHER SERVICE"):
+                if muted and line.startswith("National Weather Service"):
                     muted = False
                     line = ""
                 elif line == "&&":
@@ -394,11 +401,11 @@ def get_options(config):
             + "flash_flood_statement," \
             + "flash_flood_warning," \
             + "flash_flood_watch," \
-            + "flood_statement," \
             + "flood_warning," \
             + "severe_thunderstorm_warning," \
             + "severe_weather_statement," \
             + "special_weather_statement," \
+            + "tornado," \
             + "urgent_weather_message"
     option_parser.add_option("--atypes",
         dest="atypes",
@@ -1227,7 +1234,7 @@ def gecos(formatted):
     return tuple(coordinates)
 
 def correlate():
-    import codecs, csv, datetime, hashlib, os, re, sys, time, zipfile
+    import codecs, csv, datetime, hashlib, os, re, sys, time, zipfile, zoneinfo
     if pyversion("3"): import configparser
     else: import ConfigParser as configparser
     for filename in os.listdir("."):
@@ -1518,6 +1525,9 @@ def correlate():
             zone = "z".join( fields[:2] ).lower()
             if zone in zones:
                 state = fields[0]
+                description = fields[3].strip()
+                fips = "fips%s"%fields[6]
+                countycode = "%sc%s" % (state.lower(), fips[-3:])
                 if state:
                     zones[zone]["coastal_flood_statement"] = (
                         "https://tgftp.nws.noaa.gov/data/watches_warnings/"
@@ -1525,27 +1535,25 @@ def correlate():
                     zones[zone]["flash_flood_statement"] = (
                         "https://tgftp.nws.noaa.gov/data/watches_warnings/"
                         "flash_flood/statement/%s/%s.txt"
-                        % (state.lower(), zone))
+                        % (state.lower(), countycode))
                     zones[zone]["flash_flood_warning"] = (
                         "https://tgftp.nws.noaa.gov/data/watches_warnings/"
                         "flash_flood/warning/%s/%s.txt"
-                        % (state.lower(), zone))
+                        % (state.lower(), countycode))
                     zones[zone]["flash_flood_watch"] = (
                         "https://tgftp.nws.noaa.gov/data/watches_warnings/"
                         "flash_flood/watch/%s/%s.txt" % (state.lower(), zone))
-                    zones[zone]["flood_statement"] = (
-                        "https://tgftp.nws.noaa.gov/data/watches_warnings/"
-                        "flood/statement/%s/%s.txt" % (state.lower(), zone))
                     zones[zone]["flood_warning"] = (
                         "https://tgftp.nws.noaa.gov/data/watches_warnings/"
-                        "flood/warning/%s/%s.txt" % (state.lower(), zone))
+                        "flood/warning/%s/%s.txt"
+                        % (state.lower(), countycode))
                     zones[zone]["severe_thunderstorm_warning"] = (
                         "https://tgftp.nws.noaa.gov/data/watches_warnings/"
-                        "thunderstorm/%s/%s.txt" % (state.lower(), zone))
+                        "thunderstorm/%s/%s.txt" % (state.lower(), countycode))
                     zones[zone]["severe_weather_statement"] = (
                         "https://tgftp.nws.noaa.gov/data/watches_warnings/"
                         "severe_weather_stmt/%s/%s.txt"
-                        % (state.lower(), zone))
+                        % (state.lower(), countycode))
                     zones[zone]["short_term_forecast"] = (
                         "https://tgftp.nws.noaa.gov/data/forecasts/nowcast/"
                         "%s/%s.txt" % (state.lower(), zone))
@@ -1556,14 +1564,46 @@ def correlate():
                     zones[zone]["state_forecast"] = (
                         "https://tgftp.nws.noaa.gov/data/forecasts/state/"
                         "%s/%s.txt" % (state.lower(), zone))
+                    zones[zone]["tornado"] = (
+                        "https://tgftp.nws.noaa.gov/data/watches_warnings/"
+                        "tornado/%s/%s.txt" % (state.lower(), countycode))
                     zones[zone]["urgent_weather_message"] = (
                         "https://tgftp.nws.noaa.gov/data/watches_warnings/"
                         "non_precip/%s/%s.txt" % (state.lower(), zone))
                     zones[zone]["zone_forecast"] = (
                         "https://tgftp.nws.noaa.gov/data/forecasts/zone/"
                         "%s/%s.txt" % (state.lower(), zone))
-                description = fields[3].strip()
-                fips = "fips%s"%fields[6]
+                tzcode = fields[7]
+                if tzcode == "A":
+                    zones[zone]["tz"] = "US/Alaska"
+                elif tzcode == "AH":
+                    zones[zone]["tz"] = "US/Aleutian"
+                elif tzcode in ("C", "CE", "CM"):
+                    zones[zone]["tz"] = "US/Central"
+                elif tzcode in ("E", "e"):
+                    zones[zone]["tz"] = "US/Eastern"
+                elif tzcode == "F":
+                    zones[zone]["tz"] = "Pacific/Guadalcanal"
+                elif tzcode == "G":
+                    zones[zone]["tz"] = "Pacific/Guam"
+                elif tzcode == "H":
+                    zones[zone]["tz"] = "US/Hawaii"
+                elif tzcode == "J":
+                    zones[zone]["tz"] = "Japan"
+                elif tzcode == "K":
+                    zones[zone]["tz"] = "Pacific/Kwajalein"
+                elif tzcode in ("M", "MC", "MP"):
+                    zones[zone]["tz"] = "US/Mountain"
+                elif tzcode == "m":
+                    zones[zone]["tz"] = "US/Arizona"
+                elif tzcode == "P":
+                    zones[zone]["tz"] = "US/Pacific"
+                elif tzcode == "S":
+                    zones[zone]["tz"] = "US/Samoa"
+                elif tzcode == "V":
+                    zones[zone]["tz"] = "America/Virgin"
+                else:
+                    zones[zone]["tz"] = ""
                 county = fields[5]
                 if county:
                     if description.endswith(county):
@@ -2093,6 +2133,7 @@ def correlate():
             zctas_nocentroid += 1
     zones_nocentroid = 0
     zones_nodescription = 0
+    zones_notz = 0
     zones_noforecast = 0
     zones_overlapping = 0
     zonetable = {}
@@ -2120,6 +2161,10 @@ def correlate():
         if not zones.has_option(zone, "description"):
             qalog.append("%s: no description\n" % zone)
             zones_nodescription += 1
+        if not zones.has_option(zone, "tz") or not zones.get(
+                zone, "tz") in zoneinfo.available_timezones():
+            qalog.append("%s: no time zone\n" % zone)
+            zones_notz += 1
         if not zones.has_option(zone, "zone_forecast"):
             qalog.append("%s: no forecast\n" % zone)
             zones_noforecast += 1
@@ -2156,6 +2201,8 @@ def correlate():
             print("   %s zones with no centroid"%zones_nocentroid)
         if zones_nodescription:
             print("   %s zones with no description"%zones_nodescription)
+        if zones_notz:
+            print("   %s zones with no time zone"%zones_notz)
         if zones_noforecast:
             print("   %s zones with no forecast"%zones_noforecast)
         if zones_overlapping: