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?
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 }