1

我正在尝试使用 Data:ICal 生成 ICal 提要,但有些事件是在没有时间的情况下打印的。我已经读到如果它是 000000 则不需要时间,但谷歌日历没有正确处理这些事件。

这是一个示例脚本和输出。我需要输出在 UTC 时区。

#!/usr/bin/perl -w
use strict;
use Date::ICal;
use Data::ICal;
use Data::ICal::Entry::Event;
use DateTime;
use Data::Dumper;

sub get_utc_offset($) {
    my ($orig_tz_str) = @_;

    # Using a set winter date to avoid problems with daylight savings time
    my $utc_compare_datetime = DateTime->new(
        year      => 2012,
        month     => 1,
        day       => 1,
        hour      => 1,
        minute    => 1,
        time_zone => 'UTC'
    );

    my $tz             = DateTime::TimeZone->new(name => $orig_tz_str);
    my $utc_offset     = $tz->offset_for_datetime($utc_compare_datetime);
    my $utc_offset_str = DateTime::TimeZone->offset_as_string($utc_offset);

    return $utc_offset_str;
}

sub add_ical_event($$$$$$) {
    my ($calendar, $start, $end, $summary, $description, $timezone) = @_;
    my $offset   = get_utc_offset($timezone);
    $description = 'none' if (!$description);

    my $event = Data::ICal::Entry::Event->new();
    $event->add_properties(
        summary     => $summary,
        description => $description,
        dtstart     => Date::ICal->new( ical  => $start, offset => $offset )->ical,
        dtend       => Date::ICal->new( ical  => $end,   offset => $offset )->ical,
        dtstamp     => Date::ICal->new( epoch => time                      )->ical
    );
    $calendar->add_entry($event);
}


# Tests
# ----------------------------------------------------------------------------

my $timezone = 'America/New_York';

my $calendar = Data::ICal->new();
$calendar->add_properties(
    method         => "PUBLISH",
    prodid         => "-//Test Cal//NONSGML Calendar//EN",
    'X-WR-CALNAME' => 'Test Cal'
);

my (%events) = (
    1 => {
        summary     => 'Test Shift Tool - Testing Shift',
        description => '',
        start       => '20130828T160000',
        end         => '20130828T190000',
        timezone    => $timezone
    },
    2 => {
        summary     => 'New Member Meeting',
        description => '',
        start       => '20130722T190000',
        end         => '20130722T210000',
        timezone    => $timezone
    },
    3 => {
        summary     => 'public',
        description => '',
        start       => '20130630T130000',
        end         => '20130630T140000',
        timezone    => $timezone
    }
);
foreach my $key (sort keys %events) {
    my $e = $events{$key};
    add_ical_event(
        $calendar,
        $e->{start},
        $e->{end},
        $e->{summary},
        $e->{description},
        $e->{timezone}
    );
}
print $calendar->as_string;

请注意,某些事件的开始或结束日期没有时间。当我手动添加 T000000Z 时,这些事件会正确导入 Google 日历。关于如何强制所有事件有时间的任何建议?

BEGIN:VCALENDAR
VERSION:2.0
METHOD:PUBLISH
PRODID:-//Digital Cheetah//NONSGML Calendar//EN
X-WR-CALNAME:Digital Cheetah
BEGIN:VEVENT
DESCRIPTION:none
DTEND:20130829Z
DTSTAMP:20130823T214317Z
DTSTART:20130828T210000Z
SUMMARY:Test Shift Tool - Testing Shift
END:VEVENT
BEGIN:VEVENT
DESCRIPTION:none
DTEND:20130723T020000Z
DTSTAMP:20130823T214317Z
DTSTART:20130723Z
SUMMARY:New Member Meeting
END:VEVENT
BEGIN:VEVENT
DESCRIPTION:none
DTEND:20130630T190000Z
DTSTAMP:20130823T214317Z
DTSTART:20130630T180000Z
SUMMARY:public
END:VEVENT
END:VCALENDAR
4

1 回答 1

1

我读过如果它是 000000 则不需要时间

这不是 RFC 所说的。让我们参考以下部分:

  • 4.3.4 日期
  • 4.3.5 日期时间
  • 4.8.7.2 日期/时间

我将在这里引用相关的格式规范:

date               = date-value

date-value         = date-fullyear date-month date-mday
date-fullyear      = 4DIGIT

date-time  = date "T" time ;As specified in the date and time
                             ;value definitions

dtstamp    = "DTSTAMP" stmparam ":" date-time CRLF

当您的输出包含 DTSTAMP 时,ICal 规范需要date-time遵循它。

这将我们带到 Date::ICal 及其ical方法。它返回一个 iCaldate还是一个date-time? 事实证明,它试图通过检查您的时间戳是否具有000000. 请在ICal.pm 的第 286 行亲自查看。

我们可能期望 Data::ICal::Entry 来处理这种情况。为此我可能会丢失验证代码,但目前我没有看到任何明显相关的内容。看起来它接受了属性值而不检查它们。

根据您的观点,这听起来像是库的错误或限制。

那么......你应该如何解决这个问题?理想情况下,这些库之一可能应该检查并处理这种情况。但是,与此同时,您需要重新站起来:

快速而肮脏的修复:如果您的时间为零,则将其增加一秒;ical现在将返回一个有效但稍微不准确的date-time字符串。

更好一点:ical检查来自;的返回值 如果是 a date,则将其重新格式化为date-time.

在使用它之前测试它,但可能是这样的:

dtstart => $ical =~ s/(\d{8})Z/$1T000000Z/r;
于 2013-08-23T23:45:55.227 回答