Add the Chronological

This commit is contained in:
2026-02-16 13:39:26 +01:00
parent c091ed1371
commit f1ac693fe8
162 changed files with 25868 additions and 1 deletions

View File

@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Sabre\VObject\TimezoneGuesser;
use DateTimeZone;
/**
* Some clients add 'X-LIC-LOCATION' with the olson name.
*/
class FindFromOffset implements TimezoneFinder
{
public function find(string $tzid, bool $failIfUncertain = false): ?DateTimeZone
{
// Maybe the author was hyper-lazy and just included an offset. We
// support it, but we aren't happy about it.
if (preg_match('/^GMT(\+|-)([0-9]{4})$/', $tzid, $matches)) {
// Note that the path in the source will never be taken from PHP 5.5.10
// onwards. PHP 5.5.10 supports the "GMT+0100" style of format, so it
// already gets returned early in this function. Once we drop support
// for versions under PHP 5.5.10, this bit can be taken out of the
// source.
// @codeCoverageIgnoreStart
return new DateTimeZone('Etc/GMT'.$matches[1].ltrim(substr($matches[2], 0, 2), '0'));
// @codeCoverageIgnoreEnd
}
return null;
}
}

View File

@@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
namespace Sabre\VObject\TimezoneGuesser;
use DateTimeZone;
use Exception;
/**
* Some clients add 'X-LIC-LOCATION' with the olson name.
*/
class FindFromTimezoneIdentifier implements TimezoneFinder
{
public function find(string $tzid, bool $failIfUncertain = false): ?DateTimeZone
{
// First we will just see if the tzid is a support timezone identifier.
//
// The only exception is if the timezone starts with (. This is to
// handle cases where certain microsoft products generate timezone
// identifiers that for instance look like:
//
// (GMT+01.00) Sarajevo/Warsaw/Zagreb
//
// Since PHP 5.5.10, the first bit will be used as the timezone and
// this method will return just GMT+01:00. This is wrong, because it
// doesn't take DST into account
if (!isset($tzid[0])) {
return null;
}
if ('(' === $tzid[0]) {
return null;
}
// PHP has a bug that logs PHP warnings even it shouldn't:
// https://bugs.php.net/bug.php?id=67881
//
// That's why we're checking if we'll be able to successfully instantiate
// \DateTimeZone() before doing so. Otherwise we could simply instantiate
// and catch the exception.
$tzIdentifiers = DateTimeZone::listIdentifiers();
try {
if (
(in_array($tzid, $tzIdentifiers)) ||
(preg_match('/^GMT(\+|-)([0-9]{4})$/', $tzid, $matches)) ||
(in_array($tzid, $this->getIdentifiersBC()))
) {
return new DateTimeZone($tzid);
}
} catch (Exception $e) {
}
return null;
}
/**
* This method returns an array of timezone identifiers, that are supported
* by DateTimeZone(), but not returned by DateTimeZone::listIdentifiers().
*
* We're not using DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC) because:
* - It's not supported by some PHP versions as well as HHVM.
* - It also returns identifiers, that are invalid values for new DateTimeZone() on some PHP versions.
* (See timezonedata/php-bc.php and timezonedata php-workaround.php)
*
* @return array
*/
private function getIdentifiersBC()
{
return include __DIR__.'/../timezonedata/php-bc.php';
}
}

View File

@@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
namespace Sabre\VObject\TimezoneGuesser;
use DateTimeZone;
/**
* Some clients add 'X-LIC-LOCATION' with the olson name.
*/
class FindFromTimezoneMap implements TimezoneFinder
{
private $map = [];
private $patterns = [
'/^\((UTC|GMT)(\+|\-)[\d]{2}\:[\d]{2}\) (.*)/',
'/^\((UTC|GMT)(\+|\-)[\d]{2}\.[\d]{2}\) (.*)/',
];
public function find(string $tzid, bool $failIfUncertain = false): ?DateTimeZone
{
// Next, we check if the tzid is somewhere in our tzid map.
if ($this->hasTzInMap($tzid)) {
return new DateTimeZone($this->getTzFromMap($tzid));
}
// Some Microsoft products prefix the offset first, so let's strip that off
// and see if it is our tzid map. We don't want to check for this first just
// in case there are overrides in our tzid map.
foreach ($this->patterns as $pattern) {
if (!preg_match($pattern, $tzid, $matches)) {
continue;
}
$tzidAlternate = $matches[3];
if ($this->hasTzInMap($tzidAlternate)) {
return new DateTimeZone($this->getTzFromMap($tzidAlternate));
}
}
return null;
}
/**
* This method returns an array of timezone identifiers, that are supported
* by DateTimeZone(), but not returned by DateTimeZone::listIdentifiers().
*
* We're not using DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC) because:
* - It's not supported by some PHP versions as well as HHVM.
* - It also returns identifiers, that are invalid values for new DateTimeZone() on some PHP versions.
* (See timezonedata/php-bc.php and timezonedata php-workaround.php)
*
* @return array
*/
private function getTzMaps()
{
if ([] === $this->map) {
$this->map = array_merge(
include __DIR__.'/../timezonedata/windowszones.php',
include __DIR__.'/../timezonedata/lotuszones.php',
include __DIR__.'/../timezonedata/exchangezones.php',
include __DIR__.'/../timezonedata/php-workaround.php'
);
}
return $this->map;
}
private function getTzFromMap(string $tzid): string
{
return $this->getTzMaps()[$tzid];
}
private function hasTzInMap(string $tzid): bool
{
return isset($this->getTzMaps()[$tzid]);
}
}

View File

@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Sabre\VObject\TimezoneGuesser;
use DateTimeZone;
use Sabre\VObject\Component\VTimeZone;
use Sabre\VObject\TimeZoneUtil;
/**
* Some clients add 'X-LIC-LOCATION' with the olson name.
*/
class GuessFromLicEntry implements TimezoneGuesser
{
public function guess(VTimeZone $vtimezone, bool $failIfUncertain = false): ?DateTimeZone
{
if (!isset($vtimezone->{'X-LIC-LOCATION'})) {
return null;
}
$lic = (string) $vtimezone->{'X-LIC-LOCATION'};
// Libical generators may specify strings like
// "SystemV/EST5EDT". For those we must remove the
// SystemV part.
if ('SystemV/' === substr($lic, 0, 8)) {
$lic = substr($lic, 8);
}
return TimeZoneUtil::getTimeZone($lic, null, $failIfUncertain);
}
}

View File

@@ -0,0 +1,119 @@
<?php
declare(strict_types=1);
namespace Sabre\VObject\TimezoneGuesser;
use DateTimeZone;
use Sabre\VObject\Component\VTimeZone;
class GuessFromMsTzId implements TimezoneGuesser
{
/**
* List of microsoft exchange timezone ids.
*
* Source: http://msdn.microsoft.com/en-us/library/aa563018(loband).aspx
*/
public static $microsoftExchangeMap = [
0 => 'UTC',
31 => 'Africa/Casablanca',
// Insanely, id #2 is used for both Europe/Lisbon, and Europe/Sarajevo.
// I'm not even kidding.. We handle this special case in the
// getTimeZone method.
2 => 'Europe/Lisbon',
1 => 'Europe/London',
4 => 'Europe/Berlin',
6 => 'Europe/Prague',
3 => 'Europe/Paris',
69 => 'Africa/Luanda', // This was a best guess
7 => 'Europe/Athens',
5 => 'Europe/Bucharest',
49 => 'Africa/Cairo',
50 => 'Africa/Harare',
59 => 'Europe/Helsinki',
27 => 'Asia/Jerusalem',
26 => 'Asia/Baghdad',
74 => 'Asia/Kuwait',
51 => 'Europe/Moscow',
56 => 'Africa/Nairobi',
25 => 'Asia/Tehran',
24 => 'Asia/Muscat', // Best guess
54 => 'Asia/Baku',
48 => 'Asia/Kabul',
58 => 'Asia/Yekaterinburg',
47 => 'Asia/Karachi',
23 => 'Asia/Calcutta',
62 => 'Asia/Kathmandu',
46 => 'Asia/Almaty',
71 => 'Asia/Dhaka',
66 => 'Asia/Colombo',
61 => 'Asia/Rangoon',
22 => 'Asia/Bangkok',
64 => 'Asia/Krasnoyarsk',
45 => 'Asia/Shanghai',
63 => 'Asia/Irkutsk',
21 => 'Asia/Singapore',
73 => 'Australia/Perth',
75 => 'Asia/Taipei',
20 => 'Asia/Tokyo',
72 => 'Asia/Seoul',
70 => 'Asia/Yakutsk',
19 => 'Australia/Adelaide',
44 => 'Australia/Darwin',
18 => 'Australia/Brisbane',
76 => 'Australia/Sydney',
43 => 'Pacific/Guam',
42 => 'Australia/Hobart',
68 => 'Asia/Vladivostok',
41 => 'Asia/Magadan',
17 => 'Pacific/Auckland',
40 => 'Pacific/Fiji',
67 => 'Pacific/Tongatapu',
29 => 'Atlantic/Azores',
53 => 'Atlantic/Cape_Verde',
30 => 'America/Noronha',
8 => 'America/Sao_Paulo', // Best guess
32 => 'America/Argentina/Buenos_Aires',
60 => 'America/Godthab',
28 => 'America/St_Johns',
9 => 'America/Halifax',
33 => 'America/Caracas',
65 => 'America/Santiago',
35 => 'America/Bogota',
10 => 'America/New_York',
34 => 'America/Indiana/Indianapolis',
55 => 'America/Guatemala',
11 => 'America/Chicago',
37 => 'America/Mexico_City',
36 => 'America/Edmonton',
38 => 'America/Phoenix',
12 => 'America/Denver', // Best guess
13 => 'America/Los_Angeles', // Best guess
14 => 'America/Anchorage',
15 => 'Pacific/Honolulu',
16 => 'Pacific/Midway',
39 => 'Pacific/Kwajalein',
];
public function guess(VTimeZone $vtimezone, bool $throwIfUnsure = false): ?DateTimeZone
{
// Microsoft may add a magic number, which we also have an
// answer for.
if (!isset($vtimezone->{'X-MICROSOFT-CDO-TZID'})) {
return null;
}
$cdoId = (int) $vtimezone->{'X-MICROSOFT-CDO-TZID'}->getValue();
// 2 can mean both Europe/Lisbon and Europe/Sarajevo.
if (2 === $cdoId && false !== strpos((string) $vtimezone->TZID, 'Sarajevo')) {
return new DateTimeZone('Europe/Sarajevo');
}
if (isset(self::$microsoftExchangeMap[$cdoId])) {
return new DateTimeZone(self::$microsoftExchangeMap[$cdoId]);
}
return null;
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace Sabre\VObject\TimezoneGuesser;
use DateTimeZone;
interface TimezoneFinder
{
public function find(string $tzid, bool $failIfUncertain = false): ?DateTimeZone;
}

View File

@@ -0,0 +1,11 @@
<?php
namespace Sabre\VObject\TimezoneGuesser;
use DateTimeZone;
use Sabre\VObject\Component\VTimeZone;
interface TimezoneGuesser
{
public function guess(VTimeZone $vtimezone, bool $failIfUncertain = false): ?DateTimeZone;
}