Commit d2491d2e authored by Robert Rollins's avatar Robert Rollins

FEATURE: Added support for Windows timezone names.

By using the amazing, free database of Windows timezone names curtesy of the
CLDR project (http://cldr.unicode.org), I was able to add support for the
non-standard timezone names created by Microsoft Outlook. Hopefully, the
"not a valid timezone" error will now be much less common.
parent f4e0b600
Date iCal
This module allows users to export iCal feeds with Views, and import iCal feeds
from other sites with the Feeds module. Any entity can act as the source of
events for an iCal feed, as long as that entity contains a Date field. Date
from other sites with Feeds. Any Entity can act as the source of events for an
iCal feed, as long as that entity contains a Date field. Date
iCal creates a new iCal "view mode" for all entities, which is used to format
the Description field of the events in the iCal feed (when using the iCal
Entity plugin).
......@@ -11,8 +11,9 @@ For an easier-to-read HTML version of these instructions, please go to
http://www.drupal.org/project/date_ical and click the "Read documentation" link
in the Resources section of the right sidebar.
===============================================================================
INSTALLATION
===============================================================================
Date iCal has several required dependencies, and an optional one:
- The Views (version 3.5+), Entity API, Libraries API (version 2.0), and Date
modules are required.
......@@ -45,7 +46,10 @@ green, Date iCal is ready to go. If it's red, the iCalcreator library is not
properly installed. If it's missing, you'll need to enable Date iCal and then
come back to this page.
===============================================================================
EXPORTING AN ICAL FEED USING Views
===============================================================================
There are two plugins that export iCal feeds. You can use either one, though
the iCal Fields plugin is a bit more versatile.
......@@ -106,7 +110,9 @@ HOW TO EXPORT AN ICAL FEED USING THE iCal Fields PLUGIN
10+ These steps are the same as above.
IMPORTING ICAL FEEDS FROM ANOTHER SITE USING Feeds
===============================================================================
IMPORTING AN ICAL FEED FROM ANOTHER SITE USING Feeds
===============================================================================
- Install the Feeds module, which is the framework upon which Date iCal's
import functionality is built.
- Login to your Drupal site and navigate to the admin/structure/feeds page.
......@@ -151,7 +157,9 @@ IMPORTING ICAL FEEDS FROM ANOTHER SITE USING Feeds
Remember, you have to map the UID source to the GUID target, and make it
unique, or your imports won't work!
===============================================================================
IMPORTANT NOTE:
===============================================================================
If you're building a site that will be viewed by out-of-state users, and you
allow said users to set their own timezone, you'll want to set up your Date
fields to use the "Date's time zone" option. If you don't, then users who live
......@@ -160,13 +168,33 @@ timezone, rather than your events' timezone. This makes sense if your events
will be broadcast live to these out-of-state users, but if they need to travel
to your event, they may end up arriving at the wrong time.
===============================================================================
HOW TO FIX THE "not a valid timezone" ERROR
===============================================================================
If you are seeing a warning about invalid timezones when you import an iCal
feed, you'll need to implement hook_date_ical_import_timezone_alter() in a
custom module to fix it. To do so, either edit an existing custom module, or
make a new module and add this function to it:
<?php
/**
* Implements hook_date_ical_import_timezone_alter().
*/
function <module>_date_ical_import_timezone_alter(&$tzid, $context) {
if (!empty($tzid)) {
// Do something to fix your invalid timezone.
// For instance, if all your events take place in one timezone, find your
// region's official TZID, and replace $tzid with it. Like this:
// $tzid = 'America/Los_Angeles';
}
}
?>
Replace <module> with the name of your module, change the code to do whatever
needs to be done to fix your timezones, and clear your Drupal cache.
Additional Notes:
The Feeds plugin was originally written by ekes, for the "iCal feed parser"
module (http://www.drupal.org/project/parser_ical). It was modified and
improved for Date iCal by coredumperror. In Date iCal 3.0, the plugin was
re-written from scratch to conform to the Feeds APIs.
Additional Notes:
At this time, Date iCal only supports outputting iCal calendars through Views.
To put an "Add to calendar" button on individual event nodes, try the
<a href="http://drupal.org/project/addtocal">Add to Cal</a> module, or follow
......@@ -179,3 +207,6 @@ iCal feeds.
Developers who wish to implement more powerful manipulation of event data can
read the date_ical.api.php file to learn about the various alter hooks that
date_ical exposes.
The libraries/windowsZones.json file is from Version24 of the Unicode CLDR:
http://cldr.unicode.org/.
......@@ -179,6 +179,17 @@ function date_ical_libraries_info() {
return $libraries;
}
/**
* Implements hook_help().
*/
function date_ical_help($path, $arg) {
switch ($path) {
case 'admin/help#date_ical':
// Return a line-break version of the module README.txt
return check_markup(file_get_contents(dirname(__FILE__) . "/README.txt"));
}
}
/**
* Implements hook_ctools_plugin_api().
*/
......
......@@ -226,8 +226,8 @@ class ParserVcalendar {
if ($is_all_day) {
if ($property_key == 'DTEND') {
if ($dtstart === FALSE) {
// This will almost certainly never happen, but the error message in
// this would be incomprehensible without this check.
// This will almost certainly never happen, but the error message
// would be incomprehensible without this check.
throw new DateIcalParseException(t('Feed import failed! The event with UID %uid is invalid: it has a DTEND but no DTSTART!', array('%uid' => $uid)));
}
......@@ -370,11 +370,34 @@ class ParserVcalendar {
$datetimezone = new DateTimeZone($tzid);
}
catch (Exception $e) {
$link = l(t('here'), 'http://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List', array('absolute' => TRUE));
// In case this is a Windows TZID, read the mapping file to try and
// convert it to a real TZID.
$zones = file_get_contents(drupal_get_path('module', 'date_ical') . '/libraries/windowsZones.json');
$zones_assoc = json_decode($zones, TRUE);
$windows_to_olson_map = array();
foreach ($zones_assoc['supplemental']['windowsZones']['mapTimezones'] as $mapTimezone) {
if ($mapTimezone['mapZone']['_other'] == $tzid) {
// $mapTimezone['mapZone']['_type'] is space-separated TZIDs.
$tzids = preg_split('/\s/', $mapTimezone['mapZone']['_type']);
try {
// They all have the same UTC offset, so for our purposes we can
// just take the first one.
return new DateTimeZone($tzids[0]);
}
catch (Exception $e) {
// If this one also fails, we're out of luck, so just fall through
// to the regular error report code.
break;
}
}
}
$tz_wiki = l(t('here'), 'http://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List');
$help = l(t('README'), 'admin/help/date_ical', array('absolute' => TRUE));
$msg = t(
'"@tz" is not a valid timezone (see the TZ column !here), so Date iCal used UTC (which is probably wrong!).<br>
Try implementing hook_date_ical_import_timezone_alter() in a custom module to fix this problem.',
array('@tz' => $tzid, '!here' => $link)
'"@tz" is not a valid timezone (see the TZ column !here), so Date iCal had to fall back to UTC (which is probably wrong!).<br>
Please read the Date iCal !readme for instructions on how to fix this.',
array('@tz' => $tzid, '!here' => $tz_wiki, '!readme' => $help)
);
$this->source->log('parse', $msg, array(), WATCHDOG_WARNING);
drupal_set_message($msg, 'warning', FALSE);
......
{
"supplemental": {
"version": {
"_cldrVersion": "24",
"_number": "$Revision: 9178 $"
},
"generation": {
"_date": "$Date: 2013-08-08 13:59:22 -0500 (Thu, 08 Aug 2013) $"
},
"windowsZones": {
"mapTimezones": [
{
"mapZone": {
"_other": "Greenwich Standard Time",
"_territory": "CI",
"_type": "Africa/Abidjan"
}
},
{
"mapZone": {
"_other": "Greenwich Standard Time",
"_territory": "GH",
"_type": "Africa/Accra"
}
},
{
"mapZone": {
"_other": "E. Africa Standard Time",
"_territory": "ET",
"_type": "Africa/Addis_Ababa"
}
},
{
"mapZone": {
"_other": "W. Central Africa Standard Time",
"_territory": "DZ",
"_type": "Africa/Algiers"
}
},
{
"mapZone": {
"_other": "E. Africa Standard Time",
"_territory": "ER",
"_type": "Africa/Asmera"
}
},
{
"mapZone": {
"_other": "Greenwich Standard Time",
"_territory": "ML",
"_type": "Africa/Bamako"
}
},
{
"mapZone": {
"_other": "W. Central Africa Standard Time",
"_territory": "CF",
"_type": "Africa/Bangui"
}
},
{
"mapZone": {
"_other": "Greenwich Standard Time",
"_territory": "GM",
"_type": "Africa/Banjul"
}
},
{
"mapZone": {
"_other": "Greenwich Standard Time",
"_territory": "GW",
"_type": "Africa/Bissau"
}
},
{
"mapZone": {
"_other": "South Africa Standard Time",
"_territory": "MW",
"_type": "Africa/Blantyre"
}
},
{
"mapZone": {
"_other": "W. Central Africa Standard Time",
"_territory": "CG",
"_type": "Africa/Brazzaville"
}
},
{
"mapZone": {
"_other": "South Africa Standard Time",
"_territory": "BI",
"_type": "Africa/Bujumbura"
}
},
{
"mapZone": {
"_other": "Egypt Standard Time",
"_territory": "001",
"_type": "Africa/Cairo"
}
},
{
"mapZone": {
"_other": "Egypt Standard Time",
"_territory": "EG",
"_type": "Africa/Cairo"
}
},
{
"mapZone": {
"_other": "Morocco Standard Time",
"_territory": "001",
"_type": "Africa/Casablanca"
}
},
{
"mapZone": {
"_other": "Morocco Standard Time",
"_territory": "MA",
"_type": "Africa/Casablanca"
}
},
{
"mapZone": {
"_other": "Greenwich Standard Time",
"_territory": "GN",
"_type": "Africa/Conakry"
}
},
{
"mapZone": {
"_other": "Greenwich Standard Time",
"_territory": "SN",
"_type": "Africa/Dakar"
}
},
{
"mapZone": {
"_other": "E. Africa Standard Time",
"_territory": "TZ",
"_type": "Africa/Dar_es_Salaam"
}
},
{
"mapZone": {
"_other": "E. Africa Standard Time",
"_territory": "DJ",
"_type": "Africa/Djibouti"
}
},
{
"mapZone": {
"_other": "W. Central Africa Standard Time",
"_territory": "CM",
"_type": "Africa/Douala"
}
},
{
"mapZone": {
"_other": "Greenwich Standard Time",
"_territory": "EH",
"_type": "Africa/El_Aaiun"
}
},
{
"mapZone": {
"_other": "Greenwich Standard Time",
"_territory": "SL",
"_type": "Africa/Freetown"
}
},
{
"mapZone": {
"_other": "South Africa Standard Time",
"_territory": "BW",
"_type": "Africa/Gaborone"
}
},
{
"mapZone": {
"_other": "South Africa Standard Time",
"_territory": "ZW",
"_type": "Africa/Harare"
}
},
{
"mapZone": {
"_other": "South Africa Standard Time",
"_territory": "001",
"_type": "Africa/Johannesburg"
}
},
{
"mapZone": {
"_other": "South Africa Standard Time",
"_territory": "ZA",
"_type": "Africa/Johannesburg"
}
},
{
"mapZone": {
"_other": "E. Africa Standard Time",
"_territory": "SS",
"_type": "Africa/Juba"
}
},
{
"mapZone": {
"_other": "E. Africa Standard Time",
"_territory": "UG",
"_type": "Africa/Kampala"
}
},
{
"mapZone": {
"_other": "E. Africa Standard Time",
"_territory": "SD",
"_type": "Africa/Khartoum"
}
},
{
"mapZone": {
"_other": "South Africa Standard Time",
"_territory": "RW",
"_type": "Africa/Kigali"
}
},
{
"mapZone": {
"_other": "W. Central Africa Standard Time",
"_territory": "CD",
"_type": "Africa/Kinshasa"
}
},
{
"mapZone": {
"_other": "W. Central Africa Standard Time",
"_territory": "001",
"_type": "Africa/Lagos"
}
},
{
"mapZone": {
"_other": "W. Central Africa Standard Time",
"_territory": "NG",
"_type": "Africa/Lagos"
}
},
{
"mapZone": {
"_other": "W. Central Africa Standard Time",
"_territory": "GA",
"_type": "Africa/Libreville"
}
},
{
"mapZone": {
"_other": "Greenwich Standard Time",
"_territory": "TG",
"_type": "Africa/Lome"
}
},
{
"mapZone": {
"_other": "W. Central Africa Standard Time",
"_territory": "AO",
"_type": "Africa/Luanda"
}
},
{
"mapZone": {
"_other": "South Africa Standard Time",
"_territory": "CD",
"_type": "Africa/Lubumbashi"
}
},
{
"mapZone": {
"_other": "South Africa Standard Time",
"_territory": "ZM",
"_type": "Africa/Lusaka"
}
},
{
"mapZone": {
"_other": "W. Central Africa Standard Time",
"_territory": "GQ",
"_type": "Africa/Malabo"
}
},
{
"mapZone": {
"_other": "South Africa Standard Time",
"_territory": "MZ",
"_type": "Africa/Maputo"
}
},
{
"mapZone": {
"_other": "South Africa Standard Time",
"_territory": "LS",
"_type": "Africa/Maseru"
}
},
{
"mapZone": {
"_other": "South Africa Standard Time",
"_territory": "SZ",
"_type": "Africa/Mbabane"
}
},
{
"mapZone": {
"_other": "E. Africa Standard Time",
"_territory": "SO",
"_type": "Africa/Mogadishu"
}
},
{
"mapZone": {
"_other": "Greenwich Standard Time",
"_territory": "LR",
"_type": "Africa/Monrovia"
}
},
{
"mapZone": {
"_other": "E. Africa Standard Time",
"_territory": "001",
"_type": "Africa/Nairobi"
}
},
{
"mapZone": {
"_other": "E. Africa Standard Time",
"_territory": "KE",
"_type": "Africa/Nairobi"
}
},
{
"mapZone": {
"_other": "W. Central Africa Standard Time",
"_territory": "TD",
"_type": "Africa/Ndjamena"
}
},
{
"mapZone": {
"_other": "W. Central Africa Standard Time",
"_territory": "NE",
"_type": "Africa/Niamey"
}
},
{
"mapZone": {
"_other": "Greenwich Standard Time",
"_territory": "MR",
"_type": "Africa/Nouakchott"
}
},
{
"mapZone": {
"_other": "Greenwich Standard Time",
"_territory": "BF",
"_type": "Africa/Ouagadougou"
}
},
{
"mapZone": {
"_other": "W. Central Africa Standard Time",
"_territory": "BJ",
"_type": "Africa/Porto-Novo"
}
},
{
"mapZone": {
"_other": "Greenwich Standard Time",
"_territory": "ST",
"_type": "Africa/Sao_Tome"
}
},
{
"mapZone": {
"_other": "Libya Standard Time",
"_territory": "001",
"_type": "Africa/Tripoli"
}
},
{
"mapZone": {
"_other": "Libya Standard Time",
"_territory": "LY",
"_type": "Africa/Tripoli"
}
},
{
"mapZone": {
"_other": "W. Central Africa Standard Time",
"_territory": "TN",
"_type": "Africa/Tunis"
}
},
{
"mapZone": {
"_other": "Namibia Standard Time",
"_territory": "001",
"_type": "Africa/Windhoek"
}
},
{
"mapZone": {
"_other": "Namibia Standard Time",
"_territory": "NA",
"_type": "Africa/Windhoek"
}
},
{
"mapZone": {
"_other": "Alaskan Standard Time",
"_territory": "001",
"_type": "America/Anchorage"
}
},
{
"mapZone": {
"_other": "Alaskan Standard Time",
"_territory": "US",
"_type": "America/Anchorage America/Juneau America/Nome America/Sitka America/Yakutat"
}
},
{
"mapZone": {
"_other": "SA Western Standard Time",
"_territory": "AI",
"_type": "America/Anguilla"
}
},
{
"mapZone": {
"_other": "SA Western Standard Time",
"_territory": "AG",
"_type": "America/Antigua"
}
},
{
"mapZone": {
"_other": "SA Western Standard Time",
"_territory": "AW",
"_type": "America/Aruba"
}
},
{
"mapZone": {
"_other": "Paraguay Standard Time",
"_territory": "001",
"_type": "America/Asuncion"
}
},
{
"mapZone": {
"_other": "Paraguay Standard Time",
"_territory": "PY",
"_type": "America/Asuncion"
}
},
{
"mapZone": {
"_other": "Bahia Standard Time",
"_territory": "001",
"_type": "America/Bahia"
}
},
{
"mapZone": {
"_other": "Bahia Standard Time",
"_territory": "BR",
"_type": "America/Bahia"
}
},
{
"mapZone": {
"_other": "SA Western Standard Time",
"_territory": "BB",
"_type": "America/Barbados"
}
},
{
"mapZone": {
"_other": "Central America Standard Time",
"_territory": "BZ",
"_type": "America/Belize"
}
},
{
"mapZone": {
"_other": "SA Western Standard Time",
"_territory": "CA",
"_type": "America/Blanc-Sablon"
}
},
{
"mapZone": {
"_other": "SA Pacific Standard Time",
"_territory": "001",
"_type": "America/Bogota"
}
},
{
"mapZone": {
"_other": "SA Pacific Standard Time",
"_territory": "CO",
"_type": "America/Bogota"
}
},
{
"mapZone": {
"_other": "Argentina Standard Time",
"_territory": "001",
"_type": "America/Buenos_Aires"
}
},
{
"mapZone": {
"_other": "Argentina Standard Time",
"_territory": "AR",
"_type": "America/Buenos_Aires America/Argentina/La_Rioja America/Argentina/Rio_Gallegos America/Argentina/Salta America/Argentina/San_Juan America/Argentina/San_Luis America/Argentina/Tucuman America/Argentina/Ushuaia America/Catamarca America/Cordoba America/Jujuy America/Mendoza"
}
},
{