Banner Image

ICS to JSON using R

Apr 2, 2025

Tags:

Script to convert an ics / iCalendar file to json. You’ll find a bash version arround here in another post too.

# Load required libraries
library(lubridate)
library(dplyr)
library(jsonlite)
library(tibble)
library(tidyr)

# Function to parse ICS file and convert to JSON
parse_ics_to_json <- function(file_path) {
  # Read the ICS file
  ics_content <- readLines(file_path)
  
  # Initialize variables
  events <- list()
  current_event <- list()
  
  # Loop through each line in the ICS file
  for (line in ics_content) {
    line <- trimws(line)  # Trim whitespace
    
    # Check for event start
    if (line == "BEGIN:VEVENT") {
      current_event <- list()  # Reset current event
    }
    
    # Check for event details
    if (grepl("^SUMMARY:", line)) {
      current_event$summary <- sub("^SUMMARY:", "", line) %>% trimws()
    }
    if (grepl("^DTSTART(;VALUE=DATE)?:", line)) {
      start_value <- sub("^DTSTART(;VALUE=DATE)?:", "", line) %>% trimws()
      if (grepl("^\\d{8}$", start_value)) {  # Check for date-only format
        current_event$start <- paste0(start_value, "T000000")  # Set time to start of the day
      } else {
        current_event$start <- start_value
      }
    }
    if (grepl("^DTEND(;VALUE=DATE)?:", line)) {
      end_value <- sub("^DTEND(;VALUE=DATE)?:", "", line) %>% trimws()
      if (grepl("^\\d{8}$", end_value)) {  # Check for date-only format
        current_event$end <- paste0(end_value, "T000000")  # Set time to end of the day, wrong set it to beginning otherwise you have a 2 day event and leave Z (time zone) away
      } else {
        current_event$end <- end_value
      }
    }
    if (grepl("^LOCATION:", line)) {
      current_event$location <- sub("^LOCATION:", "", line) %>% trimws()
    }
    
    # Check for event end
    if (line == "END:VEVENT") {
      if (length(current_event) > 0) {
        events <- append(events, list(current_event))  # Add event to the list
      }
    }
  }
  
  # Check if any events were found
  if (length(events) == 0) {
    stop("No events found in the ICS file.")
  }
  
  events_df <- tibble(data = events)
  
  events_df <- events_df |>
    unnest_wider(data)
  
  # Convert start and end to POSIXct
  events_df$start <- ymd_hms(events_df$start, tz = 'CET')
  events_df$end <- ymd_hms(events_df$end, tz = 'CET')
  
  # Sort events by start date
  events_df <- events_df %>% arrange(start)
  
  # Format dates to German format
  events_df$start <- format(events_df$start, "%d.%m.%Y %H:%M")
  events_df$end <- format(events_df$end, "%d.%m.%Y %H:%M")
  

  
  # Convert to JSON
  json_output <- toJSON(events_df, pretty = TRUE, auto_unbox = TRUE)
  
  return(json_output)
}

# Example usage
file_path <- "/home/files/google.ics" # Replace with your .ics file path
json_result <- parse_ics_to_json(file_path)

write(json_result, "/home/files/calendar.json")
# Print the JSON output
cat(json_result)