1 /*
2  * timestamp.c
3  *
4  * Conversion between Windows NT timestamps and UNIX timestamps.
5  */
6 
7 /*
8  * Copyright (C) 2012-2017 Eric Biggers
9  *
10  * This file is free software; you can redistribute it and/or modify it under
11  * the terms of the GNU Lesser General Public License as published by the Free
12  * Software Foundation; either version 3 of the License, or (at your option) any
13  * later version.
14  *
15  * This file is distributed in the hope that it will be useful, but WITHOUT
16  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this file; if not, see http://www.gnu.org/licenses/.
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27 
28 #include "wimlib.h" /* for struct wimlib_timespec */
29 #include "wimlib/timestamp.h"
30 
31 /*
32  * Timestamps in WIM files are Windows NT timestamps, or FILETIMEs: 64-bit
33  * values storing the number of 100-nanosecond ticks since January 1, 1601.
34  *
35  * Note: UNIX timestamps are signed; Windows timestamps are not.  Negative UNIX
36  * timestamps represent times before 1970-01-01.  When such a timestamp is
37  * converted to a Windows timestamp, we can preserve the correct date provided
38  * that it is not also before 1601-01-01.
39  */
40 
41 #define NANOSECONDS_PER_TICK	100
42 #define TICKS_PER_SECOND	(1000000000 / NANOSECONDS_PER_TICK)
43 #define TICKS_PER_MICROSECOND	(TICKS_PER_SECOND / 1000000)
44 
45 /*
46  * EPOCH_DISTANCE is the number of seconds separating the Windows NT and UNIX
47  * epochs.  This is equal to ((1970-1601)*365+89)*24*60*60.  89 is the number
48  * of leap years between 1970 and 1601.
49  */
50 #define EPOCH_DISTANCE		11644473600
51 
52 /* Windows NT timestamps to UNIX timestamps  */
53 
54 time_t
wim_timestamp_to_time_t(u64 timestamp)55 wim_timestamp_to_time_t(u64 timestamp)
56 {
57 	return (timestamp / TICKS_PER_SECOND) - EPOCH_DISTANCE;
58 }
59 
60 void
wim_timestamp_to_wimlib_timespec(u64 timestamp,struct wimlib_timespec * wts,s32 * high_part_ret)61 wim_timestamp_to_wimlib_timespec(u64 timestamp, struct wimlib_timespec *wts,
62 				 s32 *high_part_ret)
63 {
64 	s64 sec = (timestamp / TICKS_PER_SECOND) - EPOCH_DISTANCE;
65 
66 	wts->tv_sec = sec;
67 	wts->tv_nsec = (timestamp % TICKS_PER_SECOND) * NANOSECONDS_PER_TICK;
68 
69 	if (sizeof(wts->tv_sec) == 4)
70 		*high_part_ret = sec >> 32;
71 }
72 
73 #ifdef __WIN32__
74 static _unused_attribute void
check_sizeof_time_t(void)75 check_sizeof_time_t(void)
76 {
77 	/* Windows builds should always be using 64-bit time_t now. */
78 	STATIC_ASSERT(sizeof(time_t) == 8);
79 }
80 #else
81 struct timeval
wim_timestamp_to_timeval(u64 timestamp)82 wim_timestamp_to_timeval(u64 timestamp)
83 {
84 	return (struct timeval) {
85 		.tv_sec = wim_timestamp_to_time_t(timestamp),
86 		.tv_usec = (timestamp % TICKS_PER_SECOND) / TICKS_PER_MICROSECOND,
87 	};
88 }
89 
90 struct timespec
wim_timestamp_to_timespec(u64 timestamp)91 wim_timestamp_to_timespec(u64 timestamp)
92 {
93 	return (struct timespec) {
94 		.tv_sec = wim_timestamp_to_time_t(timestamp),
95 		.tv_nsec = (timestamp % TICKS_PER_SECOND) * NANOSECONDS_PER_TICK,
96 	};
97 }
98 
99 /* UNIX timestamps to Windows NT timestamps  */
100 
101 u64
time_t_to_wim_timestamp(time_t t)102 time_t_to_wim_timestamp(time_t t)
103 {
104 	return ((u64)t + EPOCH_DISTANCE) * TICKS_PER_SECOND;
105 }
106 
107 u64
timeval_to_wim_timestamp(const struct timeval * tv)108 timeval_to_wim_timestamp(const struct timeval *tv)
109 {
110 	return time_t_to_wim_timestamp(tv->tv_sec) +
111 		(u32)tv->tv_usec * TICKS_PER_MICROSECOND;
112 }
113 
114 u64
timespec_to_wim_timestamp(const struct timespec * ts)115 timespec_to_wim_timestamp(const struct timespec *ts)
116 {
117 	return time_t_to_wim_timestamp(ts->tv_sec) +
118 		(u32)ts->tv_nsec / NANOSECONDS_PER_TICK;
119 }
120 
121 /* Retrieve the current time as a WIM timestamp.  */
122 u64
now_as_wim_timestamp(void)123 now_as_wim_timestamp(void)
124 {
125 	struct timeval tv;
126 
127 	gettimeofday(&tv, NULL);
128 	return timeval_to_wim_timestamp(&tv);
129 }
130 #endif /* !__WIN32__ */
131 
132 /* Translate a WIM timestamp into a human-readable string.  */
133 void
wim_timestamp_to_str(u64 timestamp,tchar * buf,size_t len)134 wim_timestamp_to_str(u64 timestamp, tchar *buf, size_t len)
135 {
136 	struct tm tm;
137 	time_t t = wim_timestamp_to_time_t(timestamp);
138 
139 	gmtime_r(&t, &tm);
140 	tstrftime(buf, len, T("%a %b %d %H:%M:%S %Y UTC"), &tm);
141 }
142