time.c 28.5 KB
Newer Older
Juergen Schmied's avatar
Juergen Schmied committed
1
/*
2
 * Nt time functions.
Juergen Schmied's avatar
Juergen Schmied committed
3
 *
4
 * RtlTimeToTimeFields, RtlTimeFieldsToTime and defines are taken from ReactOS and
5 6
 * adapted to wine with special permissions of the author. This code is
 * Copyright 2002 Rex Jolliff (rex@lvcablemodem.com)
7
 *
8
 * Copyright 1999 Juergen Schmied
9
 * Copyright 2007 Dmitry Timoshkov
10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
23
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
Juergen Schmied's avatar
Juergen Schmied committed
24 25
 */

26
#include "config.h"
27
#include "wine/port.h"
28

29
#include <stdarg.h>
30
#include <stdlib.h>
31
#include <errno.h>
David Luyer's avatar
David Luyer committed
32
#include <string.h>
33
#include <limits.h>
34
#include <time.h>
35 36 37 38 39 40
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
41 42 43

#define NONAMELESSUNION
#define NONAMELESSSTRUCT
44 45
#include "ntstatus.h"
#define WIN32_NO_STATUS
46
#include "windef.h"
47
#include "winternl.h"
48
#include "wine/unicode.h"
49
#include "wine/debug.h"
50
#include "ntdll_misc.h"
51

52
WINE_DEFAULT_DEBUG_CHANNEL(ntdll);
Juergen Schmied's avatar
Juergen Schmied committed
53

54 55
static int init_tz_info(RTL_TIME_ZONE_INFORMATION *tzi);

56
static RTL_CRITICAL_SECTION TIME_tz_section;
57
static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
58
{
59
    0, 0, &TIME_tz_section,
60
    { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
61
      0, 0, { (DWORD_PTR)(__FILE__ ": TIME_tz_section") }
62
};
63
static RTL_CRITICAL_SECTION TIME_tz_section = { &critsect_debug, -1, 0, 0, 0, 0 };
64

Juergen Schmied's avatar
Juergen Schmied committed
65 66 67 68 69 70 71
#define TICKSPERSEC        10000000
#define TICKSPERMSEC       10000
#define SECSPERDAY         86400
#define SECSPERHOUR        3600
#define SECSPERMIN         60
#define MINSPERHOUR        60
#define HOURSPERDAY        24
72
#define EPOCHWEEKDAY       1  /* Jan 1, 1601 was Monday */
Juergen Schmied's avatar
Juergen Schmied committed
73 74 75 76 77
#define DAYSPERWEEK        7
#define EPOCHYEAR          1601
#define DAYSPERNORMALYEAR  365
#define DAYSPERLEAPYEAR    366
#define MONSPERYEAR        12
78 79 80
#define DAYSPERQUADRICENTENNIUM (365 * 400 + 97)
#define DAYSPERNORMALCENTURY (365 * 100 + 24)
#define DAYSPERNORMALQUADRENNIUM (365 * 4 + 1)
Juergen Schmied's avatar
Juergen Schmied committed
81

82
/* 1601 to 1970 is 369 years plus 89 leap days */
83 84
#define SECS_1601_TO_1970  ((369 * 365 + 89) * (ULONGLONG)SECSPERDAY)
#define TICKS_1601_TO_1970 (SECS_1601_TO_1970 * TICKSPERSEC)
85
/* 1601 to 1980 is 379 years plus 91 leap days */
86 87
#define SECS_1601_TO_1980  ((379 * 365 + 91) * (ULONGLONG)SECSPERDAY)
#define TICKS_1601_TO_1980 (SECS_1601_TO_1980 * TICKSPERSEC)
88 89
/* max ticks that can be represented as Unix time */
#define TICKS_1601_TO_UNIX_MAX ((SECS_1601_TO_1970 + INT_MAX) * TICKSPERSEC)
90 91


Juergen Schmied's avatar
Juergen Schmied committed
92 93 94 95 96 97
static const int MonthLengths[2][MONSPERYEAR] =
{
	{ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
	{ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
};

Patrik Stridvall's avatar
Patrik Stridvall committed
98
static inline int IsLeapYear(int Year)
Juergen Schmied's avatar
Juergen Schmied committed
99 100 101 102 103
{
	return Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) ? 1 : 0;
}

/******************************************************************************
104 105
 *       RtlTimeToTimeFields [NTDLL.@]
 *
106
 * Convert a time into a TIME_FIELDS structure.
107
 *
Jon Griffiths's avatar
Jon Griffiths committed
108
 * PARAMS
109 110
 *   liTime     [I] Time to convert.
 *   TimeFields [O] Destination for the converted time.
Juergen Schmied's avatar
Juergen Schmied committed
111
 *
Jon Griffiths's avatar
Jon Griffiths committed
112
 * RETURNS
113
 *   Nothing.
Juergen Schmied's avatar
Juergen Schmied committed
114 115
 */
VOID WINAPI RtlTimeToTimeFields(
116
	const LARGE_INTEGER *liTime,
Juergen Schmied's avatar
Juergen Schmied committed
117 118
	PTIME_FIELDS TimeFields)
{
119 120
	int SecondsInDay;
        long int cleaps, years, yearday, months;
Juergen Schmied's avatar
Juergen Schmied committed
121
	long int Days;
122
	LONGLONG Time;
Juergen Schmied's avatar
Juergen Schmied committed
123 124

	/* Extract millisecond from time and convert time into seconds */
125 126 127
	TimeFields->Milliseconds =
            (CSHORT) (( liTime->QuadPart % TICKSPERSEC) / TICKSPERMSEC);
	Time = liTime->QuadPart / TICKSPERSEC;
Juergen Schmied's avatar
Juergen Schmied committed
128

129 130
	/* The native version of RtlTimeToTimeFields does not take leap seconds
	 * into account */
Juergen Schmied's avatar
Juergen Schmied committed
131 132 133 134 135 136 137 138 139 140 141 142 143 144

	/* Split the time into days and seconds within the day */
	Days = Time / SECSPERDAY;
	SecondsInDay = Time % SECSPERDAY;

	/* compute time of day */
	TimeFields->Hour = (CSHORT) (SecondsInDay / SECSPERHOUR);
	SecondsInDay = SecondsInDay % SECSPERHOUR;
	TimeFields->Minute = (CSHORT) (SecondsInDay / SECSPERMIN);
	TimeFields->Second = (CSHORT) (SecondsInDay % SECSPERMIN);

	/* compute day of week */
	TimeFields->Weekday = (CSHORT) ((EPOCHWEEKDAY + Days) % DAYSPERWEEK);

145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
        /* compute year, month and day of month. */
        cleaps=( 3 * ((4 * Days + 1227) / DAYSPERQUADRICENTENNIUM) + 3 ) / 4;
        Days += 28188 + cleaps;
        years = (20 * Days - 2442) / (5 * DAYSPERNORMALQUADRENNIUM);
        yearday = Days - (years * DAYSPERNORMALQUADRENNIUM)/4;
        months = (64 * yearday) / 1959;
        /* the result is based on a year starting on March.
         * To convert take 12 from Januari and Februari and
         * increase the year by one. */
        if( months < 14 ) {
            TimeFields->Month = months - 1;
            TimeFields->Year = years + 1524;
        } else {
            TimeFields->Month = months - 13;
            TimeFields->Year = years + 1525;
        }
        /* calculation of day of month is based on the wonderful
         * sequence of INT( n * 30.6): it reproduces the 
         * 31-30-31-30-31-31 month lengths exactly for small n's */
        TimeFields->Day = yearday - (1959 * months) / 64 ;
        return;
Juergen Schmied's avatar
Juergen Schmied committed
166
}
167

Juergen Schmied's avatar
Juergen Schmied committed
168
/******************************************************************************
169 170
 *       RtlTimeFieldsToTime [NTDLL.@]
 *
171
 * Convert a TIME_FIELDS structure into a time.
Juergen Schmied's avatar
Juergen Schmied committed
172
 *
Jon Griffiths's avatar
Jon Griffiths committed
173
 * PARAMS
174 175
 *   ftTimeFields [I] TIME_FIELDS structure to convert.
 *   Time         [O] Destination for the converted time.
176
 *
Jon Griffiths's avatar
Jon Griffiths committed
177
 * RETURNS
178 179
 *   Success: TRUE.
 *   Failure: FALSE.
Juergen Schmied's avatar
Juergen Schmied committed
180 181 182 183 184
 */
BOOLEAN WINAPI RtlTimeFieldsToTime(
	PTIME_FIELDS tfTimeFields,
	PLARGE_INTEGER Time)
{
185
        int month, year, cleaps, day;
Juergen Schmied's avatar
Juergen Schmied committed
186 187

	/* FIXME: normalize the TIME_FIELDS structure here */
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
        /* No, native just returns 0 (error) if the fields are not */
        if( tfTimeFields->Milliseconds< 0 || tfTimeFields->Milliseconds > 999 ||
                tfTimeFields->Second < 0 || tfTimeFields->Second > 59 ||
                tfTimeFields->Minute < 0 || tfTimeFields->Minute > 59 ||
                tfTimeFields->Hour < 0 || tfTimeFields->Hour > 23 ||
                tfTimeFields->Month < 1 || tfTimeFields->Month > 12 ||
                tfTimeFields->Day < 1 ||
                tfTimeFields->Day > MonthLengths
                    [ tfTimeFields->Month ==2 || IsLeapYear(tfTimeFields->Year)]
                    [ tfTimeFields->Month - 1] ||
                tfTimeFields->Year < 1601 )
            return FALSE;

        /* now calculate a day count from the date
         * First start counting years from March. This way the leap days
         * are added at the end of the year, not somewhere in the middle.
         * Formula's become so much less complicate that way.
         * To convert: add 12 to the month numbers of Jan and Feb, and 
         * take 1 from the year */
        if(tfTimeFields->Month < 3) {
            month = tfTimeFields->Month + 13;
            year = tfTimeFields->Year - 1;
        } else {
            month = tfTimeFields->Month + 1;
            year = tfTimeFields->Year;
        }
        cleaps = (3 * (year / 100) + 3) / 4;   /* nr of "century leap years"*/
        day =  (36525 * year) / 100 - cleaps + /* year * dayperyr, corrected */
                 (1959 * month) / 64 +         /* months * daypermonth */
                 tfTimeFields->Day -          /* day of the month */
                 584817 ;                      /* zero that on 1601-01-01 */
        /* done */
        
        Time->QuadPart = (((((LONGLONG) day * HOURSPERDAY +
            tfTimeFields->Hour) * MINSPERHOUR +
            tfTimeFields->Minute) * SECSPERMIN +
            tfTimeFields->Second ) * 1000 +
            tfTimeFields->Milliseconds ) * TICKSPERMSEC;

        return TRUE;
Juergen Schmied's avatar
Juergen Schmied committed
228
}
229

230 231 232 233 234 235 236 237 238 239 240 241
/***********************************************************************
 *       TIME_GetBias [internal]
 *
 * Helper function calculates delta local time from UTC. 
 *
 * PARAMS
 *   utc [I] The current utc time.
 *   pdaylight [I] Local daylight.
 *
 * RETURNS
 *   The bias for the current timezone.
 */
242
static LONG TIME_GetBias(void)
243
{
244
    static time_t last_utc;
245 246 247 248 249
    static LONG last_bias;
    LONG ret;
    time_t utc;

    utc = time( NULL );
250

251
    RtlEnterCriticalSection( &TIME_tz_section );
252
    if (utc != last_utc)
253
    {
254 255 256
        RTL_TIME_ZONE_INFORMATION tzi;
        int is_dst = init_tz_info( &tzi );

257
	last_utc = utc;
258 259 260
        last_bias = tzi.Bias;
        last_bias += is_dst ? tzi.DaylightBias : tzi.StandardBias;
        last_bias *= SECSPERMIN;
261
    }
262 263 264

    ret = last_bias;

265
    RtlLeaveCriticalSection( &TIME_tz_section );
266
    return ret;
267 268
}

269 270 271
/******************************************************************************
 *        RtlLocalTimeToSystemTime [NTDLL.@]
 *
272
 * Convert a local time into system time.
273
 *
Jon Griffiths's avatar
Jon Griffiths committed
274
 * PARAMS
275 276
 *   LocalTime  [I] Local time to convert.
 *   SystemTime [O] Destination for the converted time.
277
 *
Jon Griffiths's avatar
Jon Griffiths committed
278
 * RETURNS
279 280
 *   Success: STATUS_SUCCESS.
 *   Failure: An NTSTATUS error code indicating the problem.
281 282 283 284
 */
NTSTATUS WINAPI RtlLocalTimeToSystemTime( const LARGE_INTEGER *LocalTime,
                                          PLARGE_INTEGER SystemTime)
{
285
    LONG bias;
286 287 288

    TRACE("(%p, %p)\n", LocalTime, SystemTime);

289 290
    bias = TIME_GetBias();
    SystemTime->QuadPart = LocalTime->QuadPart + bias * (LONGLONG)TICKSPERSEC;
291 292
    return STATUS_SUCCESS;
}
Juergen Schmied's avatar
Juergen Schmied committed
293 294

/******************************************************************************
295 296
 *       RtlSystemTimeToLocalTime [NTDLL.@]
 *
297
 * Convert a system time into a local time.
298
 *
Jon Griffiths's avatar
Jon Griffiths committed
299 300
 * PARAMS
 *   SystemTime [I] System time to convert.
301
 *   LocalTime  [O] Destination for the converted time.
302
 *
Jon Griffiths's avatar
Jon Griffiths committed
303
 * RETURNS
304 305
 *   Success: STATUS_SUCCESS.
 *   Failure: An NTSTATUS error code indicating the problem.
Juergen Schmied's avatar
Juergen Schmied committed
306
 */
307 308
NTSTATUS WINAPI RtlSystemTimeToLocalTime( const LARGE_INTEGER *SystemTime,
                                          PLARGE_INTEGER LocalTime )
Juergen Schmied's avatar
Juergen Schmied committed
309
{
310
    LONG bias;
311 312

    TRACE("(%p, %p)\n", SystemTime, LocalTime);
Juergen Schmied's avatar
Juergen Schmied committed
313

314 315
    bias = TIME_GetBias();
    LocalTime->QuadPart = SystemTime->QuadPart - bias * (LONGLONG)TICKSPERSEC;
316
    return STATUS_SUCCESS;
Juergen Schmied's avatar
Juergen Schmied committed
317
}
318

Juergen Schmied's avatar
Juergen Schmied committed
319
/******************************************************************************
320 321
 *       RtlTimeToSecondsSince1970 [NTDLL.@]
 *
322
 * Convert a time into a count of seconds since 1970.
323
 *
Jon Griffiths's avatar
Jon Griffiths committed
324
 * PARAMS
325 326
 *   Time    [I] Time to convert.
 *   Seconds [O] Destination for the converted time.
327
 *
Jon Griffiths's avatar
Jon Griffiths committed
328
 * RETURNS
329 330
 *   Success: TRUE.
 *   Failure: FALSE, if the resulting value will not fit in a DWORD.
Juergen Schmied's avatar
Juergen Schmied committed
331
 */
332
BOOLEAN WINAPI RtlTimeToSecondsSince1970( const LARGE_INTEGER *Time, LPDWORD Seconds )
Juergen Schmied's avatar
Juergen Schmied committed
333
{
334
    ULONGLONG tmp = ((ULONGLONG)Time->u.HighPart << 32) | Time->u.LowPart;
335
    tmp = RtlLargeIntegerDivide( tmp, TICKSPERSEC, NULL );
336 337
    tmp -= SECS_1601_TO_1970;
    if (tmp > 0xffffffff) return FALSE;
338
    *Seconds = (DWORD)tmp;
339
    return TRUE;
Juergen Schmied's avatar
Juergen Schmied committed
340 341 342
}

/******************************************************************************
343 344
 *       RtlTimeToSecondsSince1980 [NTDLL.@]
 *
345
 * Convert a time into a count of seconds since 1980.
346
 *
Jon Griffiths's avatar
Jon Griffiths committed
347
 * PARAMS
348 349
 *   Time    [I] Time to convert.
 *   Seconds [O] Destination for the converted time.
350
 *
Jon Griffiths's avatar
Jon Griffiths committed
351
 * RETURNS
352 353
 *   Success: TRUE.
 *   Failure: FALSE, if the resulting value will not fit in a DWORD.
Juergen Schmied's avatar
Juergen Schmied committed
354
 */
355
BOOLEAN WINAPI RtlTimeToSecondsSince1980( const LARGE_INTEGER *Time, LPDWORD Seconds )
Juergen Schmied's avatar
Juergen Schmied committed
356
{
357
    ULONGLONG tmp = ((ULONGLONG)Time->u.HighPart << 32) | Time->u.LowPart;
358
    tmp = RtlLargeIntegerDivide( tmp, TICKSPERSEC, NULL );
359
    tmp -= SECS_1601_TO_1980;
360
    if (tmp > 0xffffffff) return FALSE;
361
    *Seconds = (DWORD)tmp;
362 363 364 365
    return TRUE;
}

/******************************************************************************
366 367
 *       RtlSecondsSince1970ToTime [NTDLL.@]
 *
368
 * Convert a count of seconds since 1970 to a time.
369
 *
Jon Griffiths's avatar
Jon Griffiths committed
370
 * PARAMS
371 372
 *   Seconds [I] Time to convert.
 *   Time    [O] Destination for the converted time.
373
 *
Jon Griffiths's avatar
Jon Griffiths committed
374
 * RETURNS
375
 *   Nothing.
376
 */
377
void WINAPI RtlSecondsSince1970ToTime( DWORD Seconds, LARGE_INTEGER *Time )
378
{
379
    ULONGLONG secs = Seconds * (ULONGLONG)TICKSPERSEC + TICKS_1601_TO_1970;
380 381
    Time->u.LowPart  = (DWORD)secs;
    Time->u.HighPart = (DWORD)(secs >> 32);
382 383 384
}

/******************************************************************************
385 386
 *       RtlSecondsSince1980ToTime [NTDLL.@]
 *
387
 * Convert a count of seconds since 1980 to a time.
388
 *
Jon Griffiths's avatar
Jon Griffiths committed
389
 * PARAMS
390 391
 *   Seconds [I] Time to convert.
 *   Time    [O] Destination for the converted time.
392
 *
Jon Griffiths's avatar
Jon Griffiths committed
393
 * RETURNS
394
 *   Nothing.
395
 */
396
void WINAPI RtlSecondsSince1980ToTime( DWORD Seconds, LARGE_INTEGER *Time )
397
{
398
    ULONGLONG secs = Seconds * (ULONGLONG)TICKSPERSEC + TICKS_1601_TO_1980;
399 400
    Time->u.LowPart  = (DWORD)secs;
    Time->u.HighPart = (DWORD)(secs >> 32);
Juergen Schmied's avatar
Juergen Schmied committed
401 402 403
}

/******************************************************************************
404 405
 *       RtlTimeToElapsedTimeFields [NTDLL.@]
 *
406 407 408 409 410 411
 * Convert a time to a count of elapsed seconds.
 *
 * PARAMS
 *   Time       [I] Time to convert.
 *   TimeFields [O] Destination for the converted time.
 *
Jon Griffiths's avatar
Jon Griffiths committed
412
 * RETURNS
413
 *   Nothing.
Juergen Schmied's avatar
Juergen Schmied committed
414
 */
415
void WINAPI RtlTimeToElapsedTimeFields( const LARGE_INTEGER *Time, PTIME_FIELDS TimeFields )
Juergen Schmied's avatar
Juergen Schmied committed
416
{
417
    LONGLONG time;
Mike McCormack's avatar
Mike McCormack committed
418
    INT rem;
419 420 421 422 423 424 425 426 427 428 429 430 431 432

    time = RtlExtendedLargeIntegerDivide( Time->QuadPart, TICKSPERSEC, &rem );
    TimeFields->Milliseconds = rem / TICKSPERMSEC;

    /* time is now in seconds */
    TimeFields->Year  = 0;
    TimeFields->Month = 0;
    TimeFields->Day   = RtlExtendedLargeIntegerDivide( time, SECSPERDAY, &rem );

    /* rem is now the remaining seconds in the last day */
    TimeFields->Second = rem % 60;
    rem /= 60;
    TimeFields->Minute = rem % 60;
    TimeFields->Hour = rem / 60;
Juergen Schmied's avatar
Juergen Schmied committed
433
}
434 435

/***********************************************************************
436 437 438
 *       NtQuerySystemTime [NTDLL.@]
 *       ZwQuerySystemTime [NTDLL.@]
 *
439
 * Get the current system time.
440
 *
Jon Griffiths's avatar
Jon Griffiths committed
441
 * PARAMS
442
 *   Time [O] Destination for the current system time.
443
 *
Jon Griffiths's avatar
Jon Griffiths committed
444
 * RETURNS
445 446
 *   Success: STATUS_SUCCESS.
 *   Failure: An NTSTATUS error code indicating the problem.
447
 */
448
NTSTATUS WINAPI NtQuerySystemTime( PLARGE_INTEGER Time )
449 450 451 452
{
    struct timeval now;

    gettimeofday( &now, 0 );
453 454
    Time->QuadPart = now.tv_sec * (ULONGLONG)TICKSPERSEC + TICKS_1601_TO_1970;
    Time->QuadPart += now.tv_usec * 10;
455
    return STATUS_SUCCESS;
456
}
457

458 459 460 461 462
/******************************************************************************
 *  NtQueryPerformanceCounter	[NTDLL.@]
 *
 *  Note: Windows uses a timer clocked at a multiple of 1193182 Hz. There is a
 *  good number of applications that crash when the returned frequency is either
463
 *  lower or higher than what Windows gives. Also too high counter values are
464 465 466 467
 *  reported to give problems.
 */
NTSTATUS WINAPI NtQueryPerformanceCounter( PLARGE_INTEGER Counter, PLARGE_INTEGER Frequency )
{
468
    LARGE_INTEGER now;
469 470

    if (!Counter) return STATUS_ACCESS_VIOLATION;
471 472

    /* convert a counter that increments at a rate of 10 MHz
473
     * to one of 1.193182 MHz, with some care for arithmetic
474 475 476
     * overflow and good accuracy (21/176 = 0.11931818) */
    NtQuerySystemTime( &now );
    Counter->QuadPart = ((now.QuadPart - server_start_time) * 21) / 176;
477 478 479 480
    if (Frequency) Frequency->QuadPart = 1193182;
    return STATUS_SUCCESS;
}

481 482 483 484 485 486 487

/******************************************************************************
 * NtGetTickCount   (NTDLL.@)
 * ZwGetTickCount   (NTDLL.@)
 */
ULONG WINAPI NtGetTickCount(void)
{
488
    LARGE_INTEGER now;
489

490 491
    NtQuerySystemTime( &now );
    return (now.QuadPart - server_start_time) / 10000;
492 493
}

494 495
/* calculate the mday of dst change date, so that for instance Sun 5 Oct 2007
 * (last Sunday in October of 2007) becomes Sun Oct 28 2007
496
 *
497
 * Note: year, day and month must be in unix format.
498
 */
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558
static int weekday_to_mday(int year, int day, int mon, int day_of_week)
{
    struct tm date;
    time_t tmp;
    int wday, mday;

    /* find first day in the month matching week day of the date */
    memset(&date, 0, sizeof(date));
    date.tm_year = year;
    date.tm_mon = mon;
    date.tm_mday = -1;
    date.tm_wday = -1;
    do
    {
        date.tm_mday++;
        tmp = mktime(&date);
    } while (date.tm_wday != day_of_week || date.tm_mon != mon);

    mday = date.tm_mday;

    /* find number of week days in the month matching week day of the date */
    wday = 1; /* 1 - 1st, ...., 5 - last */
    while (wday < day)
    {
        struct tm *tm;

        date.tm_mday += 7;
        tmp = mktime(&date);
        tm = localtime(&tmp);
        if (tm->tm_mon != mon)
            break;
        mday = tm->tm_mday;
        wday++;
    }

    return mday;
}

static BOOL match_tz_date(const RTL_SYSTEM_TIME *st, const RTL_SYSTEM_TIME *reg_st)
{
    WORD wDay;

    if (st->wMonth != reg_st->wMonth) return FALSE;

    if (!st->wMonth) return TRUE; /* no transition dates */

    wDay = reg_st->wDay;
    if (!reg_st->wYear) /* date in a day-of-week format */
        wDay = weekday_to_mday(st->wYear - 1900, reg_st->wDay, reg_st->wMonth - 1, reg_st->wDayOfWeek);

    if (st->wDay != wDay ||
        st->wHour != reg_st->wHour ||
        st->wMinute != reg_st->wMinute ||
        st->wSecond != reg_st->wSecond ||
        st->wMilliseconds != reg_st->wMilliseconds) return FALSE;

    return TRUE;
}

static BOOL match_tz_info(const RTL_TIME_ZONE_INFORMATION *tzi, const RTL_TIME_ZONE_INFORMATION *reg_tzi)
559
{
560 561 562 563 564 565
    if (tzi->Bias == reg_tzi->Bias &&
        match_tz_date(&tzi->StandardDate, &reg_tzi->StandardDate) &&
        match_tz_date(&tzi->DaylightDate, &reg_tzi->DaylightDate))
        return TRUE;

    return FALSE;
566 567
}

568 569 570 571 572 573 574 575
static BOOL reg_query_value(HKEY hkey, LPCWSTR name, DWORD type, void *data, DWORD count)
{
    UNICODE_STRING nameW;
    char buf[256];
    KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buf;

    if (count > sizeof(buf) - sizeof(KEY_VALUE_PARTIAL_INFORMATION))
        return FALSE;
576

577 578 579 580 581 582 583 584 585 586 587
    RtlInitUnicodeString(&nameW, name);

    if (NtQueryValueKey(hkey, &nameW, KeyValuePartialInformation,
                        buf, sizeof(buf), &count))
        return FALSE;

    if (info->Type != type) return FALSE;

    memcpy(data, info->Data, info->DataLength);
    return TRUE;
}
588

589
static void find_reg_tz_info(RTL_TIME_ZONE_INFORMATION *tzi)
590
{
591 592 593 594 595 596
    static const WCHAR Time_ZonesW[] = { 'M','a','c','h','i','n','e','\\',
        'S','o','f','t','w','a','r','e','\\',
        'M','i','c','r','o','s','o','f','t','\\',
        'W','i','n','d','o','w','s',' ','N','T','\\',
        'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
        'T','i','m','e',' ','Z','o','n','e','s',0 };
597
    HANDLE hkey;
598
    ULONG idx;
599 600
    OBJECT_ATTRIBUTES attr;
    UNICODE_STRING nameW;
601
    WCHAR buf[128];
602 603 604 605 606 607 608

    attr.Length = sizeof(attr);
    attr.RootDirectory = 0;
    attr.ObjectName = &nameW;
    attr.Attributes = 0;
    attr.SecurityDescriptor = NULL;
    attr.SecurityQualityOfService = NULL;
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
    RtlInitUnicodeString(&nameW, Time_ZonesW);
    if (NtOpenKey(&hkey, KEY_READ, &attr))
    {
        WARN("Unable to open the time zones key\n");
        return;
    }

    idx = 0;
    nameW.Buffer = buf;
    nameW.Length = sizeof(buf);
    nameW.MaximumLength = sizeof(buf);

    while (!RtlpNtEnumerateSubKey(hkey, &nameW, idx++))
    {
        static const WCHAR stdW[] = { 'S','t','d',0 };
        static const WCHAR dltW[] = { 'D','l','t',0 };
        static const WCHAR tziW[] = { 'T','Z','I',0 };
        RTL_TIME_ZONE_INFORMATION reg_tzi;
        HANDLE hSubkey;
        struct tz_reg_data
        {
            LONG bias;
            LONG std_bias;
            LONG dlt_bias;
            RTL_SYSTEM_TIME std_date;
            RTL_SYSTEM_TIME dlt_date;
        } tz_data;

        attr.Length = sizeof(attr);
        attr.RootDirectory = hkey;
        attr.ObjectName = &nameW;
        attr.Attributes = 0;
        attr.SecurityDescriptor = NULL;
        attr.SecurityQualityOfService = NULL;
        if (NtOpenKey(&hSubkey, KEY_READ, &attr))
        {
            WARN("Unable to open subkey %s\n", debugstr_wn(nameW.Buffer, nameW.Length/sizeof(WCHAR)));
            continue;
647
        }
648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689

#define get_value(hkey, name, type, data, len) \
    if (!reg_query_value(hkey, name, type, data, len)) \
    { \
        WARN("can't read data from %s\n", debugstr_w(name)); \
        NtClose(hkey); \
        continue; \
    }

        get_value(hSubkey, stdW, REG_SZ, reg_tzi.StandardName, sizeof(reg_tzi.StandardName));
        get_value(hSubkey, dltW, REG_SZ, reg_tzi.DaylightName, sizeof(reg_tzi.DaylightName));
        get_value(hSubkey, tziW, REG_BINARY, &tz_data, sizeof(tz_data));

#undef get_value

        reg_tzi.Bias = tz_data.bias;
        reg_tzi.StandardBias = tz_data.std_bias;
        reg_tzi.DaylightBias = tz_data.dlt_bias;
        reg_tzi.StandardDate = tz_data.std_date;
        reg_tzi.DaylightDate = tz_data.dlt_date;

        TRACE("%s: bias %d\n", debugstr_wn(nameW.Buffer, nameW.Length/sizeof(WCHAR)), reg_tzi.Bias);
        TRACE("std (d/m/y): %u/%02u/%04u day of week %u %u:%02u:%02u.%03u bias %d\n",
            reg_tzi.StandardDate.wDay, reg_tzi.StandardDate.wMonth,
            reg_tzi.StandardDate.wYear, reg_tzi.StandardDate.wDayOfWeek,
            reg_tzi.StandardDate.wHour, reg_tzi.StandardDate.wMinute,
            reg_tzi.StandardDate.wSecond, reg_tzi.StandardDate.wMilliseconds,
            reg_tzi.StandardBias);
        TRACE("dst (d/m/y): %u/%02u/%04u day of week %u %u:%02u:%02u.%03u bias %d\n",
            reg_tzi.DaylightDate.wDay, reg_tzi.DaylightDate.wMonth,
            reg_tzi.DaylightDate.wYear, reg_tzi.DaylightDate.wDayOfWeek,
            reg_tzi.DaylightDate.wHour, reg_tzi.DaylightDate.wMinute,
            reg_tzi.DaylightDate.wSecond, reg_tzi.DaylightDate.wMilliseconds,
            reg_tzi.DaylightBias);

        NtClose(hSubkey);

        if (match_tz_info(tzi, &reg_tzi))
        {
            memcpy(tzi, &reg_tzi, sizeof(*tzi));
            NtClose(hkey);
            return;
690
        }
691

692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728
        /* reset len */
        nameW.Length = sizeof(buf);
        nameW.MaximumLength = sizeof(buf);
    }

    NtClose(hkey);

    FIXME("Can't find matching timezone information in the registry for "
          "bias %d, std (d/m/y): %u/%02u/%04u, dlt (d/m/y): %u/%02u/%04u\n",
          tzi->Bias,
          tzi->StandardDate.wDay, tzi->StandardDate.wMonth, tzi->StandardDate.wYear,
          tzi->DaylightDate.wDay, tzi->DaylightDate.wMonth, tzi->DaylightDate.wYear);
}

static time_t find_dst_change(unsigned long min, unsigned long max, int *is_dst)
{
    time_t start;
    struct tm *tm;

    start = min;
    tm = localtime(&start);
    *is_dst = !tm->tm_isdst;
    TRACE("starting date isdst %d, %s", !*is_dst, ctime(&start));

    while (min <= max)
    {
        time_t pos = (min + max) / 2;
        tm = localtime(&pos);

        if (tm->tm_isdst != *is_dst)
            min = pos + 1;
        else
            max = pos - 1;
    }
    return min;
}

729
static int init_tz_info(RTL_TIME_ZONE_INFORMATION *tzi)
730
{
731 732
    static RTL_TIME_ZONE_INFORMATION cached_tzi;
    static int current_year = -1;
733 734
    struct tm *tm;
    time_t year_start, year_end, tmp, dlt = 0, std = 0;
735 736 737
    int is_dst, current_is_dst;

    RtlEnterCriticalSection( &TIME_tz_section );
738 739 740 741

    year_start = time(NULL);
    tm = localtime(&year_start);

742 743 744 745 746 747 748
    current_is_dst = tm->tm_isdst;
    if (current_year == tm->tm_year)
    {
        *tzi = cached_tzi;
        RtlLeaveCriticalSection( &TIME_tz_section );
        return current_is_dst;
    }
749 750 751 752

    memset(tzi, 0, sizeof(*tzi));

    TRACE("tz data will be valid through year %d\n", tm->tm_year + 1900);
753
    current_year = tm->tm_year;
754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788

    tm->tm_isdst = 0;
    tm->tm_mday = 1;
    tm->tm_mon = tm->tm_hour = tm->tm_min = tm->tm_sec = tm->tm_wday = tm->tm_yday = 0;
    year_start = mktime(tm);
    TRACE("year_start: %s", ctime(&year_start));

    tm->tm_mday = tm->tm_wday = tm->tm_yday = 0;
    tm->tm_mon = 12;
    tm->tm_hour = 23;
    tm->tm_min = tm->tm_sec = 59;
    year_end = mktime(tm);
    TRACE("year_end: %s", ctime(&year_end));

    tm = gmtime(&year_start);
    tzi->Bias = (LONG)(mktime(tm) - year_start) / 60;
    TRACE("bias: %d\n", tzi->Bias);

    tmp = find_dst_change(year_start, year_end, &is_dst);
    if (is_dst)
        dlt = tmp;
    else
        std = tmp;

    tmp = find_dst_change(tmp, year_end, &is_dst);
    if (is_dst)
        dlt = tmp;
    else
        std = tmp;

    TRACE("std: %s", ctime(&std));
    TRACE("dlt: %s", ctime(&dlt));

    if (dlt == std || !dlt || !std)
        TRACE("there is no daylight saving rules in this time zone\n");
789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831
    else
    {
        tmp = dlt - tzi->Bias * 60;
        tm = gmtime(&tmp);
        TRACE("dlt gmtime: %s", asctime(tm));

        tzi->DaylightBias = -60;
        tzi->DaylightDate.wYear = tm->tm_year + 1900;
        tzi->DaylightDate.wMonth = tm->tm_mon + 1;
        tzi->DaylightDate.wDayOfWeek = tm->tm_wday;
        tzi->DaylightDate.wDay = tm->tm_mday;
        tzi->DaylightDate.wHour = tm->tm_hour;
        tzi->DaylightDate.wMinute = tm->tm_min;
        tzi->DaylightDate.wSecond = tm->tm_sec;
        tzi->DaylightDate.wMilliseconds = 0;

        TRACE("daylight (d/m/y): %u/%02u/%04u day of week %u %u:%02u:%02u.%03u bias %d\n",
            tzi->DaylightDate.wDay, tzi->DaylightDate.wMonth,
            tzi->DaylightDate.wYear, tzi->DaylightDate.wDayOfWeek,
            tzi->DaylightDate.wHour, tzi->DaylightDate.wMinute,
            tzi->DaylightDate.wSecond, tzi->DaylightDate.wMilliseconds,
            tzi->DaylightBias);

        tmp = std - tzi->Bias * 60 - tzi->DaylightBias * 60;
        tm = gmtime(&tmp);
        TRACE("std gmtime: %s", asctime(tm));

        tzi->StandardBias = 0;
        tzi->StandardDate.wYear = tm->tm_year + 1900;
        tzi->StandardDate.wMonth = tm->tm_mon + 1;
        tzi->StandardDate.wDayOfWeek = tm->tm_wday;
        tzi->StandardDate.wDay = tm->tm_mday;
        tzi->StandardDate.wHour = tm->tm_hour;
        tzi->StandardDate.wMinute = tm->tm_min;
        tzi->StandardDate.wSecond = tm->tm_sec;
        tzi->StandardDate.wMilliseconds = 0;

        TRACE("standard (d/m/y): %u/%02u/%04u day of week %u %u:%02u:%02u.%03u bias %d\n",
            tzi->StandardDate.wDay, tzi->StandardDate.wMonth,
            tzi->StandardDate.wYear, tzi->StandardDate.wDayOfWeek,
            tzi->StandardDate.wHour, tzi->StandardDate.wMinute,
            tzi->StandardDate.wSecond, tzi->StandardDate.wMilliseconds,
            tzi->StandardBias);
832
    }
833 834

    find_reg_tz_info(tzi);
835 836 837 838 839
    cached_tzi = *tzi;

    RtlLeaveCriticalSection( &TIME_tz_section );

    return current_is_dst;
840 841
}

842 843 844
/***********************************************************************
 *      RtlQueryTimeZoneInformation [NTDLL.@]
 *
845
 * Get information about the current timezone.
846
 *
Jon Griffiths's avatar
Jon Griffiths committed
847
 * PARAMS
848
 *   tzinfo [O] Destination for the retrieved timezone info.
849
 *
Jon Griffiths's avatar
Jon Griffiths committed
850
 * RETURNS
851 852
 *   Success: STATUS_SUCCESS.
 *   Failure: An NTSTATUS error code indicating the problem.
853
 */
854
NTSTATUS WINAPI RtlQueryTimeZoneInformation(RTL_TIME_ZONE_INFORMATION *tzinfo)
855
{
856
    init_tz_info( tzinfo );
857 858 859 860 861 862 863

    return STATUS_SUCCESS;
}

/***********************************************************************
 *       RtlSetTimeZoneInformation [NTDLL.@]
 *
864
 * Set the current time zone information.
865
 *
Jon Griffiths's avatar
Jon Griffiths committed
866
 * PARAMS
867
 *   tzinfo [I] Timezone information to set.
868
 *
Jon Griffiths's avatar
Jon Griffiths committed
869
 * RETURNS
870 871
 *   Success: STATUS_SUCCESS.
 *   Failure: An NTSTATUS error code indicating the problem.
872 873
 *
 */
874
NTSTATUS WINAPI RtlSetTimeZoneInformation( const RTL_TIME_ZONE_INFORMATION *tzinfo )
875 876 877 878 879 880 881 882
{
    return STATUS_PRIVILEGE_NOT_HELD;
}

/***********************************************************************
 *        NtSetSystemTime [NTDLL.@]
 *        ZwSetSystemTime [NTDLL.@]
 *
883
 * Set the system time.
884
 *
885 886 887
 * PARAMS
 *   NewTime [I] The time to set.
 *   OldTime [O] Optional destination for the previous system time.
888
 *
Jon Griffiths's avatar
Jon Griffiths committed
889
 * RETURNS
890 891
 *   Success: STATUS_SUCCESS.
 *   Failure: An NTSTATUS error code indicating the problem.
892 893 894 895
 */
NTSTATUS WINAPI NtSetSystemTime(const LARGE_INTEGER *NewTime, LARGE_INTEGER *OldTime)
{
    struct timeval tv;
896 897 898
    time_t tm_t;
    DWORD sec, oldsec;
    LARGE_INTEGER tm;
899 900

    /* Return the old time if necessary */
901 902 903 904 905 906
    if (!OldTime) OldTime = &tm;

    NtQuerySystemTime( OldTime );
    RtlTimeToSecondsSince1970( OldTime, &oldsec );

    RtlTimeToSecondsSince1970( NewTime, &sec );
907 908 909

    /* set the new time */
    tv.tv_sec = sec;
910
    tv.tv_usec = 0;
911 912

#ifdef HAVE_SETTIMEOFDAY
913 914
    if (!settimeofday(&tv, NULL)) /* 0 is OK, -1 is error */
        return STATUS_SUCCESS;
915
    tm_t = sec;
916 917 918
    ERR("Cannot set time to %s, time adjustment %ld: %s\n",
        ctime(&tm_t), (long)(sec-oldsec), strerror(errno));
    if (errno == EPERM)
919 920
        return STATUS_PRIVILEGE_NOT_HELD;
    else
921 922 923 924 925 926 927
        return STATUS_INVALID_PARAMETER;
#else
    tm_t = sec;
    FIXME("setting time to %s not implemented for missing settimeofday\n",
        ctime(&tm_t));
    return STATUS_NOT_IMPLEMENTED;
#endif
928
}