ical

package module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 21, 2025 License: GPL-3.0 Imports: 9 Imported by: 0

README

iCal

iCal is a Go library for generating iCalendar (.ics) files with support for recurring events. It provides an easy-to-use API for creating events, defining recurrence rules, and exporting calendars in the standard iCalendar format.

Go Reference codecov

Features

  • Create events with start and end times, summaries, descriptions, and locations.
  • Define recurrence rules for events (daily, weekly, monthly, yearly).
  • Handle 1 time exceptions for recurring events.
  • Support for To-Do and Journal components.
  • Set reminders for events with various actions (display, email, audio).
  • Support for all major time zones via the iCal_VTIMEZONE library.
  • Export calendars to .ics files compatible with popular calendar applications.

Installation

To install the iCal library, use the following command:

go get github.com/Tylerchristensen100/iCal
Creating a Calendar

Here is a simple example of how to create a calendar with a recurring event:

package main

import (
	"os"
	"strings"
	"testing"
	"time"

	ical "github.com/Tylerchristensen100/iCal"
)

func main() {
    calendar := ical.Create("Team Calendar", "Coordinate team meetings and events")

    event := ical.Event{
        Summary:     "Weekly Meeting",
        Description: "Team sync-up",
        Location:    "Conference Room",
        StartTime:   time.Date(2025, time.July, 1, 9, 0, 0, 0, time.UTC),
        EndTime:     time.Date(2026, time.September, 1, 10, 0, 0, 0, time.UTC),
        Recurrences: ical.Recurrences{
            Frequency: ical.WeeklyFrequency,
            Day:       time.Thursday,
            StartTime: time.Date(2025, time.July, 1, 9, 0, 0, 0, time.UTC),
            EndTime:   time.Date(2025, time.July, 1, 10, 0, 0, 0, 0, time.UTC),
            Exceptions: []time.Time{
                time.Date(2025, time.November, 27, 9, 0, 0, 0, time.UTC), // Skip Thanksgiving
        },
    }
    }

    thanksgiving := ical.Event{
        Summary:     "Thanksgiving",
        Description: "Time Off",
        Location:    "Anywhere but the office",
        StartTime:   time.Date(2025, time.November, 27, 0, 0, 0, 0, time.UTC),
        EndTime:     time.Date(2025, time.November, 27, 23, 59, 59, 0, time.UTC),
    }

    calendar.AddEvent(event)
    calendar.AddEvent(thanksgiving)

    icsData, err := calendar.Generate()
	if err != nil {
		panic(err)
	}

    err = os.WriteFile("team_calendar.ics", []byte(icsData), 0644)
	if err != nil {
		panic(err)
	}
}

Notes

  • TimeZones are provided via the iCal_VTIMEZONE library.
  • Timezones are embedded directly into a map within this library for ease of use. This means there is a 104kb increase in binary size. The total size of the built library is approximately 550Kb.

Resources

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrInvalidEvent is returned when an event is not valid.
	ErrInvalidEvent = fmt.Errorf(errInvalidEventMessage)

	// ErrInvalidRecurrence is returned when a recurrence rule is not valid.
	ErrInvalidRecurrence = fmt.Errorf(errInvalidRecurrenceMessage)

	// ErrInvalidDayOfWeek is returned when a day of the week is not valid.
	ErrInvalidDayOfWeek = fmt.Errorf(errInvalidDayOfWeekMessage)

	// ErrNoConflictFound is returned when no conflict is found for the specified date.
	ErrNoConflictFound = fmt.Errorf(errNoConflictFoundMessage)

	// ErrNoRecurrenceFound is returned when no recurrence is found for the specified day.
	ErrNoRecurrenceFound = fmt.Errorf(errNoRecurrenceFoundMessage)

	// ErrInvalidEmail is returned when an email format is invalid.
	ErrInvalidEmail = fmt.Errorf(errInvalidEmailMessage)

	// ErrInvalidReminder is returned when a reminder is not valid.
	ErrInvalidReminder = fmt.Errorf(errInvalidReminderMessage)

	// ErrInvalidJournal is returned when a journal entry is not valid.
	ErrInvalidJournal = fmt.Errorf(errInvalidJournalMessage)

	// ErrInvalidTodo is returned when a todo component is not valid.
	ErrInvalidTodo = fmt.Errorf(errInvalidTodoMessage)

	// ErrInvalidCalendar is returned when a calendar is not valid.
	ErrInvalidCalendar = fmt.Errorf(errInvalidCalendarMessage)

	// ErrEventUIDsNotUnique is returned when event UIDs are not unique.
	ErrEventUIDsNotUnique = fmt.Errorf(errEventUIDsNotUniqueMessage)

	// ErrImageNotUrlOrBase64 is returned when an image is neither a URL nor base64 encoded data.
	ErrImageNotUrlOrBase64 = fmt.Errorf(errImageNotUrlOrBase64Message)

	// ErrInvalidMimeType is returned when an image has an invalid MIME type.
	ErrInvalidMimeType = fmt.Errorf(errInvalidMimeTypeMessage)
)

Functions

func DayOfWeekFromString

func DayOfWeekFromString(day string) (time.Weekday, error)

func ErrEndTimeBeforeStartTime

func ErrEndTimeBeforeStartTime(endTime, startTime string) error

ErrEndTimeBeforeStartTime is returned when the end time is before the start time.

Types

type Calendar

type Calendar struct {
	// REQUIRED: The name of the calendar
	Name string

	// REQUIRED: The description of the calendar
	Description string

	// OPTIONAL: List of events in the calendar
	Events []Event
	// OPTIONAL: List of journals in the calendar
	Journals []Journal
	// OPTIONAL: List of todos in the calendar
	Todos []Todo
}

func Create

func Create(name, description string) *Calendar

Create initializes a new Calendar with the given name and description.

If either the name or description is an empty string, it returns nil. Otherwise, it returns a pointer to the newly created Calendar.

func (*Calendar) AddEvent

func (c *Calendar) AddEvent(e Event) error
Example
cal := Create("Example Calendar", "An example calendar")
event := Event{
	Title:     "Meeting with Bob",
	StartDate: time.Date(2024, 7, 1, 10, 0, 0, 0, time.UTC),
	EndDate:   time.Date(2024, 7, 1, 11, 0, 0, 0, time.UTC),
	Organizer: &Participant{Name: "Organizer", Email: "example@github.com"},
	TimeZone:  TimeZone(timezones.UTC),
}
err := cal.AddEvent(event)
if err != nil {
	panic(err)
}

output, err := cal.Generate()
if err != nil {
	panic(err)
}

// Remove the generated attributes for consistent output
re := regexp.MustCompile(`DTSTAMP:\d{8}T\d{6}Z\n?`)
regUid := regexp.MustCompile(`(UID:[^\n]*?)-\d+@iCal\.go`)
// Generated value at generation time, replace with fixed value
validOutput := re.ReplaceAllString(string(output), "DTSTAMP:20251114T212240Z")
// Remove the random unique int in the UID for testing
validOutput = regUid.ReplaceAllString(validOutput, "$1-<unique>@iCal.go")
// Normalize line endings for consistent output across platforms
validOutput = strings.ReplaceAll(validOutput, "\r\n", "\n")
// END the removal of generated attributes

fmt.Println(validOutput)
Output:
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//TylerChristensen100//iCal_Generator//EN
CALSCALE:GREGORIAN
METHOD:PUBLISH
BEGIN:VTIMEZONE
TZID:UTC
COMMENT:This timezone only works from 1970-01-01 to 2038-01-01.
BEGIN:STANDARD
DTSTART:19700101T000000
TZNAME:UTC
TZOFFSETFROM:+0000
TZOFFSETTO:+0000
END:STANDARD
END:VTIMEZONE
BEGIN:VEVENT
UID:Meeting_with_Bob-Monday-Monday-<unique>@iCal.go
DTSTART;TZID=UTC:20240701T100000
DTEND;TZID=UTC:20240701T110000
DTSTAMP:20251114T212240Z
SUMMARY:Meeting with Bob
ORGANIZER;CN=Organizer:mailto:example@github.com
END:VEVENT
END:VCALENDAR

func (*Calendar) AddJournal added in v0.9.4

func (c *Calendar) AddJournal(j Journal) error

func (*Calendar) AddTodo added in v0.9.4

func (c *Calendar) AddTodo(t Todo) error
Example
cal := Create("Example Calendar", "An example calendar")
todo := Todo{
	Summary:     "Finish Report",
	Description: "Complete the quarterly report.",
	Status:      InProcessStatus,
	Organizer:   Participant{Name: "Manager", Email: "manager@example.com"},
}
err := cal.AddTodo(todo)
if err != nil {
	panic(err)
}

output, err := cal.Generate()
if err != nil {
	panic(err)
}

// Remove the generated attributes for consistent output
regDTSTAMP := regexp.MustCompile(`DTSTAMP:\d{8}T\d{6}\n?`)
regUid := regexp.MustCompile(`(UID:[^\n]*?)-\d+@iCal\.go`)
// Generated value at generation time, replace with fixed value
validOutput := regDTSTAMP.ReplaceAllString(string(output), "DTSTAMP:20251114T212240Z")
validOutput = regUid.ReplaceAllString(validOutput, "$1-<unique>@iCal.go")
// Normalize line endings for consistent output across platforms
validOutput = strings.ReplaceAll(validOutput, "\r\n", "\n")
// END the removal of generated attributes

fmt.Println(validOutput)
Output:
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//TylerChristensen100//iCal_Generator//EN
CALSCALE:GREGORIAN
METHOD:PUBLISH
BEGIN:VTODO
UID:Finish_Report-<unique>@iCal.go
DTSTAMP:20251114T212240Z
SUMMARY:Finish Report
STATUS:IN-PROCESS
DESCRIPTION:Complete the quarterly report.
ORGANIZER;CN=Manager:mailto:manager@example.com
END:VTODO
END:VCALENDAR

func (*Calendar) Generate

func (c *Calendar) Generate() ([]byte, error)

Generate creates the iCal formatted string for the entire calendar.

func (*Calendar) ListConflicts

func (c *Calendar) ListConflicts() []*Event

ListConflicts returns a list of events that have scheduling conflicts with other events in the calendar.

func (*Calendar) ResolveConflicts

func (c *Calendar) ResolveConflicts(resolveFunc ResolveFunc)

ResolveConflicts checks for scheduling conflicts between events in the calendar and applies the provided resolveFunc to each pair of conflicting events.

This is for interactive conflict resolution where the user can define how to handle conflicts.

func (*Calendar) Save

func (c *Calendar) Save(path string) error

Saves the calendar to a file at the specified path.

func (*Calendar) Valid added in v0.9.4

func (c *Calendar) Valid() error

type DisplayType added in v1.0.1

type DisplayType string
const (
	DisplayBadge     DisplayType = "BADGE"
	DisplayThumbnail DisplayType = "THUMBNAIL"
	DisplayFull      DisplayType = "FULL"
)

type Event

type Event struct {
	// OPTIONAL: Unique identifier for the event
	// If not provided, it will be auto-generated
	UID string

	// REQUIRED: Title of the event
	Title string

	// REQUIRED: Description of the event
	Description string

	// OPTIONAL: Location of the event
	Location string

	// OPTIONAL: Organizer of the event
	Organizer *Participant

	// REQUIRED: Start and end date/time of the event
	StartDate time.Time

	// REQUIRED: End date/time of the event
	EndDate time.Time

	// REQUIRED: Time zone of the event
	TimeZone TimeZone

	// OPTIONAL: Recurrence rules for the event
	// If empty, the event is non-recurring
	Recurrences []Recurrences

	// OPTIONAL: List of attendees for the event
	// Should be in email format
	Attendees []Participant

	// OPTIONAL: List of reminders for the event
	Reminders []Reminder

	// OPTIONAL: Image associated with the event
	// Can be a URL or Base64 encoded string
	Image *Image

	// OPTIONAL: a URL associated with the event
	URL string
}

iCalendar VEVENT component

func (*Event) AddAttendee

func (e *Event) AddAttendee(name, email string) error

func (*Event) AddImage added in v1.0.1

func (e *Event) AddImage(img Image) error

func (*Event) AddOrganizer added in v0.9.3

func (e *Event) AddOrganizer(name, email string) error

func (*Event) AddReminder added in v0.9.4

func (e *Event) AddReminder(reminder Reminder) error

func (*Event) CancelOnDate

func (e *Event) CancelOnDate(cancelDate time.Time) error

Adds a cancellation for the event on the specified date.

If the event does not recur on that day, returns an error.

Strips out the time component of the cancelDate to only consider the date.

func (*Event) ConflictsWith

func (e *Event) ConflictsWith(other *Event) (bool, time.Time)

func (*Event) Generate

func (e *Event) Generate() (string, error)

Generate creates the iCal formatted string for the event.

func (*Event) HasRecurrences

func (e *Event) HasRecurrences() bool

func (*Event) Valid

func (e *Event) Valid() bool

type Frequency

type Frequency string

Frequency represents the frequency of recurrence for an event. It can be daily, weekly, monthly, or yearly.

const (
	DailyFrequency    Frequency = "DAILY"
	WeeklyFrequency   Frequency = "WEEKLY"
	BiWeeklyFrequency Frequency = "BIWEEKLY"
	MonthlyFrequency  Frequency = "MONTHLY"
	YearlyFrequency   Frequency = "YEARLY"
)

func (*Frequency) Valid

func (f *Frequency) Valid() bool

type Image added in v1.0.1

type Image struct {
	URL      string
	MIMEType string
	// BASE64 encoded string
	Value string
	// Optional: Defaults to DisplayBadge
	Display DisplayType
}

func ImageFromBase64 added in v1.0.1

func ImageFromBase64(mimeType string, base64 string) (*Image, error)

func ImageFromFile added in v1.0.1

func ImageFromFile(filePath string) (*Image, error)

func ImageFromURL added in v1.0.1

func ImageFromURL(url string) *Image

type Journal added in v0.9.4

type Journal struct {

	// REQUIRED: Short summary or title of the journal entry
	Summary string

	// REQUIRED: Detailed description of the journal entry
	Description string

	// OPTIONAL: Current status of the journal entry.
	//
	// Possible Values: DraftJournal, FinalJournal, CancelledJournal
	Status JournalStatus

	// OPTIONAL: Start date of the journal entry.
	StartDate *time.Time

	// OPTIONAL: Organizer's name and email
	Organizer Participant
}

iCalendar VJOURNAL component https://icalendar.org/iCalendar-RFC-5545/3-6-3-journal-component.html

type JournalStatus added in v0.9.4

type JournalStatus string
const (
	DraftJournal     JournalStatus = "DRAFT"
	FinalJournal     JournalStatus = "FINAL"
	CancelledJournal JournalStatus = "CANCELLED"
)

type Participant added in v0.9.3

type Participant struct {
	// REQUIRED: Name of the participant
	Name string

	// REQUIRED: Email of the participant
	Email string
}

func AddOrganizer added in v0.9.4

func AddOrganizer(name, email string) *Participant

type Recurrences

type Recurrences struct {

	// REQUIRED: Frequency of the recurrence
	Frequency Frequency

	// REQUIRED: Day of the week for the recurrence
	Day time.Weekday

	// REQUIRED: Start and end time for each occurrence
	StartTime time.Time

	// REQUIRED: End time for each occurrence
	EndTime time.Time

	// OPTIONAL: List of exception dates for the recurrence
	Exceptions []time.Time
}

Recurrences represents the recurrence rules for an event.

func (*Recurrences) ConflictsWith

func (r *Recurrences) ConflictsWith(other Recurrences) (bool, time.Time)

func (*Recurrences) Generate

func (r *Recurrences) Generate(startDate, endDate time.Time, timeZone TimeZone) (string, error)

func (*Recurrences) Occurrences

func (r *Recurrences) Occurrences(startDate time.Time, endDate time.Time) []time.Time

func (*Recurrences) Valid

func (r *Recurrences) Valid() bool

type Reminder added in v0.9.4

type Reminder struct {

	// REQUIRED: Description of the reminder
	Description string

	// REQUIRED: Action to be taken when the reminder is triggered
	//
	// Possible Values: DisplayReminderAction, EmailReminderAction, AudioReminderAction
	//
	// **EmailReminderAction** requires SUMMARY & ATTENDEES properties
	Action ReminderAction

	// REQUIRED: Time before the event when the reminder should trigger
	Trigger time.Duration

	// OPTIONAL: Number of times the reminder should repeat
	Repeat *int

	// OPTIONAL: List of attendees to notify
	//
	// **Only for EMAIL action**
	Attendees []Participant
}

iCalendar VALARM component

https://icalendar.org/iCalendar-RFC-5545/3-6-6-alarm-component.html

func (*Reminder) String added in v0.9.4

func (r *Reminder) String() string

Pretty print a user friendly representation of the reminder

type ReminderAction added in v0.9.4

type ReminderAction string

The Action to be taken when the reminder is triggered

const (
	DisplayReminderAction ReminderAction = "DISPLAY"

	// Requires SUMMARY & ATTENDEES properties
	EmailReminderAction ReminderAction = "EMAIL"

	AudioReminderAction ReminderAction = "AUDIO"
)

type ResolveFunc

type ResolveFunc func(event1, event2 *Event, conflictingDay time.Time)

Function to resolve conflicts between two events.

event1 and event2 are the conflicting events, conflictingDay is the day on which the conflict occurs.

Used for more interactive conflict resolution.

 func exampleResolveFunc(event1, event2 *Event, conflictingDay time.Time) {
	if event1.HasRecurrences() && !event2.HasRecurrences() {
		event1.AddException(conflictingDay)
	}
 }

type TimeZone

type TimeZone timezones.TZID

TimeZone represents the time zone for an event. It includes all valid time zones defined by the IANA Time Zone Database. All TimeZones are copied from https://github.com/Tylerchristensen100/iCal_VTIMEZONE

iCalendar VTIMEZONE component

func (*TimeZone) ID added in v0.9.3

func (tz *TimeZone) ID() string

Return the ID of the TimeZone

E.g., "America/New_York"

type Todo added in v0.9.4

type Todo struct {

	// REQUIRED: Short summary or title of the To-Do
	Summary string

	// OPTIONAL: Detailed description of the To-Do
	Description string

	// OPTIONAL: Date/Time the To-Do is due.
	Due *time.Time

	// OPTIONAL: Date/Time the To-Do was actually completed.
	Completed *time.Time

	// OPTIONAL: Current status of the To-Do.
	//
	// Possible Values: NeedsActionStatus, CompletedStatus, InProcessStatus, CancelledStatus
	Status TodoStatus

	// OPTIONAL: Priority (0-9).
	Priority *int

	// OPTIONAL: Percentage of the To-Do completed (0-100).
	PercentComplete *int

	// OPTIONAL: Start date/time of the To-Do.
	StartDate *time.Time

	// OPTIONAL: Reminders
	Reminders []Reminder

	// OPTIONAL: Organizer's name and email
	Organizer Participant

	// OPTIONAL: Recurrence rules for the To-Do
	Recurrence *Recurrences
}

iCalendar VTODO component https://icalendar.org/iCalendar-RFC-5545/3-6-2-to-do-component.html

type TodoStatus added in v0.9.4

type TodoStatus string
const (
	NeedsActionStatus TodoStatus = "NEEDS-ACTION"
	CompletedStatus   TodoStatus = "COMPLETED"
	InProcessStatus   TodoStatus = "IN-PROCESS"
	CancelledStatus   TodoStatus = "CANCELLED"
)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL