From 33d158d76651732dad7c121682208beb993b37e5 Mon Sep 17 00:00:00 2001 From: redxef Date: Wed, 30 Nov 2022 13:19:03 +0100 Subject: [PATCH] Init commit. --- .gitignore | 2 ++ README.md | 7 +++++++ export.py | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100755 export.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1f4917d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.vcf +*.ics diff --git a/README.md b/README.md new file mode 100644 index 0000000..aaf1a34 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# vCard to iCalendar Birthday export + +Export birthdays from contacts to a iCalendar. + +## Usage + +`./export.py < contacts.vcf > birthdays.ics` diff --git a/export.py b/export.py new file mode 100755 index 0000000..ca90857 --- /dev/null +++ b/export.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import re +import sys +import datetime +import vobject + +card = vobject.vCard() +cal = vobject.iCalendar() + +R_FMT = ( + (re.compile('^[0-9]{8}$'), '%Y%m%d'), + (re.compile('^--[0-9]{4}$'), '--%m%d'), + (re.compile('^[0-9]{4}-[0-9]{2}-[0-9]{2}$'), '%Y-%m-%d') +) + +def vobj_str2date(content_line): + v = content_line.value + for r, f in R_FMT: + if r.match(v): + return datetime.datetime.strptime(v, f) + raise ValueError(f'cannot parse specified string {v}') + + +for o in vobject.readComponents(sys.stdin): + if 'bday' not in o.contents: + continue + #print(o.fn, o.bday, type(o.bday), o.bday.value) + name = o.fn.value + date = vobj_str2date(o.bday) + if date.year <= 1900: + date = date.replace(year=datetime.datetime.now().year) + date_end = date + datetime.timedelta(days=1) + cal.add('vevent') + cal.vevent_list[-1].add('summary').value = o.fn.value + cal.vevent_list[-1].add('dtstart').value = date.date() + cal.vevent_list[-1].add('dtend').value = date_end.date() + cal.vevent_list[-1].add('rrule').value = 'FREQ=YEARLY' +print(cal.serialize()) +