Admin
2 weeks ago
Ajax
2 weeks ago
Asset
2 weeks ago
Context
2 weeks ago
Customizer
2 weeks ago
Debug_Bar
2 weeks ago
Dialog
2 weeks ago
Documentation
2 weeks ago
Duplicate
2 weeks ago
Editor
2 weeks ago
Image
2 weeks ago
JSON_LD
2 weeks ago
Languages
2 weeks ago
Log
2 weeks ago
Meta
2 weeks ago
Models
2 weeks ago
PUE
2 weeks ago
Process
2 weeks ago
Promoter
2 weeks ago
REST
2 weeks ago
Repository
2 weeks ago
Service_Providers
2 weeks ago
Shortcode
2 weeks ago
Support
2 weeks ago
Tabbed_View
2 weeks ago
Tooltip
2 weeks ago
Traits
2 weeks ago
Utils
2 weeks ago
Validator
2 weeks ago
Widget
2 weeks ago
Abstract_Deactivation.php
2 weeks ago
Abstract_Plugin_Register.php
2 weeks ago
App_Shop.php
2 weeks ago
Assets.php
2 weeks ago
Assets_Pipeline.php
2 weeks ago
Autoloader.php
2 weeks ago
Cache.php
2 weeks ago
Cache_Listener.php
2 weeks ago
Changelog_Reader.php
2 weeks ago
Container.php
2 weeks ago
Context.php
2 weeks ago
Cost_Utils.php
2 weeks ago
Credits.php
2 weeks ago
Customizer.php
2 weeks ago
DB_Lock.php
2 weeks ago
Data.php
2 weeks ago
Date_Utils.php
2 weeks ago
Db.php
2 weeks ago
Debug.php
2 weeks ago
Dependency.php
2 weeks ago
Deprecation.php
2 weeks ago
Editor.php
2 weeks ago
Error.php
2 weeks ago
Exception.php
2 weeks ago
Extension.php
2 weeks ago
Extension_Loader.php
2 weeks ago
Feature_Detection.php
2 weeks ago
Field.php
2 weeks ago
Field_Conditional.php
2 weeks ago
Freemius.php
2 weeks ago
Log.php
2 weeks ago
Main.php
2 weeks ago
Notices.php
2 weeks ago
Plugin_Meta_Links.php
2 weeks ago
Plugins.php
2 weeks ago
Plugins_API.php
2 weeks ago
Post_History.php
2 weeks ago
Post_Transient.php
2 weeks ago
Promise.php
2 weeks ago
Repository.php
2 weeks ago
Rewrite.php
2 weeks ago
Settings.php
2 weeks ago
Settings_Manager.php
2 weeks ago
Settings_Tab.php
2 weeks ago
Simple_Table.php
2 weeks ago
Support.php
2 weeks ago
Tabbed_View.php
2 weeks ago
Template.php
2 weeks ago
Template_Factory.php
2 weeks ago
Template_Part_Cache.php
2 weeks ago
Templates.php
2 weeks ago
Terms.php
2 weeks ago
Timezones.php
2 weeks ago
Tracker.php
2 weeks ago
Updater.php
2 weeks ago
Validate.php
2 weeks ago
View_Helpers.php
2 weeks ago
Timezones.php
626 lines
| 1 | <?php |
| 2 | |
| 3 | /** |
| 4 | * Helpers for handling timezone based event datetimes. |
| 5 | * |
| 6 | * In our timezone logic, the term "local" refers to the locality of an event |
| 7 | * rather than the local WordPress timezone. |
| 8 | */ |
| 9 | class Tribe__Timezones { |
| 10 | const SITE_TIMEZONE = 'site'; |
| 11 | const EVENT_TIMEZONE = 'event'; |
| 12 | |
| 13 | /** |
| 14 | * Container for reusable DateTimeZone objects. |
| 15 | * |
| 16 | * @var array |
| 17 | */ |
| 18 | protected static $timezones = []; |
| 19 | |
| 20 | public static function init() { |
| 21 | self::invalidate_caches(); |
| 22 | } |
| 23 | |
| 24 | /** |
| 25 | * Clear any cached timezone-related values when appropriate. |
| 26 | * |
| 27 | * Currently we are concerned only with the site timezone abbreviation. |
| 28 | */ |
| 29 | protected static function invalidate_caches() { |
| 30 | add_filter( 'pre_update_option_gmt_offset', [ __CLASS__, 'clear_site_timezone_abbr' ] ); |
| 31 | add_filter( 'pre_update_option_timezone_string', [ __CLASS__, 'clear_site_timezone_abbr' ] ); |
| 32 | } |
| 33 | |
| 34 | /** |
| 35 | * Wipe the cached site timezone abbreviation, if set. |
| 36 | * |
| 37 | * @param mixed $option_val (passed through without modification) |
| 38 | * |
| 39 | * @return mixed |
| 40 | */ |
| 41 | public static function clear_site_timezone_abbr( $option_val ) { |
| 42 | delete_transient( 'tribe_events_wp_timezone_abbr' ); |
| 43 | return $option_val; |
| 44 | } |
| 45 | |
| 46 | /** |
| 47 | * Returns the current site-wide timezone string abbreviation, if it can be |
| 48 | * determined or falls back on the full timezone string/offset text. |
| 49 | * |
| 50 | * @param string $date |
| 51 | * |
| 52 | * @return string |
| 53 | */ |
| 54 | public static function wp_timezone_abbr( $date ) { |
| 55 | $timezone_string = self::wp_timezone_string(); |
| 56 | $abbr = self::abbr( $date, $timezone_string ); |
| 57 | |
| 58 | return empty( $abbr ) ? $timezone_string : $abbr; |
| 59 | } |
| 60 | |
| 61 | /** |
| 62 | * Returns the current site-wide timezone string. |
| 63 | * |
| 64 | * Based on the core WP code found in wp-admin/options-general.php. |
| 65 | * |
| 66 | * @return string |
| 67 | */ |
| 68 | public static function wp_timezone_string() { |
| 69 | $current_offset = get_option( 'gmt_offset' ); |
| 70 | $tzstring = get_option( 'timezone_string' ); |
| 71 | |
| 72 | // Return the timezone string if already set |
| 73 | if ( ! empty( $tzstring ) ) { |
| 74 | return $tzstring; |
| 75 | } |
| 76 | |
| 77 | // Otherwise return the UTC offset |
| 78 | if ( 0 == $current_offset ) { |
| 79 | return 'UTC+0'; |
| 80 | } elseif ( $current_offset < 0 ) { |
| 81 | return 'UTC' . $current_offset; |
| 82 | } |
| 83 | |
| 84 | return 'UTC+' . $current_offset; |
| 85 | } |
| 86 | |
| 87 | /** |
| 88 | * Attempts to provide the correct timezone abbreviation for the provided timezone string |
| 89 | * on the date given (and so should account for daylight saving time, etc). |
| 90 | * |
| 91 | * @param string|DateTime|DateTimeImmutable $date The date string representation or object. |
| 92 | * @param string|DateTimeZone $timezone_string The timezone string or object. |
| 93 | * |
| 94 | * @return string |
| 95 | */ |
| 96 | public static function abbr( $date, $timezone_string ) { |
| 97 | try { |
| 98 | $timezone_object = $timezone_string instanceof DateTimeZone |
| 99 | ? $timezone_string |
| 100 | : new DateTimeZone( $timezone_string ); |
| 101 | $date_time = $date instanceof DateTime |
| 102 | || ( class_exists( 'DateTimeImmutable' ) && $date instanceof DateTimeImmutable ) |
| 103 | ? $date |
| 104 | : Tribe__Date_Utils::build_date_object( $date, $timezone_object ); |
| 105 | |
| 106 | $abbr = $date_time->format( 'T' ); |
| 107 | |
| 108 | // If PHP date "T" format is a -03 or +03, it's a bugged abbreviation, we can find it manually. |
| 109 | if ( 0 === strpos( $abbr, '-' ) || 0 === strpos( $abbr, '+' ) ) { |
| 110 | $abbreviations = timezone_abbreviations_list(); |
| 111 | |
| 112 | foreach ( $abbreviations as $abbreviation => $timezones ) { |
| 113 | foreach ( $timezones as $timezone ) { |
| 114 | if ( $timezone['timezone_id'] === $timezone_string ) { |
| 115 | return strtoupper( $abbreviation ); |
| 116 | } |
| 117 | } |
| 118 | } |
| 119 | } |
| 120 | } catch ( Exception $e ) { |
| 121 | $abbr = ''; |
| 122 | } |
| 123 | |
| 124 | return $abbr; |
| 125 | } |
| 126 | |
| 127 | /** |
| 128 | * Helper function to retrieve the timezone string for a given UTC offset |
| 129 | * |
| 130 | * This is a close copy of WooCommerce's wc_timezone_string() method |
| 131 | * |
| 132 | * @param string $offset UTC offset |
| 133 | * |
| 134 | * @return string |
| 135 | */ |
| 136 | public static function generate_timezone_string_from_utc_offset( $offset ) { |
| 137 | if ( ! self::is_utc_offset( $offset ) ) { |
| 138 | return $offset; |
| 139 | } |
| 140 | |
| 141 | // ensure we have the minutes on the offset |
| 142 | if ( ! strpos( $offset, ':' ) ) { |
| 143 | $offset .= ':00'; |
| 144 | } |
| 145 | |
| 146 | $offset = str_replace( 'UTC', '', $offset ); |
| 147 | |
| 148 | list( $hours, $minutes ) = explode( ':', $offset ); |
| 149 | $seconds = $hours * 60 * 60 + $minutes * 60; |
| 150 | |
| 151 | // attempt to guess the timezone string from the UTC offset |
| 152 | $timezone = timezone_name_from_abbr( '', $seconds, 0 ); |
| 153 | |
| 154 | if ( false === $timezone ) { |
| 155 | $is_dst = (bool) date( 'I' ); |
| 156 | |
| 157 | foreach ( timezone_abbreviations_list() as $abbr ) { |
| 158 | foreach ( $abbr as $city ) { |
| 159 | if ( |
| 160 | (bool) $city['dst'] === $is_dst |
| 161 | && intval( $city['offset'] ) === intval( $seconds ) |
| 162 | && $city['timezone_id'] |
| 163 | ) { |
| 164 | return $city['timezone_id']; |
| 165 | } |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | // fallback to UTC |
| 170 | return 'UTC'; |
| 171 | } |
| 172 | |
| 173 | return $timezone; |
| 174 | } |
| 175 | |
| 176 | /** |
| 177 | * Tests to see if the timezone string is a UTC offset, ie "UTC+2". |
| 178 | * |
| 179 | * @param string $timezone |
| 180 | * |
| 181 | * @return bool |
| 182 | */ |
| 183 | public static function is_utc_offset( $timezone ) { |
| 184 | $timezone = trim( $timezone ); |
| 185 | return ( 0 === strpos( $timezone, 'UTC' ) && strlen( $timezone ) > 3 ); |
| 186 | } |
| 187 | |
| 188 | /** |
| 189 | * Returns a DateTimeZone object matching the representation in $tzstring where |
| 190 | * possible, or else representing UTC (or, in the worst case, false). |
| 191 | * |
| 192 | * If optional parameter $with_fallback is true, which is the default, then in |
| 193 | * the event it cannot find/create the desired timezone it will try to return the |
| 194 | * UTC DateTimeZone before bailing. |
| 195 | * |
| 196 | * @param string $tzstring |
| 197 | * @param bool $with_fallback = true |
| 198 | * |
| 199 | * @return DateTimeZone|false |
| 200 | */ |
| 201 | public static function get_timezone( $tzstring, $with_fallback = true ) { |
| 202 | if ( isset( self::$timezones[ $tzstring ] ) ) { |
| 203 | return self::$timezones[ $tzstring ]; |
| 204 | } |
| 205 | |
| 206 | try { |
| 207 | self::$timezones[ $tzstring ] = new DateTimeZone( $tzstring ); |
| 208 | return self::$timezones[ $tzstring ]; |
| 209 | } |
| 210 | catch ( Exception $e ) { |
| 211 | if ( $with_fallback ) { |
| 212 | return self::get_timezone( 'UTC', true ); |
| 213 | } |
| 214 | } |
| 215 | |
| 216 | return false; |
| 217 | } |
| 218 | |
| 219 | /** |
| 220 | * Confirms if the current timezone mode matches the $possible_mode. |
| 221 | * |
| 222 | * @param string $possible_mode |
| 223 | * |
| 224 | * @return bool |
| 225 | */ |
| 226 | public static function is_mode( $possible_mode ) { |
| 227 | return $possible_mode === self::mode(); |
| 228 | } |
| 229 | |
| 230 | /** |
| 231 | * Returns a string representing the timezone/offset currently desired for |
| 232 | * the display of dates and times. |
| 233 | * |
| 234 | * @return string |
| 235 | */ |
| 236 | public static function mode() { |
| 237 | $mode = self::EVENT_TIMEZONE; |
| 238 | |
| 239 | if ( 'site' === tribe_get_option( 'tribe_events_timezone_mode' ) ) { |
| 240 | $mode = self::SITE_TIMEZONE; |
| 241 | } |
| 242 | |
| 243 | return apply_filters( 'tribe_events_current_display_timezone', $mode ); |
| 244 | } |
| 245 | |
| 246 | /** |
| 247 | * Tries to convert the provided $datetime to UTC from the timezone represented by $tzstring. |
| 248 | * |
| 249 | * Though the usual range of formats are allowed, $datetime ordinarily ought to be something |
| 250 | * like the "Y-m-d H:i:s" format (ie, no timezone information). If it itself contains timezone |
| 251 | * data, the results may be unexpected. |
| 252 | * |
| 253 | * In those cases where the conversion fails to take place, the $datetime string will be |
| 254 | * returned untouched. |
| 255 | * |
| 256 | * @param string $datetime |
| 257 | * @param string $tzstring |
| 258 | * @param string $format The optional format of the resulting date, defaults to |
| 259 | * `Tribe__Date_Utils::DBDATETIMEFORMAT`. |
| 260 | * |
| 261 | * @return string |
| 262 | */ |
| 263 | public static function to_utc( $datetime, $tzstring, $format = null ) { |
| 264 | if ( self::is_utc_offset( $tzstring ) ) { |
| 265 | return self::apply_offset( $datetime, $tzstring, true ); |
| 266 | } |
| 267 | |
| 268 | $local = self::get_timezone( $tzstring ); |
| 269 | $utc = self::get_timezone( 'UTC' ); |
| 270 | |
| 271 | $new_datetime = date_create( $datetime, $local ); |
| 272 | |
| 273 | if ( $new_datetime ) { |
| 274 | $new_datetime->setTimezone( $utc ); |
| 275 | $format = ! empty( $format ) ? $format : Tribe__Date_Utils::DBDATETIMEFORMAT; |
| 276 | |
| 277 | return $new_datetime->format( $format ); |
| 278 | } |
| 279 | |
| 280 | // Fallback to the unmodified datetime if there was a failure during conversion |
| 281 | return $datetime; |
| 282 | } |
| 283 | |
| 284 | /** |
| 285 | * Tries to convert the provided $datetime to the timezone represented by $tzstring. |
| 286 | * |
| 287 | * This is the sister function of self::to_utc() - please review the docs for that method |
| 288 | * for more information. |
| 289 | * |
| 290 | * @param string $datetime |
| 291 | * @param string $tzstring |
| 292 | * |
| 293 | * @return string |
| 294 | */ |
| 295 | public static function to_tz( $datetime, $tzstring ) { |
| 296 | |
| 297 | if ( self::is_utc_offset( $tzstring ) ) { |
| 298 | |
| 299 | return self::apply_offset( $datetime, $tzstring ); |
| 300 | } |
| 301 | |
| 302 | $local = self::get_timezone( $tzstring ); |
| 303 | $utc = self::get_timezone( 'UTC' ); |
| 304 | |
| 305 | $new_datetime = date_create( $datetime, $utc ); |
| 306 | |
| 307 | if ( $new_datetime && $new_datetime->setTimezone( $local ) ) { |
| 308 | return $new_datetime->format( Tribe__Date_Utils::DBDATETIMEFORMAT ); |
| 309 | } |
| 310 | |
| 311 | // Fallback to the unmodified datetime if there was a failure during conversion |
| 312 | return $datetime; |
| 313 | } |
| 314 | |
| 315 | /** |
| 316 | * Localizes a date or timestamp using WordPress timezone and returns it in the specified format. |
| 317 | * |
| 318 | * @param string $format The format the date shouuld be formatted to. |
| 319 | * @param string|int $date The date UNIX timestamp or `strtotime` parseable string. |
| 320 | * @param string $timezone An optional timezone string identifying the timezone the date shoudl be localized |
| 321 | * to; defaults to the WordPress installation timezone (if available) or to the system |
| 322 | * timezone. |
| 323 | * |
| 324 | * @return string|bool The parsed date in the specified format and localized to the system or specified |
| 325 | * timezone, or `false` if the specified date is not a valid date string or timestamp |
| 326 | * or the specified timezone is not a valid timezone string. |
| 327 | */ |
| 328 | public static function localize_date( $format = null, $date = null, $timezone = null ) { |
| 329 | if ( empty( $timezone ) ) { |
| 330 | $timezone = self::wp_timezone_string(); |
| 331 | } |
| 332 | |
| 333 | $timezone = self::generate_timezone_string_from_utc_offset( $timezone ); |
| 334 | |
| 335 | try { |
| 336 | $timezone_object = new DateTimeZone( $timezone ); |
| 337 | |
| 338 | if ( Tribe__Date_Utils::is_timestamp( $date ) ) { |
| 339 | $date = new DateTime( "@{$date}" ); |
| 340 | } else { |
| 341 | $date = new DateTime( $date ); |
| 342 | } |
| 343 | } catch ( Exception $e ) { |
| 344 | return false; |
| 345 | } |
| 346 | |
| 347 | $date->setTimezone( $timezone_object ); |
| 348 | |
| 349 | return $date->format( $format ); |
| 350 | } |
| 351 | |
| 352 | /** |
| 353 | * Converts a date string or timestamp to a destination timezone. |
| 354 | * |
| 355 | * @param string|int $date Either a string parseable by the `strtotime` function or a UNIX timestamp. |
| 356 | * @param string $from_timezone The timezone of the source date. |
| 357 | * @param string $to_timezone The timezone the destination date should use. |
| 358 | * @param string $format The format that should be used for the destination date. |
| 359 | * |
| 360 | * @return string The formatted and converted date. |
| 361 | */ |
| 362 | public static function convert_date_from_timezone( $date, $from_timezone, $to_timezone, $format ) { |
| 363 | if ( ! Tribe__Date_Utils::is_timestamp( $date ) ) { |
| 364 | $from_date = new DateTime( $date, new DateTimeZone( $from_timezone ) ); |
| 365 | $timestamp = $from_date->format( 'U' ); |
| 366 | } else { |
| 367 | $timestamp = $date; |
| 368 | } |
| 369 | |
| 370 | $to_date = new DateTime( "@{$timestamp}", new DateTimeZone( $to_timezone ) ); |
| 371 | |
| 372 | return $to_date->format( $format ); |
| 373 | } |
| 374 | |
| 375 | /** |
| 376 | * Whether the candidate timezone is a valid PHP timezone or a supported UTC offset. |
| 377 | * |
| 378 | * @param string $candidate |
| 379 | * |
| 380 | * @return bool |
| 381 | */ |
| 382 | public static function is_valid_timezone( $candidate ) { |
| 383 | if ( self::is_utc_offset( $candidate ) ) { |
| 384 | return true; |
| 385 | } |
| 386 | try { |
| 387 | new DateTimeZone( $candidate ); |
| 388 | } catch ( Exception $e ) { |
| 389 | return false; |
| 390 | } |
| 391 | |
| 392 | return true; |
| 393 | } |
| 394 | |
| 395 | /** |
| 396 | * Given a string in the form "UTC+2.5" returns the corresponding DateTimeZone object. |
| 397 | * |
| 398 | * If this is not possible or if $utc_offset_string does not match the expected pattern, |
| 399 | * boolean false is returned. |
| 400 | * |
| 401 | * @todo revise to eliminate all of these: maybe_get_tz_name, apply_offset, timezone_from_utc_offset, and adjust_timestamp |
| 402 | * |
| 403 | * @since 4.6.3 |
| 404 | * |
| 405 | * @param string $utc_offset_string |
| 406 | * |
| 407 | * @return DateTimeZone | bool |
| 408 | */ |
| 409 | public static function timezone_from_utc_offset( $utc_offset_string ) { |
| 410 | // Test for strings looking like "UTC-2" or "UTC+5.25" etc |
| 411 | if ( ! preg_match( '/^UTC[+-][0-9.]{1,4}$/', $utc_offset_string ) ) { |
| 412 | return false; |
| 413 | } |
| 414 | |
| 415 | // Breakdown into polarity, hours and minutes |
| 416 | $parts = explode( '.', substr( $utc_offset_string, 4 ) ); |
| 417 | $hours = (int) $parts[ 0 ]; |
| 418 | $fraction = isset( $parts[ 1 ] ) ? '0.' . (int) $parts[ 1 ] : 0; |
| 419 | $minutes = $fraction * 60; |
| 420 | $polarity = substr( $utc_offset_string, 3, 1 ); |
| 421 | |
| 422 | // Reassemble in the form +/-hhmm (ie "-0200" or "+0930") |
| 423 | $utc_offset = sprintf( $polarity . "%'.02d%'.02d", $hours, $minutes ); |
| 424 | |
| 425 | if ( '+0000' === $utc_offset || '-0000' === $utc_offset ) { |
| 426 | $utc_offset = 'UTC'; |
| 427 | } |
| 428 | |
| 429 | // Use this to build a new DateTimeZone |
| 430 | try { |
| 431 | return new DateTimeZone( $utc_offset ); |
| 432 | } catch ( Exception $e ) { |
| 433 | return false; |
| 434 | } |
| 435 | } |
| 436 | |
| 437 | /** |
| 438 | * Applies an time offset to the specified date time. |
| 439 | * |
| 440 | * @todo revise to eliminate all of these: maybe_get_tz_name, apply_offset, timezone_from_utc_offset, and adjust_timestamp |
| 441 | * |
| 442 | * @param string $datetime The date and time string in a valid date format. |
| 443 | * @param int|string $offset (string or numeric offset) |
| 444 | * @param bool $invert = false Whether the offset should be added (`true`) or |
| 445 | * subtracted (`false`); signum operations carry over so |
| 446 | * `-(-23) = +23`. |
| 447 | * |
| 448 | * @return string |
| 449 | */ |
| 450 | public static function apply_offset( $datetime, $offset, $invert = false ) { |
| 451 | // Normalize |
| 452 | $offset = strtolower( trim( $offset ) ); |
| 453 | |
| 454 | // Strip any leading "utc" text if set |
| 455 | if ( 0 === strpos( $offset, 'utc' ) ) { |
| 456 | $offset = substr( $offset, 3 ); |
| 457 | } |
| 458 | |
| 459 | // It's possible no adjustment will be needed |
| 460 | if ( 0 === (int) $offset ) { |
| 461 | return $datetime; |
| 462 | } |
| 463 | |
| 464 | // if the offset contains fractions like :15, :30 or :45 convert them |
| 465 | $supported_offsets = [ |
| 466 | '/:15$/' => '.25', |
| 467 | '/:30$/' => '.5', |
| 468 | '/:45$/' => '.75', |
| 469 | ]; |
| 470 | $offset = preg_replace( array_keys( $supported_offsets ), array_values( $supported_offsets ), $offset ); |
| 471 | |
| 472 | // Convert the offset to minutes for easier handling of fractional offsets |
| 473 | $offset = (int) ( $offset * 60 ); |
| 474 | |
| 475 | // Invert the offset? Useful for stripping an offset that has already been applied |
| 476 | if ( $invert ) { |
| 477 | $offset *= - 1; |
| 478 | } |
| 479 | |
| 480 | if ( $offset > 0 ) { |
| 481 | $offset = '+' . $offset; |
| 482 | } |
| 483 | |
| 484 | $offset = $offset . ' minutes'; |
| 485 | |
| 486 | $offset_datetime = date_create( $datetime ); |
| 487 | |
| 488 | if ( $offset_datetime && $offset_datetime->modify( $offset ) ) { |
| 489 | return $offset_datetime->format( Tribe__Date_Utils::DBDATETIMEFORMAT ); |
| 490 | } |
| 491 | |
| 492 | return $datetime; |
| 493 | } |
| 494 | |
| 495 | /** |
| 496 | * Try to figure out the Timezone name base on offset |
| 497 | * |
| 498 | * @since 4.0.7 |
| 499 | * |
| 500 | * @todo revise to eliminate all of these: maybe_get_tz_name, apply_offset, timezone_from_utc_offset, and adjust_timestamp |
| 501 | * |
| 502 | * @param string|int|float $timezone The timezone |
| 503 | * |
| 504 | * @return string The Guessed Timezone String |
| 505 | */ |
| 506 | public static function maybe_get_tz_name( $timezone ) { |
| 507 | if ( ! self::is_utc_offset( $timezone ) && ! is_numeric( $timezone ) ) { |
| 508 | return $timezone; |
| 509 | } |
| 510 | |
| 511 | if ( ! is_numeric( $timezone ) ) { |
| 512 | $offset = str_replace( 'utc', '', trim( strtolower( $timezone ) ) ); |
| 513 | } else { |
| 514 | $offset = $timezone; |
| 515 | } |
| 516 | |
| 517 | |
| 518 | // try to get timezone from gmt_offset, respecting daylight savings |
| 519 | $timezone = timezone_name_from_abbr( null, $offset * 3600, true ); |
| 520 | |
| 521 | // if that didn't work, maybe they don't have daylight savings |
| 522 | if ( false === $timezone ) { |
| 523 | $timezone = timezone_name_from_abbr( null, $offset * 3600, false ); |
| 524 | } |
| 525 | |
| 526 | // and if THAT didn't work, round the gmt_offset down and then try to get the timezone respecting daylight savings |
| 527 | if ( false === $timezone ) { |
| 528 | $timezone = timezone_name_from_abbr( null, (int) $offset * 3600, true ); |
| 529 | } |
| 530 | |
| 531 | // lastly if that didn't work, round the gmt_offset down and maybe that TZ doesn't do daylight savings |
| 532 | if ( false === $timezone ) { |
| 533 | $timezone = timezone_name_from_abbr( null, (int) $offset * 3600, false ); |
| 534 | } |
| 535 | |
| 536 | return $timezone; |
| 537 | } |
| 538 | |
| 539 | /** |
| 540 | * Accepts a unix timestamp and adjusts it so that when it is used to constitute |
| 541 | * a new datetime string, that string reflects the designated timezone. |
| 542 | * |
| 543 | * @todo revise to eliminate all of these: maybe_get_tz_name, apply_offset, timezone_from_utc_offset, and adjust_timestamp |
| 544 | * |
| 545 | * @deprecated 4.7.12 |
| 546 | * |
| 547 | * @param string $unix_timestamp |
| 548 | * @param string $tzstring |
| 549 | * |
| 550 | * @return string |
| 551 | */ |
| 552 | public static function adjust_timestamp( $unix_timestamp, $tzstring ) { |
| 553 | try { |
| 554 | $local = self::get_timezone( $tzstring ); |
| 555 | |
| 556 | $datetime = date_create_from_format( 'U', $unix_timestamp )->format( Tribe__Date_Utils::DBDATETIMEFORMAT ); |
| 557 | |
| 558 | // We prefer format('U') to getTimestamp() here due to our requirement for compatibility with PHP 5.2 |
| 559 | return date_create_from_format( 'Y-m-d H:i:s', $datetime, $local )->format( 'U' ); |
| 560 | } |
| 561 | catch( Exception $e ) { |
| 562 | return $unix_timestamp; |
| 563 | } |
| 564 | } |
| 565 | |
| 566 | /** |
| 567 | * Returns a valid timezone object built from the passed timezone or from the |
| 568 | * site one if a timezone in not passed. |
| 569 | * |
| 570 | * @since 4.9.5 |
| 571 | * |
| 572 | * @param string|null|DateTimeZone $timezone A DateTimeZone object, a timezone string |
| 573 | * or `null` to build an object using the site one. |
| 574 | * |
| 575 | * @return DateTimeZone The built DateTimeZone object. |
| 576 | */ |
| 577 | public static function build_timezone_object( $timezone = null ) { |
| 578 | if ( $timezone instanceof DateTimeZone ) { |
| 579 | return $timezone; |
| 580 | } |
| 581 | |
| 582 | /** @var Tribe__Cache $cache */ |
| 583 | $cache = tribe('cache'); |
| 584 | |
| 585 | if ( is_string( $timezone ) && $cached = $cache[ __METHOD__ . $timezone ] ) { |
| 586 | return clone $cached; |
| 587 | } |
| 588 | |
| 589 | $timezone = null === $timezone ? self::wp_timezone_string() : $timezone; |
| 590 | |
| 591 | try { |
| 592 | $object = new DateTimeZone( self::get_valid_timezone( $timezone ) ); |
| 593 | } catch ( Exception $e ) { |
| 594 | return new DateTimeZone( 'UTC' ); |
| 595 | } |
| 596 | |
| 597 | if ( is_string( $timezone ) ) { |
| 598 | $cache[ __METHOD__ . $timezone ] = $object; |
| 599 | } |
| 600 | |
| 601 | return $object; |
| 602 | } |
| 603 | |
| 604 | /** |
| 605 | * Parses the timezone string to validate or convert it into a valid one. |
| 606 | * |
| 607 | * @since 4.9.5 |
| 608 | * |
| 609 | * @param string|\DateTimeZone $timezone_candidate The timezone string candidate. |
| 610 | * |
| 611 | * @return string The validated timezone string or a valid timezone string alternative. |
| 612 | */ |
| 613 | public static function get_valid_timezone( $timezone_candidate ) { |
| 614 | if ( $timezone_candidate instanceof DateTimeZone ) { |
| 615 | return $timezone_candidate->getName(); |
| 616 | } |
| 617 | |
| 618 | $timezone_string = preg_replace( '/[+-]0$/', '', $timezone_candidate ); |
| 619 | $timezone_string = self::is_utc_offset( $timezone_string ) |
| 620 | ? self::generate_timezone_string_from_utc_offset( $timezone_string ) |
| 621 | : $timezone_string; |
| 622 | |
| 623 | return $timezone_string; |
| 624 | } |
| 625 | } |
| 626 |