Windows VMs on XenServer Mysteriously Jumping a Day Ahead

It came to my attention today that there is a seemingly new issue with XenServer that is causing the time in Windows VMs to become offset by one day. My first thought was that this could be related to 2012 being a leap year, so I pulled up the source code for the Xen 4.1 hypervisor that handles leap years from http://fossies.org/unix/misc/xen-4.1.2.tar.gz:a/xen-4.1.2/xen/common/time.c.

I ran through the code with a calculator for today’s date (January 18, 2012) and ended up with tbuf.tm_mday being 19 because line 84 of the code adds one day for some reason. I can see this causing a VM’s time to be offset ahead by one day, but not backward. Can anyone validate or correct my thinking on this?

1 /******************************************************************************
2  * time.c
3  * 
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18 
19 #include <xen/config.h>
20 #include <xen/time.h>
21 
22 /* Nonzero if YEAR is a leap year (every 4 years,
23    except every 100th isn't, and every 400th is).  */
24 #define __isleap(year) \
25   ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
26 
27 /* How many days are in each month.  */
28 const unsigned short int __mon_lengths[2][12] = {
29     /* Normal years.  */
30     {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
31     /* Leap years.  */
32     {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
33 };
34 
35 #define SECS_PER_HOUR (60 * 60)
36 #define SECS_PER_DAY  (SECS_PER_HOUR * 24)
37 
38 struct tm gmtime(unsigned long t)
39 {
40     struct tm tbuf;
41     long days, rem;
42     int y;
43     const unsigned short int *ip;
44 
45     y = 1970;
46 #ifdef __x86_64__
47     /* Allow the concept of time before 1970.  64-bit only; for 32-bit
48      * time after 2038 seems more important than time before 1970. */
49     while ( t & (1UL<<39) )
50     {
51         y -= 400;
52         t += ((unsigned long)(365 * 303 + 366 * 97)) * SECS_PER_DAY;
53     }
54     t &= (1UL << 40) - 1;
55 #endif
56 
57     days = t / SECS_PER_DAY;
58     rem = t % SECS_PER_DAY;
59 
60     tbuf.tm_hour = rem / SECS_PER_HOUR;
61     rem %= SECS_PER_HOUR;
62     tbuf.tm_min = rem / 60;
63     tbuf.tm_sec = rem % 60;
64     /* January 1, 1970 was a Thursday.  */
65     tbuf.tm_wday = (4 + days) % 7;
66     if ( tbuf.tm_wday < 0 )
67         tbuf.tm_wday += 7;
68     while ( days >= (rem = __isleap(y) ? 366 : 365) )
69     {
70         ++y;
71         days -= rem;
72     }
73     while ( days < 0 )
74     {
75         --y;
76         days += __isleap(y) ? 366 : 365;
77     }
78     tbuf.tm_year = y - 1900;
79     tbuf.tm_yday = days;
80     ip = (const unsigned short int *)__mon_lengths[__isleap(y)];
81     for ( y = 0; days >= ip[y]; ++y )
82         days -= ip[y];
83     tbuf.tm_mon = y;
84     tbuf.tm_mday = days + 1;
85     tbuf.tm_isdst = -1;
86 
87     return tbuf;
88 }