Facebook Timezone Confusion

Lately there seems to have been a bit of confusion over importing events from Facebook into Modern Tribe’s The Events Calendar plugin for WordPress (when using the Facebook Events add-on)[1].

Update: future versions of Facebook Events ought to have a fix (for the problem I’m describing here) built right in, and Tim has posted an official fix you can use in the interim – see here.

This has been typified by people living outside of the PST timezone importing events only to find they are incorrectly offset by a number of hours. Today I wanted to look at how this can be controlled when setting up an event on Facebook as well as an alternative means of dealing with the problem.

Screengrab of the Facebook add event modal

Control over timezones for events can be exercised when setting up the event.

When you create a new event Facebook will intelligently determine what the timezone should be. Of course, it can only do this if it has a valid address to work with. If for some reason it can’t then you may be interested to know that you can clear the place automatically inserted in the Where field and enter a new location, giving you an opportunity to play around with different addresses until you create one that Facebook recognises.

By exercising control over the timezone in this way you should be able to import events into The Events Calendar without any timezone issues. If for some reason you are still hitting a wall then an alternative solution is available that offsets the imported time by a number of hours programmatically.

Enforced Offsets

So let’s say the above steps (for setting the event timezone) just aren’t working for you and events are being pulled in from Facebook that are consistently 5hrs out of synch. We can actually deal with this by adding a little code to your theme’s functions.php file[2] as follows (or see here).

/**
 * Automates the process of correcting times imported from Facebook by a fixed
 * number of hours (plus or minus).
 *
 * @uses DateTime which introduces a requirement for PHP 5.2 or greater
 */
class FBEventsTimeCorrection {
        protected $negative = false;
        protected $hours = 0;
        protected $modifier = '';
        protected $eventData = array();
        const CORRECTION_FORMAT = '#^([\\+-]?)(\\d{1,2})$#';


        /**
         * Sets up automated correction of imported Facebook event times.
         *
         * The correction should be specified in the form of an optional positive or
         * negative operator (+/-) followed by an integral number in the range 0-12,
         * such as +11 or -7.
         *
         * If not preceded by a positive/negative operator then positive is assumed.
         *
         * @param string $correction
         * @throws Exception
         */
        public function __construct($correction) {
                if ($this->setCorrection($correction))
                        add_filter('tribe_fb_parse_facebook_event', array($this, 'applyCorrection'));

                else throw new Exception('The specified correction cannot be understood.');
        }


        /**
         * Tests to see if the specified correction is in an understandable format.
         *
         * @param string $correction
         * @return bool
         */
        public function setCorrection($correction) {
                $matches = array();

                // Test to see if a basic format of [+/-]n has been specified then try to set
                if (preg_match(self::CORRECTION_FORMAT, $correction, $matches) === 1) {
                        if (count($matches) !== 3) return false;
                        else return $this->setDirectionAndHours($matches[1], $matches[2]);
                }

                return false;
        }


        /**
         * Sets the internal variables used to record the correction.
         *
         * If the $hours param is out of bounds this method will return bool false.
         *
         * @param $direction
         * @param $hours
         * @return bool
         */
        protected function setDirectionAndHours($direction, $hours) {
                // Do we have a negative operator?
                if ($direction === '-') $this->negative = true;

                // Extract the number of hours and ensure they are within range
                $this->hours = (int) $hours;
                if ($this->hours < 0 or $this->hours > 12) return false;

                return true;
        }


        /**
         * Attempts to adjust the event date/time.
         *
         * Although we require the DateTime class (available as of PHP 5.2) we will
         * degrade gracefully for earlier versions of PHP and give an opportunity
         * for a replacement class to be used on those versions (ie, from PEAR).
         *
         * @param array $eventData
         * @return array
         */
        public function applyCorrection($eventData) {
                if (class_exists('DateTime')) {
                        $this->correctDateTime('Start', $eventData);
                        $this->correctDateTime('End', $eventData);
                }

                return $eventData;
        }


        /**
         * Performs the correction of event date/time.
         *
         * @param $modifier
         * @param $eventData
         */
        protected function correctDateTime($modifier, &$eventData) {
                $this->modifier = $modifier;
                $this->eventData = $eventData;

                $datetime = $this->getDateTimeObject();
                $datetime->modify($this->getAdjustment());

                $this->rebuildEventData($datetime);
                $eventData = $this->eventData;
        }


        /**
         * Creates a DateTime object based on the Start|End date/time. Whether it
         * is the Start or End is determined by the $modifier property.
         *
         * @return DateTime
         */
        protected function getDateTimeObject() {
                $date = $this->eventData["Event{$this->modifier}Date"];
                $hour = $this->eventData["Event{$this->modifier}Hour"];
                $minute = $this->eventData["Event{$this->modifier}Minute"];
                $meridian = $this->eventData["Event{$this->modifier}Meridian"];

                // Convert hour to 24hr format
                if ($meridian === 'PM') $hour += 12;
                $hour = sprintf('%02d', $hour);
                $minute = sprintf('%02d', $minute);

                return new DateTime("$date $hour:$minute:00");
        }


        /**
         * Generates a strtotime() compatible modifier string.
         *
         * @return string
         */
        protected function getAdjustment() {
                $adjustment = '';

                if ($this->negative) $adjustment .= '-';
                $adjustment .= $this->hours.' hours';

                return $adjustment;
        }


        /**
         * Rebuilds the event data array with our adjusted date/time.
         */
        protected function rebuildEventData(DateTime $datetime) {
                $this->eventData["Event{$this->modifier}Date"] = $datetime->format('Y-m-d');
                $this->eventData["Event{$this->modifier}Hour"] = $datetime->format('g');
                $this->eventData["Event{$this->modifier}Minute"] = $datetime->format('i');
                $this->eventData["Event{$this->modifier}Meridian"] = $datetime->format('A');
        }
}

The above code by itself won’t do a lot, to make it work you need to add one more line of code (which is used to specify the adjustment to be made):

/**
 * Here we are enforcing an adjustment of -5hrs. If instead 
 * we wanted the imported times to be set 4hrs ahead we would
 * use +4, etc.
 */
new FBEventsTimeCorrection('-5');

As the comment in the above snippet suggests we can express the adjustment using simple terms such as -5 to knock the imported time back 5 hours or +9 if we wanted to push it ahead 9 hours. The accepted range is -12 to +12.

This is a last resort, it’s better to control timezones for imported events by using the tools provided by Facebook and by ensuring the date/time settings for WordPress and your server are both correct – but it does provide an alternative avenue.

Footnotes
  1. There are quite a few threads about this on the Facebook Events forum suggesting this is a bit of a sticking point for many users.
  2. Of course, you could also wrap this up as a plugin rather than placing the code inside your theme. For many users it is simply easier to lump snippets like this into their functions.php file, however, hence my suggestion.

Comments

  1. Hi Jan. It’s hard to say without seeing your site/code for myself. Are you able to share your theme’s functions.php code using a service like Pastebin so I can check out what you’ve tried (… what is the actual correction value you are using)?

  2. Thanks Jan. I’m struggling to see any obvious problems there … what version of Facebook Events are you using?

    • Version 1.0.4

      I’m in Denmark – is there a problem with the Danish date format? We write dates as dd-mm-yyyy unlike the US where it’s mm-dd-yyyy

      ???

    • I don’t think that’s an issue here. In any case (with uncanny timing!) it looks like Tim – one of the Modern Tribe devs – has just posted an official fix that you can use in the interim until this is resolved in the next version of the plugin, so my recommendation would be to use that approach.

  3. Hi Barry,

    Thanks for taking a look, but sadly the official fix does the same – - – Nothing :-(

    Maybe I’m missing something, but I can’t seem to find anywhere in the official fix to set the timezone offset – so maybe it does work, just need to find a place for the offset.

  4. I guess some other factor is at play here, I’m not sure there’s a great deal else I can suggest (but why not post on the Facebook Events forum if you haven’t already done so? Jonah or another member of the team there will be happy to help).

  5. @Jan I am wondering after you pasted the code in if you tried importing another event or are you expecting it to correctly offset? The gist I wrote (https://gist.github.com/4705028) mentioned in the forum thread only applies to new imports. What I wrote could be adjusted to also retroactively go through your blog and update prior imported events by filtering the start/end dates through the tribe_patch_convert_to_timezone method.

    • Hi Tim,

      Yes I understand that it’s only for new events – I’ve created a new facebook fanpage event – imported it, after inserting your gist at the end of my theme functions.php.

      Initially I got an error when I incerted the code (unexpected <?php) at the beginning of your code. I deleted this entirely, so my functions.php looks like this: http://pastebin.com/M8uN0cnW

      Am I missing something ?

  6. Sadly this code inserted into my themes functions.php, changes nothing at all.

    I’ve added it at the end of my current functions.php – should it be added elsewhere?

Trackbacks

  1. [...] at his blog, our own support guru Barry Hughes published a post this weekend that overlaps nicely with the work he’s doing for us at Modern Tribe. It focused on a point [...]

Speak Your Mind

*