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