I noticed some differences in values when calling File obj Created and Modified properties from ListItemCollection and FileCollection. Details below:
Below is some old code that fetched files from folder in FileCollection where I initialize required properties:
using (var spClientContext = new AuthenticationManager().GetACSAppOnlyContext(siteUrl, _clientId, _clientSecret))
{
if (spClientContext != null)
{
List list = spClientContext.Web.Lists.GetByTitle("Documents");
spClientContext.Load(list);
spClientContext.Load(list.RootFolder);
spClientContext.Load(list.RootFolder.Folders);
spClientContext.ExecuteQuery();
FolderCollection fcol = list.RootFolder.Folders;
if (fcol != null)
{
foreach (Folder f in fcol)
{
if (f.Name.Equals(folderName, StringComparison.InvariantCultureIgnoreCase))
{
spClientContext.Load(f.Files,
items => items.Include(
item => item.Name,
item => item.Author,
item => item.ModifiedBy,
item => item.ListItemAllFields["Created"],
item => item.ListItemAllFields["Modified"],
item => item.ListItemAllFields["FileRef"],
item => item.Length));
spClientContext.ExecuteQuery();
FileCollection fileCol = f.Files;
foreach (File file in fileCol)
{
// Do Stuff
}
}
}
}
}
}
When I output the File Created and Modified date/time values I get the following:
Below is my updated code which collects files in ListItemCollection and initializes File properties:
using (ClientContext spClientContext = new AuthenticationManager().GetACSAppOnlyContext(siteUrl, _clientId, _clientSecret))
{
if (spClientContext != null)
{
CamlQuery camlQuery = new CamlQuery();
camlQuery.FolderServerRelativeUrl = getSiteUrlAbsolutePath(siteUrl) + "/Shared Documents/" + folderPath;
camlQuery.ViewXml = "<View Scope="RecursiveAll">" +
"<Query>" +
"<Where>" +
"<Eq><FieldRef Name='FSObjType' /><Value Type='Integer'>0</Value></Eq>" +
"</Where>" +
"</Query>" +
"</View>";
List list = spClientContext.Web.Lists.GetByTitle("Documents");
ListItemCollection listItems = list.GetItems(camlQuery);
spClientContext.Load(listItems,
items => items.Include(
item => item.DisplayName,
item => item.FileSystemObjectType,
item => item.File,
item => item.File.Name,
item => item.File.Author,
item => item.File.ModifiedBy,
item => item.File.ListItemAllFields["Created"],
item => item.File.ListItemAllFields["Modified"],
item => item.File.ListItemAllFields["FileRef"],
item => item.File.Length));
spClientContext.ExecuteQuery();
if (listItems != null && listItems.Count > 0)
{
foreach (ListItem item in listItems)
{
if (item.FileSystemObjectType.Equals(FileSystemObjectType.File))
{
// Do Stuff
}
}
}
}
}
When I output the File Created and Modified date/time values I get the following:
All the other File properties in the new code appearing to be correct. From what I can tell in the new code Created and Modified are outputting UTC and not recognizing time zone setting in SharePoint site settings? Or am I initializing the File properties in the wrong spot?
Thanks for any feedback and help!
EDIT:
Testing the following code w/ Regional site settings loaded that @Antonio Leonardo suggested:
using (ClientContext spClientContext = new AuthenticationManager().GetACSAppOnlyContext(siteUrl, _clientId, _clientSecret))
{
if (spClientContext != null)
{
Web web = spClientContext.Web;
spClientContext.Load(web.RegionalSettings);
spClientContext.ExecuteQuery();
int localId = (int)web.RegionalSettings.LocaleId;
string cultureInfo = (new CultureInfo(localId)).Name;
System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(cultureInfo);
CamlQuery camlQuery = new CamlQuery();
camlQuery.FolderServerRelativeUrl = getSiteUrlAbsolutePath(siteUrl) + "/Shared Documents/" + folderPath;
camlQuery.ViewXml = "<View Scope="RecursiveAll">" +
"<Query>" +
"<Where>" +
"<Eq><FieldRef Name='FSObjType' /><Value Type='Integer'>0</Value></Eq>" +
"</Where>" +
"</Query>" +
"</View>";
List list = spClientContext.Web.Lists.GetByTitle("Documents");
ListItemCollection listItems = list.GetItems(camlQuery);
spClientContext.Load(listItems,
items => items.Include(
item => item.DisplayName,
item => item.FileSystemObjectType,
item => item.File,
item => item.File.Name,
item => item.File.Author,
item => item.File.ModifiedBy,
item => item.File.ListItemAllFields["Created"],
item => item.File.ListItemAllFields["Modified"],
item => item.File.ListItemAllFields["FileRef"],
item => item.File.Length));
spClientContext.ExecuteQuery();
if (listItems != null && listItems.Count > 0)
{
foreach (ListItem item in listItems)
{
if (item.FileSystemObjectType.Equals(FileSystemObjectType.File))
{
// Do Stuff
}
}
}
}
}
When I output the File Created and Modified date/time values with regional site settings loaded I get the following:
Found a robust solution to my original issue. Below is a copy of the ConvertUTCToLocalDateTime() method I am using. I pass all SharePoint DateTime properties through it and check if DateTime is UTC or Local. If UTC converts to Local using site timezone regional settings. This saves the pain of trying to figure out which properties are UTC or Local as they vary from property:
// File object DateTime ListItemAllFields property example calling ConvertUTCToLocalDateTime()
DateTime fileCreated = File.IsObjectPropertyInstantiated("ListItemAllFields") ?
ConvertUTCToLocalDateTime(spClientContext, (DateTime)File.ListItemAllFields["Created"]) : default(DateTime);
.
.
.
// FileVersion object DateTime property example calling ConvertUTCToLocalDateTime()
DateTime versionModified = FileVersion.IsPropertyAvailable("Created") ?
ConvertUTCToLocalDateTime(spClientContext, FileVersion.Created) : default(DateTime);
.
.
.
/// <summary>
/// Converts SharePoint UTC DateTime to Local DateTime using site regional time zone settings.
/// </summary>
/// <remarks>
/// SharePoint DateTime values can be a mix of UTC and Local in API. Need to pass all DateTime values through this method to check.
/// </remarks>
/// <param name="spClientContext">ClientContext</param>
/// <param name="dateObj">DateTime</param>
/// <returns>DateTime</returns>
public static DateTime ConvertUTCToLocalDateTime(ClientContext spClientContext, DateTime dateObj)
{
try
{
if (spClientContext != null)
{
DateTime temp;
if (DateTime.TryParse(dateObj.ToString(), out temp))
{
if (dateObj.Kind == DateTimeKind.Utc)
{
// convert utc datetime to local datetime using sharepoint site regional time zone settings
Microsoft.SharePoint.Client.TimeZone sharePointTimeZone = spClientContext.Web.RegionalSettings.TimeZone;
ClientResult<DateTime> localDateTime = sharePointTimeZone.UTCToLocalTime(dateObj);
spClientContext.ExecuteQuery();
return localDateTime.Value;
}
else if (dateObj.Kind == DateTimeKind.Local)
{
// local datetime just return (no convertion required)
return dateObj;
}
else if(dateObj.Kind == DateTimeKind.Unspecified)
{
// unspecified datetime handle as required
return dateObj;
}
}
}
}
catch (Exception ex)
{
// log exception
}
return dateObj;
}
In addition, as an alternative (or extra measures) if you are using CamlQuery to get ListItemCollection List Items then you can set the CamlQuery.DatesInUtc property to False and this will return Local DateTime values for all initialized properties:
using (ClientContext spClientContext = new AuthenticationManager().GetACSAppOnlyContext(siteUrl, _clientId, _clientSecret))
{
if (spClientContext != null)
{
Web web = spClientContext.Web;
spClientContext.Load(web.RegionalSettings);
spClientContext.ExecuteQuery();
int localId = (int)web.RegionalSettings.LocaleId;
string cultureInfo = (new CultureInfo(localId)).Name;
System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(cultureInfo);
CamlQuery camlQuery = new CamlQuery();
camlQuery.DatesInUtc = false; // uses site regional time zone datetime when set to false
camlQuery.FolderServerRelativeUrl = getSiteUrlAbsolutePath(siteUrl) + "/Shared Documents/" + folderPath;
camlQuery.ViewXml = "<View Scope="RecursiveAll">" +
"<Query>" +
"<Where>" +
"<Eq><FieldRef Name='FSObjType' /><Value Type='Integer'>0</Value></Eq>" +
"</Where>" +
"</Query>" +
"</View>";
List list = spClientContext.Web.Lists.GetByTitle("Documents");
ListItemCollection listItems = list.GetItems(camlQuery);
spClientContext.Load(listItems,
items => items.Include(
item => item.DisplayName,
item => item.FileSystemObjectType,
item => item.File,
item => item.File.Name,
item => item.File.Author,
item => item.File.ModifiedBy,
item => item.File.ListItemAllFields["Created"],
item => item.File.ListItemAllFields["Modified"],
item => item.File.ListItemAllFields["FileRef"],
item => item.File.Length));
spClientContext.ExecuteQuery();
if (listItems != null && listItems.Count > 0)
{
foreach (ListItem item in listItems)
{
if (item.FileSystemObjectType.Equals(FileSystemObjectType.File))
{
// Do Stuff
}
}
}
}
}
Hope these solutions are helpful as there is not much official documentation on the matter. Thanks for all feedback on the post!