SharePoint中的重复事件-不正确;“持续时间”;

SharePoint中的重复事件-不正确;“持续时间”;,sharepoint,sharepoint-2010,calendar,Sharepoint,Sharepoint 2010,Calendar,背景: 我工作的公司有一个常规的SharePoint列表,该列表具有用于事件的自定义ContentType(不从日历列表项继承)。然后使用日历视图显示这些内容。看起来很简单。 我们需要允许用户为他们正在添加的事件选择一个时区(不同于他们的区域设置),并将信息添加到sharepoint中,以便在全球范围内显示每个用户观看事件的正确时间(当然基于他们的区域设置)。 我向SharePoint添加了一个用于查找系统时区的列表(基本上是TimeZoneInfo.GetSystemTimeZones()的S

背景: 我工作的公司有一个常规的SharePoint列表,该列表具有用于事件的自定义ContentType(不从日历列表项继承)。然后使用日历视图显示这些内容。看起来很简单。 我们需要允许用户为他们正在添加的事件选择一个时区(不同于他们的区域设置),并将信息添加到sharepoint中,以便在全球范围内显示每个用户观看事件的正确时间(当然基于他们的区域设置)。 我向SharePoint添加了一个用于查找系统时区的列表(基本上是TimeZoneInfo.GetSystemTimeZones()的SharePoint列表表示形式)

我将此列表用于此列表的自定义添加和编辑表单中EventTimeZone的查找项。这些表单是SharePoint Designer将创建的内容的直接副本(因为它们使用的是SharePoint:FormField),它们位于Visual Studio bc中,我需要代码隐藏。我想让用户在他们的区域时区中查看事件,但是当他们编辑事件时,我想在输入的时区中显示它们。(即我所在的地区时区是中心时区,因此当我观看山区会议时,它会显示上午10-11点,但当我编辑同一会议时,它会显示上午9-10点)。因此,在编辑页面加载时,我会调整时间:

                        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);
                            } 
整天发生的事情都很奇怪,但我能够通过编写测试用例并查看发生了什么(正如您从我的评论中看到的)

然后,我将事件接收者ItemAdded和ItemUpdate绑定到列表中,以“修复”时间,因为SharePoint将根据用户的区域设置而不是用户选择的时区来保存它们。(当然,我对SharePoint有点陌生——不是c#——所以我可能把这件事复杂化了很多,但我能够在网上处理一些文档)。最后,我设置了:

                    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;
                    }  
它仍然会在更新()时重置。因此,当您在日历视图中查看定期项目时,该项目将跨越整个星期,而不是每天显示一次

为了弄明白这一点,我几乎把头发拔了出来。任何指导都会很好。我知道Duration是一个计算字段,但我不明白为什么调用listItem.Update()会忽略这样一个事实,即它确实被正确地标记为重复事件,并且无法正确计算Duration。老实说,这似乎是SP 2010的一个bug

提前谢谢

**

编辑:以下评论后的其他信息。。。 ** 此SharePoint env在太平洋时间有一台服务器,用户跨越所有美国时区、伦敦、东京、阿布达比等。一个时区的用户需要能够在其他时区创建事件。由于用户配置文件中没有任何内容(无论如何,对我们来说)会告诉我们他们希望在哪个时区查看所有内容,因此我们在母版页中添加了代码以查看本地机器的时区,并始终相应地设置其区域设置

示例:我在纳什维尔,我想创建一个将在洛杉矶发生的事件:

ItemAdded中的数据显示StartDate是我在上午9点输入的。因此,我正在创建一个日期,该日期末尾有PST:

DateTime correctedEventStart = DateTime.Parse(addedItem["StartDate"] + " " + eventTimeZone.GetUtcOffset(DateTime.UtcNow).Hours + ":" + eventTimeZone.GetUtcOffset(DateTime.UtcNow).Minutes);
DateTime correctedEventEnd = DateTime.Parse(addedItem["EndDate"] + " " + eventTimeZone.GetUtcOffset(DateTime.UtcNow).Hours + ":" + eventTimeZone.GetUtcOffset(DateTime.UtcNow).Minutes);
然后,为了“欺骗”SharePoint,我将PST时间转换为用户的区域时间(因此用户不必知道任何有关其区域设置的信息,也不必思考)。所以太平洋标准时间上午9点是中央标准时间上午7点(卑诗省),这是SharePoint预计的时间,因为这是我的区域设置。下面是从正确的时间+时区到用户区域时区的对话:

addedItem["StartDate"] = regionalTimeZone.UTCToLocalTime(correctedEventStart.ToUniversalTime());
addedItem["EndDate"] = regionalTimeZone.UTCToLocalTime(correctedEventEnd.ToUniversalTime());
我不知道这对我以外的人来说是否有意义。但SharePoint显然希望时间在用户的区域(或web)时区内。从单元测试中可以明显看出这一点。如果我有一种OOB方式,允许中央时间的用户在太平洋时间上午9点到10点在自定义列表中创建会议,我希望能够使用它。但我什么也没找到

再一次,所有这些都非常有效。。。直到你遇到重复发生的事情。实际上,它适用于重复发生的事件,直到您尝试在日历视图中查看所述事件。然后看起来是这样的:


请注意,“重复8”按预期方式重复,每天重复2次。然而,复发的“跨度”或“持续时间”是2天而不是1小时。其中“重复15”正确显示。输出到调试时,这两个字段值之间的唯一区别是“持续时间”字段。定期8在ItemAdded中更新了其开始和结束日期,定期15在ItemAdded中进行了更新,但ListItem.Update()被注释掉。根据文档,SharePoint应该以与单个项目不同的方式计算重复项目的持续时间。使用对象模型更改开始和结束日期的事实不应否定这一点。

我认为您与SharePoint用于记录事件的shennagins相冲突。基本上,事件存储在一个数据库中。这使得事件的存储方式与您所期望的完全相反

从那篇文章来看,EventDate和EndDate字段的使用似乎有所不同,这取决于是否重复

“在引擎盖下”,并转换回显示的用户(或网站)时区。您可以利用这些知识优化某些日期逻辑

更多信息

下面是我用来在另一个时区创建发生事件的代码(注意:我没有明确设置持续时间)

public void AddRecurringItemGTM8Perth(SPList列表)
{
string recData=“su2013-02-20T01:00:00Z”;
SPListItem newitem=list.Items.Add();
newitem[“Title”]=“Perth”+DateTime.Now.ToString();
newitem[“RecurrenceData”]=recData;
newitem[“EventType”]=1;
DateTime correctedEventStart=新日期时间(2013,2,3,12,0,0);
//注意dat
addedItem["StartDate"] = regionalTimeZone.UTCToLocalTime(correctedEventStart.ToUniversalTime());
addedItem["EndDate"] = regionalTimeZone.UTCToLocalTime(correctedEventEnd.ToUniversalTime());
 public void AddRecurringItemGTM8Perth(SPList list)
    {
        string recData = "<recurrence><rule><firstDayOfWeek>su</firstDayOfWeek><repeat><daily dayFrequency=\"1\" /></repeat><windowEnd>2013-02-20T01:00:00Z</windowEnd></rule></recurrence>";

        SPListItem newitem = list.Items.Add();
        newitem["Title"] = "Perth " + DateTime.Now.ToString();
        newitem["RecurrenceData"] = recData;
        newitem["EventType"] = 1;
        DateTime correctedEventStart =  new DateTime(2013, 2, 3, 12, 0, 0);
        //note that date is end of event and time is event end to calculate duration
        DateTime correctedEventEnd = new DateTime(2013, 2, 20, 13, 0, 0);

        SPTimeZone spTz = SPRegionalSettings.GlobalTimeZones[74]; //perth

        correctedEventStart = spTz.LocalTimeToUTC(correctedEventStart);
        correctedEventEnd = spTz.LocalTimeToUTC(correctedEventEnd);

        correctedEventStart = list.ParentWeb.RegionalSettings.TimeZone.UTCToLocalTime(correctedEventStart);
        correctedEventEnd = list.ParentWeb.RegionalSettings.TimeZone.UTCToLocalTime(correctedEventEnd);
        newitem["Start Time"] = correctedEventStart;
        newitem["End Time"] = correctedEventEnd;
        newitem["Recurrence"] = true;
        newitem["fAllDayEvent"] = false;
        newitem["WorkspaceLink"] = false;
        newitem["UID"] = Guid.NewGuid();
        newitem.Update();
        list.Update();
    }
string recData = "<recurrence><rule><firstDayOfWeek>su</firstDayOfWeek><repeat><daily dayFrequency=\"1\" /></repeat><repeatInstances>13</repeatInstances></rule></recurrence>";
DateTime correctedEventEnd = new DateTime(2013, 2, 3, 13, 0, 0).AddDays(13);
string recData = "<recurrence><rule><firstDayOfWeek>su</firstDayOfWeek><repeat><daily dayFrequency=\"1\" /></repeat><repeatForever>FALSE</repeatForever></rule></recurrence>";
DateTime correctedEventEnd = new DateTime(2013, 2, 3, 13, 0, 0).AddDays(998);
    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();
        }
    }
   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);
            }
        }
    }