Thread 20 Byte für Datum und Uhrzeit (5 answers)
Opened by rosti at 2025-05-19 18:06

rosti
 2025-05-27 17:16
#197015 #197015
User since
2011-03-19
3617 Artikel
BenutzerIn
[Homepage]
user image
Quote
Wie würdest du denn Datum+Uhrzeit+TZ kürzer in Bytes kodieren?


Je nach Endian pack("N", $timestamp); oder pack("V", $timestamp);  ergibt genau 4 Bytes.

Die Image-File-Directory (IFD) ist ein ziemliches Wirrwar. Um an das Datum zu kommen, muß man die ganze IFD0 auslesen und den das Datum betreffenden Marker herausfischen. Damit bekommt man dann den Offset wo man seinen Dateizeiger draufsetzen darf um da diese 20 Bytes 2025:05:7 11:11:11 lesen zu dürfen.

Also wenn man von Exif nur das Datum haben will, geht das noch relativ zügig. Ih habe mein Fotoarchiv (ca. 30 Tausend jpeg) mal drüberlaufen lassen, das war in einer Minute erledigt. Untenstehend die ganze Klasse.

Code (perl): (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
package ExifDate; # Zweckbestimmung: Auslesen Aufnahmedatum in IFD0

use strict;
use warnings;
use IO::File;
require Time::Local;    
use IO::String;

sub new{
    my $class = shift;
    my $file  = shift; # lokale Datei oder binary
    return eval{
        my $self = bless{}, $class;
        
        if( -f $file ){
            $self->{FH} = IO::File->new;
            $self->{FH}->open($file, O_BINARY|O_RDONLY) or die $^E;
        }
        else{
            $self->{FH} = IO::String->new;
            $self->{FH}->print( $file );
            $self->{FH}->seek(0,0);
        }
        
        $self->{offsetIFD0} = $self->findIFD0;
        if( $self->{offsDateTime} = $self->findMarker($self->{offsetIFD0}, 0x0132) ){
            $self->{DateTime} = $self->readDateTime($self->{offsDateTime});
        }
        else{
            die "Kein Datum in Bilddatei\n";
        }
        $self;
    };
}

# Jeder Eintrag einer IFD hat eine Länge von 12 Bytes
# Lese an $entryOffset 2, 2, 4, 4 Bytes
#                               |_________ Daten des Eintrags (bei einfachen Daten) oder Verweis auf die Daten
#                            |____________ Länge der Daten des Eintrags als 32-Bit-Wert
#                         |_______________ Datentyp des Eintrags
#                      |__________________ Typ des Eintrags als 16-Bit-Wert (der sog. Marker)
sub readDateTime{
    my $self = shift;
    my $offs = shift;
    my $rec = $self->readon(12,$offs);
    my $mask = $self->{bigEnd} ? "nnNN" : "vvVV"; 
    my ($marker, $type, $len, $val) = unpack $mask, $rec;
    # Hilfreich beim Entwickeln
    # my $info = sprintf "MARKER: 0x%02X Typ:%d Len:%d Val:%d\n", $marker, $type, $len, $val;
    # MARKER: 0x132 Typ:2 Len:20 Val:2376
    #print "len: $len\n"; # 20 2022-06-08 06:59:55
    return unpack "A*", $self->readon($len, $val + $self->{tiffOffset});
}

sub readon{
    my $self = shift; 
    my $fh   = $self->{FH}; # hier das FileHandle
    my $len  = shift;
    my $offs = shift;
    $offs = defined $offs ? $offs : $fh->tell;

    $fh->seek($offs,0);
    $fh->read(my $buffer, $len);
    return $buffer;
}

sub findIFD0{
    my $self = shift;
    my $IntelString    = pack("C8", 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x49, 0x49);
    my $MotorolaString = pack("C8", 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x4D, 0x4D);
    my $offset = 0;
    my @fifo = ();
    for( $offset = 0; $offset < 50; $offset++ ){
        my $byt = $self->readon(1);
        push @fifo, $byt;
        shift @fifo if scalar @fifo > 8;
        if( join('', @fifo) eq $IntelString){
            $self->{format} = 'Intel';
            $self->{bigEnd} = 0;
            last;
        }
        elsif( join('', @fifo) eq $MotorolaString){
            $self->{format} = 'Motorola';
            $self->{bigEnd} = 1;
            last;
        }
    }
    die "Keine Exif-Daten in Bilddatei\n" if $offset > 49;
    $self->{tiffOffset} = $offset - 1;
    $self->{IFD0pointer} = $self->getUint32($self->{tiffOffset}+4);
    die "Not Valid Tiff-Data\n" if $self->{IFD0pointer} < 8;
    return $self->{IFD0pointer} + $self->{tiffOffset};
}

sub getUint16{
    my $self = shift;
    my $offs = shift;
    my $bin = $self->readon(2,$offs);
    return $self->{bigEnd} ? unpack "n", $bin : unpack "v", $bin;
}

sub getUint32{
    my $self = shift;
    my $offs = shift;
    my $bin = $self->readon(4,$offs);
    return $self->{bigEnd} ? unpack "N", $bin : unpack "V", $bin;
}

# ExifIFDPointer' => 'MARKER: 0x8769 Typ:4 Len:1 Val:118
sub readTagValue{
    my $self = shift;
    my $offs = shift;
    my $rec = $self->{FH}->readon(12,$offs);
    my $mask = $self->{bigEnd} ? "nnNN" : "vvVV"; 
    my ($marker, $type, $len, $val) = unpack $mask, $rec;
    # Hilfreich beim Entwickeln
    #my $info = sprintf "MARKER: 0x%02X Typ:%d Len:%d Val:%d", $marker, $type, $len, $val;
    return $val;
}

sub findMarker{
    my $self = shift;
    my $dirStart = shift;
    my $marker = shift;
    my $entries   = $self->getUint16($dirStart);
    for (my $i = 0; $i < $entries; $i++) {
        my $entryOffset = $dirStart + $i*12 + 2;
        my $m = $self->getUint16($entryOffset); #printf "%04X\n", $m;
        if( $m == 0x8769 ){
            $self->{ExifIFDPointer} = $self->readTagValue($entryOffset);
        }
        return $entryOffset if $marker == $m;
    }
    # versuche ExifPointer zu finden
    # 0x8769 => "ExifIFDPointer",
    return;    
}

sub DateTime{
    my $self = shift;
    my($date, $time) = split /\s+/, $self->{DateTime};
    $date =~ s/:/-/g;
    return "$date $time";
}

sub gmtime{
    my $self = shift;
    my ($date, $xtime) = split /\s/, $self->DateTime;
    my ($year, $mon, $mday) = split "-", $date;
    my ($hour, $min, $sec)  = split ":", $xtime;
    my $time = Time::Local::timegm( $sec, $min, $hour, $mday, $mon-1, $year );
    return wantarray ? ($sec, $min, $hour, $mday, $mon, $year) : $time;
}

1;#########################################################################
__END__
package main;
use strict;
use Data::Dumper;
use IO::File;

my $file = "/fotoarchiv/2022/06/08.06.2022_003.jpg";

my $fh = IO::File->new();
$fh->open($file, O_BINARY|O_RDONLY) or die $!;
read($fh, my $bin, -s $fh);

print ExifDate->new($bin)->DateTime, "\n", join "\n",ExifDate->new($file)->gmtime;
http://blog.rolfrost.de/

The art of steam.

View full thread 20 Byte für Datum und Uhrzeit