linuxpython-3.xawkvcf-vcardcaldav

convert vcf birthdays to caldav on linux


I have a vCard file mycontacts.vcf:

BEGIN:VCARD
N:Montana;Joe;;;
FN:Joe Montana
BDAY;value=date:1988-05-20
END:VCARD

BEGIN:VCARD
N:Smith;Joe;;;
FN:Joe Smith
BDAY;value=date:1999-07-04
END:VCARD

How can one create a CalDAV file from this from linux command line, e.g. using awk or a simple python script, such that the output is:

BEGIN:VEVENT
SUMMARY:Joe Montana
DTSTART;VALUE=DATE:19880520
DTEND;VALUE=DATE:19880521
END:VEVENT

BEGIN:VEVENT
SUMMARY:Joe Smith
DTSTART;VALUE=DATE:19990704
DTEND;VALUE=DATE:19990705
END:VEVENT

By the way, contacts were exported from an ipad using pythonista app:

import contacts
people = contacts.get_all_people()
for p in people:
  print(p.vcard.decode('utf-8'))

The resulting CalDAV is to be imported into thunderbird.


Solution

  • With any awk you can set the end date to the current date:

    $ cat vcf2dav.awk
    BEGIN { OFS=":" }
    !NF { print; next }
    {
        tag = val = $0
        sub(/[:;].*$/,"",tag)
        sub(/^[^:]+:/,"",val)
    }
    tag ~ /^(BEGIN|END)$/ { print tag, "VEVENT" }
    tag == "FN" { print "SUMMARY", val }
    tag == "BDAY" {
        gsub(/-/,"",val)
        print "DTSTART;VALUE=DATE", val
        print "DTEND;VALUE=DATE", val
    }
    

    .

    $ awk -f vcf2dav.awk mycontacts.vcf
    BEGIN:VEVENT
    SUMMARY:Joe Montana
    DTSTART;VALUE=DATE:19880520
    DTEND;VALUE=DATE:19880520
    END:VEVENT
    
    BEGIN:VEVENT
    SUMMARY:Joe Smith
    DTSTART;VALUE=DATE:19990704
    DTEND;VALUE=DATE:19990704
    END:VEVENT
    

    but if you really want it to be the day after then you need to employ time functions, e.g. with GNU awk for built in time functions:

    $ cat vcf2dav.awk
    BEGIN { OFS=":" }
    !NF { print; next }
    {
        tag = val = $0
        sub(/[:;].*$/,"",tag)
        sub(/^[^:]+:/,"",val)
    }
    tag ~ /^(BEGIN|END)$/ { print tag, "VEVENT" }
    tag == "FN" { print "SUMMARY", val }
    tag == "BDAY" {
        begDate = val
        endDate = dayAfter(begDate)
        gsub(/-/,"",begDate)
        print "DTSTART;VALUE=DATE", begDate
        print "DTEND;VALUE=DATE",   endDate
    }
    
    function dayAfter(curDate,      curSecs, nxtDate) {
        curSecs = mktime(gensub(/-/," ","g",curDate)" 0 0 0")
        nxtDate = strftime("%Y%m%d",curSecs + 24*60*60)
        return nxtDate
    }
    

    .

    $ awk -f vcf2dav.awk mycontacts.vcf
    BEGIN:VEVENT
    SUMMARY:Joe Montana
    DTSTART;VALUE=DATE:19880520
    DTEND;VALUE=DATE:19880521
    END:VEVENT
    
    BEGIN:VEVENT
    SUMMARY:Joe Smith
    DTSTART;VALUE=DATE:19990704
    DTEND;VALUE=DATE:19990705
    END:VEVENT
    

    With other awks you'd change the dayAfter() function to call UNIX date or do whatever else works in your environment to get the next day.