从 PHP 时区值生成 iCalendar VTIMEZONE 组件
Posted
技术标签:
【中文标题】从 PHP 时区值生成 iCalendar VTIMEZONE 组件【英文标题】:Generating an iCalender VTIMEZONE Component from PHP's Timezone Value 【发布时间】:2011-10-04 16:02:38 【问题描述】:我正在向我的活动日历应用程序添加一项功能,以便为活动提供 iCalendar (ics) 文件下载。我想生成VTIMEZONE
Component,但我只有来自date_default_timezone_get()
的php's Timezone 值。下面是 Outlook 生成的东部时间(美国和加拿大)VTIMEZONE
组件示例:
BEGIN:VTIMEZONE
TZID:Eastern Time (US & Canada)
BEGIN:STANDARD
DTSTART:16011104T020000
RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:16010311T020000
RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
END:DAYLIGHT
END:VTIMEZONE
这类似于 PHP 的“美国/纽约”时区,但我将如何自动生成它?
【问题讨论】:
看iCalcreatorvtimezone类。 @Zyava - 感谢您的参考!那是一些悲惨的代码,但我会尽力解决。 【参考方案1】:PHP 的 DateTimezone
类与 Olson 时区数据库一起使用,并且有一些(有限的)methods 可以访问偏移量、转换和短名称。
根据RFC 5545,RRULE 属性是可选的,因此我们应该能够使用内置实用程序生成有效的VTIMEZONE
定义。按照 RFC 的建议,以下函数正是这样做的:
use \Sabre\VObject;
/**
* Returns a VTIMEZONE component for a Olson timezone identifier
* with daylight transitions covering the given date range.
*
* @param string Timezone ID as used in PHP's Date functions
* @param integer Unix timestamp with first date/time in this timezone
* @param integer Unix timestap with last date/time in this timezone
*
* @return mixed A Sabre\VObject\Component object representing a VTIMEZONE definition
* or false if no timezone information is available
*/
function generate_vtimezone($tzid, $from = 0, $to = 0)
if (!$from) $from = time();
if (!$to) $to = $from;
try
$tz = new \DateTimeZone($tzid);
catch (\Exception $e)
return false;
// get all transitions for one year back/ahead
$year = 86400 * 360;
$transitions = $tz->getTransitions($from - $year, $to + $year);
$vt = new VObject\Component('VTIMEZONE');
$vt->TZID = $tz->getName();
$std = null; $dst = null;
foreach ($transitions as $i => $trans)
$cmp = null;
// skip the first entry...
if ($i == 0)
// ... but remember the offset for the next TZOFFSETFROM value
$tzfrom = $trans['offset'] / 3600;
continue;
// daylight saving time definition
if ($trans['isdst'])
$t_dst = $trans['ts'];
$dst = new VObject\Component('DAYLIGHT');
$cmp = $dst;
// standard time definition
else
$t_std = $trans['ts'];
$std = new VObject\Component('STANDARD');
$cmp = $std;
if ($cmp)
$dt = new DateTime($trans['time']);
$offset = $trans['offset'] / 3600;
$cmp->DTSTART = $dt->format('Ymd\THis');
$cmp->TZOFFSETFROM = sprintf('%s%02d%02d', $tzfrom >= 0 ? '+' : '', floor($tzfrom), ($tzfrom - floor($tzfrom)) * 60);
$cmp->TZOFFSETTO = sprintf('%s%02d%02d', $offset >= 0 ? '+' : '', floor($offset), ($offset - floor($offset)) * 60);
// add abbreviated timezone name if available
if (!empty($trans['abbr']))
$cmp->TZNAME = $trans['abbr'];
$tzfrom = $offset;
$vt->add($cmp);
// we covered the entire date range
if ($std && $dst && min($t_std, $t_dst) < $from && max($t_std, $t_dst) > $to)
break;
// add X-MICROSOFT-CDO-TZID if available
$microsoftExchangeMap = array_flip(VObject\TimeZoneUtil::$microsoftExchangeMap);
if (array_key_exists($tz->getName(), $microsoftExchangeMap))
$vt->add('X-MICROSOFT-CDO-TZID', $microsoftExchangeMap[$tz->getName()]);
return $vt;
上面的代码示例使用Sabre VObject 库来创建VTIMEZONE
定义,但可以很容易地重写以生成纯字符串输出。
除了时区标识符之外,它还需要两个 unix 时间戳作为参数来定义我们需要时区信息的时间范围。然后列出给定时间范围内的所有相关转换。
我使用发送到 Outlook 的 iTip 邀请成功测试了生成的输出,否则无法将普通 Olson 时区标识符与 Microsoft 系统匹配。
【讨论】:
感谢您推荐这个库 - Sabre VObject 正是我想要的! 关于如何制作纯字符串输出的任何想法?看起来返回的对象很复杂。 @mozgras 返回的对象有一个输出字符串的serialize()
方法:$vtimezone = generate_vtimezone('Europe/Berlin'); print $vtimezone->serialize();
这是一个更新的示例,适用于 Sabre/VObject 版本 4.x gist.github.com/thomascube/47ff7d530244c669825736b10877a200【参考方案2】:
另一种解决方法,而不是尝试为目标时区生成 VTIMEZONE
配置,而是采用一个固定的基准时区(例如,“America/New_York”/“东部时间(美国和加拿大)" 区域),并使用 PHP 的 DateTime
类将 VEVENT
的值转换为它。
$Date = new DateTime( $event_date ); // this will be in the server's time zone
// convert it to the 'internal' time zone
$Date->setTimezone( new DateTimeZone( 'America/New_York' ) );
// ...
echo "BEGIN:VEVENT\n";
echo "DTSTART;TZID=America/New_York:" . $Date->format( 'Ymd\THis' ) . "\n"
收件人的日历客户端会自动将时间转换为目标时区!
不是直接的答案,但它解决了我的问题。
【讨论】:
它似乎也适用于我,但似乎不符合 iCalendar 规范 (RFC 2445)。也许将所有内容都转换为 UTC,然后嵌入该时区规范将是可行的方法。【参考方案3】:PHP 有一个名为 intl 的国际化扩展,它是 IBM 底层库 ICU - International Components for Unicode 的简单包装器,可以完成所有繁琐的工作。
现在,ICU 实现了com.ibm.icu.util.VTimeZone,能够生成具有夏令时规则的 iCal VTIMEZONE 组件(使用 Olson tz db of transitions)。
太好了!!但是.. 那个类并没有在 PHP 中获得包装器。
【讨论】:
以上是关于从 PHP 时区值生成 iCalendar VTIMEZONE 组件的主要内容,如果未能解决你的问题,请参考以下文章
php 活动日历:将活动档案的iCalendar导出链接更改为webcal://
python 为洛杉矶健身俱乐部生成一个ics(icalendar / ical)。你必须知道俱乐部的身份证,你可以通过他们的网站获得