<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[RETIO]]></title><description><![CDATA[RETIO]]></description><link>https://retiodisplays.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Sat, 07 Nov 2026 10:07:27 GMT</lastBuildDate><atom:link href="https://retiodisplays.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Turning a trade-fair calendar CSV into an .ics feed with Python]]></title><description><![CDATA[Exhibitors often copy fair dates into their calendars by hand. If you start from a clean CSV with name, start_date, end_date and venue columns, a short…]]></description><link>https://retiodisplays.hashnode.dev/turning-a-trade-fair-calendar-csv-into-an-ics-feed-with-python</link><guid isPermaLink="true">https://retiodisplays.hashnode.dev/turning-a-trade-fair-calendar-csv-into-an-ics-feed-with-python</guid><category><![CDATA[Python]]></category><category><![CDATA[#OpenData]]></category><category><![CDATA[csv]]></category><category><![CDATA[calendar]]></category><dc:creator><![CDATA[RETIO]]></dc:creator><pubDate>Thu, 29 Oct 2026 11:08:33 GMT</pubDate><content:encoded><![CDATA[<p>Exhibitors often copy fair dates into their calendars by hand. If you start from a clean CSV with name, start_date, end_date and venue columns, a short script gives you a calendar file you can import into Google Calendar or Outlook:</p>
<pre><code class="language-python">import csv, datetime as dt

rows = csv.DictReader(open("trade_fairs_de_2027.csv", encoding="utf-8"))
out = ["BEGIN:VCALENDAR", "VERSION:2.0", "PRODID:-//fairs2027//EN"]
seen = set()
for r in rows:
    key = (r["name"], r["start_date"])
    if key in seen:          # skip duplicates
        continue
    seen.add(key)
    end = dt.date.fromisoformat(r["end_date"]) + dt.timedelta(days=1)  # DTEND is exclusive
    place = r["city"] or r["venue"]   # city is blank for many rows
    out += ["BEGIN:VEVENT",
            f"UID:{abs(hash(key))}-{r['start_date']}@fairs2027",
            f"DTSTART;VALUE=DATE:{r['start_date'].replace('-', '')}",
            f"DTEND;VALUE=DATE:{end.strftime('%Y%m%d')}",
            f"SUMMARY:{r['name']}",
            f"LOCATION:{place}",
            "END:VEVENT"]
out.append("END:VCALENDAR")
open("fairs2027.ics", "w", encoding="utf-8").write("\r\n".join(out) + "\r\n")
</code></pre>
<p>Gotchas worth knowing:</p>
<ul>
<li>For all-day events DTEND is exclusive, so add one day.</li>
<li>Lines must end with CRLF.</li>
<li>Every UID must be unique, which is why duplicates are skipped first.</li>
<li>Commas and semicolons in text fields should be escaped with a backslash if your calendar app is strict.</li>
</ul>
<p>The input is the open (CC BY 4.0) "Trade Fairs in Europe 2027" dataset: <a href="https://huggingface.co/datasets/retiodisplays/trade-fairs-europe-2027">https://huggingface.co/datasets/retiodisplays/trade-fairs-europe-2027</a> (also on Kaggle). There is one file per country. The readable version with all fairs is here: <a href="https://retio.eu/trade-fairs-in-europe-2027">https://retio.eu/trade-fairs-in-europe-2027</a></p>
<p>Dates come from the exhibition centres and can change, so check with the organiser before you book anything.</p>
<p>Przemek (RETIO)</p>
]]></content:encoded></item></channel></rss>