set all to oldest known date

Started by Andreas999, January 12, 2017, 08:42:46 PM

Previous topic - Next topic

Andreas999

Hi,
I have 30000 different pictures in many folders. Some of this pictures have no exif data, but they have at least an created file date or last changes file data, so i am lookong for a function that first looks for the oldes date (exif date, file creation date, last change file date) and then this function should set all these dates to this oldest date:
- the exif date
- the created file date
- the last changes file date

(speziel treatment to files with dates like  1.1.1970)

is this possible ??




---------------------
german:
1. zunächst soll das älteste vorhanden Datum ermittelt werden aus den Dateiangaben (Created / geändert / erstellt) sowie eventuell vorhandenen Exif Datums.

2. Dieses ermittelte älteste Datum ist meist dann das wirkliche Fotograftierdatum. Zumindest kommt dieses datum den Fotografierdatum immer am nähesten. Darum soll diese Funktion nun alle Datei Angaben (zumindest aber das File created Datum) auf dieses älteste gefundene Datum setzen:
- Windows Created Datum
- Windows geändert-Datumsangabe
- Exif Datum

(Sonderfälle ergeben sich natürlich, wenn alte Dateidaten wie 1.1.1970 drinstehen, die nicht stimmen können)


lg von Andreas

Phil Harvey

Yes, this is possible.  The best solution would be to create a Composite user-defined tag based on all of the date/time tags you are interested in.  This may include any complex logic that you want.  I don't have time right now to help any more other than pointing you to the sample config file (take a look at the Composite tags there).

- Phil
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

Hayo Baan

I had a look at this and I think below config file content will do what you want:

%Image::ExifTool::UserDefined = (
    'Image::ExifTool::Composite' => {
        # Select oldest date from a number of date tags
        OldestDateTime => {
            Desire => {
                0 => 'FileModifyDate',
                1 => 'MDItemFSContentChangeDate',
                2 => 'FileCreateDate',
                3 => 'MDItemFSCreationDate',
                4 => 'ModifyDate',
                5 => 'CreateDate',
                6 => 'DateTimeCreated',
                7 => 'DateTimeOriginal',
            },
            ValueConv => q{
                my $oldest = undef;
                for my $date (@val) {
                    $date =~ s/[+-]\d{2}:\d{2}$//; # Strip TimeZone
                    if ($date && (!$oldest || $date lt $oldest)) {
                        $oldest = $date;
                    }
                }
                return $oldest;
            },
        },
    },
);

1;


If you save this (include the 1; at the end!) to file (e.g. oldest_datetime_config) and tell exiftool to use that config file you can now request the oldest date from the 8 fields listed (extend this list if necessary, but I think I covered the usual suspects and even made sure it can be used on both Mac and Windows).
For example:
exiftool -config oldest_datetime_config -filename -OldestDateTime FILEorDIR

If you want to use that oldest date to set e.g., the DateTimeOriginal of the file, you can use something like this:
exiftool -config oldest_datetime_config -filename "-DateTimeOrignal<OldestDateTime" FILEorDIR

If you have any questions, let us know.

P.S. I don't think any special treatment of dates before 1970 is required.
Hayo Baan – Photography
Web: www.hayobaan.nl

Phil Harvey

#3
Thanks Hayo,

I think that ignoring 1970 dates is what Andreas wanted:

%Image::ExifTool::UserDefined = (
    'Image::ExifTool::Composite' => {
        # Select oldest date from a number of date tags
        OldestDateTime => {
            Desire => {
                0 => 'FileModifyDate',
                1 => 'MDItemFSContentChangeDate',
                2 => 'FileCreateDate',
                3 => 'MDItemFSCreationDate',
                4 => 'ModifyDate',
                5 => 'CreateDate',
                6 => 'DateTimeCreated',
                7 => 'DateTimeOriginal',
            },
            ValueConv => q{
                my $oldest = undef;
                for my $date (@val) {
                    next if not defined $date or $date lt '1970:01:02';
                    $date =~ s/[+-]\d{2}:\d{2}$//; # Strip TimeZone
                    if ($date && (!$oldest || $date lt $oldest)) {
                        $oldest = $date;
                    }
                }
                return $oldest;
            },
        },
    },
);

1;


I have problems myself with file system date/times getting reset occasionally to Jan 1, 1970 too.  With time zone offsets they sometimes show up as 1969, so I ignore everything before Jan 2, 1970.

- Phil

Edit:  Add this line to the tag definition for using OldestDateTime with the -d option:

            PrintConv => '$self->ConvertDateTime($val)',
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

Hayo Baan

Quote from: Phil Harvey on January 13, 2017, 07:16:46 AM
I have problems myself with file system date/times getting reset occasionally to Jan 1, 1970 too.  With time zone offsets they sometimes show up as 1969, so I ignore everything before Jan 2, 1970.

Is that on a Mac or windows? I've never seen (nor even heard of) this issue myself. IMO It would be very bad if the file system did this to you...
Hayo Baan – Photography
Web: www.hayobaan.nl

Phil Harvey

It has happened for me on a Mac when using "touch" to set the date/time for a file on a FAT32-formatted thumb drive.  The date/time information on these is questionable to begin with (it stores local time, not UTC, so it changes with time zone and daylight savings time).  The problem has never occurred for me when using a real file system.

I run into this problem occasionally because I have a script that backs up and restores files to/from a thumb drive.

- Phil
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

Hayo Baan

Quote from: Phil Harvey on January 14, 2017, 10:31:09 AM
It has happened for me on a Mac when using "touch" to set the date/time for a file on a FAT32-formatted thumb drive.  The date/time information on these is questionable to begin with (it stores local time, not UTC, so it changes with time zone and daylight savings time).  The problem has never occurred for me when using a real file system.

I run into this problem occasionally because I have a script that backs up and restores files to/from a thumb drive.

Ah, you were talking FAT32 file systems here, that explains ;)
Hayo Baan – Photography
Web: www.hayobaan.nl

pedroreis

Hi guys,

I've finally had some time to hammer some code for my specific needs: avoid dates into the future and too old dates to be considered.
What is the exiftool mechanism to print color for warnings and errors? And is this a good perl-exiftool way to determine current date?


# TODO: test if this works
#use warnings;
#use strict;
#use Win32::Console;
use TermANSIColor::lib::Term::ANSIColor;
# TODO : test if this works
use POSIX qw(strftime);

my $force_move_even_if_invalid_dates_found = false;

# source: https://exiftool.org/forum/index.php?topic=9686.0
# https://exiftool.org/forum/index.php?topic=7986.0
%Image::ExifTool::UserDefined = (
    'Image::ExifTool::Composite' => {
# Select oldest date from a number of date tags
        oldest_date => {
            Desire => {
                0 => 'DateTimeOriginal', # para fotos: "Date Taken"
                1 => 'CreateDate', # para videos: "Media Created" or "Date Acquired"
2 => 'filename', # nome do ficheiro: ex: VID-20191220-WA0011.mp4 TODO: test this!
3 => 'zipmodifydate', # para fotos HEIC : é mais antigo que date
                4 => 'FileModifyDate', # data de criação original do ficheiro "Date" (do video/imagem)
5 => 'FileCreateDate', # "Date created" que é a data criada na cópia (do [File:System])
6 => 'ModifyDate', # ?data de modificação "Date modified" (do [File:System]) ?
7 => 'DateTimeCreated',
8 => 'MDItemFSContentChangeDate',
9 => 'MDItemFSCreationDate',
            },
            ValueConv => q{
                my $oldest = undef;
my $oldest_with_bad = undef;
my $min_date_threshold = '2005:01:01';
my $now = strftime "%Y/%m/%d", localtime;
#my $nu = -1;
                for my $date (@val) {
                    $date =~ s/[+-]\d{2}:\d{2}$//; # Strip TimeZone
#$nu++;

#print "Data: $date\n"; #DEBUG
if($date && (!$oldest_with_bad || $date lt $oldest_with_bad)) {
$oldest_with_bad = $date;
}

# Avoid Invalid dates like "1900:01:00 00:00:00" or "1725:04" or "0000:00:00 00:00:00".
# Actually, for our purpose, we can accept only after 2020 (plus back some time, due to old photos (google photos) being reshared):
if( not defined $date or $date lt $min_date_threshold ){
if( $force_move_even_if_invalid_dates_found ){
                            print colored( "WARNING: date ($date) is undefined or smaller than threshold ($min_date_threshold).", 'yellow' ), "\n";
next;
} else {
print colored( "ERROR: date ($date) is undefined or smaller than threshold ($min_date_threshold).", 'red' ), "\n";
exit;
}
}

# give error if date into the future + STOP execution and wait user to run with force parameter
if( $date gt $now ){
if( $force_move_even_if_invalid_dates_found ){
                            print colored( "WARNING: date $date is bigger than today ($now).", 'yellow' ), "\n";
next;
} else {
print colored( "ERROR: date $date is bigger than today ($now).", 'red' ), "\n";
exit;
}
}

#print "$Desire[$nu] : $date\n";
print "Data (valid): $date\n";

if ($date && (!$oldest || $date lt $oldest)) {
$oldest = $date;
}
                }

if ($oldest_with_bad ne $oldest) {
# give warning showing current threshold that avoided date to be considered
print "WARNING: oldest valid date $oldest doens't match with older one found: $oldest_with_bad.  ( Min Date threshold = $min_date_threshold )";
print colored( "WARNING: oldest valid date $oldest doens't match with older one found: $oldest_with_bad.  ( Min Date threshold = $min_date_threshold )", 'yellow' ), "\n";
}

                return $oldest;
},
            PrintConv => '$self->ConvertDateTime($val)',
        },
    },
);

#------------------------------------------------------------------------------
1;  #end

StarGeek

Quote from: pedroreis on October 17, 2021, 02:16:32 PM
What is the exiftool mechanism to print color for warnings and errors?

There isn't any.  You'll have to look into adding ANSI escape codes to your print output.  I found this page which might help.
* Did you read FAQ #3 and use the command listed there?
* Please use the Code button for exiftool code/output.
 
* Please include your OS, Exiftool version, and type of file you're processing (MP4, JPG, etc).

pedroreis

Any advice on the

"And is this a good perl-exiftool way to determine current date?"

part?

StarGeek

I'm not sure what you're asking.  Do you want the current date or the oldest date?

You can set a time stamp to the current date/time with now
exiftool -DateTimeOriginal=now file.jpg

But if your config is working correctly, then go with it.
* Did you read FAQ #3 and use the command listed there?
* Please use the Code button for exiftool code/output.
 
* Please include your OS, Exiftool version, and type of file you're processing (MP4, JPG, etc).

djcroustibat

Quote from: Phil Harvey on January 13, 2017, 07:16:46 AM
Thanks Hayo,

I think that ignoring 1970 dates is what Andreas wanted:

%Image::ExifTool::UserDefined = (
    'Image::ExifTool::Composite' => {
        # Select oldest date from a number of date tags
        OldestDateTime => {
            Desire => {
                0 => 'FileModifyDate',
                1 => 'MDItemFSContentChangeDate',
                2 => 'FileCreateDate',
                3 => 'MDItemFSCreationDate',
                4 => 'ModifyDate',
                5 => 'CreateDate',
                6 => 'DateTimeCreated',
                7 => 'DateTimeOriginal',
            },
            ValueConv => q{
                my $oldest = undef;
                for my $date (@val) {
                    next if not defined $date or $date lt '1970:01:02';
                    $date =~ s/[+-]\d{2}:\d{2}$//; # Strip TimeZone
                    if ($date && (!$oldest || $date lt $oldest)) {
                        $oldest = $date;
                    }
                }
                return $oldest;
            },
        },
    },
);

1;


I have problems myself with file system date/times getting reset occasionally to Jan 1, 1970 too.  With time zone offsets they sometimes show up as 1969, so I ignore everything before Jan 2, 1970.

- Phil

Edit:  Add this line to the tag definition for using OldestDateTime with the -d option:

            PrintConv => '$self->ConvertDateTime($val)',


Hi, I tried to use this function but I got error that DateTimeOrignal does not exist. how can I get updated DateTimeOrignal with the oldest found date time in all metadata even if this one it's not present ? and what about the -d parameter you suggested ? Thank You

Phil Harvey

What was the command you used?

It should be something like this:

exiftool -config oldest_datetime_config "-DateTimeOrignal<OldestDateTime" DIR

I don't see how this could give the error you mentioned.

- Phil
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

djcroustibat

exiftool -config /path/to/script/oldest_datetime_config "-DateTimeOrignal<OldestDateTime" "/path/to/photo/IMG.jpg"

And i got this : Warning: Tag 'datetimeorignal' is not defined

So I tried to change the tag destination with one already present in this file just for test : FileAccessDate

And got this error : Warning: Sorry, fileaccessdate is not writable

Available tag in this photo :

exiftool -time:all -s /path/to/photo/IMG.jpg
FileModifyDate                  : 2021:06:20 12:55:08+02:00
FileAccessDate                  : 2022:02:04 18:08:42+01:00
FileInodeChangeDate             : 2022:02:04 11:58:44+01:00

What I'm trying to do is batch replace 80.000+ photos to recover real date because of MacOS not recognizing the real date when import photos to Apple Photos App.
( I don't had this problem when all photos were in Google Photos, Google choosed each time the correct (oldest) date in all tags available I suppose )

What I understand is that the DateTimeOrignal tag does not exist in this photo.
So what is the best way to update all photos with the correct (oldest) date even if the destination tag does not exists ?

Thank You for your help.

StarGeek

DateTimeOriginal, not DateTimeOrignal
* Did you read FAQ #3 and use the command listed there?
* Please use the Code button for exiftool code/output.
 
* Please include your OS, Exiftool version, and type of file you're processing (MP4, JPG, etc).