/*
* [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 );
}
}
}