从PHP';s时区值
我正在向我的事件日历应用程序添加一项功能,以便为事件提供iCalendar(ics)文件下载。我想生成,但我所拥有的只是来自的值。以下是Outlook为东部时间(美国和加拿大)生成的从PHP';s时区值,php,timezone,icalendar,Php,Timezone,Icalendar,我正在向我的事件日历应用程序添加一项功能,以便为事件提供iCalendar(ics)文件下载。我想生成,但我所拥有的只是来自的值。以下是Outlook为东部时间(美国和加拿大)生成的VTIMEZONE组件的示例: BEGIN:VTIMEZONE TZID:Eastern Time (US & Canada) BEGIN:STANDARD DTSTART:16011104T020000 RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11 TZOFFSETFROM:
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的“美国/纽约”时区,但我如何自动生成它呢?另一种方法,不是尝试为目标时区生成
VTIMEZONE
配置,而是采用一个固定的基准时区(例如,“美国/纽约”/“东部时间(美国和加拿大)”区域),并使用PHP类将VEVENT
的值转换为它
收件人的日历客户端将自动将时间转换为目标时区
这不是一个直接的答案,但它解决了我的问题。PHP有一个国际化扩展名为intl,它是IBM底层库的一个简单包装器,它完成了所有脏活 现在,ICU实现了,能够生成具有夏令时规则的iCalVTIMEZONE组件(使用Olson tz db转换)
太好了!!但是该类在PHP中没有获得包装器。PHP的
DateTimezone
类与奥尔森时区数据库一起工作,并且具有一些(有限的)访问偏移量、转换和短名称的权限
根据,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;
}
use\Sabre\VObject;
/**
*返回Olson时区标识符的VTIMEZONE组件
*具有覆盖给定日期范围的日光过渡。
*
*PHP日期函数中使用的@param字符串时区ID
*@param integer Unix时间戳,具有此时区中的第一个日期/时间
*@param integer Unix timestap,最后日期/时间在此时区
*
*@return混合了一个Sabre\VObject\Component对象,表示VTIMEZONE定义
*如果没有可用的时区信息,则为false
*/
函数generate_vtimezone($tzid,$from=0,$to=0)
{
如果(!$from)$from=time();
如果(!$to)$to=$from;
试一试{
$tz=新建\日期时区($tzid);
}
捕获(\异常$e){
返回false;
}
//提前/提前一年完成所有过渡
$year=86400*360;
$transitions=$tz->getTransitions($from-$year,$to+$year);
$vt=新的VOObject\Component('VTIMEZONE');
$vt->TZID=$tz->getName();
$std=null;$dst=null;
foreach($i=>$trans){
$cmp=null;
//跳过第一个条目。。。
如果($i==0){
//…但请记住下一个TZOFFSETFROM值的偏移量
$tzfrom=$trans['offset']/3600;
继续;
}
//夏令时定义
如果($trans['isdst'])){
$t_dst=$trans['ts'];
$dst=新VObject\Component(“日光”);
$cmp=$dst;
}
//标准时间定义
否则{
$t_std=$trans['ts'];
$std=新对象\组件(“标准”);
$cmp=$std;
}
如果($cmp){
$dt=新日期时间($trans['time']);
$offset=$trans['offset']/3600;
$cmp->DTSTART=$dt->format('Ymd\THis');
$cmp->TZOFFSETFROM=sprintf(“%s%02d%02d',$tzfrom>=0?“+”:“”,地板($tzfrom),($tzfrom-地板($tzfrom))*60);
$cmp->tzoffetto=sprintf(“%s%02d%02d”,$offset>=0?“+”:“”,地板($offset),($offset-floor($offset))*60);
//添加缩写时区名称(如果可用)
如果(!empty($trans['abbr'])){
$cmp->TZNAME=$trans['abbr'];
}
$tzfrom=$offset;
$vt->add($cmp);
}
//我们涵盖了整个约会范围
如果($std&&$dst&&min($t_std,$t_dst)<$from&&max($t_std,$t_dst)>$to){
打破
}
}
//添加X-MICROSOFT-CDO-TZID(如果可用)
$microsoftExchangeMap=array_flip(VObject\TimeZoneUtil::$microsoftExchangeMap);
如果(数组\键\存在($tz->getName(),$microsoftExchangeMap)){
$vt->add('X-MICROSOFT-CDO-TZID',$microsoftExchangeMap[$tz->getName());
}
返回$vt;
}
上面的代码示例使用该库创建VTIMEZONE
定义,但可以轻松地重新编写以生成纯字符串输出
除了时区标识符之外,它还使用两个unix时间戳作为参数来定义需要时区信息的时间范围。然后列出给定时间范围内的所有相关转换
我使用发送到Outlook的iTip邀请成功测试了生成的输出,否则无法将普通的Olson时区标识符与Microsoft系统匹配。查看vtimezone类。@Zyava-感谢您的参考!这是一些糟糕的代码,但我会设法通过。它似乎对我也适用,但似乎不符合iCalendar规范(RFC2445)。也许将所有内容转换为UTC,然后嵌入时区规范将是一个不错的选择。感谢您推荐这个库-Sabre VObject正是我想要的!关于如何生成纯字符串输出有什么想法吗?返回的对象看起来很复杂。@mozgras返回的对象有一个输出字符串的
serialize()
方法:$vtimezone=generate\u vtimezone('Europe/Berlin');打印$vtimezone->serialize()代码>下面是一个使用Sabre/VObject 4.x版的更新示例
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;
}