Search code examples
perldatetimeepoch

Error in elapsed time since modification of file using "DateTime"


I am trying to clean up a rather large file system. I use the stat function to obtain the modification time of each file.

According to perldoc -f stat, the tenth element of the returned list is the last modified time in seconds since the epoch.

I use DateTime->from_epoch and subtract DateTime->now to calculate the age of the fule

    #!/usr/bin/perl

    use strict;
    use warnings;

    use DateTime;

    my $now = DateTime->now();
    #my $now = DateTime->now( time_zone => "America/New_York" );

    $self->{dir} = '/tmp/test';
    opendir(DIR, $self->{dir}) or die $@;
    my @files = grep(/\.txt$/, readdir(DIR));
    closedir(DIR);

    for ( @files ) {

            my $file = stat($self->{dir} . '/' . $_);
            my $mtime = DateTime->from_epoch(epoch => $file->mtime);
            #my $mtime = DateTime->from_epoch(epoch => $file->mtime, time_zone=> "America/New_York");
            my $elapsed = $now - $mtime;
            push(@{$self->{stale}}, {file => $self->{dir} . '/' . $_, mtime => $elapsed->in_units('minutes')}) if $elapsed->in_units('minutes') > 15;
            push(@{$self->{remove}}, {file => $self->{dir} . '/' . $_, mtime => $elapsed->in_units('days')}) if $elapsed->in_units('days') > 10;
    }

If I manually create test files and change the modification time, the result is off by 30 days

$ touch /tmp/test/test{100..104}.txt -d '-45 days'
$ perl MTIME.pm 
$VAR1 = {
          'mtime' => 15,
          'file' => '/tmp/test/test100.txt'
        }; $VAR1 = {
          'mtime' => 15,
          'file' => '/tmp/test/test104.txt'
        }; $VAR1 = {
          'mtime' => 15,
          'file' => '/tmp/test/test103.txt'
        }; $VAR1 = {
          'mtime' => 15,
          'file' => '/tmp/test/test101.txt'
        }; $VAR1 = {
          'mtime' => 15,
          'file' => '/tmp/test/test102.txt'
        };

I've tried DateTime objects both with and without the time zone set with no difference in results.

$ touch /tmp/test/test{100..104}.txt -d '-45 days'
$ touch /tmp/test/test{105..110}.txt
$ ll /tmp/test
total 11
-rw-r--r-- 1 root root    0 Apr  3 19:31 test100.txt
-rw-r--r-- 1 root root    0 Apr  3 19:31 test101.txt
-rw-r--r-- 1 root root    0 Apr  3 19:31 test102.txt
-rw-r--r-- 1 root root    0 Apr  3 19:31 test103.txt
-rw-r--r-- 1 root root    0 Apr  3 19:31 test104.txt
-rw-r--r-- 1 root root    0 May 18 19:30 test105.txt
-rw-r--r-- 1 root root    0 May 18 19:30 test106.txt
-rw-r--r-- 1 root root    0 May 18 19:30 test107.txt
-rw-r--r-- 1 root root    0 May 18 19:30 test108.txt
-rw-r--r-- 1 root root    0 May 18 19:30 test109.txt
-rw-r--r-- 1 root root    0 May 18 19:30 test110.txt

Working solution:

#!/usr/bin/perl

use strict;
use warnings 'all';

use Data::Dumper;

my $self = bless { }, 'My::Class';

my @files = glob '/tmp/test/*.txt';

for (@files) {
        my $days = int(-M $_);
        my $mins = int((time - (stat $_)[9]) / 60);
        my $item = {
                file  => $_,
                days => $days,
                minutes => $mins
        };
        push @{ $self->{remove} }, $item if $days > 10;
        push @{ $self->{stale} },  $item if $mins > 15;
}

print Dumper $self;

Solution

  • Your question is hard to understand because you are writing an object-oriented module and then running it as a program. The code you show won't compile, mainly because $self is never declared or defined. If you hope for useful answers then please post a complete program that we can run and that demonstrates the problem that you are asking about

    I can't try your program and see the problem for myself, but there are two obvious improvements to make

    • It is much easier to make a call to glob than to open and read a directory, remove the files that you don't want, and rebuild the path to each file by adding back the directory

    • You can use the built-in -M operator to discover the age of a file in floating-point days

    I've written this, which creates an empty object in class My::Class and adds data to it. It includes the ideas I''ve talked about, but like your own code provides no output. Hopefully you will understand how to interpret this into your own structure

    The only problem you may have is that the mtime fields are floating-point days. You may want to apply int or POSIX::ceil depending on what those values are used for

    use strict;
    use warnings 'all';
    
    my $self = bless { }, 'My::Class';
    
    my @files = glob '/temp/text/*.txt';
    
    for my $file ( @files ) {
    
        my $age = -M $file;
    
        if ( $age >= 10.0 ) {
    
            my $item = {
                file  => $file,
                mtime => $age,
            };
    
            push @{ $self->{remove} }, $item;
    
            push @{ $self->{stale} },  $item if $age >= 15.0;
        }
    }