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