Reputation: 1960
I want to display time in the same format as is displayed by the linux diff
command.
D:\>diff -dub old.cfg new.cfg
--- old.cfg 2019-05-15 15:03:14.289950700 +0530
+++ new.cfg 2019-05-14 16:07:21.119695300 +0530
I want to show last modified time of a file as 2019-05-15 15:03:14.289950700 +0530
.
I used stat
but I am not getting that nanosecond part. Alo, need timezone in given format. I tried Time::HiRes
too, but couldn't get.
my $dt = strftime("%Y-%m-%d %H:%M:%S", localtime((stat($old_file))[9]));
It returns: 2019-05-15 15:03:14
. Can you please help me to append the other info?
Upvotes: 5
Views: 1647
Reputation: 66964
I must first say, that's not going to be accurate. Even as your hardware likely supports nanosecond resolution by the time it gets back to software it's hundreds of times off. (With a file's timestamp, presumably used only as relative to other files, that may be less important.)
Having said that, the only way I find to claim nanoseconds is to use system's tools, so stat
my ($ts) = grep { /^\s*Access: [0-9]{4}/ } qx(stat $file);
$ts =~ s/^\s*Access:\s+//;
or, using map
also as a filter
my ($ts) = map { /^\s*Access:\s+([0-9]{4}.*)/ ? $1 : () } qx(stat $file);
One problem here, of course, is that we have to parse output; so see your man pages and test your stat
, and don't hope for much portability. Watch for system changes as output format of programs may change. But I don't find nanoseconds claim in Perl.
For reference, on my CentOS 7 (under both bash
and tcsh
)
perl -we'$f=shift; print grep { /^\s*Access: [0-9]{4}/ } qx(stat $f)' file
prints, for a random file
Access: 2019-05-15 13:21:57.723987422 -0700
Once again, the "nano"seconds aren't accurate at all.
Another option, for perhaps more reliable output parsing, is to use ls
with its --full-time
my $ts = join ' ', (split ' ', qx(ls --full-time $file))[5..7];
As always, when running external commands build your variables with care and quote and escape appropriately. A good tool is String::ShellQuote. Or, better avoid the shell altogether unless it's specifically needed. Best do all this using a module for running external tools.
Upvotes: 3
Reputation: 1019
This is a multi-part challenge. The first challenge is to get a sub-second resolution on your file times, which is both OS and filesystem-dependent. The second part is to get the local timezone offset. All of this is doable on a modern system, however. You were on the right track with Time::HiRes
, but strftime()
is only going to get you part way. Here is an example program which takes files as arguments and prints the hi-res mtime per your requirements.
#!/usr/bin/perl
use strict;
use warnings;
use Time::HiRes qw(stat);
use Time::Piece;
use Time::Seconds;
sub timestr {
my $t = localtime($_[0]);
my $tstr = $t->strftime('%Y-%m-%d %H:%M:%S');
if ($_[0] =~ /(\.\d+$)/) { $tstr .= $1 }
my $off = $t->tzoffset;
return sprintf('%s %+02d%02d', $tstr, int($off->hours), $off->minutes % 60);
}
for (@ARGV) {
my @stat = stat($_);
print "$_\n ".($! ? "Error: $!" : timestr($stat[9]))."\n";
}
exit(0);
Example output:
$ ./stat.pl stat.pl
stat.pl
2019-05-16 20:02:38.76708 +1000
A lot of the heavy lifting is being done by Time::Piece
, but there is a little bit of hackery going on to extract the fractional part of the time, and convert the timezone to the desired format (and I haven't tested it extensively). You may want to use sprintf()
on the original incoming value to coerce a particular number of decimal places. My code just honestly reproduces the original fractional part.
Bear in mind that not all timestamps will have a fractional part, even on a filesystem which supports sub-second resolution. The file may have been copied from a source which does not have sub-second resolution, for example.
Upvotes: 2