Windows timestamp accessor library
With help from C#, now Java and Python programs can get and set high-precision Windows file timestamps.
Windows stores timestamps for each file and folder in a file system to the granularity of 100 ns. However, most tools that are not specifically designed for Windows cannot operate at this precision, especially cross-platform file formats and programming languages that have a strong presence in the Unix world. For example, file archive formats might only store timestamps to the granularity of one second, or programming languages might only let you query the modification timestamp but not the creation timestamp. Furthermore, some programming languages don’t provide a way to set any file timestamps at all.
I’ve implemented a standalone Windows C# program that can get and set all the 3 types of file/
This set of software can be considered a meta-tool – a tool for building application tools. With the library, it’s easy to write programs like these: A tool that adds an offset to a file’s timestamps, a tool to copy timestamps from one file to another, a tool to archive file timestamps to a text file.
Download
Libraries:
- WindowsTimestampAccessor.cs (C#)
- WindowsTimestampAccessor.java (Java)
- wintimestamp.py (Python)
Extras:
- WindowsTimestampAccessorDemo.java (Java)
- WindowsTimestampAccessorDatetimeTest.java (Java, JUnit)
- wintimestamp-demo.py (Python)
- GetTimestamps.cs (C#)
- SetCreationTime.cs (C#)
- SetModificationTime.cs (C#)
- SetAccessTime.cs (C#)
The EXE file must be placed in the current working directory when running the Java VM or Python interpreter.
API and examples
Java
API summary:
class WindowsTimestampAccessor { WindowsTimestampAccessor(); void close(); long getCreationTime(File f); long getModificationTime(File f); long getAccessTime(File f); void setCreationTime(File f, long ticks); void setModificationTime(File f, long ticks); void setAccessTime(File f, long ticks); static int[] ticksToDatetime(long ticks); static long datetimeToTicks(int... dt); }
Usage example:
try (var wt = new WindowsTimestampAccessor()) { File f0 = new File("hello.txt"); long ct = wt.getCreationTime(f0); File f1 = new File("goodbye.txt"); long newct = ct + 86400_0000000L; // A day later wt.setModificationTime(f1, newct); File f2 = new File("你好.png"); // Supports Unicode System.out.println(wt.getAccessTime(f2)); }
Notes:
Objects of this class are thread-safe.
-
Avoid creating more than one
WindowsTimestampAccessor
object:The object is stateless, so it doesn’t matter what transactions were previously processed.
Each object creates a new external native process, which is expensive in time and memory.
Multiple threads can use one object without worrying about synchronization.
Please explicitly call
close()
to terminate the external native process. Otherwise, the process may linger until the Java VM exists. (Or if you’re lucky, the output stream on the Java side will be closed at garbage collection, the external process will see the end of its input and terminate itself.)To convert from Unix time in seconds to Windows time in ticks:
ticks = unixSeconds * 10_000_000L + WindowsTimestampAccessor
..datetimeToTicks( 1970, 1, 1, 0, 0, 0, 0);
Python
API summary:
class WindowsTimestampAccessor: def __init__(self) def close(self) def __enter__(self) # with-statement def __exit__(self, t, v, b) # with-statement def get_creation_time (self, path) # Returns int def get_modification_time(self, path) # Returns int def get_access_time (self, path) # Returns int def set_creation_time (self, path, ticks) # Returns None def set_modification_time(self, path, ticks) # Returns None def set_access_time (self, path, ticks) # Returns None # Module functions def ticks_to_datetime(ticks) # Returns datetime.datetime object def datetime_to_ticks(dt) # Returns int
Usage example:
import wintimestamp # Using with-statement (preferred) with wintimestamp.WindowsTimestampAccessor() as wt: t = wt.get_modification_time("myfile.doc") print(wt.ticks_to_datetime(t)) print("Ticks: ", t) # Old-style resource management wt = wintimestamp.WindowsTimestampAccessor() try: # Supports Unicode file names ct = 621355968000000000 wt.set_creation_time(u"\u4F60\u597D.png", ct) finally: wt.close()
Notes:
Objects of this class are not thread-safe. You will need to add your own locks when multithreading.
Please either use the
with
-statement or explicitly callclose()
, to ensure that the external native process is terminated in a timely manner.
C# program’s protocol
Syntax for both standard input and output (stdin, stdout):
The binary streams are interpreted as UTF-8 without BOM.
Newlines are universal (CR+LF / LF / CR). (Both sides must accept universal newlines.)
Each line is composed of one or more tab-separated tokens.
Each input line is a query command, which generates a single output line as a response.
The response to each input line is either “error” or the appropriate affirmative response.
Commands:
Input: GetCreationTime <tab> <path>
Output: ok <tab> <int64 ticks>Input: GetModificationTime <tab> <path>
Output: ok <tab> <int64 ticks>Input: GetAccessTime <tab> <path>
Output: ok <tab> <int64 ticks>Input: SetCreationTime <tab> <path> <tab> <int64 ticks>
Output: okInput: SetModificationTime <tab> <path> <tab> <int64 ticks>
Output: okInput: SetAccessTime <tab> <path> <tab> <int64 ticks>
Output: okInput: (EOF)
Output: (EOF, and the process exits with status 0)
Usage example:
C:\>WindowsTimestampAccessor.exe GetModificationTime C:\ ok 635462611321362140 SetCreationTime D:\pic.jpg 635463434961076893 ok GetFoobar error GetAccessTime X:\notexist error SetAccessTime C:\thing.doc abc123 error
Notes:
The raw protocol is shown here for hardcore developers only. Typical developers can ignore this information and use the Java or Python library.
It is strongly recommended to use absolute paths rather than relative paths. Otherwise you need to be careful in keeping track of the process’s working directory.
The program cannot handle file paths that contain newline or tab characters (
\r
,\n
,\t
).It is acceptable to connect stdin to a file and/or stdout to a file.
The program takes no command-line arguments.
The caller of the program should check the exit status code.
Notes
Windows’s native timestamp format, called ticks, is a signed 64-bit integer representing 107 times the number of seconds since midnight UTC on January 1st, Year 1 on the proleptic Gregorian calendar.
NTFS provides all 3 timestamps to the precision of ticks. But FAT12/
FAT16/ FAT32 provides the creation timestamp to 10 ms precision, the modification timestamp to 2 s precision, and the access timestamp to 1 day precision. NTFS stores timestamps in UTC, so the values stay consistent even if the system time zone changes or the files are moved to another NTFS network share.
FAT stores timestamps in local time, so the meaning of the values depends on the current system time zone. Moving files between computers or between FAT and NTFS volumes has a high chance of wrecking meaningful timestamps.
Since the C# program uses stdin and stdout instead of foreign function interfaces, it’s relatively to interface with it from any programming language.
The C# program will set timestamps on a read-only file by temporarily unmarking it as read-only, changing the timestamp, and restoring the read-only attribute.
-
Java SE 7 adds a set of APIs to get and set these 3 timestamps on Windows systems. However, their API takes some effort to convert between linear timestamps and date-and-time fields. The set of relevant functions consists of:
java.nio.Files.readAttributes()
,java.nio.Files.setAttribute()
,java.nio.file.attribute.BasicFileAttributes
,java.nio.file.attribute.FileTime
.OpenJDK’s initial implementation of Windows file timestamps, from Java 7 to 13, only supported microsecond granularity (i.e. 10 ticks, even when using
TimeUnit.NANOSECONDS
). This means that information is lost when reading or writing timestamps. However, this was fixed in OpenJDK 14.