gcal-to-org
One-way sync from Google Calendar to org-mode file.
Features
- One-way sync: Google Calendar → org-mode only (read-only, no modifications to Google Calendar)
- Separate file: Generates
~/desktop/org/calendar.orgwhich is easily regenerated - Automatic cleanup: Keeps only events from 2 weeks ago onward (configurable)
- Works with gcalcli: Uses your existing gcalcli setup and authentication
- Automatic categorization: Events are automatically categorized as “personal” or “work” based on their source calendar
- Smart filtering: Only includes events you’ve accepted or marked as “maybe” (declined events are excluded)
Usage
Basic usage
# Sync default calendars to default file
gcal-to-org
# This generates ~/desktop/org/calendar.org with:
# - Events from 2 weeks ago to 4 weeks forward
# - Personal and work calendars
Custom configuration
# Sync specific calendars
gcal-to-org --calendar "My Calendar" --calendar "Work Calendar"
# Custom date range
gcal-to-org --days-back 7 --days-forward 14
# Custom output file
gcal-to-org --output ~/my-calendar.org
# Combine options
gcal-to-org \
--calendar "Vincent Demeester (personal)" \
--days-back 14 \
--days-forward 28 \
--output ~/desktop/org/calendar.org
Configuration
Default settings
- Calendars:
- Vincent Demeester (personal)
- vdemeest@redhat.com
- Output file:
~/desktop/org/calendar.org - Date range: 14 days back to 28 days forward
Changing defaults
Edit the constants in main.go:
var (
defaultCalendars = []string{
"Vincent Demeester (personal)",
"vdemeest@redhat.com",
}
)
const (
defaultDaysBack = 14
defaultDaysForward = 28
)
Automation
NixOS/home-manager (recommended)
If using NixOS with home-manager, add to your home configuration:
{
imports = [
../../home/common/services/gcal-to-org.nix
];
services.gcal-to-org = {
enable = true;
interval = "hourly"; # Can be "hourly", "daily", or any systemd timer format
};
}
This will automatically:
- Create a systemd service to run gcal-to-org
- Create a systemd timer to run it on your chosen schedule
- Start the timer on login
Check timer status:
systemctl --user list-timers gcal-to-org
systemctl --user status gcal-to-org.service
Manual systemd timer
Create ~/.config/systemd/user/gcal-sync.service:
[Unit]
Description=Sync Google Calendar to org-mode
[Service]
Type=oneshot
ExecStart=%h/.nix-profile/bin/gcal-to-org
Create ~/.config/systemd/user/gcal-sync.timer:
[Unit]
Description=Sync Google Calendar every hour
[Timer]
OnCalendar=hourly
Persistent=true
[Install]
WantedBy=timers.target
Enable and start:
systemctl --user enable --now gcal-sync.timer
systemctl --user status gcal-sync.timer
Cron
# Add to crontab -e
0 * * * * /path/to/gcal-to-org
Output format
The generated org file includes:
#+TITLE: Google Calendar Sync
#+DESCRIPTION: Auto-generated from Google Calendar (one-way sync)
#+STARTUP: overview
#+CATEGORY: calendar
#+FILETAGS: :calendar:gcal:
This file is automatically generated from Google Calendar.
DO NOT EDIT MANUALLY - changes will be overwritten.
* 2026-01-14 Wednesday
* Meeting with team
:PROPERTIES:
:CATEGORY: work
:END:
<2026-01-14 Wed 09:00-10:00>
* All-day event
:PROPERTIES:
:CATEGORY: personal
:END:
<2026-01-14 Wed>
* Multi-day conference
:PROPERTIES:
:CATEGORY: work
:END:
<2026-01-14 Wed>--<2026-01-16 Fri>
Event Categorization
Events are automatically categorized based on their source calendar:
- Work: Calendars containing “redhat”, “work”, or “vdemeest@redhat” are tagged with
CATEGORY: work - Personal: All other calendars are tagged with
CATEGORY: personal
This allows you to:
- Filter events by category in org-agenda views
- Color-code events based on category
- Create separate agenda views for personal vs work events
Building
# Build with Nix
nix build .#gcal-to-org
# Or build with Go
cd tools/gcal-to-org
go build
Troubleshooting
Authentication errors
Make sure gcalcli is set up and authenticated:
gcalcli list
If this fails, run:
gcalcli init
Missing calendars
List available calendars:
gcalcli list
Then use the exact calendar names with --calendar flag.
Parsing errors
If you encounter TSV parsing errors, this is likely due to special characters in event titles. The tool uses LazyQuotes mode to handle most cases, but if issues persist, try filtering specific calendars with --calendar.
Integration with org-mode
To use the calendar in your agenda views, add to your Emacs config:
;; Add calendar.org to agenda files
(add-to-list 'org-agenda-files "~/desktop/org/calendar.org")
;; Optional: exclude from refile targets (read-only)
(setq org-refile-targets
'((org-agenda-files :maxlevel . 3)))
;; Optional: Color-code categories in agenda
(setq org-agenda-category-icon-alist
'(("work" "💼" nil nil :ascent center)
("personal" "🏠" nil nil :ascent center)))
;; Optional: Create custom agenda views by category
(setq org-agenda-custom-commands
'(("w" "Work events" agenda ""
((org-agenda-category-filter-preset '("+work"))))
("p" "Personal events" agenda ""
((org-agenda-category-filter-preset '("+personal"))))))
License
Apache 2.0