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 (which you can download) 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!
February 2nd, 2009 at 15:23
Thanks a lot for sharing the script! However, I’m unable to copy it - as well as having the line numbers, all the indentation gets lost and as far as I know it’s important in Python. Do you know of some workaround? If not, I’ll re-type it by hand…
February 2nd, 2009 at 22:40
Here’s a plaintext copy.
February 4th, 2009 at 08:02
Thanks a lot!
It worked, though I had to manually add a year and 17 days to the birthdays (corrected the script for future use).
March 29th, 2009 at 13:31
“ImportError: No module named AddressBook”
something wrong…
March 30th, 2009 at 17:19
Hello, I have a bug with birthday date in AdressBook application. For example, if I enter april 17th 0001 in the AdressBook, it’s read: BDAY;value=date:1-04-15 when I edit the vCard and it shows on April 15th in iCal. I have the same problem with all my vCards with birthday. I don’t know hiw to fix this bug. Pierre.
June 17th, 2009 at 21:58
i get the error
python fixBirthdays Traceback (most recent call last): File “fixBirthdays”, line 6, in import AddressBook ImportError: No module named AddressBook
any way to make it work?
August 11th, 2009 at 13:50
Wondering if tila will fix the issues I’m having, where random days are added to the birthday. Example: mine is Feb 6, but iCal adds a day to it, making it the 7th. Another friend’s is 8.02.1960, and it’s added 10 days to the entry, and now reads 8.12.60!!?
I’ve deleted the entry in address book, and re-entered data. It stays for awhile (maybe. Week?) and then today, I looke again and the date was changed back to the 12th (from the 2nd)
September 25th, 2009 at 14:42
I also discovered that iCal cannot handle dates before 1900-01-01. So I extended the fixBirthday function as follows:
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) elif year < 1900: return birthday.dateByAddingYears_months_days_hours_minutes_seconds_( 1900-year, 0, 0, 0, 0, 0) else: return NoneThis moves all birthdays before 1900-01-01 to their respective date in the year 1900. Voila, all birthday headaches with iCal fixed.
Thanks Matthew, for a) finding out about this brain-dead bug, and b) sharing your fix. Great job!
January 21st, 2010 at 06:01
I just stumbled onto this bug when I called a relative to congratulate her birthday… Geeez…
Anyway, now it should be fixed. But checking the dates it strikes me that this bug has been around since 2007 and Apple still hasn’t fixed it.