Files
FendxPHP/fendx-framework/fendx-i18n/src/Timezone/DST/DaylightSavingTimeHandler.php
Lawson 2782d765fb feat(database): 添加用户角色权限系统及相关监控功能
- 创建用户表(users)包含基本信息和认证字段
- 创建角色表(roles)用于权限控制
- 创建权限表(permissions)定义系统权限
- 创建用户角色关联表(user_roles)建立用户与角色关系
- 创建角色权限关联表(role_permissions)建立角色与权限关系
- 创建迁移记录表(migrations)追踪数据库变更
- 添加AdminController提供管理员面板功能
- 实现系统监控、配置管理、缓存清理等功能
- 添加AOP切面编程支持的各种通知类型
- 实现告警管理AlertManager支持多渠道告警
- 添加文档注解接口规范
2026-04-08 17:00:28 +08:00

897 lines
30 KiB
PHP

<?php
declare(strict_types=1);
namespace Fendx\I18n\Timezone\DST;
use Fendx\I18n\Timezone\DST\Calculator\DSTCalculator;
use Fendx\I18n\Timezone\DST\Detector\DSTDetector;
use Fendx\I18n\Timezone\DST\Transition\DSTTransition;
class DaylightSavingTimeHandler
{
protected DSTCalculator $calculator;
protected DSTDetector $detector;
protected DSTTransition $transition;
protected array $config = [];
protected array $dstRules = [];
protected array $transitionCache = [];
public function __construct(array $config = [])
{
$this->config = array_merge($this->getDefaultConfig(), $config);
$this->calculator = new DSTCalculator($this->config);
$this->detector = new DSTDetector($this->config);
$this->transition = new DSTTransition($this->config);
$this->dstRules = $this->loadDSTRules();
}
/**
* Check if DST is active for timezone at specific datetime.
*/
public function isDSTActive(string $timezone, \DateTimeInterface $datetime = null): bool
{
$datetime = $datetime ?? new \DateTime();
if (!$this->isValidTimezone($timezone)) {
throw new \InvalidArgumentException("Invalid timezone: {$timezone}");
}
$tz = new \DateTimeZone($timezone);
$dt = new \DateTime($datetime->format('Y-m-d H:i:s'), $tz);
return $dt->format('I') === '1';
}
/**
* Get DST information for timezone at specific datetime.
*/
public function getDSTInfo(string $timezone, \DateTimeInterface $datetime = null): array
{
$datetime = $datetime ?? new \DateTime();
if (!$this->isValidTimezone($timezone)) {
throw new \InvalidArgumentException("Invalid timezone: {$timezone}");
}
$tz = new \DateTimeZone($timezone);
$dt = new \DateTime($datetime->format('Y-m-d H:i:s'), $tz);
$isDST = $dt->format('I') === '1';
$offset = $tz->getOffset($dt);
$abbreviation = $dt->format('T');
return [
'timezone' => $timezone,
'datetime' => $datetime->format('Y-m-d H:i:s'),
'is_dst' => $isDST,
'offset' => $offset,
'offset_hours' => $offset / 3600,
'abbreviation' => $abbreviation,
'dst_offset' => $isDST ? $this->getDSTOffset($timezone) : 0,
'standard_offset' => $isDST ? $offset - $this->getDSTOffset($timezone) : $offset
];
}
/**
* Get DST transitions for timezone in specific year.
*/
public function getDSTTransitions(string $timezone, int $year = null): array
{
$year = $year ?? (int) date('Y');
if (!$this->isValidTimezone($timezone)) {
throw new \InvalidArgumentException("Invalid timezone: {$timezone}");
}
$cacheKey = "{$timezone}_{$year}";
if (isset($this->transitionCache[$cacheKey])) {
return $this->transitionCache[$cacheKey];
}
$tz = new \DateTimeZone($timezone);
// Get transitions for the entire year
$transitions = $tz->getTransitions(
strtotime("{$year}-01-01"),
strtotime("{$year}-12-31 23:59:59")
);
$dstTransitions = [];
foreach ($transitions as $transition) {
if ($transition['dst'] !== $transition['isdst']) {
$dstTransitions[] = [
'timestamp' => $transition['ts'],
'datetime' => date('Y-m-d H:i:s', $transition['ts']),
'offset' => $transition['offset'],
'is_dst' => $transition['dst'],
'abbreviation' => $transition['abbr'],
'type' => $transition['dst'] ? 'start' : 'end',
'offset_change' => $this->calculateOffsetChange($transition, $tz, $year)
];
}
}
$this->transitionCache[$cacheKey] = $dstTransitions;
return $dstTransitions;
}
/**
* Get next DST transition for timezone.
*/
public function getNextDSTTransition(string $timezone, \DateTimeInterface $datetime = null): ?array
{
$datetime = $datetime ?? new \DateTime();
$transitions = $this->getDSTTransitions($timezone, (int) $datetime->format('Y'));
$timestamp = $datetime->getTimestamp();
foreach ($transitions as $transition) {
if ($transition['timestamp'] > $timestamp) {
return $transition;
}
}
// Check next year if no transitions found this year
$nextYear = (int) $datetime->format('Y') + 1;
$nextYearTransitions = $this->getDSTTransitions($timezone, $nextYear);
return !empty($nextYearTransitions) ? $nextYearTransitions[0] : null;
}
/**
* Get previous DST transition for timezone.
*/
public function getPreviousDSTTransition(string $timezone, \DateTimeInterface $datetime = null): ?array
{
$datetime = $datetime ?? new \DateTime();
$transitions = $this->getDSTTransitions($timezone, (int) $datetime->format('Y'));
$timestamp = $datetime->getTimestamp();
$previousTransition = null;
foreach ($transitions as $transition) {
if ($transition['timestamp'] < $timestamp) {
$previousTransition = $transition;
} else {
break;
}
}
// Check previous year if no transitions found this year
if ($previousTransition === null) {
$previousYear = (int) $datetime->format('Y') - 1;
$previousYearTransitions = $this->getDSTTransitions($timezone, $previousYear);
return !empty($previousYearTransitions) ? end($previousYearTransitions) : null;
}
return $previousTransition;
}
/**
* Get DST offset for timezone.
*/
public function getDSTOffset(string $timezone): int
{
if (!$this->isValidTimezone($timezone)) {
throw new \InvalidArgumentException("Invalid timezone: {$timezone}");
}
// Most DST offsets are 1 hour (3600 seconds)
// Some exceptions exist (like Lord Howe Island with 30 minutes)
$exceptions = [
'Australia/Lord_Howe' => 1800, // 30 minutes
'Antarctica/Macquarie' => 1800, // 30 minutes
];
return $exceptions[$timezone] ?? 3600;
}
/**
* Check if timezone observes DST.
*/
public function observesDST(string $timezone): bool
{
if (!$this->isValidTimezone($timezone)) {
throw new \InvalidArgumentException("Invalid timezone: {$timezone}");
}
// Check if timezone has any DST transitions in current year
$transitions = $this->getDSTTransitions($timezone);
return !empty($transitions);
}
/**
* Get DST periods for timezone in specific year.
*/
public function getDSTPeriods(string $timezone, int $year = null): array
{
$year = $year ?? (int) date('Y');
$transitions = $this->getDSTTransitions($timezone, $year);
$periods = [];
$startTransition = null;
foreach ($transitions as $transition) {
if ($transition['type'] === 'start') {
$startTransition = $transition;
} elseif ($transition['type'] === 'end' && $startTransition) {
$periods[] = [
'start' => $startTransition,
'end' => $transition,
'duration' => $transition['timestamp'] - $startTransition['timestamp'],
'duration_hours' => ($transition['timestamp'] - $startTransition['timestamp']) / 3600
];
$startTransition = null;
}
}
return $periods;
}
/**
* Convert datetime with DST awareness.
*/
public function convertWithDST(\DateTimeInterface $datetime, string $fromTimezone, string $toTimezone): \DateTime
{
if (!$this->isValidTimezone($fromTimezone)) {
throw new \InvalidArgumentException("Invalid source timezone: {$fromTimezone}");
}
if (!$this->isValidTimezone($toTimezone)) {
throw new \InvalidArgumentException("Invalid target timezone: {$toTimezone}");
}
$fromTz = new \DateTimeZone($fromTimezone);
$toTz = new \DateTimeZone($toTimezone);
// Create DateTime in source timezone
$sourceDt = new \DateTime($datetime->format('Y-m-d H:i:s'), $fromTz);
// Convert to target timezone (PHP handles DST automatically)
$targetDt = $sourceDt->setTimezone($toTz);
// Add DST metadata
$targetDt->dstInfo = [
'source_dst' => $this->isDSTActive($fromTimezone, $datetime),
'target_dst' => $this->isDSTActive($toTimezone, $targetDt),
'source_offset' => $fromTz->getOffset($datetime),
'target_offset' => $toTz->getOffset($targetDt),
'offset_change' => $toTz->getOffset($targetDt) - $fromTz->getOffset($datetime)
];
return $targetDt;
}
/**
* Handle ambiguous or non-existent times during DST transitions.
*/
public function handleAmbiguousTime(\DateTimeInterface $datetime, string $timezone, string $preference = 'standard'): \DateTime
{
if (!$this->isValidTimezone($timezone)) {
throw new \InvalidArgumentException("Invalid timezone: {$timezone}");
}
$tz = new \DateTimeZone($timezone);
$dt = new \DateTime($datetime->format('Y-m-d H:i:s'), $tz);
// Check if this is an ambiguous time (during fall back transition)
$previousTransition = $this->getPreviousDSTTransition($timezone, $datetime);
if ($previousTransition && $previousTransition['type'] === 'end') {
$transitionDateTime = new \DateTime($previousTransition['datetime']);
$transitionDateTime->setTimezone($tz);
// Check if our datetime is within the ambiguous hour
$diff = $datetime->getTimestamp() - $transitionDateTime->getTimestamp();
if ($diff >= 0 && $diff < 3600) { // Within the ambiguous hour
switch ($preference) {
case 'standard':
// Use standard time (first occurrence)
return $dt;
case 'dst':
// Use DST time (second occurrence)
$dt->modify('+1 hour');
return $dt;
case 'earlier':
// Use earlier time
return $dt;
case 'later':
// Use later time
$dt->modify('+1 hour');
return $dt;
default:
throw new \InvalidArgumentException("Invalid preference: {$preference}");
}
}
}
return $dt;
}
/**
* Handle non-existent time during DST transition.
*/
public function handleNonExistentTime(\DateTimeInterface $datetime, string $timezone, string $strategy = 'forward'): \DateTime
{
if (!$this->isValidTimezone($timezone)) {
throw new \InvalidArgumentException("Invalid timezone: {$timezone}");
}
$tz = new \DateTimeZone($timezone);
// Check if this is a non-existent time (during spring forward transition)
$previousTransition = $this->getPreviousDSTTransition($timezone, $datetime);
if ($previousTransition && $previousTransition['type'] === 'start') {
$transitionDateTime = new \DateTime($previousTransition['datetime']);
$transitionDateTime->setTimezone($tz);
// Check if our datetime is within the skipped hour
$diff = $datetime->getTimestamp() - $transitionDateTime->getTimestamp();
if ($diff >= 0 && $diff < 3600) { // Within the skipped hour
switch ($strategy) {
case 'forward':
// Move forward to the next valid time
return new \DateTime($previousTransition['datetime'], $tz);
case 'backward':
// Move backward to the previous valid time
$dt = new \DateTime($previousTransition['datetime'], $tz);
$dt->modify('-1 hour');
return $dt;
case 'adjust':
// Adjust to the nearest valid time
return new \DateTime($previousTransition['datetime'], $tz);
default:
throw new \InvalidArgumentException("Invalid strategy: {$strategy}");
}
}
}
return new \DateTime($datetime->format('Y-m-d H:i:s'), $tz);
}
/**
* Get DST rules for timezone.
*/
public function getDSTRules(string $timezone): array
{
if (!$this->isValidTimezone($timezone)) {
throw new \InvalidArgumentException("Invalid timezone: {$timezone}");
}
return $this->dstRules[$timezone] ?? $this->generateDSTRules($timezone);
}
/**
* Calculate DST-aware time difference.
*/
public function calculateTimeDifference(\DateTimeInterface $start, \DateTimeInterface $end, string $timezone): array
{
if (!$this->isValidTimezone($timezone)) {
throw new \InvalidArgumentException("Invalid timezone: {$timezone}");
}
$tz = new \DateTimeZone($timezone);
$startDt = new \DateTime($start->format('Y-m-d H:i:s'), $tz);
$endDt = new \DateTime($end->format('Y-m-d H:i:s'), $tz);
$interval = $endDt->diff($startDt);
// Check if DST transitions occurred between the two times
$transitions = $this->getDSTTransitions($timezone, (int) $start->format('Y'));
$dstTransitionsInRange = [];
foreach ($transitions as $transition) {
if ($transition['timestamp'] > $start->getTimestamp() &&
$transition['timestamp'] < $end->getTimestamp()) {
$dstTransitionsInRange[] = $transition;
}
}
return [
'interval' => $interval,
'total_seconds' => $end->getTimestamp() - $start->getTimestamp(),
'dst_transitions' => $dstTransitionsInRange,
'dst_adjusted_seconds' => $this->calculateDSTAdjustedDifference($start, $end, $timezone),
'has_dst_transition' => !empty($dstTransitionsInRange)
];
}
/**
* Get DST-aware business hours calculation.
*/
public function calculateBusinessHours(\DateTimeInterface $datetime, string $timezone, array $businessHours = null): array
{
$businessHours = $businessHours ?? $this->config['business_hours'];
if (!$this->isValidTimezone($timezone)) {
throw new \InvalidArgumentException("Invalid timezone: {$timezone}");
}
$tz = new \DateTimeZone($timezone);
$dt = new \DateTime($datetime->format('Y-m-d H:i:s'), $tz);
$isDST = $this->isDSTActive($timezone, $dt);
$hour = (int) $dt->format('H');
$dayOfWeek = (int) $dt->format('w');
// Adjust business hours for DST if needed
$adjustedBusinessHours = $businessHours;
if ($isDST && $this->config['adjust_business_hours_for_dst']) {
$adjustedBusinessHours = $this->adjustBusinessHoursForDST($businessHours, $timezone);
}
$isBusinessHours = in_array($dayOfWeek, $adjustedBusinessHours['weekdays']) &&
$hour >= $adjustedBusinessHours['start_hour'] &&
$hour < $adjustedBusinessHours['end_hour'];
return [
'datetime' => $dt,
'timezone' => $timezone,
'is_dst' => $isDST,
'is_business_hours' => $isBusinessHours,
'business_hours' => $adjustedBusinessHours,
'next_business_hour' => $this->getNextBusinessHour($dt, $timezone, $adjustedBusinessHours),
'previous_business_hour' => $this->getPreviousBusinessHour($dt, $timezone, $adjustedBusinessHours)
];
}
/**
* Validate DST configuration.
*/
public function validateDSTConfig(array $config): array
{
$errors = [];
$warnings = [];
// Validate timezone
if (isset($config['timezone']) && !$this->isValidTimezone($config['timezone'])) {
$errors[] = "Invalid timezone: {$config['timezone']}";
}
// Validate business hours
if (isset($config['business_hours'])) {
$bh = $config['business_hours'];
if (!isset($bh['start_hour']) || !is_int($bh['start_hour']) || $bh['start_hour'] < 0 || $bh['start_hour'] > 23) {
$errors[] = "Invalid start_hour in business_hours";
}
if (!isset($bh['end_hour']) || !is_int($bh['end_hour']) || $bh['end_hour'] < 0 || $bh['end_hour'] > 24) {
$errors[] = "Invalid end_hour in business_hours";
}
if (isset($bh['start_hour']) && isset($bh['end_hour']) && $bh['start_hour'] >= $bh['end_hour']) {
$warnings[] = "Business hours start_hour should be less than end_hour";
}
}
// Validate preference settings
if (isset($config['ambiguous_time_preference'])) {
$validPreferences = ['standard', 'dst', 'earlier', 'later'];
if (!in_array($config['ambiguous_time_preference'], $validPreferences)) {
$errors[] = "Invalid ambiguous_time_preference";
}
}
if (isset($config['non_existent_time_strategy'])) {
$validStrategies = ['forward', 'backward', 'adjust'];
if (!in_array($config['non_existent_time_strategy'], $validStrategies)) {
$errors[] = "Invalid non_existent_time_strategy";
}
}
return [
'valid' => empty($errors),
'errors' => $errors,
'warnings' => $warnings
];
}
/**
* Generate DST report for timezone.
*/
public function generateDSTReport(string $timezone, int $year = null): array
{
$year = $year ?? (int) date('Y');
if (!$this->isValidTimezone($timezone)) {
throw new \InvalidArgumentException("Invalid timezone: {$timezone}");
}
$transitions = $this->getDSTTransitions($timezone, $year);
$periods = $this->getDSTPeriods($timezone, $year);
$observesDST = $this->observesDST($timezone);
$report = [
'timezone' => $timezone,
'year' => $year,
'observes_dst' => $observesDST,
'total_transitions' => count($transitions),
'total_periods' => count($periods),
'dst_offset' => $this->getDSTOffset($timezone),
'current_status' => $this->getDSTInfo($timezone),
'transitions' => $transitions,
'periods' => $periods
];
if ($observesDST) {
$report['statistics'] = [
'total_dst_hours' => $this->calculateTotalDSTHours($timezone, $year),
'dst_percentage' => $this->calculateDSTPercentage($timezone, $year),
'longest_dst_period' => $this->getLongestDSTPeriod($timezone, $year),
'shortest_dst_period' => $this->getShortestDSTPeriod($timezone, $year)
];
}
return $report;
}
/**
* Check if timezone is valid.
*/
protected function isValidTimezone(string $timezone): bool
{
return in_array($timezone, \DateTimeZone::listIdentifiers());
}
/**
* Load DST rules.
*/
protected function loadDSTRules(): array
{
return [
// US DST rules (historical and current)
'America/New_York' => [
'start_month' => 3,
'start_day' => 'second_sunday',
'start_time' => '02:00',
'end_month' => 11,
'end_day' => 'first_sunday',
'end_time' => '02:00',
'offset' => 3600
],
'America/Chicago' => [
'start_month' => 3,
'start_day' => 'second_sunday',
'start_time' => '02:00',
'end_month' => 11,
'end_day' => 'first_sunday',
'end_time' => '02:00',
'offset' => 3600
],
'America/Denver' => [
'start_month' => 3,
'start_day' => 'second_sunday',
'start_time' => '02:00',
'end_month' => 11,
'end_day' => 'first_sunday',
'end_time' => '02:00',
'offset' => 3600
],
'America/Los_Angeles' => [
'start_month' => 3,
'start_day' => 'second_sunday',
'start_time' => '02:00',
'end_month' => 11,
'end_day' => 'first_sunday',
'end_time' => '02:00',
'offset' => 3600
],
// European DST rules
'Europe/London' => [
'start_month' => 3,
'start_day' => 'last_sunday',
'start_time' => '01:00',
'end_month' => 10,
'end_day' => 'last_sunday',
'end_time' => '01:00',
'offset' => 3600
],
'Europe/Paris' => [
'start_month' => 3,
'start_day' => 'last_sunday',
'start_time' => '01:00',
'end_month' => 10,
'end_day' => 'last_sunday',
'end_time' => '01:00',
'offset' => 3600
],
'Europe/Berlin' => [
'start_month' => 3,
'start_day' => 'last_sunday',
'start_time' => '01:00',
'end_month' => 10,
'end_day' => 'last_sunday',
'end_time' => '01:00',
'offset' => 3600
]
];
}
/**
* Generate DST rules for timezone.
*/
protected function generateDSTRules(string $timezone): array
{
// Try to infer rules from transitions
$transitions = $this->getDSTTransitions($timezone);
if (empty($transitions)) {
return [
'observes_dst' => false
];
}
// Analyze transitions to generate rules
$rules = ['observes_dst' => true];
// This is a simplified rule generation
// In practice, you'd want more sophisticated analysis
foreach ($transitions as $transition) {
$dt = new \DateTime($transition['datetime']);
if ($transition['type'] === 'start') {
$rules['start_month'] = (int) $dt->format('m');
$rules['start_day'] = 'unknown'; // Would need more analysis
$rules['start_time'] = $dt->format('H:i');
} elseif ($transition['type'] === 'end') {
$rules['end_month'] = (int) $dt->format('m');
$rules['end_day'] = 'unknown'; // Would need more analysis
$rules['end_time'] = $dt->format('H:i');
}
}
$rules['offset'] = $this->getDSTOffset($timezone);
return $rules;
}
/**
* Calculate offset change for transition.
*/
protected function calculateOffsetChange(array $transition, \DateTimeZone $tz, int $year): int
{
// Get offset before and after transition
$before = new \DateTime($transition['datetime'], $tz);
$before->modify('-1 second');
$after = new \DateTime($transition['datetime'], $tz);
$after->modify('+1 second');
return $after->getOffset() - $before->getOffset();
}
/**
* Calculate DST-adjusted time difference.
*/
protected function calculateDSTAdjustedDifference(\DateTimeInterface $start, \DateTimeInterface $end, string $timezone): int
{
$tz = new \DateTimeZone($timezone);
$startDt = new \DateTime($start->format('Y-m-d H:i:s'), $tz);
$endDt = new \DateTime($end->format('Y-m-d H:i:s'), $tz);
// Calculate actual difference in seconds
return $endDt->getTimestamp() - $startDt->getTimestamp();
}
/**
* Adjust business hours for DST.
*/
protected function adjustBusinessHoursForDST(array $businessHours, string $timezone): array
{
// Some businesses may want to adjust their hours during DST
// This is a placeholder for more complex logic
return $businessHours;
}
/**
* Get next business hour.
*/
protected function getNextBusinessHour(\DateTime $datetime, string $timezone, array $businessHours): \DateTime
{
$next = clone $datetime;
$dayOfWeek = (int) $next->format('w');
// Move to next business day if needed
while (!in_array($dayOfWeek, $businessHours['weekdays'])) {
$next->modify('+1 day');
$dayOfWeek = (int) $next->format('w');
}
// Set to start of business hours
$next->setTime($businessHours['start_hour'], 0, 0);
return $next;
}
/**
* Get previous business hour.
*/
protected function getPreviousBusinessHour(\DateTime $datetime, string $timezone, array $businessHours): \DateTime
{
$previous = clone $datetime;
$dayOfWeek = (int) $previous->format('w');
// Move to previous business day if needed
while (!in_array($dayOfWeek, $businessHours['weekdays'])) {
$previous->modify('-1 day');
$dayOfWeek = (int) $previous->format('w');
}
// Set to end of business hours
$previous->setTime($businessHours['end_hour'] - 1, 59, 59);
return $previous;
}
/**
* Calculate total DST hours in year.
*/
protected function calculateTotalDSTHours(string $timezone, int $year): int
{
$periods = $this->getDSTPeriods($timezone, $year);
$totalHours = 0;
foreach ($periods as $period) {
$totalHours += $period['duration_hours'];
}
return (int) $totalHours;
}
/**
* Calculate DST percentage for year.
*/
protected function calculateDSTPercentage(string $timezone, int $year): float
{
$totalHours = $this->calculateTotalDSTHours($timezone, $year);
$yearHours = ($this->isLeapYear($year) ? 366 : 365) * 24;
return ($totalHours / $yearHours) * 100;
}
/**
* Check if year is leap year.
*/
protected function isLeapYear(int $year): bool
{
return ($year % 4 === 0 && $year % 100 !== 0) || ($year % 400 === 0);
}
/**
* Get longest DST period.
*/
protected function getLongestDSTPeriod(string $timezone, int $year): ?array
{
$periods = $this->getDSTPeriods($timezone, $year);
if (empty($periods)) {
return null;
}
$longest = $periods[0];
foreach ($periods as $period) {
if ($period['duration_hours'] > $longest['duration_hours']) {
$longest = $period;
}
}
return $longest;
}
/**
* Get shortest DST period.
*/
protected function getShortestDSTPeriod(string $timezone, int $year): ?array
{
$periods = $this->getDSTPeriods($timezone, $year);
if (empty($periods)) {
return null;
}
$shortest = $periods[0];
foreach ($periods as $period) {
if ($period['duration_hours'] < $shortest['duration_hours']) {
$shortest = $period;
}
}
return $shortest;
}
/**
* Clear transition cache.
*/
public function clearCache(): void
{
$this->transitionCache = [];
}
/**
* Get default configuration.
*/
protected function getDefaultConfig(): array
{
return [
'business_hours' => [
'weekdays' => [1, 2, 3, 4, 5], // Monday to Friday
'start_hour' => 9,
'end_hour' => 17
],
'adjust_business_hours_for_dst' => false,
'ambiguous_time_preference' => 'standard',
'non_existent_time_strategy' => 'forward',
'cache_enabled' => true,
'cache_ttl' => 3600
];
}
/**
* Get configuration.
*/
public function getConfig(): array
{
return $this->config;
}
/**
* Set configuration.
*/
public function setConfig(array $config): void
{
$this->config = array_merge($this->config, $config);
}
/**
* Create DST handler instance.
*/
public static function create(array $config = []): self
{
return new self($config);
}
/**
* Create for US timezones.
*/
public static function forUS(): self
{
return new self([
'business_hours' => [
'weekdays' => [1, 2, 3, 4, 5],
'start_hour' => 9,
'end_hour' => 17
],
'ambiguous_time_preference' => 'standard',
'non_existent_time_strategy' => 'forward'
]);
}
/**
* Create for European timezones.
*/
public static function forEurope(): self
{
return new self([
'business_hours' => [
'weekdays' => [1, 2, 3, 4, 5],
'start_hour' => 9,
'end_hour' => 17
],
'ambiguous_time_preference' => 'standard',
'non_existent_time_strategy' => 'forward'
]);
}
}