--- /dev/null
+<?php
+
+/**
+ * This is free and unencumbered software released into the public domain.
+ *
+ * Anyone is free to copy, modify, publish, use, compile, sell, or
+ * distribute this software, either in source code form or as a compiled
+ * binary, for any purpose, commercial or non-commercial, and by any
+ * means.
+ *
+ * In jurisdictions that recognize copyright laws, the author or authors
+ * of this software dedicate any and all copyright interest in the
+ * software to the public domain. We make this dedication for the benefit
+ * of the public at large and to the detriment of our heirs and
+ * successors. We intend this dedication to be an overt act of
+ * relinquishment in perpetuity of all present and future rights to this
+ * software under copyright law.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * For more information, please refer to <http://unlicense.org>
+ *
+ * ICS.php
+ * =============================================================================
+ * Use this class to create an .ics file.
+ *
+ *
+ * Usage
+ * -----------------------------------------------------------------------------
+ * Basic usage - generate ics file contents (see below for available properties):
+ * $ics = new ICS($props);
+ * $ics_file_contents = $ics->to_string();
+ *
+ * Setting properties after instantiation
+ * $ics = new ICS();
+ * $ics->set('summary', 'My awesome event');
+ *
+ * You can also set multiple properties at the same time by using an array:
+ * $ics->set(array(
+ * 'dtstart' => 'now + 30 minutes',
+ * 'dtend' => 'now + 1 hour'
+ * ));
+ *
+ * Available properties
+ * -----------------------------------------------------------------------------
+ * description
+ * String description of the event.
+ * dtend
+ * A date/time stamp designating the end of the event. You can use either a
+ * DateTime object or a PHP datetime format string (e.g. "now + 1 hour").
+ * dtstart
+ * A date/time stamp designating the start of the event. You can use either a
+ * DateTime object or a PHP datetime format string (e.g. "now + 1 hour").
+ * location
+ * String address or description of the location of the event.
+ * summary
+ * String short summary of the event - usually used as the title.
+ * url
+ * A url to attach to the the event. Make sure to add the protocol (http://
+ * or https://).
+ */
+
+class ICS {
+ const DT_FORMAT = 'Ymd\THis\Z';
+
+ protected $properties = array();
+ private $available_properties = array(
+ 'description',
+ 'dtend',
+ 'dtstart',
+ 'location',
+ 'summary',
+ 'url'
+ );
+
+ public function __construct($props) {
+ $this->set($props);
+ }
+
+ public function set($key, $val = false) {
+ if (is_array($key)) {
+ foreach ($key as $k => $v) {
+ $this->set($k, $v);
+ }
+ } else {
+ if (in_array($key, $this->available_properties)) {
+ $this->properties[$key] = $this->sanitize_val($val, $key);
+ }
+ }
+ }
+
+ public function to_string() {
+ $rows = $this->build_props();
+ return implode("\r\n", $rows);
+ }
+
+ private function build_props() {
+ // Build ICS properties - add header
+ $ics_props = array(
+ 'BEGIN:VCALENDAR',
+ 'VERSION:2.0',
+ 'PRODID:-//hacksw/handcal//NONSGML v1.0//EN',
+ 'CALSCALE:GREGORIAN',
+ 'BEGIN:VEVENT'
+ );
+
+ // Build ICS properties - add header
+ $props = array();
+ foreach($this->properties as $k => $v) {
+ $props[strtoupper($k . ($k === 'url' ? ';VALUE=URI' : ''))] = $v;
+ }
+
+ // Set some default values
+ $props['DTSTAMP'] = $this->format_timestamp('now');
+ $props['UID'] = uniqid();
+
+ // Append properties
+ foreach ($props as $k => $v) {
+ $ics_props[] = "$k:$v";
+ }
+
+ // Build ICS properties - add footer
+ $ics_props[] = 'END:VEVENT';
+ $ics_props[] = 'END:VCALENDAR';
+
+ return $ics_props;
+ }
+
+ private function sanitize_val($val, $key = false) {
+ switch($key) {
+ case 'dtend':
+ case 'dtstamp':
+ case 'dtstart':
+ $val = $this->format_timestamp($val);
+ break;
+ default:
+ $val = $this->escape_string($val);
+ }
+
+ return $val;
+ }
+
+ private function format_timestamp($timestamp) {
+ $dt = new DateTime($timestamp);
+ return $dt->format(self::DT_FORMAT);
+ }
+
+ private function escape_string($str) {
+ return preg_replace('/([\,;])/','\\\$1', $str);
+ }
+}
--- /dev/null
+#! /usr/bin/php
+<?php
+
+// Copyright 2022 Andrew Engelbrecht
+// GPL-3.0-or-later
+
+// expected format:
+//
+// "Saturday","10:00 - 10:15 EDT - Welcome address","Welcome Address by FSF","Jupiter","LibrePlanet special sessions","LibrePlanet 2022","","","http://libreplanet.org/2022/speakers/#5910"
+
+include 'ICS.php';
+
+$csv = array_map('str_getcsv', explode("\n", stream_get_contents(STDIN)));
+
+foreach ($csv as $linenum => $line) {
+
+ if (count($line) <= 1) {
+ continue;
+ }
+
+ // get the date stamp
+ if ($line[0] == "Saturday") {
+ $date = "2022-03-19";
+ } else if ($line[0] == "Sunday") {
+ $date = "2022-03-20";
+ } else {
+ throw new Exception("Invalid date: " . $line[0]);
+ }
+
+ // grab the time string
+ $timestamps = array();
+ preg_match('/([0-9][0-9]:[0-9][0-9]) - ([0-9][0-9]:[0-9][0-9]) EDT.*/', $line[1], $timestamps);
+
+ if (count($timestamps) < 3) {
+ throw new Exception("Invalid time range, title: " . $line[0]);
+ }
+
+ // parse and adjust time zone by 4 hours (to UTC)
+ $start_time = intval(str_replace(':', '', $timestamps[1])) + 400;
+ $end_time = intval(str_replace(':', '', $timestamps[2])) + 400;
+
+ if ($start_time >= 2400 || $end_time >= 2400) {
+ throw new Exception("Invalid times when hackishly converting to UTC: " . $start_time . " - " . $end_time);
+ }
+
+ $ics = new ICS(array(
+ 'location' => $line[3],
+ 'description' => $line[2],
+ 'dtstart' => $date . "T" . $start_time . "Z",
+ 'dtend' => $date . "T" . $end_time . "Z",
+ 'summary' => $line[4],
+ 'url' => str_replace("http:", "https:", $line[8]),
+ ));
+
+ echo $ics->to_string();
+
+ echo "\n\n";
+}
+