Outlook issue: Birthday of contacts is one day too early/late / some ideas

Copper Contributor
*************************************************************
Outlook issue: Birthday of contacts is one day too early/late
Author: Daniel Warner
*************************************************************
************************************
Issue description (date: 2020-03-26)
************************************
The problem occurs in the following situations on an up-to-date Windows 10 Pro machine with an up-to-date Outlook 365 (2019) Desktop App as part of an Office 365 Home Premium subscription:
1) In Outlook 2007: Adding a new contact with a birthday to the Exchange account in Outlook 2007 and syncing with outlook.live.com
2) In Outlook 365: Copying an existing contact with a birthday from an Outlook 2007 PST file into the contacts of the Exchange account in Outlook 365 (2019) and syncing with outlook.live.com
After synchronization with outlook.live.com the birthday of the contact is one day too early on OWA. Time zones are identical on the Windows 10 Desktop and the OWA.
**************
Probable cause
**************
Issue 1.18 (Wrong Birthday date in iOS Contact (- 1 Day)) on the following website served as my first starting point:
https://support.microsoft.com/en-us/help/2563324/current-issues-with-exchange-activesync-and-third-party-device
***
When creating a contact with an anniversary date in Outlook, the information will be added in several MAPI properties such as :
PR_BIRTHDAY - This MAPI property is dependent on the Time Zone of the device from which the contact is created.
This property is one of the properties that provides identification and access information about a contact. These properties are defined by the user and the user's organization.
PidlidBirthdayLocal - The PidLidBirthdayLocal property specifies the birthday of the contact, at 0:00 in the client's local time zone. It is saved without any time zone conversions.
The device shows the Birthdate as sent by the server without modification. Correct conversion of MAPI properties into EAS must be handled by the server. The EAS protocol specification mentions explicitly that the time should be ignored to prevent shifts in Birthday dates when other time zones are used.
The Birthday element specifies the birth date of the contact. It is defined as an element in the Contacts namespace and is used in ActiveSync command requests and responses.
The value of this element is a dateTime data type in Coordinated Universal Time (UTC) format. The time portion of the dateTime value might be 11:59 and SHOULD be ignored, so that synchronizing between different time zones does not change the date.
The behavior observed might occur in situations where the time zone is different or greater than GMT and the application consulting this property does not have an appropriate configuration to calculate and add the time zone difference.
***
PidTagBirthday Property (PR_BIRTHDAY)
Sources:
https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxocntc/c03c3635-6747-4b77-b2a5-565d1b017256
https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxprops/fa67c222-22a1-4aeb-8532-b6a92f8aeca1
https://interoperability.blob.core.windows.net/files/MS-OXPROPS/%5BMS-OXPROPS%5D.pdf
The PidTagBirthday property ([MS-OXPROPS] section 2.612) specifies the birthday of the contact, at 11:59 in Coordinated Universal Time (UTC).
PidLidBirthdayLocal Property
Sources:
https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxocntc/2864e0f8-8a0e-4ca8-ae9a-74955b73bb7d
https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxprops/e9be69c8-f39c-41b0-b61f-f6baf363abf5
https://interoperability.blob.core.windows.net/files/MS-OXPROPS/%5BMS-OXPROPS%5D.pdf
The PidLidBirthdayLocal property ([MS-OXPROPS] section 2.45) specifies the birthday of the contact, at 0:00 in the client's local time zone. It is saved without any time zone conversions.
***
A check of the two mentioned properties in contacts showed that Outlook 2007 behaves differently when saving the birthday than Outlook 365 (2019) and the current OWA, namely only saves the birthday in the PidTagBirthday Property whereas the PidLidBirthdayLocal Property is Null. Since the specification seems to be different in the meantime (see above) the properties are interpreted differently/wrongly by the newer applications.
The following VBA macro shows the (birthday related) properties for the contacts in IPM.Contact:
***
Public Sub ShowDates()
Dim bdate, lbdate As Variant
Dim itemContact As ContactItem
Dim propertyAccessor As Outlook.propertyAccessor

Set ns = Application.GetNamespace("MAPI")
Set foldContact = ns.GetDefaultFolder(olFolderContacts)
Set colItems = foldContact.Items.Restrict("[MessageClass]='IPM.Contact'")

For Each itemContact In colItems
Set propertyAccessor = itemContact.propertyAccessor
bdate = propertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x3a420040") ' PR_BIRTHDAY / PidTagBirthday property
lbdate = propertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/id/{00062004-0000-0000-C000-000000000046}/80de0040") ' PidLidBirthdayLocal Property
Debug.Print itemContact.FullName, bdate, lbdate, itemContact.Birthday
Next
End Sub
***
*****************
Solution approach
*****************
A somewhat unsatisfactory, because time-consuming first remedy can be to open each contact before or after the migration (it doesn't matter whether this is done in Outlook 365 or in OWA), to store the (correct) birthday and to save the contact. Then the birthday (+time) is stored according to the current specification in both aforementioned properties and will be displayed correctly on the desktop as well as in the OWA after synchronization.
The following VBA macro automates this process for all birthdays and all anniversaries in IPM.Contact:
***
Public Sub UpdateDates()
Dim bdate, annivdate As Date
Dim itemContact As ContactItem

Set ns = Application.GetNamespace("MAPI")
Set foldContact = ns.GetDefaultFolder(olFolderContacts)
Set colItems = foldContact.Items.Restrict("[MessageClass]='IPM.Contact'")

For Each itemContact In colItems
bdate = itemContact.Birthday ' save birthday in auxiliary variable
annivdate = itemContact.Anniversary ' save anniversary date in auxiliary variable

Debug.Print "PRE", itemContact.FullName, bdate, annivdate

With itemContact
.Birthday = #1/1/4501# ' set birthday to "None"
.Anniversary = #1/1/4501# ' set anniversary date to "None"
.Save
End With

With itemContact
.Birthday = bdate
.Anniversary = annivdate
.Save
End With

Debug.Print "POST", itemContact.FullName, itemContact.Birthday, itemContact.Anniversary
Next
End Sub
***
1 Reply
Could it be that 2020 was a leap year?