/* $Id: filetime.c 2058 2005-05-12 12:51:47Z twogood $ */ #include "synce.h" #include "synce_log.h" static void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime, DWORD remainder ); static time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder ); void filetime_from_unix_time(time_t unix_time, FILETIME *filetime) { if (0 == unix_time || -1 == unix_time) { filetime->dwLowDateTime = 0; filetime->dwHighDateTime = 0; } else DOSFS_UnixTimeToFileTime(unix_time, filetime, 0); } time_t filetime_to_unix_time(const FILETIME *filetime) { synce_trace("This function is deprecated. Use time_fields_from_filetime() instead."); if (0 == filetime->dwLowDateTime && 0 == filetime->dwHighDateTime) return 0; else return DOSFS_FileTimeToUnixTime(filetime, NULL); } /* * Copyright (c) 1993-2002 the Wine project authors * * These functions come from wine/files/dos_fs.c in release 20020228 of the * WINE project. See http://www.winehq.org/ for more information. * * Licensing information for the code below: * * http://source.winehq.org/source/LICENSE?v=wine20020228 * */ /*********************************************************************** * DOSFS_UnixTimeToFileTime * * Convert a Unix time to FILETIME format. * The FILETIME structure is a 64-bit value representing the number of * 100-nanosecond intervals since January 1, 1601, 0:00. * 'remainder' is the nonnegative number of 100-ns intervals * corresponding to the time fraction smaller than 1 second that * couldn't be stored in the time_t value. */ static void DOSFS_UnixTimeToFileTime( time_t unix_time, FILETIME *filetime, DWORD remainder ) { /* NOTES: CONSTANTS: The time difference between 1 January 1601, 00:00:00 and 1 January 1970, 00:00:00 is 369 years, plus the leap years from 1604 to 1968, excluding 1700, 1800, 1900. This makes (1968 - 1600) / 4 - 3 = 89 leap days, and a total of 134774 days. Any day in that period had 24 * 60 * 60 = 86400 seconds. The time difference is 134774 * 86400 * 10000000, which can be written 116444736000000000 27111902 * 2^32 + 3577643008 413 * 2^48 + 45534 * 2^32 + 54590 * 2^16 + 32768 If you find that these constants are buggy, please change them in all instances in both conversion functions. VERSIONS: There are two versions, one of them uses long long variables and is presumably faster but not ISO C. The other one uses standard C data types and operations but relies on the assumption that negative numbers are stored as 2's complement (-1 is 0xffff....). If this assumption is violated, dates before 1970 will not convert correctly. This should however work on any reasonable architecture where WINE will run. DETAILS: Take care not to remove the casts. I have tested these functions (in both versions) for a lot of numbers. I would be interested in results on other compilers than GCC. The operations have been designed to account for the possibility of 64-bit time_t in future UNICES. Even the versions without internal long long numbers will work if time_t only is 64 bit. A 32-bit shift, which was necessary for that operation, turned out not to work correctly in GCC, besides giving the warning. So I used a double 16-bit shift instead. Numbers are in the ISO version represented by three limbs, the most significant with 32 bit, the other two with 16 bit each. As the modulo-operator % is not well-defined for negative numbers, negative divisors have been avoided in DOSFS_FileTimeToUnixTime. There might be quicker ways to do this in C. Certainly so in assembler. Claus Fischer, fischer@iue.tuwien.ac.at */ #if SIZEOF_LONG_LONG >= 8 # define USE_LONG_LONG 1 #else # define USE_LONG_LONG 0 #endif #if USE_LONG_LONG /* gcc supports long long type */ long long int t = unix_time; t *= 10000000; t += 116444736000000000LL; t += remainder; filetime->dwLowDateTime = (UINT)t; filetime->dwHighDateTime = (UINT)(t >> 32); #else /* ISO version */ UINT a0; /* 16 bit, low bits */ UINT a1; /* 16 bit, medium bits */ UINT a2; /* 32 bit, high bits */ /* Copy the unix time to a2/a1/a0 */ a0 = unix_time & 0xffff; a1 = (unix_time >> 16) & 0xffff; /* This is obsolete if unix_time is only 32 bits, but it does not hurt. Do not replace this by >> 32, it gives a compiler warning and it does not work. */ a2 = (unix_time >= 0 ? (unix_time >> 16) >> 16 : ~((~unix_time >> 16) >> 16)); /* Multiply a by 10000000 (a = a2/a1/a0) Split the factor into 10000 * 1000 which are both less than 0xffff. */ a0 *= 10000; a1 = a1 * 10000 + (a0 >> 16); a2 = a2 * 10000 + (a1 >> 16); a0 &= 0xffff; a1 &= 0xffff; a0 *= 1000; a1 = a1 * 1000 + (a0 >> 16); a2 = a2 * 1000 + (a1 >> 16); a0 &= 0xffff; a1 &= 0xffff; /* Add the time difference and the remainder */ a0 += 32768 + (remainder & 0xffff); a1 += 54590 + (remainder >> 16 ) + (a0 >> 16); a2 += 27111902 + (a1 >> 16); a0 &= 0xffff; a1 &= 0xffff; /* Set filetime */ filetime->dwLowDateTime = (a1 << 16) + a0; filetime->dwHighDateTime = a2; #endif } /*********************************************************************** * DOSFS_FileTimeToUnixTime * * Convert a FILETIME format to Unix time. * If not NULL, 'remainder' contains the fractional part of the filetime, * in the range of [0..9999999] (even if time_t is negative). */ static time_t DOSFS_FileTimeToUnixTime( const FILETIME *filetime, DWORD *remainder ) { /* Read the comment in the function DOSFS_UnixTimeToFileTime. */ #if USE_LONG_LONG long long int t = filetime->dwHighDateTime; t <<= 32; t += (UINT)filetime->dwLowDateTime; t -= 116444736000000000LL; if (t < 0) { if (remainder) *remainder = 9999999 - (-t - 1) % 10000000; return -1 - ((-t - 1) / 10000000); } else { if (remainder) *remainder = t % 10000000; return t / 10000000; } #else /* ISO version */ UINT a0; /* 16 bit, low bits */ UINT a1; /* 16 bit, medium bits */ UINT a2; /* 32 bit, high bits */ UINT r; /* remainder of division */ unsigned int carry; /* carry bit for subtraction */ int negative; /* whether a represents a negative value */ /* Copy the time values to a2/a1/a0 */ a2 = (UINT)filetime->dwHighDateTime; a1 = ((UINT)filetime->dwLowDateTime ) >> 16; a0 = ((UINT)filetime->dwLowDateTime ) & 0xffff; /* Subtract the time difference */ if (a0 >= 32768 ) a0 -= 32768 , carry = 0; else a0 += (1 << 16) - 32768 , carry = 1; if (a1 >= 54590 + carry) a1 -= 54590 + carry, carry = 0; else a1 += (1 << 16) - 54590 - carry, carry = 1; a2 -= 27111902 + carry; /* If a is negative, replace a by (-1-a) */ negative = (a2 >= ((UINT)1) << 31); if (negative) { /* Set a to -a - 1 (a is a2/a1/a0) */ a0 = 0xffff - a0; a1 = 0xffff - a1; a2 = ~a2; } /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r. Split the divisor into 10000 * 1000 which are both less than 0xffff. */ a1 += (a2 % 10000) << 16; a2 /= 10000; a0 += (a1 % 10000) << 16; a1 /= 10000; r = a0 % 10000; a0 /= 10000; a1 += (a2 % 1000) << 16; a2 /= 1000; a0 += (a1 % 1000) << 16; a1 /= 1000; r += (a0 % 1000) * 10000; a0 /= 1000; /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */ if (negative) { /* Set a to -a - 1 (a is a2/a1/a0) */ a0 = 0xffff - a0; a1 = 0xffff - a1; a2 = ~a2; r = 9999999 - r; } if (remainder) *remainder = r; /* Do not replace this by << 32, it gives a compiler warning and it does not work. */ return ((((time_t)a2) << 16) << 16) + (a1 << 16) + a0; #endif }