Clicky

Windows VMs on XenServer Mysteriously Jumping a Day Ahead

Share on:

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?

 11 /******************************************************************************
 22  * time.c
 33  * 
 44  * This program is free software; you can redistribute it and/or modify
 55  * it under the terms of the GNU General Public License as published by
 66  * the Free Software Foundation; either version 2 of the License, or
 77  * (at your option) any later version.
 88  * 
 99  * This program is distributed in the hope that it will be useful,
1010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1111  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1212  * GNU General Public License for more details.
1313  * 
1414  * You should have received a copy of the GNU General Public License
1515  * along with this program; if not, write to the Free Software
1616  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
1717  */
1818 
1919 #include <xen/config.h>
2020 #include <xen/time.h>
2121 
2222 /* Nonzero if YEAR is a leap year (every 4 years,
2323    except every 100th isn't, and every 400th is).  */
2424 #define __isleap(year) \
2525   ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
2626 
2727 /* How many days are in each month.  */
2828 const unsigned short int __mon_lengths[2][12] = {
2929     /* Normal years.  */
3030     {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
3131     /* Leap years.  */
3232     {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
3333 };
3434 
3535 #define SECS_PER_HOUR (60 * 60)
3636 #define SECS_PER_DAY  (SECS_PER_HOUR * 24)
3737 
3838 struct tm gmtime(unsigned long t)
3939 {
4040     struct tm tbuf;
4141     long days, rem;
4242     int y;
4343     const unsigned short int *ip;
4444 
4545     y = 1970;
4646 #ifdef __x86_64__
4747     /* Allow the concept of time before 1970.  64-bit only; for 32-bit
4848      * time after 2038 seems more important than time before 1970. */
4949     while ( t & (1UL<<39) )
5050     {
5151         y -= 400;
5252         t += ((unsigned long)(365 * 303 + 366 * 97)) * SECS_PER_DAY;
5353     }
5454     t &= (1UL << 40) - 1;
5555 #endif
5656 
5757     days = t / SECS_PER_DAY;
5858     rem = t % SECS_PER_DAY;
5959 
6060     tbuf.tm_hour = rem / SECS_PER_HOUR;
6161     rem %= SECS_PER_HOUR;
6262     tbuf.tm_min = rem / 60;
6363     tbuf.tm_sec = rem % 60;
6464     /* January 1, 1970 was a Thursday.  */
6565     tbuf.tm_wday = (4 + days) % 7;
6666     if ( tbuf.tm_wday < 0 )
6767         tbuf.tm_wday += 7;
6868     while ( days >= (rem = __isleap(y) ? 366 : 365) )
6969     {
7070         ++y;
7171         days -= rem;
7272     }
7373     while ( days < 0 )
7474     {
7575         --y;
7676         days += __isleap(y) ? 366 : 365;
7777     }
7878     tbuf.tm_year = y - 1900;
7979     tbuf.tm_yday = days;
8080     ip = (const unsigned short int *)__mon_lengths[__isleap(y)];
8181     for ( y = 0; days >= ip[y]; ++y )
8282         days -= ip[y];
8383     tbuf.tm_mon = y;
8484     tbuf.tm_mday = days + 1;
8585     tbuf.tm_isdst = -1;
8686 
8787     return tbuf;
8888 }