1

Background: The company I work for has a regular SharePoint list with a custom ContentType (that does not inherit from a calendar list item) that it uses for Events. It then shows these using a calendar view. Seems simple enough. We have the need to allow the user to choose a timezone for the event (different from their regional setting) that they are adding and to add the information to sharepoint such that it will show the correct time for each user looking at it world wide (based on their regional setting of course). I added a list to SharePoint that is used to lookup SystemTimeZones (basically a SharePoint List representation of TimeZoneInfo.GetSystemTimeZones())

                SPList timeZonesList = thisWeb.Lists.TryGetList("SystemTimeZones");

                if(timeZonesList == null)
                {
                    string title = "SystemTimeZones";
                    string description = "SharePoint List representation of TimeZoneInfo.GetSystemTimeZones() used for lookup.";

                    Guid newListId = thisWeb.Lists.Add(title, description, SPListTemplateType.GenericList);

                    timeZonesList = thisWeb.Lists.GetList(newListId, true);
                    timeZonesList.Fields.Add("SystemTimeZoneId", SPFieldType.Text, true);
                    timeZonesList.Fields.Add("SystemTimeZoneName", SPFieldType.Text, true);

                    SPView defaultTimeZonesView = timeZonesList.DefaultView;
                    defaultTimeZonesView.ViewFields.Add("SystemTimeZoneId");
                    defaultTimeZonesView.ViewFields.Add("SystemTimeZoneName");
                    defaultTimeZonesView.Update();

                    foreach (TimeZoneInfo timeZone in TimeZoneInfo.GetSystemTimeZones())
                    {
                        SPListItem temp = timeZonesList.AddItem();
                        temp["SystemTimeZoneId"] = timeZone.Id;
                        temp["SystemTimeZoneName"] = timeZone.DisplayName;
                        temp.Update();
                    }
                } 

I'm using this list for the lookup item for EventTimeZone in my custom add and edit forms for this list. The forms are direct copies of what SharePoint Designer would create (in that they are using the SharePoint:FormField's) they are just in Visual Studio bc I needed code-behind. I wanted to allow the users to see the events in their Regional TimeZone however when they edit them I wanted to show them in the TimeZone they were entered. (IE my regional timezone is Central so when I look at a Mountain meeting it will show me 10-11am but when I edit that same meeting it will say it is 9-10am). So on page load of edit I adjust the times:

                        SPListItem thisEvent = eventsList.GetItemById(savebutton1.ItemId);

                        if (thisEvent != null)
                        {
                            bool isAllDayEvent = false;
                            if (thisEvent["fAllDayEvent"] != null)
                            {
                                isAllDayEvent = (bool)thisEvent["fAllDayEvent"];
                            }

                            if (!isAllDayEvent)
                            {
                                SPFieldLookupValue lookupValue = new SPFieldLookupValue(thisEvent["Event Time Zone"].ToString());
                                TimeZoneInfo eventTimeZone = GetEventTimeZoneByListItemId(lookupValue.LookupId, rootWeb);
                                SPTimeZone regionalTimeZone = GetRegionalTimeZone(rootWeb);

                                DateTime regionalStartDateTime = Convert.ToDateTime(thisEvent["StartDate"]);
                                DateTime originalStartDateTime = TimeZoneInfo.ConvertTimeFromUtc(regionalTimeZone.LocalTimeToUTC(regionalStartDateTime), eventTimeZone);

                                ff3.ListItemFieldValue = originalStartDateTime;

                                DateTime regionalEndDateTime = Convert.ToDateTime(thisEvent["EndDate"]);
                                DateTime originalEndDateTime = TimeZoneInfo.ConvertTimeFromUtc(regionalTimeZone.LocalTimeToUTC(regionalEndDateTime), eventTimeZone);

                                ff4.ListItemFieldValue = originalEndDateTime;
                            }
                            else
                            {
                                // for some reason with all day events, sharepoint saves them 
                                // as the previous day 6pm. but when they show up to any user
                                // they will show as 12am to 1159pm and show up correctly on the calendar
                                // HOWEVER, when it comes to edit, the start date isn't corrected on the 
                                // form, so continuing to save without fixing it will continue to decrease
                                // the start date/time by one day
                                DateTime regionalStartDateTime = Convert.ToDateTime(thisEvent["StartDate"]);
                                ff3.ListItemFieldValue = regionalStartDateTime.AddDays(1);
                            } 

All day events were strange but I was able to make it work by just writing test cases and see what happened (as you can see from my comments).

Then I tie into the list event receivers ItemAdded and ItemUpdated to "fix" the times since SharePoint is going to save them based on the user's regional setting and not the timezone the user chose. (Of course I'm slightly new to SharePoint -- not c# -- so I may have very much over complicated this, but I have been able to fine little documentation online). In the end I end up setting:

                    addedItem["StartDate"] = regionalTimeZone.UTCToLocalTime(correctedEventStart.ToUniversalTime());
                    addedItem["EndDate"] = regionalTimeZone.UTCToLocalTime(correctedEventEnd.ToUniversalTime()); TADA!! It saves and display perfectly! I was so excited! Until... I tried to save a recurring event. All of my recurring events save wonderfully, it's not the recurring part that's messed up. For some reason, after I change the StartDate and EndDate on a recurring event and call addedItem.Update() it is recalculating the "Duration" as if it is a single even instead of a recurring event.  Example: I have an event that happens for a week daily from 9-10.  When I first enter ItemAdded my Duration is 3600 (1 hour) as it should be bc Duration is treated differently for recurring events. However after I adjust the times and call Update() the duration spans the entire week :( If I manually set the Duration:
                     if (isRecurrence)
                    {
                        addedItem["Duration"] = (correctedEventEnd.TimeOfDay - correctedEventStart.TimeOfDay).TotalSeconds;
                    }  

It still gets reset on Update(). So when you view the recurring item in a Calendar View the item spans the entire week instead of showing once a day.

I have all but pulled my hair out trying to figure this out. Any guidance would be wonderful. I understand Duration is a calculated field but I can't understand why calling listItem.Update() would ignore the fact that it is indeed properly marked as a recurring event and not calculate the Duration correctly. This honestly seems like a bug with SP 2010.

Thanks in advance!

1 Answer 1

0

Ok, so the way I ended up handling this is as follows. I decided to back out of the list event receiver because it really does appear to be a SharePoint bug in the recalculation of Duration for recurring events now working correctly. I opted to tie into the save event on the form and changing the values before they are even sent. This seems to work so far in all scenarios. All of my math is the same as before. So in my New2.aspx (new item form for this list)

protected override void OnInit(EventArgs e)
{
    base.OnInit(e);

    if ((SPContext.Current.FormContext.FormMode == SPControlMode.New) || (SPContext.Current.FormContext.FormMode == SPControlMode.Edit))
    {
        SPContext.Current.FormContext.OnSaveHandler += new EventHandler(SaveHandler);
    }
}

protected void SaveHandler(object sender, EventArgs e)
{
    Page.Validate();

    if (Page.IsValid)
    {
        // fix times
        SPFieldLookupValue lookupValue = new SPFieldLookupValue(ff5.Value.ToString());
        TimeZoneInfo eventTimeZone = GetEventTimeZoneByListItemId(lookupValue.LookupId, SPContext.Current.Web);
        SPTimeZone regionalTimeZone = GetRegionalTimeZone(SPContext.Current.Web);

        bool isAllDayEvent = Convert.ToBoolean(ff6.Value);
        bool isRecurrence = Convert.ToBoolean(ff11.Value);
        DateTime correctedEventStart = DateTime.MinValue;
        DateTime correctedEventEnd = DateTime.MinValue;

        if (!isAllDayEvent && eventTimeZone != null && regionalTimeZone != null)
        {
            correctedEventStart = DateTime.Parse(ff3.Value.ToString() + " " + eventTimeZone.GetUtcOffset(DateTime.UtcNow).Hours + ":" + eventTimeZone.GetUtcOffset(DateTime.UtcNow).Minutes);
            correctedEventEnd = DateTime.Parse(ff4.Value.ToString() + " " + eventTimeZone.GetUtcOffset(DateTime.UtcNow).Hours + ":" + eventTimeZone.GetUtcOffset(DateTime.UtcNow).Minutes);

            ff3.ItemFieldValue = regionalTimeZone.UTCToLocalTime(correctedEventStart.ToUniversalTime());
            ff4.ItemFieldValue = regionalTimeZone.UTCToLocalTime(correctedEventEnd.ToUniversalTime());
        }

        SPContext.Current.ListItem.Update();
    }
}

This updates the times as my previous approach does but it will also calculate the duration correctly.

SharePoint handles displaying the correct time based on the user's regional settings (or web if the user hasn't set it) and displaying the correct times in calendar views. I did have to change the Edit form to have the correct values on edit:

protected void Page_Load(object sender, EventArgs e)
    {
        if (!Page.IsPostBack)
        {
            try
            {
                using (SPWeb rootWeb = SPContext.Current.Site.RootWeb)
                {
                    SPList eventsList = rootWeb.Lists.TryGetList("Events");

                    if (eventsList != null)
                    {
                        SPListItem thisEvent = eventsList.GetItemById(savebutton1.ItemId);

                        if (thisEvent != null)
                        {
                            bool isAllDayEvent = false;
                            if (thisEvent["fAllDayEvent"] != null)
                            {
                                isAllDayEvent = (bool)thisEvent["fAllDayEvent"];
                            }

                            if (!isAllDayEvent)
                            {
                                SPFieldLookupValue lookupValue = new SPFieldLookupValue(thisEvent["Event Time Zone"].ToString());
                                TimeZoneInfo eventTimeZone = GetEventTimeZoneByListItemId(lookupValue.LookupId, rootWeb);
                                SPTimeZone regionalTimeZone = GetRegionalTimeZone(rootWeb);

                                DateTime regionalStartDateTime = Convert.ToDateTime(thisEvent["StartDate"]);
                                DateTime originalStartDateTime = TimeZoneInfo.ConvertTimeFromUtc(regionalTimeZone.LocalTimeToUTC(regionalStartDateTime), eventTimeZone);

                                ff3.ListItemFieldValue = originalStartDateTime;

                                DateTime regionalEndDateTime = Convert.ToDateTime(thisEvent["EndDate"]);
                                DateTime originalEndDateTime = TimeZoneInfo.ConvertTimeFromUtc(regionalTimeZone.LocalTimeToUTC(regionalEndDateTime), eventTimeZone);

                                ff4.ListItemFieldValue = originalEndDateTime;
                            }
                            else
                            {
                                // for some reason with all day events, sharepoint saves them
                                // as the previous day 6pm. but when they show up to any user
                                // they will show as 12am to 1159pm and show up correctly on the calendar
                                // HOWEVER, when it comes to edit, the start date isn't corrected on the
                                // form, so continuing to save without fixing it will continue to decrease
                                // the start date/time by one day
                                DateTime regionalStartDateTime = Convert.ToDateTime(thisEvent["StartDate"]);
                                ff3.ListItemFieldValue = regionalStartDateTime.AddDays(1);
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                DebugLogger.WriteLine(ex);
            }
        }
    }

The Edit form has the same OnInit and SaveHandler as New.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.