3

背景:我工作的公司有一个常规的 SharePoint 列表,其中包含用于事件的自定义 ContentType(不从日历列表项继承)。然后它使用日历视图显示这些。看起来很简单。我们需要允许用户为他们正在添加的事件(不同于他们的区域设置)选择一个时区,并将信息添加到共享点,这样它就会为每个在世界范围内查看它的用户显示正确的时间(当然基于他们的区域设置)。我向 SharePoint 添加了一个用于查找 SystemTimeZones 的列表(基本上是 TimeZoneInfo.GetSystemTimeZones() 的 SharePoint 列表表示)

                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();
                    }
                } 

我在此列表的自定义添加和编辑表单中将此列表用于 EventTimeZone 的查找项。这些表单是 SharePoint Designer 将创建的内容的直接副本(因为它们使用的是 SharePoint:FormField),它们只是在 Visual Studio 中,因为我需要代码隐藏。我想让用户在他们的区域时区中查看事件,但是当他们编辑它们时,我想在输入的时区中显示它们。(即,我的区域时区是中央时区,所以当我查看山区会议时,它会显示上午 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 和 ItemUpdated 以“修复”时间,因为 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;
                    }  

它仍然会在 Update() 上重置。因此,当您在日历视图中查看重复项目时,该项目跨越整个星期,而不是每天显示一次。

我几乎把头发拉了出来,试图弄清楚这一点。任何指导都会很棒。我了解 Duration 是一个计算字段,但我不明白为什么调用 listItem.Update() 会忽略它确实被正确标记为重复事件而不正确计算 Duration 的事实。老实说,这似乎是 SP 2010 的一个错误。

提前致谢!

**

编辑:以下评论后的附加信息......

** 此 SharePoint 环境在太平洋时间拥有一台服务器,用户遍及所有美国时区、伦敦、东京、阿布达比等。一个时区的用户需要能够在其他时区创建事件。由于用户配置文件中的任何内容(无论如何对我们而言)都不会告诉我们他们希望在哪个时区查看所有内容,因此我们在母版页中添加了代码以查看本地计算机的时区并始终相应地设置其区域设置。

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

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 点创建会议,我希望能够使用它。但我一直找不到任何东西。

同样,所有这一切都很好......直到你来到经常性事件。实际上,它适用于重复事件,直到您尝试在日历视图中查看所述事件。然后它看起来像这样: 日历视图

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

4

3 回答 3

1

好的,所以我最终处理的方式如下。我决定退出列表事件接收器,因为它确实似乎是重新计算持续时间的 SharePoint 错误,因为现在可以正常工作的重复事件。我选择绑定到表单上的保存事件,并在它们发送之前更改值。到目前为止,这似乎在所有情况下都有效。我所有的数学都和以前一样。所以在我的 New2.aspx (这个列表的新项目表单)

    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();
        }
    }

这会像我以前的方法一样更新时间,但它也会正确计算持续时间。

SharePoint 会根据用户的区域设置(或 Web,如果用户未设置)处理显示正确的时间,并在日历视图中显示正确的时间。我确实必须更改编辑表单才能在编辑时获得正确的值:

   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);
            }
        }
    }

Edit 表单具有与 New 相同的 OnInit 和 SaveHandler。

于 2013-02-12T21:49:51.397 回答
0

我认为您正在与 SharePoint 用于重复事件的 shennagins 发生冲突。本质上,事件存储在单个列表项中,并在查询时展开。这使得事件的存储与您的期望完全相反。

从那篇文章看来,EventDate 和 EndDate 字段的使用方式不同,具体取决于重复与否。

另请注意,SharePoint 在“幕后”以 UTC 存储日期并转换回显示的用户(或网站)时区。您也许可以使用这些知识来优化一些日期逻辑。

更多信息

http://fatalfrenchy.wordpress.com/2010/07/16/sharepoint-recurrence-data-schema/

Share point 2010 ItemAdding 在日历上插入重复数据

http://blog.tylerholmes.com/2012/02/how-sharepoint-deals-with-time-and-time.html

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

 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();
    }

所以我从“本地”用户转换为 UTC,然后再转换回本地网络。UID 是必需的,或者点击事件时出错。

如果您想重复 13 次,请说...代码是:

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);
于 2013-02-07T20:40:48.230 回答
0

它可能已经很老了,但我的回答可能会对某人有所帮助。

您只需要明确地将 EventType = 1 也放入更新中。

于 2018-08-22T19:18:28.440 回答