In tutto questo periodo in cui ho utilizzato il client object model di SharePoint 2010, ho sempre dato per scontato che i dati ritornati dalle mie query effettuate sugli elementi di liste fossero corretti. O meglio, non ho mai avuto problemi con tutti quelli con cui ho avuto a che fare fin'ora, ma non avevo mai interagito con dei campi di tipo "data e ora".
In questa settimana invece ho avuto la necessità di leggere gli elementi presenti all'interno di una lista di task e di stamparli a video in un'applicazione eseguita client-side.
Inizialmente, una volta letti i dati dalla lista SharePoint, non mi è saltato all'occhio niente di strano. Dopo un pò invece, mi accorgo di una discrepanza tra le date presenti nella mia applicazione e quelle visibili all'interno dell'interfaccia di SharePoint 2010.
Vi rendo subito partecipi della cosa.. provate a creare una nuova lista di Task all'interno di uno dei vostri siti SharePoint 2010, inserite un pò di task con date varie ed incollate questo codice all'interno di una console application (dopo aver aggiunto la referenza sia alla libreria Microsoft.SharePoint.dll che alle librerie del client object model Microsoft.SharePoint.Client.dll e Microsoft.SharePoint.Client.Runtime.dll):

Console.WriteLine("Client object model:");

ClientContext context = new ClientContext("http://sharepoint2010/");
using (context)
{
List l = context.Web.Lists.GetByTitle("Tasks");
ListItemCollection items = l.GetItems(CamlQuery.CreateAllItemsQuery());

context.Load(items, its => its.Include(i => i["StartDate"]));
context.ExecuteQuery();

foreach (ListItem item in items)
{
Console.WriteLine("{0}", (DateTime)item["StartDate"]);
}
}

Console.WriteLine("Server object model:");
SPSite site = new SPSite("http://sharepoint2010/");
using (site)
{
SPWeb web = site.OpenWeb();
SPList list = web.Lists["Tasks"];
foreach (SPListItem item in list.GetItems("StartDate"))
{
Console.WriteLine("{0}", (DateTime)item["StartDate"]);
}
}

Bene.. se eseguite questo codice su un qualsiasi sito SharePoint (creato con qualsiasi lingua, anche la stessa della vostra macchina client), sono sicuro che vi accorgerete anche voi delle diversità dei valori di tipo data e ora.
Se non ci credete, ecco qua uno screenshot:

Date non corrette utilizzando il Client Object Model di SharePoint 2010

Come potete vedere, le prime 4 date sono diverse da quelle lette sulla stessa lista tramite il modello ad oggetti lato server. Lasciando perdere le ore (in quanto il mio campo di tipo "data e ora" permetteva all'utente di inserire solamente delle date), in questi 4 valori c’è una discrepanza di un giorno.
Li per li penso al bug (chissà come mai.. :), poi invece mi salta in mente il fatto che la mia applicazione viene eseguita sul client e che quindi è molto probabile che la lingua con cui vengono stampate queste date sia diversa da quella con cui è stato creato il sito SharePoint (o cmq da quella con cui è stata configurata la sezione "Regional settings"). Infatti, richiamando il metodo ToUniversalTime() della classe DateTime mi sono subito accorto che invece le date erano esattamente le stesse.

Quindi il problema a questo punto era: "come posso far si che le mie date vengano visualizzate attraverso la stessa culture della mia applicazione client ?".

Ho provato ad utilizzare il metodo ConvertToDateTime passando la medesima cultura presente all'interno della pagina "Regional Settings" del mio sito SharePoint, ma niente. Le date risultavano ancora diverse.

DateTime s = Convert.ToDateTime(((DateTime)item["StartDate"]), new CultureInfo("en-US"));

Console.WriteLine("{0}", s);


Cerco un pò all'interno dell'SDK e trovo la fantastica classe Utility e il suo ancor più fantastico metodo FormatDateTime, che effettua la conversione del mio DateTime secondo il formato scelto all'interno dei "Regional Settings" del mio sito SharePoint 2010.
Applicandolo al mio codice, ecco che i valori delle mie date vengono visualizzati esattamente come all'interno dell'interfaccia del mio sito.

Console.WriteLine("Client object model:");

ClientContext context = new ClientContext("http://sharepoint2010/");
using (context)
{
Web web = context.Web;
List l = web.Lists.GetByTitle("Tasks");
ListItemCollection items = l.GetItems(CamlQuery.CreateAllItemsQuery());

context.Load(web, w => w.Language);
context.Load(items, its => its.Include(i => i["StartDate"]));
context.ExecuteQuery();

foreach (ListItem item in items)
{
DateTime start = ((DateTime)item["StartDate"]);
ClientResult<string> result = Utility.FormatDateTime(context, context.Web, start, DateTimeFormat.DateTime);
context.ExecuteQuery();
DateTime rightStart = Convert.ToDateTime(result.Value, new CultureInfo((int)web.Language));
Console.WriteLine("{0}", rightStart);
}
}


In pratica, durante una normale query tramite il client object model di SharePoint 2010, le date vengono ritornate all'applicazione client in formato UTC e necessitano di essere convertite.
Ecco il risultato eseguendo il secondo listato (corretto) assieme al primo (scorretto).

Date CORRETTE utilizzando il Client Object Model di SharePoint 2010

Vi lascio anche i sorgenti, così potete provare con mano quanto vi ho detto (senza riscrivere l'applicazione da zero):
ClientObjectModelDates.zip (37,4 Kb)