背景: 我工作的公司有一个常规的SharePoint列表,其中包含用于事件的自定义ContentType(不会从日历列表项继承)。然后它使用日历视图显示这些。看起来很简单。 我们需要允许用户为他们添加的事件(不同于他们的区域设置)选择时区,并将信息添加到sharepoint,以便为每个用户在全球范围内显示正确的时间(根据他们的区域设置当然)。 我向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中,我需要代码隐藏。我想允许用户在他们的区域TimeZone中查看事件,但是当他们编辑它们时我想在他们输入的TimeZone中显示它们。 (IE我的区域时区是Central,因此当我查看Mountain会议时它将显示我上午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()上重置。因此,当您在日历视图中查看定期项目时,该项目将跨越整周,而不是每天显示一次。
我已经把我的头发拉出来试图解决这个问题。任何指导都会很精彩。我理解持续时间是一个计算字段但我无法理解为什么调用listItem.Update()会忽略它确实被正确标记为重复事件而不能正确计算持续时间的事实。老实说,这似乎是SP 2010的一个错误。
提前致谢!
**
** 此SharePoint env在太平洋时间拥有一台服务器,并且遍布所有美国时区,伦敦,东京,Abu Dabi等用户。一个时区中的用户需要能够在其他时区创建事件。由于用户的个人资料中没有任何内容(无论如何)会告诉我们他们想要查看所有内容的时区,我们在母版页中添加了代码以查看本地计算机的时区,并始终相应地设置其区域设置。
示例:我在纳什维尔,我想创建一个将在洛杉矶发生的事件:
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显然希望时间在用户的区域(或网络)时区。从单元测试中我很明显。如果有一个OOB方式让我允许中部时间的用户在太平洋时间上午9-10点在自定义列表中创建会议,我将很乐意能够使用它。但是我找不到任何东西。
同样,所有这些都很有效......直到你来到Recurring Events。实际上它适用于重复发生的事件,直到您尝试在日历视图中查看所述事件。然后它看起来像这样:
请注意,“Recurring 8”按照预期的方式重复发生,每天2次。但是,复发的“跨度”或“持续时间”是2天而不是1小时。 “重复15”的位置正确显示。输出到调试时,两者之间的字段值的唯一区别是“持续时间”字段。 Recurring 8的开始和结束日期在ItemAdded中更新,Recurring 15经过ItemAdded但ListItem.Update()被注释掉了。每个文档SharePoint应该为重复项目计算持续时间,而不是单个项目。使用对象模型更改开始日期和结束日期的事实不应该否定这一点。
答案 0 :(得分:1)
好的,所以我最终处理这个的方式如下。我决定退出列表事件接收器,因为现在正常工作的重复事件的重新计算持续时间似乎确实是一个SharePoint错误。我选择绑定到表单上的save事件,并在它们被发送之前更改值。到目前为止,这似乎在所有情况下都有效。我的所有数学都和以前一样。所以在我的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会根据用户的区域设置(如果用户尚未设置,则显示正确的时间)并在日历视图中显示正确的时间来处理。我必须更改编辑表单以在编辑时具有正确的值:
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);
}
}
}
“编辑”表单与“新建”具有相同的OnInit和SaveHandler。
答案 1 :(得分:0)
我认为您正在与SharePoint用于重新安装事件的shennagins发生冲突。基本上,事件存储在single list item and expanded at query time中。这使得事件的存储与您期望的方式完全相反。
从该帖子看,EventDate和EndDate字段的使用方式有所不同,具体取决于是否重现。
Also be aware the SharePoint stores dates in UTC'引擎盖'并转换回显示的用户(或网站)时区。您可以使用这些知识来优化某些日期逻辑。
更多信息
http://fatalfrenchy.wordpress.com/2010/07/16/sharepoint-recurrence-data-schema/
Share point 2010 ItemAdding insert Recurrence data on calendar
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);
答案 2 :(得分:0)
它可能已经很老了,但我的回答可能会对某人有所帮助。
您也只需显式地将EventType = 1更新。