update for HEAD-2003091401
[reactos.git] / lib / msvcrt / time / time.c
1 /*
2  * COPYRIGHT:   See COPYING in the top level directory
3  * PROJECT:     ReactOS system libraries
4  * FILE:        lib/msvcrt/time/time.c
5  * PURPOSE:     Get system time
6  * PROGRAMER:   Boudewijn Dekker
7  * UPDATE HISTORY:
8  *              28/12/98: Created
9  */
10
11 /*
12  * DOS file system functions
13  *
14  * Copyright 1993 Erik Bos
15  * Copyright 1996 Alexandre Julliard
16  */
17
18 #include <windows.h>
19 #include <msvcrt/time.h>
20 #include <msvcrt/internal/file.h>
21
22
23 VOID STDCALL GetSystemTimeAsFileTime(LPFILETIME lpSystemTimeAsFileTime);
24
25 /*
26  * @implemented
27  */
28 time_t time(time_t* t)
29 {
30         FILETIME  SystemTime;
31         DWORD Remainder;
32         time_t tt;
33         GetSystemTimeAsFileTime(&SystemTime);
34         tt = FileTimeToUnixTime(&SystemTime,&Remainder);
35         if (t)
36                 *t = tt;
37         return tt;
38 }
39
40 /***********************************************************************
41  *           DOSFS_UnixTimeToFileTime
42  *
43  * Convert a Unix time to FILETIME format.
44  * The FILETIME structure is a 64-bit value representing the number of
45  * 100-nanosecond intervals since January 1, 1601, 0:00.
46  * 'remainder' is the nonnegative number of 100-ns intervals
47  * corresponding to the time fraction smaller than 1 second that
48  * couldn't be stored in the time_t value.
49  */
50 void UnixTimeToFileTime( time_t unix_time, FILETIME *filetime,
51                                DWORD remainder )
52 {
53     /* NOTES:
54
55        CONSTANTS: 
56        The time difference between 1 January 1601, 00:00:00 and
57        1 January 1970, 00:00:00 is 369 years, plus the leap years
58        from 1604 to 1968, excluding 1700, 1800, 1900.
59        This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total
60        of 134774 days.
61
62        Any day in that period had 24 * 60 * 60 = 86400 seconds.
63
64        The time difference is 134774 * 86400 * 10000000, which can be written
65        116444736000000000
66        27111902 * 2^32 + 3577643008
67        413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768
68
69        If you find that these constants are buggy, please change them in all
70        instances in both conversion functions.
71
72        VERSIONS:
73        There are two versions, one of them uses long long variables and
74        is presumably faster but not ISO C. The other one uses standard C
75        data types and operations but relies on the assumption that negative
76        numbers are stored as 2's complement (-1 is 0xffff....). If this
77        assumption is violated, dates before 1970 will not convert correctly.
78        This should however work on any reasonable architecture where WINE
79        will run.
80
81        DETAILS:
82        
83        Take care not to remove the casts. I have tested these functions
84        (in both versions) for a lot of numbers. I would be interested in
85        results on other compilers than GCC.
86
87        The operations have been designed to account for the possibility
88        of 64-bit time_t in future UNICES. Even the versions without
89        internal long long numbers will work if time_t only is 64 bit.
90        A 32-bit shift, which was necessary for that operation, turned out
91        not to work correctly in GCC, besides giving the warning. So I
92        used a double 16-bit shift instead. Numbers are in the ISO version
93        represented by three limbs, the most significant with 32 bit, the
94        other two with 16 bit each.
95
96        As the modulo-operator % is not well-defined for negative numbers,
97        negative divisors have been avoided in DOSFS_FileTimeToUnixTime.
98
99        There might be quicker ways to do this in C. Certainly so in
100        assembler.
101
102        Claus Fischer, fischer@iue.tuwien.ac.at
103        */
104
105
106
107
108     unsigned long a0;                  /* 16 bit, low    bits */
109     unsigned long a1;                  /* 16 bit, medium bits */
110     unsigned long a2;                  /* 32 bit, high   bits */
111
112     /* Copy the unix time to a2/a1/a0 */
113     a0 =  unix_time & 0xffff;
114     a1 = (unix_time >> 16) & 0xffff;
115     /* This is obsolete if unix_time is only 32 bits, but it does not hurt.
116        Do not replace this by >> 32, it gives a compiler warning and it does
117        not work. */
118     a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 :
119           ~((~unix_time >> 16) >> 16));
120
121     /* Multiply a by 10000000 (a = a2/a1/a0)
122        Split the factor into 10000 * 1000 which are both less than 0xffff. */
123     a0 *= 10000;
124     a1 = a1 * 10000 + (a0 >> 16);
125     a2 = a2 * 10000 + (a1 >> 16);
126     a0 &= 0xffff;
127     a1 &= 0xffff;
128
129     a0 *= 1000;
130     a1 = a1 * 1000 + (a0 >> 16);
131     a2 = a2 * 1000 + (a1 >> 16);
132     a0 &= 0xffff;
133     a1 &= 0xffff;
134
135     /* Add the time difference and the remainder */
136     a0 += 32768 + (remainder & 0xffff);
137     a1 += 54590 + (remainder >> 16   ) + (a0 >> 16);
138     a2 += 27111902                     + (a1 >> 16);
139     a0 &= 0xffff;
140     a1 &= 0xffff;
141
142     /* Set filetime */
143     filetime->dwLowDateTime  = (a1 << 16) + a0;
144     filetime->dwHighDateTime = a2;
145 }
146
147
148 /***********************************************************************
149  *           DOSFS_FileTimeToUnixTime
150  *
151  * Convert a FILETIME format to Unix time.
152  * If not NULL, 'remainder' contains the fractional part of the filetime,
153  * in the range of [0..9999999] (even if time_t is negative).
154  */
155 time_t FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder )
156 {
157     /* Read the comment in the function DOSFS_UnixTimeToFileTime. */
158
159     unsigned long a0;                  /* 16 bit, low    bits */
160     unsigned long a1;                  /* 16 bit, medium bits */
161     unsigned long a2;                  /* 32 bit, high   bits */
162     unsigned long r;                   /* remainder of division */
163     unsigned int carry;         /* carry bit for subtraction */
164     int negative;               /* whether a represents a negative value */
165
166     /* Copy the time values to a2/a1/a0 */
167     a2 =  (unsigned long)filetime->dwHighDateTime;
168     a1 = ((unsigned long)filetime->dwLowDateTime ) >> 16;
169     a0 = ((unsigned long)filetime->dwLowDateTime ) & 0xffff;
170
171     /* Subtract the time difference */
172     if (a0 >= 32768           ) a0 -=             32768        , carry = 0;
173     else                        a0 += (1 << 16) - 32768        , carry = 1;
174
175     if (a1 >= 54590    + carry) a1 -=             54590 + carry, carry = 0;
176     else                        a1 += (1 << 16) - 54590 - carry, carry = 1;
177
178     a2 -= 27111902 + carry;
179     
180     /* If a is negative, replace a by (-1-a) */
181     negative = (a2 >= ((unsigned long)1) << 31);
182     if (negative)
183     {
184         /* Set a to -a - 1 (a is a2/a1/a0) */
185         a0 = 0xffff - a0;
186         a1 = 0xffff - a1;
187         a2 = ~a2;
188     }
189
190     /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
191        Split the divisor into 10000 * 1000 which are both less than 0xffff. */
192     a1 += (a2 % 10000) << 16;
193     a2 /=       10000;
194     a0 += (a1 % 10000) << 16;
195     a1 /=       10000;
196     r   =  a0 % 10000;
197     a0 /=       10000;
198
199     a1 += (a2 % 1000) << 16;
200     a2 /=       1000;
201     a0 += (a1 % 1000) << 16;
202     a1 /=       1000;
203     r  += (a0 % 1000) * 10000;
204     a0 /=       1000;
205
206     /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
207     if (negative)
208     {
209         /* Set a to -a - 1 (a is a2/a1/a0) */
210         a0 = 0xffff - a0;
211         a1 = 0xffff - a1;
212         a2 = ~a2;
213
214         r  = 9999999 - r;
215     }
216
217     if (remainder) *remainder = r;
218
219     /* Do not replace this by << 32, it gives a compiler warning and it does
220        not work. */
221     return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0;
222
223 }
224
225
226