]> wimlib.net Git - wimlib/blob - src/timestamp.c
timestamp.c: correctly convert negative UNIX timestamps
[wimlib] / src / timestamp.c
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/timestamp.h"
29
30 /*
31  * Timestamps in WIM files are Windows NT timestamps, or FILETIMEs: 64-bit
32  * values storing the number of 100-nanosecond ticks since January 1, 1601.
33  *
34  * Note: UNIX timestamps are signed; Windows timestamps are not.  Negative UNIX
35  * timestamps represent times before 1970-01-01.  When such a timestamp is
36  * converted to a Windows timestamp, we can preserve the correct date provided
37  * that it is not also before 1601-01-01.
38  */
39
40 #define NANOSECONDS_PER_TICK    100
41 #define TICKS_PER_SECOND        (1000000000 / NANOSECONDS_PER_TICK)
42 #define TICKS_PER_MICROSECOND   (TICKS_PER_SECOND / 1000000)
43
44 /*
45  * EPOCH_DISTANCE is the number of seconds separating the Windows NT and UNIX
46  * epochs.  This is equal to ((1970-1601)*365+89)*24*60*60.  89 is the number
47  * of leap years between 1970 and 1601.
48  */
49 #define EPOCH_DISTANCE          11644473600
50
51 /* Windows NT timestamps to UNIX timestamps  */
52
53 time_t
54 wim_timestamp_to_time_t(u64 timestamp)
55 {
56         return (timestamp / TICKS_PER_SECOND) - EPOCH_DISTANCE;
57 }
58
59 struct timeval
60 wim_timestamp_to_timeval(u64 timestamp)
61 {
62         return (struct timeval) {
63                 .tv_sec = wim_timestamp_to_time_t(timestamp),
64                 .tv_usec = (timestamp % TICKS_PER_SECOND) / TICKS_PER_MICROSECOND,
65         };
66 }
67
68 struct timespec
69 wim_timestamp_to_timespec(u64 timestamp)
70 {
71         return (struct timespec) {
72                 .tv_sec = wim_timestamp_to_time_t(timestamp),
73                 .tv_nsec = (timestamp % TICKS_PER_SECOND) * NANOSECONDS_PER_TICK,
74         };
75 }
76
77 /* UNIX timestamps to Windows NT timestamps  */
78
79 u64
80 time_t_to_wim_timestamp(time_t t)
81 {
82         return ((u64)t + EPOCH_DISTANCE) * TICKS_PER_SECOND;
83 }
84
85 u64
86 timeval_to_wim_timestamp(const struct timeval *tv)
87 {
88         return time_t_to_wim_timestamp(tv->tv_sec) +
89                 (u32)tv->tv_usec * TICKS_PER_MICROSECOND;
90 }
91
92 u64
93 timespec_to_wim_timestamp(const struct timespec *ts)
94 {
95         return time_t_to_wim_timestamp(ts->tv_sec) +
96                 (u32)ts->tv_nsec / NANOSECONDS_PER_TICK;
97 }
98
99 /* Retrieve the current time as a WIM timestamp.  */
100 u64
101 now_as_wim_timestamp(void)
102 {
103         struct timeval tv;
104
105         /* On Windows we rely on MinGW providing gettimeofday() for us.  This
106          * could be changed to calling GetSystemTimeAsFileTime() directly, but
107          * now_as_wim_timestamp() isn't called much and it's simpler to keep the
108          * code for all platforms the same.  */
109         gettimeofday(&tv, NULL);
110         return timeval_to_wim_timestamp(&tv);
111 }
112
113 /* Translate a WIM timestamp into a human-readable string.  */
114 void
115 wim_timestamp_to_str(u64 timestamp, tchar *buf, size_t len)
116 {
117         struct tm tm;
118         time_t t = wim_timestamp_to_time_t(timestamp);
119         gmtime_r(&t, &tm);
120         tstrftime(buf, len, T("%a %b %d %H:%M:%S %Y UTC"), &tm);
121 }