Wir nutzen das Ticketsystem Znuny zur Kommunikation mit externen Personen wie zum Beispiel Interessenten und Kunden. Leider werden in Znuny ICal-Dateianhänge nicht als Termin erkannt und dementsprechend auch nicht angezeigt.
Aus diesem Grund haben wir eine neue Erweiterung für das System geschrieben, die uns alle Termine eines Tickets in einem extra Bereich anzeigen soll:
Um das zu erreichen, werden alle eingehenden Mails durch einen sogenannten Postmaster-Filter auf einen Dateianhang mit dem MIME-Type text/calendar geprüft. Ist ein solcher Anhang vorhanden, werden die folgenden Informationen aus dem Anhang gezogen:
Dazu nehmen wir das Modul Data::ICal
:
# alle Anhänge eines Tickets ermitteln
my $Attachments = $Param{GetParam}->{Attachment};
# ersten Anhang des Typs "text/calendar" ermitteln
my ($IcalAttachment) = grep {
$_->{MimeType} eq 'text/calendar';
} @{ $Attachments || [] };
return 1 if !$IcalAttachment;
# Objekt mit Daten aus Anhang instanziieren
my $Ical = Data::ICal->new(
data => $IcalAttachment->{Content},
);
# Daten aus letztem Event extrahieren
for my $Entry ( @{ $Ical->entries || [] } ) {
next if !$Entry->isa('Data::ICal::Entry::Event');
my $Description = $Entry->property('SUMMARY')->[0]->value;
my $Start = $Entry->property('DTSTART')->[0];
my $End = $Entry->property('DTEND')->[0];
}
Wenn wir uns das Datum näher anschauen und uns mit Data::Printer
das Objekt ausgeben lassen, sehen wir eine Angabe zur Zeitzone. Je nachdem wie die Zeitzone angegeben ist, weiß ich nicht direkt den Zeitunterschied zu UTC, hier ein kleines Beispiel:
Data::ICal::Property {
Parents Class::Accessor
public methods (8) : as_string, decoded_value, encode, key, new, parameters, value, vcal10
private methods (5) : _fold, _parameters, _parameters_as_string, _quoted_parameter_values, _value_as_string
internals: {
_parameters {
TZID "W. Europe Standard Time"
},
key "dtstart",
value "20210601T080000",
vcal10 0
}
}
Das Datum möchte ich aber in UTC speichern, weil alle anderen Zeitangaben in der Datenbank ebenfalls in UTC gespeichert sind und damit das Datum je nach Zeitzone auch richtig angezeigt wird.
Ich muss also zunächst aus der Zeitzonenangabe den Zeitunterschied/Offset des Termins herausfinden. Anschließend muss ich das ICal-Datumsformat mit dem richtigen Offset parsen, um zum Schluss das richtige Datumsformat in UTC speichern zu können.
Es gibt eine Vielzahl von Perl-Modulen, die sich mit dem Thema Datum
befassen. Das einzige Perl-Modul jedoch, das ich für alle möglichen Varianten von Zeitzonen gefunden habe, ist Date::Manip
beziehungsweise Date::Manip::TZ
. Dieses nutze ich nun, um die Zeitzonenvariante Kontinent/Stadt (beispielsweise Europe/Berlin) zu bekommen.
# "richtiger" Name der Zeitzone
my $TimeZone;
{
# statt festem Wert dann $Start->parameters->{TZID}
local $ENV{TZ} = "W. Europe Standard Time";
$TimeZone = Date::Manip::TZ->new;
}
print $TimeZone->zone . "\n"; # Europe/Berlin
Mit dieser Variante können mehrere Module umgehen, unter anderem DateTime
.
Zum Parsen des Datums nehme ich die das Modul Date::ICal
:
my $Date = Date::ICal->new( ical => $Start->value );
Und für die Generierung des richtigen Formats nehme ich DateTime
.
my $DateTime = DateTime->new(
year => $Date->year,
month => $Date->month,
day => $Date->day,
hour => $Date->hour,
minute => $Date->minute,
second => $Date->second,
time_zone => $TimeZone->zone,
);
# Das ist dann automatisch UTC
my $Formatted = $DateTime->ymd . ' ' . $DateTime->hms;
So bekomme ich aus einem beliebigen Datum in ICal-Dateien mit einer beliebigen Zeitzonenangabe ein Datum, das in der Zeitzone UTC angegeben ist – es ist damit in dem Format angegeben, das für den Datenbankeintrag notwendig ist.