I was in need to convert an iCalendar / ics file to a json file, the result can be found here.
PS: The attempt to create the script started by asking AI, honestly, not yet very helpful.
#!/bin/bash
# Usage ./ics_to_json.sh google.ics data/calendar.json
# script to convert an ics / ical / iCalendar to a json file
# dos2unix needs to be installed to execute this script
# it can be removed if the ics file has unix format but this often is not the case
# Check if ICS file is provided
if [ -z "$1" ]; then
echo "Usage: $0 <ics_file> [output_file]"
exit 1
fi
ICS_FILE="$1"
OUTPUT_FILE="${2:-events.json}" # Default to "events.json" if not specified
SORTED_FILE="$(dirname "$OUTPUT_FILE")/sorted_$(basename "$OUTPUT_FILE")"
# Check if file exists
if [ ! -f "$ICS_FILE" ]; then
echo "Error: File '$ICS_FILE' not found!"
exit 1
fi
# Convert line endings if necessary
echo "Converting to unix format, just to make sure line endings are fine..."
dos2unix "$ICS_FILE" 2>/dev/null
# Extract events and convert dates directly in awk
# dates are converted to german format here
# if statement in scropt can be extended to include further fields into the output
echo "Extracting start, end and summary from ics..."
awk -v output="$OUTPUT_FILE" '
function convert_date(ics_date) {
if (ics_date ~ /^[0-9]{8}T[0-9]{6}Z$/) {
# Timestamp format: YYYYMMDDTHHMMSSZ
cmd = "date -u -d \"" substr(ics_date, 1, 4) "-" substr(ics_date, 5, 2) "-" substr(ics_date, 7, 2) " " substr(ics_date, 10, 2) ":" substr(ics_date, 12, 2) ":" substr(ics_date, 14, 2) "\" +\"%d.%m.%Y %H:%M\""
} else if (ics_date ~ /^[0-9]{8}$/) {
# All-day event format: YYYYMMDD
cmd = "date -u -d \"" substr(ics_date, 1, 4) "-" substr(ics_date, 5, 2) "-" substr(ics_date, 7, 2) "\" +\"%d.%m.%Y\""
} else {
return ics_date # Return the original date if it doesnt match expected formats
}
cmd | getline formatted_date
close(cmd)
# If its an all-day event, append " 00:00" to the formatted date
if (ics_date ~ /^[0-9]{8}$/) {
return formatted_date " 00:00"
}
return formatted_date
}
BEGIN {
print "[" > output
event_count = 0
}
{
if ($0 ~ /^BEGIN:VEVENT/) {
if (event_count > 0) {
print " }," >> output # Print closing brace with comma for previous event
}
print " {" >> output # Start a new event
event_count++
first_field = 1 # To track if its the first field in the event
}
if ($0 ~ /^SUMMARY:/) {
sub(/^SUMMARY:/, "", $0)
if (!first_field) {
print "," >> output # Print comma before the next field if its not the first
}
print " \"summary\": \"" $0 "\"" >> output
first_field = 0 # Mark that weve printed the first field
}
if ($0 ~ /^LOCATION:/) {
sub(/^LOCATION:/, "", $0)
if (!first_field) {
print "," >> output # Print comma before the next field if its not the first
}
print " \"location\": \"" $0 "\"" >> output
first_field = 0 # Mark that weve printed the first field
}
if ($0 ~ /^DTSTART(;VALUE=DATE)?/) {
split($0, arr, ":")
if (!first_field) {
print "," >> output # Print comma before the next field if its not the first
}
print " \"start\": \"" convert_date(arr[2]) "\"" >> output
first_field = 0 # Mark that weve printed the first field
}
if ($0 ~ /^DTEND(;VALUE=DATE)?/) {
split($0, arr, ":")
if (!first_field) {
print "," >> output # Print comma before the next field if its not the first
}
print " \"end\": \"" convert_date(arr[2]) "\"" >> output
first_field = 0 # Mark that weve printed the first field
}
}
END {
if (event_count > 0) {
print " }" >> output # Print the closing brace for the last event
}
print "]" >> output # Print the closing bracket for the array
}' "$ICS_FILE"
# Sort JSON by start date, still reflecting german format
echo "Sort by start date..."
jq 'sort_by(.start | strptime("%d.%m.%Y %H:%M"))' "$OUTPUT_FILE" > "$SORTED_FILE"
# cat sorted_"$OUTPUT_FILE"
# Replace original output with sorted version
mv "$SORTED_FILE" "$OUTPUT_FILE"
# Output final sorted JSON
#cat "$OUTPUT_FILE"
echo "File written to <<< '$OUTPUT_FILE' >>>"