Answered by:
The recurrence logic for yearly recurrence appears to be incorrect
Question

p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 9.0px Verdana}
MSOXOCAL section 2.2.1.44.1 RecurrencePattern structure, gives the following advice on computing FirstDateTime from StartDate.
1.Find the first day of the month of StartDate.
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 9.0px Verdana}2.Determine MinimumDate. For Gregorian calendars, this is midnight, January 1, 1601.For nonGregorian calendars, this is the first day of the calendar's year that falls in the Gregorian year of 1601. For example, if the CalendarType is CAL_HEBREW, the first day of that calendar's year that falls in the Gregorian year of 1601 is 1/1/5362, which is the Gregorian date of 9/27/1601.
3.Calculate the number of calendar months between midnight of the days calculated in step 1 and step 2.
4.Take that value modulo Period. 5.Add that number of months to the MinimumDate, as determined in
step 2.
6.Calculate the number of minutes between midnight that day and midnight, January 1, 1601.
However, when I try to replicate this logic myself, everything goes wrong.
Sample code in Java (I removed generalisation to all calendar systems, which is handled by our APIs, and hardcoded it to Hebrew): https://gist.github.com/d44052736cee7f741a76
I have checked all the results and they appear to be correct for each step, but the end result is not what is stored in the FirstDateTime value for the calendar entry, which is 727200 (0xB18A0).
Thinking laterally, the instructions say "take that value modulo Period", which to me sounds like it might be making the assumption that there are always 12 months in the year. This is obviously not the case, so are the steps wrong?
Answers

Hi, here is the correct way to calculate the FirstDateTime value when the calendar type is CAL_HEBREW.
 Find the first day of the month of StartDate.
 Original day = 5/22/2011
 First day of month of StartDate = 5/1/2011
 Determine MinimumDate.
 The document provides the answer for this one, its 9/27/1601.
 Calculate the number of calendar months between midnight of the days calculated in step 1 and step 2.
 How many years have passed? 2011  1601 = 410.
 Multiplied by 12 months in a year. 410 * 12 = 4920.
 Plus how many months until the month the appointment occurs in this year which is the (month number  1). 4920 + (5  1) = 4924
 Subtract how many months passed in 1601 before our start date which is the (month number  1). 4924  (9  1) = 4916
 Take that value modulo Period.
 4916 MOD 12 = 8
 Add that number of months to the MinimumDate, as determined in step 2.
 This is the tricky part. You can't actually just add 8 to the month value, that would give you 5/27/1602. The correct way to do this is to take the value from step 4 and multiply that by the number of days in a lunar month (29.53). Drop the fractional value and you get 236 days. Add 236 days to 9/27/1601 and you get the date 5/21/1602.
 Calculate the number of minutes between midnight that day and midnight, January 1, 1601.
 There are 505 days between Jan 1, 1601 and May 21, 1602
 505 * 1440 = 727200
Please let me know if you have any questions.
Josh Curry (jcurry)  Escalation Engineer  USCSS DSC Protocols Team Marked as answer by JCurryMicrosoft employee, Moderator Friday, July 15, 2011 2:29 PM
 Find the first day of the month of StartDate.
All replies


Hi AlexVas0765574, I am the engineer who will be working with you on this issue. I am currently researching the problem and will provide you with an update soon. Thank you for your patience.
Josh Curry (jcurry)  Escalation Engineer  USCSS DSC Protocols Team 
Hi AlexVas0765574, I was able to follow the process in MSOXOCAL section 2.2.1.44.1 and get the expected value in FirstDateTime. Here is how I did it…
I created a yearly recurring appointment that starts on June 1, 2011. Using MFCMAPI, I was able to get the values of the PidLidAppointmentRecur structure.
ReaderVersion: 0x3004
WriterVersion: 0x3004
RecurFrequency: 0x200D = IDC_RCEV_PAT_ORB_YEARLY
PatternType: 0x0002 = rptMonth
CalendarType: 0x0000 = CAL_DEFAULT
FirstDateTime: 0x00035160 = 217440
Period: 0x0000000C = 12
SlidingFlag: 0x00000000
PatternTypeSpecific.MonthRecurrencePattern: 0x00000001 = 1
EndType: 0x00002022 = IDC_RCEV_PAT_ERB_AFTERNOCCUR
OccurrenceCount: 0x0000000A = 10
FirstDOW: 0x00000000 = Sunday
DeletedInstanceCount: 0x00000000 = 0
ModifiedInstanceCount: 0x00000000 = 0
StartDate: 0x0CDDB380 = 215856000 = 12:00:00.000 AM 6/1/2011
EndDate: 0x0D25F280 = 220590720 = 12:00:00.000 AM 6/1/2020
Appointment Recurrence Pattern:
ReaderVersion2: 0x00003006
WriterVersion2: 0x00003009
StartTimeOffset: 0x000001E0 = 480 = 08:00:00.000 AM 1/1/1601
EndTimeOffset: 0x000001FE = 510 = 08:30:00.000 AM 1/1/1601
ExceptionCount: 0x0000
ReservedBlock1Size: 0x00000000
ReservedBlock2Size: 0x00000000
Then I followed the process:
1. Find the first day of the month of StartDate.
a. No need in my case since it's June 1, 2011.
2. Determine MinimumDate.
a. In our case this will be January 1, 1601.
3. Calculate the number of calendar months between midnight of the days calculated in step 1 and step 2.
a. How many years have passed? 2011  1601 = 410.
b. Multiplied by 12 months in a year. 410 * 12 = 4,920.
c. Plus how many months until the month the appointment occurs in this year which is the month number  1. 4920 + (6  1) = 4925.
4. Take that value modulo Period.
a. 4925 MOD 12 = 5
5. Add that number of months to the MinimumDate, as determined in step 2.
a. Jan 1, 1601 + 5 months = June 1, 1601.
1. Calculate the number of minutes between midnight that day and midnight, January 1, 1601.
a. There are 151 days between Jan 1 and June 1, 1601.
b. Multiply that by 1440 minutes in a day and you get 217440, which is the value in FirstDateTime.
Please let me know if this helps.
Josh Curry (jcurry)  Escalation Engineer  USCSS DSC Protocols Team Proposed as answer by JCurryMicrosoft employee, Moderator Tuesday, May 31, 2011 7:09 PM

Yes, when the calendar is CAL_GREGORIAN (as is the case for CAL_DEFAULT), the computations work for me too.
Could you try doing the same sequence for CAL_HEBREW_LUNAR? This is the situation where doing a "mod 12" is suspicious, because there aren't always 12 months in the year.

e.g. StartDate = 26/5/2011 = 22 Iyar 5571 in the Hebrew Lunar calendar. This is 22/9/5571 under MS's scheme where the first month of the year is not the first month of the year of the actual Hebrew calendar according to Wikipedia, but since this is MS's docs, I will assume that it is month 9.
1. Find the first day of the month of StartDate.
a. Assuming that this means in the local calendar, 1/9/5571
2. Determine MinimumDate.
a. Since we're not using the Gregorian calendar this would usually require computation, but the docs helpfully give the answer for the Hebrew calendar, which is 1/1/5362.
3. Calculate the number of calendar months between midnight of the days calculated in step 1 and step 2.
a. Because months in the Hebrew calendar are not all 12 months, I can't perform the simple multiplication by 12 trick above, and have to resort to using a real calendar. Because the number of years is large enough to be impractical, I decided to write code for it which printed out each year for sanity's sake. The end result: 5066.
4. Take that value modulo Period.
a. 5066 MOD 12 = 2
5. Add that number of months to the MinimumDate, as determined in step 2.
a. 1/1/5362 + 2 months = 1/3/5362 (1 Kislev 5362)
1. Calculate the number of minutes between midnight that day and midnight, January 1, 1601.
a. There are 28339200000 milliseconds according to the calendar class.
b. Divide that by (60*1000) and you get 472320. But the value I see in the actual data is 727200.

Working backwards:
6. The result is supposed to be 727200.
5a. 727200 is 505 days.
5b. 505 days from MinimumDate gives me 3 Adar 5363 (Gregorian: 14 February 1603.)
4. Months between MinimumDate and 2 Adar 5363: This is where it gets messy. There is no round number of months. This should be impossible, meaning that the documentation is either wrong, or Outlook has generated the wrong value for FirstDateTime for my appointment.
4a. But let's give it the benefit of the doubt  17 months from MinimumDate gives 1 Adar 5363. So perhaps this was supposed to be 17 months.
3. It is impossible to generate any value here, which will be 17 after performing a modulo 12.
Conclusion: The value 727200 found in my appointment is impossible to obtain by following the outlined steps. Therefore the steps do not represent what Outlook is actually performing.


Any progress on this?
I tried doing the month counting at step 3 using the Gregorian calendar. It leaves me with a partial month leftover which, if I count this partial month, gives the right result... but it doesn't feel like this is the right solution either.



Hi, here is the correct way to calculate the FirstDateTime value when the calendar type is CAL_HEBREW.
 Find the first day of the month of StartDate.
 Original day = 5/22/2011
 First day of month of StartDate = 5/1/2011
 Determine MinimumDate.
 The document provides the answer for this one, its 9/27/1601.
 Calculate the number of calendar months between midnight of the days calculated in step 1 and step 2.
 How many years have passed? 2011  1601 = 410.
 Multiplied by 12 months in a year. 410 * 12 = 4920.
 Plus how many months until the month the appointment occurs in this year which is the (month number  1). 4920 + (5  1) = 4924
 Subtract how many months passed in 1601 before our start date which is the (month number  1). 4924  (9  1) = 4916
 Take that value modulo Period.
 4916 MOD 12 = 8
 Add that number of months to the MinimumDate, as determined in step 2.
 This is the tricky part. You can't actually just add 8 to the month value, that would give you 5/27/1602. The correct way to do this is to take the value from step 4 and multiply that by the number of days in a lunar month (29.53). Drop the fractional value and you get 236 days. Add 236 days to 9/27/1601 and you get the date 5/21/1602.
 Calculate the number of minutes between midnight that day and midnight, January 1, 1601.
 There are 505 days between Jan 1, 1601 and May 21, 1602
 505 * 1440 = 727200
Please let me know if you have any questions.
Josh Curry (jcurry)  Escalation Engineer  USCSS DSC Protocols Team Marked as answer by JCurryMicrosoft employee, Moderator Friday, July 15, 2011 2:29 PM
 Find the first day of the month of StartDate.
