Wrong Dates in iCal Birthday Calendar
To keep track of people’s birthdays, I use Mac OS X’s1 Birthday Calendar feature of Address Book/iCal. I was going through my calendar the other day, and I noticed that a birthday which I knew was sometime in January wasn’t showing up. It was on the corresponding Address Book contact, though. I deleted the birthday from this contact and reentered it, which fixed that entry, but on the suspicion that more birthdays might be missing, I flipped through my calendar and found:2

The Address Book birthday field has the misfeature that it forces a year to be specified.3 What a rude thing for Address Book to be asking! Anyway, I’d arbitrarily picked year 14 for the year for any contacts whose birth years I didn’t know. Maybe, I thought, the Gregorian reform was throwing things off. However, changing the year to 1900 didn’t help matters, and in fact made them worse:

Turning the birthday calendar off (which wipes out iCal’s backing store for the calendar) and on didn’t help matters. A web search turned up some other people having the same problem, but the only useful solution they came up with was deleting and recreating entire contacts by hand.
I wanted to see if the raw data was wrong in Address Book’s database. Address Book uses Core Data in a way that makes the database difficult to work with at the SQLite command-line level, so instead I hacked /Developer/Examples/Python/PyObjC/AddressBook/Scripts/exportBook.py to emit the birthday field by adding ('Birthday', AddressBook.kABBirthdayProperty) to FIELD_NAMES and the following to encodeField:
elif isinstance(value, AppKit.NSCalendarDate): return value.descriptionWithCalendarFormat_("%Y-%m-%d")
It turns out that a number of entries had negative years, e.g. -1900-03-23 instead of 1900-03-23. I’m not sure how this happened, but here’s a script to fix it:
#!/usr/bin/python """ Fix negative birthday years in Address Book. This work is hereby released into the Public Domain. """ import AddressBook import AppKit def personName(person): return "%s %s" % ( person.valueForProperty_(AddressBook.kABFirstNameProperty), person.valueForProperty_(AddressBook.kABLastNameProperty) ) def formatDate(date): return date.descriptionWithCalendarFormat_("%Y-%m-%d") def fixBirthday(birthday): year = int(birthday.descriptionWithCalendarFormat_("%Y")) if year < 0: return birthday.dateByAddingYears_months_days_hours_minutes_seconds_( -year * 2, 0, 0, 0, 0, 0) else: return None def fixPersonBirthday(person): birthdayProp = AddressBook.kABBirthdayProperty birthday = person.valueForProperty_(birthdayProp) if birthday == None: return fixedBirthday = fixBirthday(birthday) if fixedBirthday != None: print "Fixing up %s: %s -> %s" % ( personName(person), formatDate(birthday), formatDate(fixedBirthday) ) person.setValue_forProperty_(fixedBirthday, birthdayProp) book = AddressBook.ABAddressBook.sharedAddressBook() for person in book.people(): fixPersonBirthday(person) book.save()
-
10.5.1, MacBook Pro Core 2 Duo ↩
-
Names have been changed to protect the innocent. ↩
-
There’s also an implementation flaw; I have my date format set to
YYYY-MM-DD, and when I try to enter a year in the field, whether or not pressing a number on the keyboard will actually result in a digit appearing in the input field appears to be random. It also behaves very weirdly if there are four digits in the field already and I press another digit. I wish I could get a video of all this, but it’s not quite worth the effort of taking a screencast and a video of my fingers on the keyboard and then splice them together… ↩ -
Anno Domini, not Anno Antidomini ↩
Tags: Address Book, code, iCal
January 15th, 2008 at 20:29
Matthew, would you mind sharing how would someone like me, who is unfamiliar with Python, go about executing this script on Leopard?
Thanks,
Frodo
January 15th, 2008 at 20:56
frodo:
TextEdit(in yourApplicationsfolder)Format > Make Plain Textfrom the menu barfixBirthdays. UncheckIf no extension is provided, use ".txt"in the save dialog.Terminal(in theUtilitiesfolder, underApplications)python fixBirthdaysand hit return.That should do it.
January 16th, 2008 at 22:25
Hey Matt
Thanks for the excellent instructions! It worked, but I still find it strange that Apple doesn’t address this issue more seriously along with the implementation flaw you described above. Anyways, thanks again!
frodo
February 3rd, 2008 at 19:38
Thanks so much! I had the exact same problem. The problem is, after I ran your script, my dates were off by 17 days. I couldn’t figure out why. Anyway, that was still a big help. =)
March 25th, 2008 at 15:27
I ran your script but all I’m getting in Address Book is a place for the initial of the month (“M” for May e.g.) and then four digits of the year. Won’t let me enter a day and says all birthdays are on the first of the month. I’m on Leopard 10.5? Thanks!
March 26th, 2008 at 15:12
I ran your script and now none of my dates in iCal will work. It won’t let me enter anything that lasts for longer than one day and it won’t allow me to put in the “day”, only the month and year - the same problem I have been having with birthdays.
September 15th, 2008 at 16:27
Thanks! It works!