/* * [FileTimes.java] * * Summary: Access and set the Windows file times created, accessed, updated. * * Copyright: (c) 1997-2017 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.8+ * * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/ * * Version History: * 1.4 2007-04-26 * 1.5 2007-05-11 tidy code, add PAD and logo * 1.6 2007-06-13 add support for directory times. * 1.7 2007-07-28 fix bug, handles not always closed. * 1.8 2007-08-30 add TouchDirs, add setFileCreated, setFileLastAccessed, setFileLastModified * 1.9 2008-09-23 fix problem with Microsoft C++ auxiliary runtime library. * 2.0 2012-12-11 add 64-bit support. */ package com.mindprod.filetimes; import java.io.File; import java.text.SimpleDateFormat; import java.util.Date; import static java.lang.System.*; /** * Access and set the Windows file times created, accessed, updated. *

* Get and Set the create date, last access and last update times on a File in Windows NT, Win2K, XP, Vista. The * corresponding DLL generated from FileTimes.C usually lives standalone in E:/com/mindprod/filetimes/filetimes.dll. To * control this, change the nativefiletimes loadlibrary/load call. * * @author Roedy Green, Canadian Mind Products * @version 2.0 2012-12-11 add 64-bit support. * @since 1997 */ public final class FileTimes { /* * Used to turn on debugging harness. */ private static final boolean DEBUGGING = false; private static final int FIRST_COPYRIGHT_YEAR = 1997; /** * 86,400,000 the number of milliseconds in 24 hour day. Easily fits into an int. * Cannot use TimeUnit in Java 1.1 */ private static final int MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000; /** * Java timestamps use 64-bit milliseconds since 1970 GMT. Windows timestamps use 64-bit value representing the * number of 100-nanosecond intervals since January 1, 1601, with ten thousand times as much precision.
* DIFF_IN_MILLIS is the difference between January 1 1601 and January 1 1970 in milliseconds. This magic number * came from com.mindprod.common18.TestDate. Done according to Gregorian Calendar, no correction for 1752-09-02 * Wednesday was followed immediately by 1752-09-14 Thursday dropping 12 days. Also according to * http://gcc.gnu.org/ml/java-patches/2003-q1/msg00565.html */ private static final long DIFF_IN_MILLIS = 11644473600000L; /** * Undisplayed copyright notice. * * @noinspection UnusedDeclaration */ private static final String EMBEDDED_COPYRIGHT = "Copyright: (c) 1997-2017 Roedy Green, Canadian Mind Products, http://mindprod.com"; /** * Date this version was released to the public. * * @noinspection UnusedDeclaration */ private static final String RELEASE_DATE = "2012-12-11"; /** * Embedded version string. * * @noinspection UnusedDeclaration */ private static final String VERSION_STRING = "2.0"; static { // get DLL loaded from somewhere on java.library path. String dll = "filetimes.x"; try { // load filetimes.32.dll or filetimes.64.dll from path // arch is x86 or amd64 if ( System.getProperty( "os.arch" ).equals( "amd64" ) ) { dll = "filetimes.64"; } else { dll = "filetimes.32"; } // without .dll suffix System.loadLibrary( dll ); // if have troubles change this code to use load( // "E:\\com\\mindprod\\filetimes\\filetimes.32.dll" ); } catch ( Exception e ) { out.println( "Unable to load " + dll ); out.println( e.getMessage() ); System.exit( 1 ); } } /* dummy constructor. class is used statically */ private FileTimes() { } /** * Convert timestamp to displayable form. * * @param timestamp Java style time stamp to display, in ms since 1970-01-01. * * @return timestamp in form Thursday 2007-04-26 11:23:28 PM PDT */ static String display( long timestamp ) { if ( DEBUGGING ) { SimpleDateFormat sdf = new SimpleDateFormat( "EEE yyyy-MM-dd hh:mm:ss.SSS aa zz" ); return sdf.format( new Date( timestamp ) ); } else { return ""; } } /** * Get created date of a file. * * @param filename Name of file to change dates on. With / or \ no trailing /. * * @return return Java timestamp when file was created, 0 = problem. Accurate only to the mllisecond. * @noinspection SameParameterValue, WeakerAccess */ public static long getFileCreated( String filename ) { final boolean isDir = new File( filename ).isDirectory(); // used Windows conventions in native filename = filename.replace( '/', '\\' ); final long whenFileCreated = isDir ? nativeGetDirCreated( filename ) : nativeGetFileCreated( filename ); return whenFileCreated == 0 ? 0 : ( whenFileCreated / 10000 ) - DIFF_IN_MILLIS; } /** * Get last access date of a file. This may be 2 hours out of date since windows does not attempt to keep * immediately up to date. * * @param filename Name of file to request date on. With / or \ no trailing /. * * @return return Java timestamp when file was last accessed, 0 = problem, accurate only to the millisecond. * @noinspection WeakerAccess, SameParameterValue */ public static long getFileLastAccessed( String filename ) { final boolean isDir = new File( filename ).isDirectory(); // used Windows conventions in native filename = filename.replace( '/', '\\' ); final long whenFileLastAccessed = isDir ? nativeGetDirLastAccessed( filename ) : nativeGetFileLastAccessed( filename ); return whenFileLastAccessed == 0 ? 0 : ( whenFileLastAccessed / 10000 ) - DIFF_IN_MILLIS; } /** * Get last modify date of a file. Similar to File.lastModified. * * @param filename Name of file to request date on. With / or \ no trailing /. * * @return return Java timestamp when file was last updated, 0 = problem. Accurate only to the millisecond. * @noinspection SameParameterValue, WeakerAccess */ public static long getFileLastModified( String filename ) { final boolean isDir = new File( filename ).isDirectory(); // used Windows conventions in native filename = filename.replace( '/', '\\' ); final long whenFileLastModified = isDir ? nativeGetDirLastModified( filename ) : nativeGetFileLastModified( filename ); return whenFileLastModified == 0 ? 0 : ( whenFileLastModified / 10000 ) - DIFF_IN_MILLIS; } /** * test harness. * * @param args not used. */ public static void main( String[] args ) { out.println( "To find out the times of a file use the FT utility, not FileTimes." ); if ( DEBUGGING ) { // test files and directories final String[] testFiles = { "_O_V_E_R_V_I_E_W.txt", "C:/temp/temp.txt", "C:\\temp", "C:/temp", "G:\\temp" }; for ( final String testFile : testFiles ) { final boolean fileSuccess = FileTimes.setFileTimes( testFile, /* set created 2 days ago */ System.currentTimeMillis() - 2 * MILLISECONDS_PER_DAY, /* set accessed 2 hours ago */ System.currentTimeMillis() - 2 * MILLISECONDS_PER_DAY, /* updated 24 hours = 1 day ago */ System.currentTimeMillis() - 24 * MILLISECONDS_PER_DAY ); final long current = System.currentTimeMillis(); // do last accessed First, in case others disturb it. final long lastAccessed = FileTimes.getFileLastAccessed( testFile ); final long created = FileTimes.getFileCreated( testFile ); final long lastModified = FileTimes.getFileLastModified( testFile ); final long javaLastModified = new File( testFile ).lastModified(); out.println( "\n\nTesting the file times for " + testFile + " setFileTimes success:" + fileSuccess ); out.println( "\n[should be now] current time (ms): " + current + "\n" + display( current ) ); out.println( "\n[should be 2 days ago] created (ms):" + created + "\n" + display( created ) ); out.println( "\n[should be 24 hours ago] lastModified (ms):" + lastModified + "\n " + display( lastModified ) ); out.println( "\n[should be 24 hours ago] javaLastModified (ms):" + javaLastModified + "\n " + display( javaLastModified ) ); out.println( "\n[should be 2 hours ago] lastAccessed (ms):" + lastAccessed + "\n" + display( lastAccessed ) ); } } } /** * Get dir create date. * * @param dirname Name of dir to request date on. * * @return return Windows timestamp dir was created. */ public static native long nativeGetDirCreated( String dirname ); /** * Get dir last access date. * * @param dirname Name of dir to request date on. * * @return return Windows timestamp dir was last accessed. */ public static native long nativeGetDirLastAccessed( String dirname ); /** * Get dir last modified date. * * @param dirname Name of dir to request date on. * * @return return Windows timestamp dir was last updated. */ public static native long nativeGetDirLastModified( String dirname ); /** * Get file create date. * * @param filename Name of file to request date on. * * @return return Windows timestamp file was created. */ public static native long nativeGetFileCreated( String filename ); /** * Get file last access date. * * @param filename Name of file to request date on. * * @return return Windows timestamp file was last accessed. */ public static native long nativeGetFileLastAccessed( String filename ); /** * Get file last modified date. * * @param filename Name of file to request date on. * * @return return Windows timestamp file was last updated. */ public static native long nativeGetFileLastModified( String filename ); /** * Native method to access Windows-specific code. Times are in 100ns intervals since since January 1, 1601. * * @param dirname Name of dir to change dates on. . * @param whenDirCreated When dir created, Windows format. * @param whenDirLastAccessed When dir last accessed, Windows format. * @param whenDirLastUpdated When dir last updated, Windows format. * * @return return true if all went ok. */ public static native boolean nativeSetDirTimes( String dirname, long whenDirCreated, long whenDirLastAccessed, long whenDirLastUpdated ); /** * Native method to access Windows-specific code. Times are in 100ns intervals since since January 1, 1601. * * @param filename Name of file to change dates on. . * @param whenFileCreated When file created, Windows format. * @param whenFileLastAccessed When file last accessed, Windows format. * @param whenFileLastUpdated When file last updated, Windows format. * * @return return true if all went ok. */ public static native boolean nativeSetFileTimes( String filename, long whenFileCreated, long whenFileLastAccessed, long whenFileLastUpdated ); /** * Set FileCreated TimeStamp on a file. * * @param filename Name of file to change dates on. With / or \ no trailing /. * @param whenFileCreated timestamp milliseconds since 1970 GMT. Date the file was created. 0 means leave as is. * Note this means you cannot set the timetame to 1970-01-01 0:00 * * @return return true if all went ok. * @noinspection SameParameterValue, WeakerAccess, UnusedReturnValue */ public static boolean setFileCreated( String filename, long whenFileCreated ) { // Java timestamps use 64-bit milliseconds since 1970 GMT. // Windows timestamps use 64-bit value representing the number // of 100-nanosecond intervals since January 1, 1601. // convert to Windows scheme. final boolean isDir = new File( filename ).isDirectory(); // used Windows conventions in native filename = filename.replace( '/', '\\' ); whenFileCreated = whenFileCreated == 0 ? 0 : ( whenFileCreated + DIFF_IN_MILLIS ) * 10000; if ( isDir ) { return nativeSetDirTimes( filename, whenFileCreated, 0, 0 ); } else { return nativeSetFileTimes( filename, whenFileCreated, 0, 0 ); } } /** * Set Last Accessed TimeStamp on a file. * * @param filename Name of file to change dates on. With / or \ no trailing /. * @param whenFileLastAccessed timestamp milliseconds since 1970 GMT. Date the file was last read. 0 means leave as * is. * * @return return true if all went ok. * @noinspection SameParameterValue, WeakerAccess, UnusedReturnValue */ public static boolean setFileLastAccessed( String filename, long whenFileLastAccessed ) { // Java timestamps use 64-bit milliseconds since 1970 GMT. // Windows timestamps use 64-bit value representing the number // of 100-nanosecond intervals since January 1, 1601. // convert to Windows scheme. final boolean isDir = new File( filename ).isDirectory(); // used Windows conventions in native filename = filename.replace( '/', '\\' ); whenFileLastAccessed = whenFileLastAccessed == 0 ? 0 : ( whenFileLastAccessed + DIFF_IN_MILLIS ) * 10000; if ( isDir ) { return nativeSetDirTimes( filename, 0, whenFileLastAccessed, 0 ); } else { return nativeSetFileTimes( filename, 0, whenFileLastAccessed, 0 ); } } /** * Set fileLastUpdated TimeStamp on a file. * * @param filename Name of file to change dates on. With / or \ no trailing /. * @param whenFileLastUpdated timestamp milliseconds since 1970 GMT. Date the file was last written. 0 means leave * as is. * * @return return true if all went ok. * @noinspection SameParameterValue, WeakerAccess, UnusedReturnValue */ public static boolean setFileLastModified( String filename, long whenFileLastUpdated ) { // Java timestamps use 64-bit milliseconds since 1970 GMT. // Windows timestamps use 64-bit value representing the number // of 100-nanosecond intervals since January 1, 1601. // convert to Windows scheme. final boolean isDir = new File( filename ).isDirectory(); // used Windows conventions in native filename = filename.replace( '/', '\\' ); whenFileLastUpdated = whenFileLastUpdated == 0 ? 0 : ( whenFileLastUpdated + DIFF_IN_MILLIS ) * 10000; if ( isDir ) { return nativeSetDirTimes( filename, 0, 0, whenFileLastUpdated ); } else { return nativeSetFileTimes( filename, 0, 0, whenFileLastUpdated ); } } /** * Set TimeStamps on a file. * * @param filename Name of file to change dates on. With / or \ no trailing /. * @param whenFileCreated timestamp milliseconds since 1970 GMT. Date the file was created. 0 means leave as * is. Note this means you cannot set the timetame to 1970-01-01 0:00 * @param whenFileLastAccessed timestamp milliseconds since 1970 GMT. Date the file was last read. 0 means leave as * is. * @param whenFileLastUpdated timestamp milliseconds since 1970 GMT. Date the file was last written. 0 means leave * as is. * * @return return true if all went ok. Monitor this carefully. Windows sometimes rejects settings for reasons like * dir is read-only or system or dates are inconsistent. * @noinspection SameParameterValue, WeakerAccess, UnusedReturnValue */ public static boolean setFileTimes( String filename, long whenFileCreated, long whenFileLastAccessed, long whenFileLastUpdated ) { // Java timestamps use 64-bit milliseconds since 1970 GMT. // Windows timestamps use 64-bit value representing the number // of 100-nanosecond intervals since January 1, 1601. // convert to Windows scheme. final boolean isDir = new File( filename ).isDirectory(); // used Windows conventions in native filename = filename.replace( '/', '\\' ); whenFileCreated = whenFileCreated == 0 ? 0 : ( whenFileCreated + DIFF_IN_MILLIS ) * 10000; whenFileLastAccessed = whenFileLastAccessed == 0 ? 0 : ( whenFileLastAccessed + DIFF_IN_MILLIS ) * 10000; whenFileLastUpdated = whenFileLastUpdated == 0 ? 0 : ( whenFileLastUpdated + DIFF_IN_MILLIS ) * 10000; if ( isDir ) { return nativeSetDirTimes( filename, whenFileCreated, whenFileLastAccessed, whenFileLastUpdated ); } else { return nativeSetFileTimes( filename, whenFileCreated, whenFileLastAccessed, whenFileLastUpdated ); } } }