Hello
Here's a rubycocoa script you may use as command line tool.
Recipe:
- Create plain text file named set_file_date.rb with the content listed below and set its mode by, e.g., -
chmod u+x set_file_date.rb
Usage:
- E.g. 1
For *.txt in current directory, set creation date to 2014-08-05 09:00:00 and modification date to 2014-08-15 14:00:00.
./set_file_date.rb -c 201408050900.00 -m 201408151400.00 *.txt
- E.g. 2
For *.txt in current directory, set creation date to 2014-08-05 09:00:00 (if it is not later than the current modification date; otherwise leave creation date unchanged) and leave modification date unchanged.
./set_file_date.rb -c 201408050900.00 *.txt
- E.g. 3
For *.txt in current directory, set modification date to 2014-08-15 14:00:00 and leave creation date unchanged (if it is not later than the specified modification date; otherwise set creation date to the specified modification date as well).
./set_file_date.rb -m 201408151400.00 *.txt
Notes:
- Creation date cannot be later than modification date.
- You may change the date format by changing DATE_PATTERN in script.
set_file_date.rb
#!/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -w
#
# ARGV = options file [file ...]
# options =
# -c DATE Set creation date to DATE
# -m DATE Set modification date to DATE
# -h, --help Display this help.
#
# * date must be in the format DATE_PATTERN defined below
# * using NSFileManager method
#
require 'osx/cocoa'
include OSX
# DATE_PATTERN = "yyyy'-'MM'-'dd HH':'mm':'ss Z" # [1]
DATE_PATTERN = "yyyyMMddHHmm.ss" # [1]
#
# [1] cf. http://unicode.org/reports/tr35/tr35-dates.html#Date_Format_Patterns
#
def parse_options(argv)
require 'optparse'
args = {:cdt => nil, :mdt => nil}
op = OptionParser.new do|o|
o.banner = "Usage: #{File.basename($0)} options file [file ...]"
o.on('-c DATE', String, "Set creation date to DATE (#{DATE_PATTERN})") do |t|
args[:cdt] = t
end
o.on('-m DATE', String, "Set modification date to DATE (#{DATE_PATTERN})") do |t|
args[:mdt] = t
end
o.on( '-h', '--help', 'Display this help.' ) do
$stderr.puts o; exit 1
end
end
begin
op.parse!(argv)
rescue => ex
$stderr.puts "#{ex.class} : #{ex.message}"
$stderr.puts op.help(); exit 1
end
if argv.length == 0 or args.values == [nil, nil]
$stderr.puts op.help(); exit 1
end
args
end
ct, mt = parse_options(ARGV).values_at(:cdt, :mdt)
df = NSDateFormatter.alloc.init
df.setDateFormat(DATE_PATTERN)
cdt, mdt = [ct, mt].map do |t|
next nil unless t
dt = df.dateFromString(t) or
begin
$stderr.puts "Invalid date string: %s (expected: %s)" % [t, DATE_PATTERN]
exit 1
end
end
attrs = []
attrs << { NSFileModificationDate => mdt } if mdt # [2]
attrs << { NSFileCreationDate => cdt } if cdt
#
# [2] Set modification date first if specified.
# If cdt > current modification date, creation date cannot be set to cdt even if cdt <= mdt
# whereas modification date can be set to mdt regardless of current modification date and creation date.
# (If mdt < current creation date, creation date is set to mdt as well.)
# Therefore, first setting modification date to mdt and then setting creation date to cdt (<= mdt) is the right order
# to set both cdt and mdt regardless of current modification date and creation date.
# (If mdt = nil and cdt > current modification date, creation date will remain as is.)
#
fm = NSFileManager.defaultManager
ARGV.each do |f|
err = OCObject.new
attrs.each do |a|
b = fm.objc_send(
:setAttributes, a,
:ofItemAtPath, f,
:error, err)
$stderr.puts "Failed to set attribute of %s: %s" % [File.expand_path(f), err.description] unless b
end
end
Briefly tested under 10.6.8.
Regards,
H