背景:我工作的公司有一个常规的 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 应该以不同于计算单个项目的方式计算重复项目的持续时间。使用对象模型更改开始和结束日期的事实不应否定这一点。