/*
* timestamp.c
+ *
+ * Conversion between Windows NT timestamps and UNIX timestamps.
*/
/*
- * Copyright (C) 2012, 2013 Eric Biggers
- *
- * This file is part of wimlib, a library for working with WIM files.
+ * Copyright (C) 2012-2017 Eric Biggers
*
- * wimlib is free software; you can redistribute it and/or modify it under the
- * terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version.
+ * This file is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
*
- * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- * A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * This file is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
- * You should have received a copy of the GNU General Public License
- * along with wimlib; if not, see http://www.gnu.org/licenses/.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this file; if not, see http://www.gnu.org/licenses/.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
-#include "wimlib/types.h"
+#include "wimlib.h" /* for struct wimlib_timespec */
#include "wimlib/timestamp.h"
-#include <time.h>
-#include <sys/time.h>
+/*
+ * Timestamps in WIM files are Windows NT timestamps, or FILETIMEs: 64-bit
+ * values storing the number of 100-nanosecond ticks since January 1, 1601.
+ *
+ * Note: UNIX timestamps are signed; Windows timestamps are not. Negative UNIX
+ * timestamps represent times before 1970-01-01. When such a timestamp is
+ * converted to a Windows timestamp, we can preserve the correct date provided
+ * that it is not also before 1601-01-01.
+ */
+
+#define NANOSECONDS_PER_TICK 100
+#define TICKS_PER_SECOND (1000000000 / NANOSECONDS_PER_TICK)
+#define TICKS_PER_MICROSECOND (TICKS_PER_SECOND / 1000000)
+
+/*
+ * EPOCH_DISTANCE is the number of seconds separating the Windows NT and UNIX
+ * epochs. This is equal to ((1970-1601)*365+89)*24*60*60. 89 is the number
+ * of leap years between 1970 and 1601.
+ */
+#define EPOCH_DISTANCE 11644473600
+
+/* Windows NT timestamps to UNIX timestamps */
+
+time_t
+wim_timestamp_to_time_t(u64 timestamp)
+{
+ return (timestamp / TICKS_PER_SECOND) - EPOCH_DISTANCE;
+}
+
+void
+wim_timestamp_to_wimlib_timespec(u64 timestamp, struct wimlib_timespec *wts,
+ s32 *high_part_ret)
+{
+ s64 sec = (timestamp / TICKS_PER_SECOND) - EPOCH_DISTANCE;
+
+ wts->tv_sec = sec;
+ wts->tv_nsec = (timestamp % TICKS_PER_SECOND) * NANOSECONDS_PER_TICK;
+
+ if (sizeof(wts->tv_sec) == 4)
+ *high_part_ret = sec >> 32;
+}
+
+#ifdef __WIN32__
+static _unused_attribute void
+check_sizeof_time_t(void)
+{
+ /* Windows builds should always be using 64-bit time_t now. */
+ STATIC_ASSERT(sizeof(time_t) == 8);
+}
+#else
+struct timeval
+wim_timestamp_to_timeval(u64 timestamp)
+{
+ return (struct timeval) {
+ .tv_sec = wim_timestamp_to_time_t(timestamp),
+ .tv_usec = (timestamp % TICKS_PER_SECOND) / TICKS_PER_MICROSECOND,
+ };
+}
+
+struct timespec
+wim_timestamp_to_timespec(u64 timestamp)
+{
+ return (struct timespec) {
+ .tv_sec = wim_timestamp_to_time_t(timestamp),
+ .tv_nsec = (timestamp % TICKS_PER_SECOND) * NANOSECONDS_PER_TICK,
+ };
+}
+
+/* UNIX timestamps to Windows NT timestamps */
u64
-get_wim_timestamp(void)
+time_t_to_wim_timestamp(time_t t)
+{
+ return ((u64)t + EPOCH_DISTANCE) * TICKS_PER_SECOND;
+}
+
+u64
+timeval_to_wim_timestamp(const struct timeval *tv)
+{
+ return time_t_to_wim_timestamp(tv->tv_sec) +
+ (u32)tv->tv_usec * TICKS_PER_MICROSECOND;
+}
+
+u64
+timespec_to_wim_timestamp(const struct timespec *ts)
+{
+ return time_t_to_wim_timestamp(ts->tv_sec) +
+ (u32)ts->tv_nsec / NANOSECONDS_PER_TICK;
+}
+
+/* Retrieve the current time as a WIM timestamp. */
+u64
+now_as_wim_timestamp(void)
{
struct timeval tv;
+
gettimeofday(&tv, NULL);
- return timeval_to_wim_timestamp(tv);
+ return timeval_to_wim_timestamp(&tv);
}
+#endif /* !__WIN32__ */
+/* Translate a WIM timestamp into a human-readable string. */
void
wim_timestamp_to_str(u64 timestamp, tchar *buf, size_t len)
{
struct tm tm;
- time_t t = wim_timestamp_to_unix(timestamp);
+ time_t t = wim_timestamp_to_time_t(timestamp);
+
gmtime_r(&t, &tm);
tstrftime(buf, len, T("%a %b %d %H:%M:%S %Y UTC"), &tm);
}
-