Skip to content

Instantly share code, notes, and snippets.

@julianoes
Created January 16, 2023 05:04
Show Gist options
  • Save julianoes/6704fb3de7464fcf89f0ff6c69a9a5e2 to your computer and use it in GitHub Desktop.
Save julianoes/6704fb3de7464fcf89f0ff6c69a9a5e2 to your computer and use it in GitHub Desktop.
Convert tide times to ICS calendar
#!/usr/bin/env python3
import argparse
import datetime
# Tides from
# https://www.linz.govt.nz/products-services/tides-and-tidal-streams/tide-predictions
# get converted to .ics files which can be imported into Google Calendar.
def main():
parser = argparse.ArgumentParser(
prog="Tides CSV to ICS converter")
parser.add_argument('input_file', help="CSV file")
parser.add_argument('output_file', help="ICS file")
args = parser.parse_args()
ics = Ics()
with open(args.input_file, 'rb') as csvfile:
# ignore first 3 lines which are comments
for line in csvfile.readlines()[3:-1]:
parse_line(ics, line.decode('utf-8').strip())
with open(args.output_file, 'w') as icsfile:
icsfile.write(ics.render())
class Event:
def __init__(self, time, height):
self.is_high = height > 1.5
self.time = time
self.height = height
class Ics:
def __init__(self):
self.events = []
def add(self, year, month, day, time, height):
if (len(time) > 0 and len(height) > 0):
hours, mins = time.split(":")
self.events.append(
Event(
datetime.datetime(
int(year),
int(month),
int(day),
int(hours),
int(mins)),
float(height)))
def render(self):
content = self.header()
for event in self.events:
if (not event.is_high):
continue
content += "BEGIN:VEVENT\n"
content += f"SUMMARY:High tide {event.height}m\n"
icsdatefmt = "%Y%m%dT%H%M%S"
before = (event.time - datetime.timedelta(hours=1)) \
.strftime(icsdatefmt)
after = (event.time + datetime.timedelta(hours=1)) \
.strftime(icsdatefmt)
at = event.time.strftime("%H:%M")
content += f"DTSTART;TZID=Pacific/Auckland:{before}\n"
content += f"DTEND;TZID=Pacific/Auckland:{after}\n"
content += f"DESCRIPTION: High tide at {at}.\n"
content += "ACTION:DISPLAY\n"
content += "END:VEVENT\n"
content += self.footer()
return content
def header(self):
return "BEGIN:VCALENDAR\nVERSION:2.0\nCALSCALE:GREGORIAN\n"
def footer(self):
return "END:VCALENDAR\n"
def parse_line(ics, line):
day, _, month, year, time1, height1, time2, height2, \
time3, height3, time4, height4 = line.split(',')
ics.add(year, month, day, time1, height1)
ics.add(year, month, day, time2, height2)
ics.add(year, month, day, time3, height3)
ics.add(year, month, day, time4, height4)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment