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