Set Date Modified with sub-second timestamp?


pokeefe0001

Well-known member
Member
VIP
Local time
2:11 AM
Posts
217
Location
Pacific Northwest USA
OS
Windows 11
I have a need (which I'll explain below ... if I remember) to set the Date Modified timestamp on each file in a folder such that each timestamp is slightly higher than the previous file's timestamp (with the directory sorted by name). The actual value of seconds is not important, but I need to be able to set the date, hour, and minute to a specific value. Almost every technique I've seen for doing this allows setting time to the second, but since I'm taking about 80-100 files per directory so I need a resolution below second. The NirSolf "NirCMD SetFileTime" does this when using the "Now" parameter, but for setting specific values it allows specifying only hh:mm:ss. Are there any utilities that allow specifying sub-second values or take timestamps in FILETIME format? I need this to be something that can be called from a script, not invoked a from a GUI. And it would be nice if that script could be a .bat file; my knowledge of PowerShell is next to nonexistent. (I could cope with a VBS script if I really had to.)

Justification:
I have a number of folders whose contained files are sorted by Date Modified - a couple hundred folders with about 50,000 files spread among them. I need to insert several hundred files into a couple dozen folders. Each group of new files is in its own folder and - luckily - is in the correct order sorted by name. I can manually find a date and time that would insert the new files at the correct place in their destination folder, assign that timestamp to the first file to be inserted, increase the timestamp by a small amount and assign it to the next file, etc.
 

My Computer

System One

  • OS
    Windows 11
    Computer type
    PC/Desktop
    Manufacturer/Model
    Microsoft
    CPU
    Intel Core i5-8400
    Motherboard
    ASUS PRIME H370-PLUS
    Memory
    16GB
    Graphics Card(s)
    Intel UHD Graphics 630
    Sound Card
    On board
    Monitor(s) Displays
    Samsung SyncMaster 2043BWX
    Screen Resolution
    1680 x 1050
    Hard Drives
    Samsung SSD 850 256GB
    WDC 1TB NVMe
    WD 3TB external USB drive
    PSU
    I don't remember
    Case
    Corsair something-or-other
    Cooling
    Air CPU + 2 case fans
    Keyboard
    DAS S Pro (Cherry Brown)
    Mouse
    Logitech USB of some sort
SetFileTime.bat
Code:
<# : batch script
@echo off
powershell -nop Invoke-Expression ('$args = @(''%*'' -split '' '')' + [System.IO.File]::ReadAllText('%~f0'))
goto :eof
#>

if ($args.Count -ne 3) {
    "Usage: SetFileTime [file] `"01-22-2005 21:22:55.123`""
    exit
}

$Filename = $args[0]
$NewTimestamp = $args[1..($args.Count -1)]

if (!(Test-Path $Filename -PathType Leaf)) {
    "File not found: $Filename"
    exit 1
}

$File = Get-Item $Filename

try { $File.LastWriteTime = [datetime][string]$NewTimestamp }
catch {
    "Invalid date format: $NewTimestamp"
    exit 1
}

$NewTimestamp = $File.LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss.fff")
Write-Host "$NewTimestamp $($File.FullName)"

The script changes the provided file's Modified Date to any correct DateTime string, where 21:22:55.123 is 123 milliseconds. Correct format means whatever matches your local date format in Windows Settings. Quotes surrounding the timestamp are optional.

If successful, the script reports the file's Modified Date for confirmation. To make it silent, just put a # in front of the "Write-Host".

SetFileTime.bat
Usage: SetFileTime [file] "01-22-2005 21:22:55.123"

SetFileTime.bat missing_file "01-22-2005 21:22:55.123"
File not found: missing_file

SetFileTime.bat testfile.txt "01-abc-2005 21:22:55.123"
Invalid date format: 01-abc-2005 21:22:55.123

SetFileTime.bat testfile.txt "01-22-2005 21:22:55.123"
2005-01-22 21:22:55.123 C:\Users\GARLIN\Downloads\testfile.txt
 

My Computer

System One

  • OS
    Windows 7
Thank you. I'm trying to figure out how the script works, but, as I said, I know almost nothing about Powershell. I can make no sense whatsoever of
Code:
powershell -nop Invoke-Expression ('$args = @(''%*'' -split '' '')' + [System.IO.File]::ReadAllText('%~f0'))
but I suspect it reads the current file (a .bat file) and executes it as a Powershell script.

If that is what's going on it would probably make more sense for to pass a directory name and a starting hh:mm:ss timestamp, have the script produce a list of files in the directory, set the millisecond field to zero and loop through the file list setting the timestamp and incrementing the millisecond field for each new file. I'm sure it will involve (Get-ChildItem -File) but I suspect I'm going to flail around a bit.

At least your solution looks more like a script that most Powershell examples I've seen. Most just seem to pipe cmdlets together. In yours I can at least see some logic statements. An "If" statement; assignments. Wow!
 

My Computer

System One

  • OS
    Windows 11
    Computer type
    PC/Desktop
    Manufacturer/Model
    Microsoft
    CPU
    Intel Core i5-8400
    Motherboard
    ASUS PRIME H370-PLUS
    Memory
    16GB
    Graphics Card(s)
    Intel UHD Graphics 630
    Sound Card
    On board
    Monitor(s) Displays
    Samsung SyncMaster 2043BWX
    Screen Resolution
    1680 x 1050
    Hard Drives
    Samsung SSD 850 256GB
    WDC 1TB NVMe
    WD 3TB external USB drive
    PSU
    I don't remember
    Case
    Corsair something-or-other
    Cooling
    Air CPU + 2 case fans
    Keyboard
    DAS S Pro (Cherry Brown)
    Mouse
    Logitech USB of some sort
I was expecting you to ask for looping through an entire folder, since that's easier on you.

Don't worry about the header block. It's a clever trick to make BATCH run a PS script without disturbing the PS Execution Policy, by reading the same file into a buffer and running it.

Given a folder, how would prefer to order the files in the final sequence? Alphabetically, sorted by their current modified date, specified by user (from a text file)? I assume it would be something resembling:

SetFileTime [folder] [base timestamp]

Also would assume it's all files on that folder level, and NOT to recursively process all subfolders or files.
 

My Computer

System One

  • OS
    Windows 7
Don't worry about the header block. It's a clever trick to make BATCH run a PS script without disturbing the PS Execution Policy, by reading the same file into a buffer and running it.
That feels vaguely creepy - like something that could be exploited by malware, although I'm not sure how.

Given a folder, how would prefer to order the files in the final sequence? Alphabetically, sorted by their current modified date, specified by user (from a text file)?

At the moment, sorted alphabetically by name would be the way to go since the files to be added currently have a meaningless modified date. I think the all have the same timestamp. However, I hope the code is clear enough that I could change that if the need arises.

I assume it would be something resembling:

SetFileTime [folder] [base timestamp]

Also would assume it's all files on that folder level, and NOT to recursively process all subfolders or files.

Both assumptions are correct, For now, there are only files in the given folders, but if one did contain a nested folder I would want to handle that nested folder on its own,

BTW, expect questions regarding the script. With luck I'll pick up a little Powershell knowledge from reading it..

Thanks for your help.
 

My Computer

System One

  • OS
    Windows 11
    Computer type
    PC/Desktop
    Manufacturer/Model
    Microsoft
    CPU
    Intel Core i5-8400
    Motherboard
    ASUS PRIME H370-PLUS
    Memory
    16GB
    Graphics Card(s)
    Intel UHD Graphics 630
    Sound Card
    On board
    Monitor(s) Displays
    Samsung SyncMaster 2043BWX
    Screen Resolution
    1680 x 1050
    Hard Drives
    Samsung SSD 850 256GB
    WDC 1TB NVMe
    WD 3TB external USB drive
    PSU
    I don't remember
    Case
    Corsair something-or-other
    Cooling
    Air CPU + 2 case fans
    Keyboard
    DAS S Pro (Cherry Brown)
    Mouse
    Logitech USB of some sort
I finished the script last night. There's two ways to proceed on the date order, depending on what works better for you.
If you consider the provided timestamp as the "anchor", we can re-stamp the files fanning out in forwards or backwards direction.

1. Starting from A to Z, the first file has the start time, and the last file is +N intervals newer in time than the first.
A, B, C, D, E ... Z
0, +1, +2,+ 3, +4 ... +25 milliseconds

2. Starting from A to Z, the last file has the start time, and the first file is +N intervals older than the last.
A, U, V, X, Y, Z
-25 milliseconds ... -3, -2, -1, 0

Whichever direction doesn't matter to me, but it depends if you're trying to fit a group into a time slot BEFORE or AFTER a known point in time. This might get important if you don't want to count how many files are in a folder, and do the math. I hope you understand what I'm anticipating here.
 

My Computer

System One

  • OS
    Windows 7
I can't think of any reason I would ever want the times decrementing, but Murphy says I'll run into it at an inconvenient time.
So how about SetFileTime [folder] [base timestamp] [increment] where increment is a signed value?
 

My Computer

System One

  • OS
    Windows 11
    Computer type
    PC/Desktop
    Manufacturer/Model
    Microsoft
    CPU
    Intel Core i5-8400
    Motherboard
    ASUS PRIME H370-PLUS
    Memory
    16GB
    Graphics Card(s)
    Intel UHD Graphics 630
    Sound Card
    On board
    Monitor(s) Displays
    Samsung SyncMaster 2043BWX
    Screen Resolution
    1680 x 1050
    Hard Drives
    Samsung SSD 850 256GB
    WDC 1TB NVMe
    WD 3TB external USB drive
    PSU
    I don't remember
    Case
    Corsair something-or-other
    Cooling
    Air CPU + 2 case fans
    Keyboard
    DAS S Pro (Cherry Brown)
    Mouse
    Logitech USB of some sort
Code:
<# : batch script
@echo off
powershell -nop Invoke-Expression ('$args = @(''%*'' -split '' '')' + [System.IO.File]::ReadAllText('%~f0'))
goto :eof
#>

$MyDateFormat = ([cultureinfo]::CurrentCulture.DateTimeFormat.SortableDateTimePattern -replace 'T',' ') + '.fff'

if ($args.Count -lt 3) {
    'USAGE:  SetFileTime.bat [folder] "{0}" [increment]' -f (Get-Date "2005-01-22 21:22:55.123").ToString($MyDateFormat)
    exit
}

$Count = $args.Count

$FolderName = $args[0]
$DateArgs = $args[1..($Count -2)]
$Increment = $args[$Count -1]

if (!(Test-Path $FolderName -PathType Container)) {
    "Not a valid folder: $FolderName"
    exit 1
}

try {
    $NewTimestamp = [datetime][string]$DateArgs
}
catch {
    "Invalid date format: $DateArgs"
    exit 1
}

try {
    $Increment = [convert]::ToInt16($Increment)
}
catch {
    "Increment: $Increment is not integer."
    exit 1
}

if ($Increment -gt 0) {
    foreach ($File in (Get-ChildItem $FolderName -File)) {
        "{0}  {1}" -f $NewTimestamp.ToString($MyDateFormat), $File.FullName
        $File.LastWriteTime = $NewTimestamp
        $NewTimestamp = $NewTimestamp.AddMilliseconds($Increment)
    }
}
else {
    @(foreach ($File in (Get-ChildItem $FolderName -File | Sort-Object Name -Descending)) {
        "{0}  {1}" -f $NewTimestamp.ToString($MyDateFormat), $File.FullName
        $File.LastWriteTime = $NewTimestamp
        $NewTimestamp = $NewTimestamp.AddMilliseconds($Increment)
    }) | Sort
}

C:\Users\GARLIN\Downloads>SetFileTime.bat NEW_FOLDER "2005-01-22 21:22:55.123" 2
2005-01-22 21:22:55.123 C:\Users\GARLIN\Downloads\NEW_FOLDER\Microsoft-Windows-Printing-PMCPPC-FoD-Package~31bf3856ad364e35~amd64~en-US~.cab
2005-01-22 21:22:55.125 C:\Users\GARLIN\Downloads\NEW_FOLDER\Microsoft-Windows-Printing-PMCPPC-FoD-Package~31bf3856ad364e35~amd64~~.cab
2005-01-22 21:22:55.127 C:\Users\GARLIN\Downloads\NEW_FOLDER\pokee.ps1
2005-01-22 21:22:55.129 C:\Users\GARLIN\Downloads\NEW_FOLDER\SetFileTime.bat
2005-01-22 21:22:55.131 C:\Users\GARLIN\Downloads\NEW_FOLDER\SetupRST.exe
2005-01-22 21:22:55.133 C:\Users\GARLIN\Downloads\NEW_FOLDER\syncui.dll.mui
2005-01-22 21:22:55.135 C:\Users\GARLIN\Downloads\NEW_FOLDER\testfile.txt
2005-01-22 21:22:55.137 C:\Users\GARLIN\Downloads\NEW_FOLDER\Untitled1.ps1
2005-01-22 21:22:55.139 C:\Users\GARLIN\Downloads\NEW_FOLDER\Untitled3.ps1
2005-01-22 21:22:55.141 C:\Users\GARLIN\Downloads\NEW_FOLDER\XML.xml

C:\Users\GARLIN\Downloads>SetFileTime.bat NEW_FOLDER "2005-01-22 21:22:55.123" -2
2005-01-22 21:22:55.105 C:\Users\GARLIN\Downloads\NEW_FOLDER\Microsoft-Windows-Printing-PMCPPC-FoD-Package~31bf3856ad364e35~amd64~~.cab
2005-01-22 21:22:55.107 C:\Users\GARLIN\Downloads\NEW_FOLDER\Microsoft-Windows-Printing-PMCPPC-FoD-Package~31bf3856ad364e35~amd64~en-US~.cab
2005-01-22 21:22:55.109 C:\Users\GARLIN\Downloads\NEW_FOLDER\pokee.ps1
2005-01-22 21:22:55.111 C:\Users\GARLIN\Downloads\NEW_FOLDER\SetFileTime.bat
2005-01-22 21:22:55.113 C:\Users\GARLIN\Downloads\NEW_FOLDER\SetupRST.exe
2005-01-22 21:22:55.115 C:\Users\GARLIN\Downloads\NEW_FOLDER\syncui.dll.mui
2005-01-22 21:22:55.117 C:\Users\GARLIN\Downloads\NEW_FOLDER\testfile.txt
2005-01-22 21:22:55.119 C:\Users\GARLIN\Downloads\NEW_FOLDER\Untitled1.ps1
2005-01-22 21:22:55.121 C:\Users\GARLIN\Downloads\NEW_FOLDER\Untitled3.ps1
2005-01-22 21:22:55.123 C:\Users\GARLIN\Downloads\NEW_FOLDER\XML.xml
 

My Computer

System One

  • OS
    Windows 7
It doesn't work for me.
Code:
C:\Batch_files>SetFileTime.bat "D:\Test\X001_Abc\Set_001" "2010-01-22 21:22:55.123" 25
Invoke-Expression : At line:54 char:11
+ }.FullName)"
+           ~
Unexpected token ')' in expression or statement.
At line:54 char:12
+ }.FullName)"
+            ~
The string is missing the terminator: ".
At line:1 char:1
+ Invoke-Expression ('$args = @(''D:\Test\X001_Abc\Set_001 2010-01-22 2 ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ParserError: (:) [Invoke-Expression], ParseException
    + FullyQualifiedErrorId : UnexpectedToken,Microsoft.PowerShell.Commands.InvokeExpressionCommand

I tried with and without quotes around around the first argument with no change in results.
On the web I saw recommendation to use the whole quoted argument within single quotes, but that didn't work.
I tried various ways to concatenate a double quote to the beginning and end of $Foldername within the script, but nothing I tried worked.
 

My Computer

System One

  • OS
    Windows 11
    Computer type
    PC/Desktop
    Manufacturer/Model
    Microsoft
    CPU
    Intel Core i5-8400
    Motherboard
    ASUS PRIME H370-PLUS
    Memory
    16GB
    Graphics Card(s)
    Intel UHD Graphics 630
    Sound Card
    On board
    Monitor(s) Displays
    Samsung SyncMaster 2043BWX
    Screen Resolution
    1680 x 1050
    Hard Drives
    Samsung SSD 850 256GB
    WDC 1TB NVMe
    WD 3TB external USB drive
    PSU
    I don't remember
    Case
    Corsair something-or-other
    Cooling
    Air CPU + 2 case fans
    Keyboard
    DAS S Pro (Cherry Brown)
    Mouse
    Logitech USB of some sort
Are you using the 2nd copy of the script (post #8)?

C:\Users\GARLIN\Downloads>SetFileTime.bat "C:\Users\GARLIN\Downloads\NEW_FOLDER" "2005-01-22 21:22:55.123" 2
2005-01-22 21:22:55.123 C:\Users\GARLIN\Downloads\NEW_FOLDER\pokee.ps1
2005-01-22 21:22:55.125 C:\Users\GARLIN\Downloads\NEW_FOLDER\SetFileTime.bat
2005-01-22 21:22:55.127 C:\Users\GARLIN\Downloads\NEW_FOLDER\syncui.dll.mui
2005-01-22 21:22:55.129 C:\Users\GARLIN\Downloads\NEW_FOLDER\Untitled3.ps1
2005-01-22 21:22:55.131 C:\Users\GARLIN\Downloads\NEW_FOLDER\Untitled4.ps1
 

My Computer

System One

  • OS
    Windows 7
I pasted the wrong stuff into my last posting, but yes, I'm using the new script.
This is what I should have pasted:
(with quotes around the path)
Code:
C:\Batch_files>SetFileTime.bat "D:\Test\X001 Abc\Set 001" "2010-01-22 21:22:55.123" 25
Not a valid folder: D:\Test\X001
(and without quotes around the path)
Code:
C:\Batch_files>SetFileTime.bat D:\Test\X001 Abc\Set 001 "2010-01-22 21:22:55.123" 25
Not a valid folder: D:\Test\X001
Either way parsing of the path stops at the first blank in the path.

The script work fine if there are no blanks in the path.
 

My Computer

System One

  • OS
    Windows 11
    Computer type
    PC/Desktop
    Manufacturer/Model
    Microsoft
    CPU
    Intel Core i5-8400
    Motherboard
    ASUS PRIME H370-PLUS
    Memory
    16GB
    Graphics Card(s)
    Intel UHD Graphics 630
    Sound Card
    On board
    Monitor(s) Displays
    Samsung SyncMaster 2043BWX
    Screen Resolution
    1680 x 1050
    Hard Drives
    Samsung SSD 850 256GB
    WDC 1TB NVMe
    WD 3TB external USB drive
    PSU
    I don't remember
    Case
    Corsair something-or-other
    Cooling
    Air CPU + 2 case fans
    Keyboard
    DAS S Pro (Cherry Brown)
    Mouse
    Logitech USB of some sort
While I've been using the CMD wrapper trick for a few months, no one's shared a foolproof method of correctly passing CMD command line arguments as array objects to PS. The reason this is important is for a complex expression like "Folder A\Folder B", which needs to be preserved as a single entity and not chopped up.

Last night I arrived at a better (and cleaner) fix. This version should do it.
 

Attachments

  • SetFileTime.bat
    1.6 KB · Views: 3

My Computer

System One

  • OS
    Windows 7
Thank you. That worked. And it showed me that script variables are actually environment variable - something I didn't know. I assume their score is that of the defining program or script.
 

My Computer

System One

  • OS
    Windows 11
    Computer type
    PC/Desktop
    Manufacturer/Model
    Microsoft
    CPU
    Intel Core i5-8400
    Motherboard
    ASUS PRIME H370-PLUS
    Memory
    16GB
    Graphics Card(s)
    Intel UHD Graphics 630
    Sound Card
    On board
    Monitor(s) Displays
    Samsung SyncMaster 2043BWX
    Screen Resolution
    1680 x 1050
    Hard Drives
    Samsung SSD 850 256GB
    WDC 1TB NVMe
    WD 3TB external USB drive
    PSU
    I don't remember
    Case
    Corsair something-or-other
    Cooling
    Air CPU + 2 case fans
    Keyboard
    DAS S Pro (Cherry Brown)
    Mouse
    Logitech USB of some sort

Latest Support Threads

Back
Top Bottom