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

Address Book says Mar 23, iCal says Mar 21

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:

Address Book says Mar 23, iCal says June 23

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:

  1. #!/usr/bin/python
  2. """
  3. Fix negative birthday years in Address Book.
  4. This work is hereby released into the Public Domain.
  5. """
  6. import AddressBook
  7. import AppKit
  8.  
  9. def personName(person):
  10. return "%s %s" % (
  11. person.valueForProperty_(AddressBook.kABFirstNameProperty),
  12. person.valueForProperty_(AddressBook.kABLastNameProperty)
  13. )
  14.  
  15. def formatDate(date):
  16. return date.descriptionWithCalendarFormat_("%Y-%m-%d")
  17.  
  18. def fixBirthday(birthday):
  19. year = int(birthday.descriptionWithCalendarFormat_("%Y"))
  20. if year < 0:
  21. return birthday.dateByAddingYears_months_days_hours_minutes_seconds_(
  22. -year * 2, 0, 0, 0, 0, 0)
  23. else:
  24. return None
  25.  
  26. def fixPersonBirthday(person):
  27. birthdayProp = AddressBook.kABBirthdayProperty
  28.  
  29. birthday = person.valueForProperty_(birthdayProp)
  30. if birthday == None: return
  31.  
  32. fixedBirthday = fixBirthday(birthday)
  33. if fixedBirthday != None:
  34. print "Fixing up %s: %s -> %s" % (
  35. personName(person),
  36. formatDate(birthday),
  37. formatDate(fixedBirthday)
  38. )
  39. person.setValue_forProperty_(fixedBirthday, birthdayProp)
  40.  
  41. book = AddressBook.ABAddressBook.sharedAddressBook()
  42.  
  43. for person in book.people():
  44. fixPersonBirthday(person)
  45.  
  46. book.save()

  1. 10.5.1, MacBook Pro Core 2 Duo 

  2. Names have been changed to protect the innocent. 

  3. 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… 

  4. Anno Domini, not Anno Antidomini 

Tags: , ,

7 Responses to “Wrong Dates in iCal Birthday Calendar”

  1. frodo says:

    Matthew, would you mind sharing how would someone like me, who is unfamiliar with Python, go about executing this script on Leopard?

    Thanks,

    Frodo

  2. Matthew says:

    frodo:

    1. Copy the script to your clipboard
    2. Open TextEdit (in your Applications folder)
    3. Select Format > Make Plain Text from the menu bar
    4. Paste in the script
    5. Save the script to your home directory, naming it fixBirthdays . Uncheck If no extension is provided, use ".txt" in the save dialog.
    6. Open Terminal (in the Utilities folder, under Applications)
    7. Type python fixBirthdays and hit return.

    That should do it.

  3. frodo says:

    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

  4. Stephen says:

    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. =)

  5. Semi says:

    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!

  6. Semi says:

    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.

  7. Samuel says:

    Thanks! It works!

Leave a Reply

Use Markdown, a wiki-like syntax, to write your comment. Basic HTML tags will also work. For source code with syntax hilighting and line numbers, wrap the code in <pre lang="language" lineno="1">...</pre>

Show Markdown help.

Write Markdown text as if you were writing a plain-text email. Some examples:

  • Paragraphs: Blank lines between blocks of text
  • Links: [link text](http://url.example.com/) or [link text][ref]
  • Bold and italic: *Single* and **double** asterisks respectively
  • Lists: List items start with * or 1.
  • Quoting: Like email, quoted lines start with >
The rise of the [hamburger](http://hamburger.example.com/)
as a form of *currency* can be **attributed** to several
aspects of [Akkadian][akad] [civilization][civ].

   [akad]: http://icanhasgilgamesh.example.com/
   [civ]: http://uruk.example.com/

Yes, the most delicious hamburger of all is not brown, but
green. The green of money. Denominations of hamburger
(and current value in USD:)

* 1/4-pounder ($3.79)
* Cuneiform, or "Cuney" ($8.00)

Problems with the currency:

1. Deflation due to hunger
2. Fraud (soy fillers)
3. Hamburgers not invented yet

As Dr. Tabi said:
> Wallets became foetid and repulsive.
> This was quite the boon for the influential
> Guild of Wallet-Washers.

The rise of the hamburger as a form of currency can be attributed to several aspects of Akkadian civilization.

Yes, the most delicious hamburger of all is not brown, but green. The green of money. Denominations of hamburger (and current value in USD:)

  • ¼-pounder ($3.79)
  • Cuneiform, or “Cuney” ($8.00)

Problems with the currency:

  1. Deflation due to hunger
  2. Fraud (soy fillers)
  3. Hamburgers not invented yet

As Dr. Tabi said:

Wallets became foetid and repulsive. This was quite the boon for the influential Guild of Wallet-Washers.

Comments will be sent to the moderation queue.

Subscribe without commenting