Fix performance for `File.GetLastWriteTimeUtc` on Unix (#53070)
authorDavid Karlaš <david.karlas@microsoft.com>
Tue, 10 Aug 2021 08:12:13 +0000 (10:12 +0200)
committerGitHub <noreply@github.com>
Tue, 10 Aug 2021 08:12:13 +0000 (10:12 +0200)
All this changes are to internal methods which are later called by other public methods which correctly call `ToLocalTime` for local time "overloads".
Performance hit is most visible for application which doesn't need to load local timezone information which takes ~30ms on 2016 MacBook Pro.

Co-authored-by: Jeff Handley <jeff.handley@microsoft.com>
src/libraries/System.IO.FileSystem/tests/Enumeration/GetTimesTests.cs [new file with mode: 0644]
src/libraries/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj
src/libraries/System.Private.CoreLib/src/System/IO/FileStatus.Unix.cs
src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs

diff --git a/src/libraries/System.IO.FileSystem/tests/Enumeration/GetTimesTests.cs b/src/libraries/System.IO.FileSystem/tests/Enumeration/GetTimesTests.cs
new file mode 100644 (file)
index 0000000..1c44f47
--- /dev/null
@@ -0,0 +1,43 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+using System.IO.Enumeration;
+using Xunit;
+
+namespace System.IO.Tests.Enumeration
+{
+    public class GetTimesTests : FileSystemTest
+    {
+        private class AllEntries : FileSystemEnumerator<(DateTimeOffset CreationTimeUtc, DateTimeOffset LastAccessTimeUtc, DateTimeOffset LastWriteTimeUtc)>
+        {
+            public AllEntries(string directory, EnumerationOptions options)
+                : base(directory, options)
+            {
+            }
+
+            protected override (DateTimeOffset CreationTimeUtc, DateTimeOffset LastAccessTimeUtc, DateTimeOffset LastWriteTimeUtc) TransformEntry(ref FileSystemEntry entry)
+            {
+                return (entry.CreationTimeUtc, entry.LastAccessTimeUtc, entry.LastWriteTimeUtc);
+            }
+        }
+
+        [Fact]
+        public void FileTimesShouldBeUtc()
+        {
+            DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
+            FileInfo fileOne = new FileInfo(Path.Combine(testDirectory.FullName, GetTestFileName()));
+
+            fileOne.Create().Dispose();
+
+            using (var enumerator = new AllEntries(testDirectory.FullName, new EnumerationOptions()))
+            {
+                Assert.True(enumerator.MoveNext());
+                Assert.Equal(TimeSpan.Zero, enumerator.Current.CreationTimeUtc.Offset);
+                Assert.Equal(TimeSpan.Zero, enumerator.Current.LastAccessTimeUtc.Offset);
+                Assert.Equal(TimeSpan.Zero, enumerator.Current.LastWriteTimeUtc.Offset);
+                Assert.False(enumerator.MoveNext());
+            }
+        }
+    }
+}
index 227d528..736d3b1 100644 (file)
@@ -39,6 +39,7 @@
     <Compile Include="FileStream\DisposeAsync.cs" />
     <Compile Include="FileStream\ReadWriteSpan.cs" />
     <Compile Include="Enumeration\ConstructionTests.cs" />
+    <Compile Include="Enumeration\GetTimesTests.cs" />
     <Compile Include="Enumeration\RecursionDepthTests.cs" />
     <Compile Include="Enumeration\SpecialDirectoryTests.cs" />
     <Compile Include="Enumeration\SkipAttributeTests.cs" />
index 1640225..b1de67c 100644 (file)
@@ -242,7 +242,7 @@ namespace System.IO
         {
             EnsureCachesInitialized(path, continueOnError);
             if (!_exists)
-                return DateTimeOffset.FromFileTime(0);
+                return new DateTimeOffset(DateTime.FromFileTimeUtc(0));
 
             if ((_fileCache.Flags & Interop.Sys.FileStatusFlags.HasBirthTime) != 0)
                 return UnixTimeToDateTimeOffset(_fileCache.BirthTime, _fileCache.BirthTimeNsec);
@@ -273,7 +273,7 @@ namespace System.IO
         {
             EnsureCachesInitialized(path, continueOnError);
             if (!_exists)
-                return DateTimeOffset.FromFileTime(0);
+                return new DateTimeOffset(DateTime.FromFileTimeUtc(0));
             return UnixTimeToDateTimeOffset(_fileCache.ATime, _fileCache.ATimeNsec);
         }
 
@@ -283,7 +283,7 @@ namespace System.IO
         {
             EnsureCachesInitialized(path, continueOnError);
             if (!_exists)
-                return DateTimeOffset.FromFileTime(0);
+                return new DateTimeOffset(DateTime.FromFileTimeUtc(0));
             return UnixTimeToDateTimeOffset(_fileCache.MTime, _fileCache.MTimeNsec);
         }
 
@@ -291,7 +291,7 @@ namespace System.IO
 
         private DateTimeOffset UnixTimeToDateTimeOffset(long seconds, long nanoseconds)
         {
-            return DateTimeOffset.FromUnixTimeSeconds(seconds).AddTicks(nanoseconds / NanosecondsPerTick).ToLocalTime();
+            return DateTimeOffset.FromUnixTimeSeconds(seconds).AddTicks(nanoseconds / NanosecondsPerTick);
         }
 
         private unsafe void SetAccessOrWriteTime(string path, DateTimeOffset time, bool isAccessTime)
index 43111fb..aab00ed 100644 (file)
@@ -521,7 +521,7 @@ namespace System.IO
 
         public static DateTimeOffset GetCreationTime(string fullPath)
         {
-            return new FileInfo(fullPath, null).CreationTime;
+            return new FileInfo(fullPath, null).CreationTimeUtc;
         }
 
         public static void SetCreationTime(string fullPath, DateTimeOffset time, bool asDirectory)
@@ -535,7 +535,7 @@ namespace System.IO
 
         public static DateTimeOffset GetLastAccessTime(string fullPath)
         {
-            return new FileInfo(fullPath, null).LastAccessTime;
+            return new FileInfo(fullPath, null).LastAccessTimeUtc;
         }
 
         public static void SetLastAccessTime(string fullPath, DateTimeOffset time, bool asDirectory)
@@ -549,7 +549,7 @@ namespace System.IO
 
         public static DateTimeOffset GetLastWriteTime(string fullPath)
         {
-            return new FileInfo(fullPath, null).LastWriteTime;
+            return new FileInfo(fullPath, null).LastWriteTimeUtc;
         }
 
         public static void SetLastWriteTime(string fullPath, DateTimeOffset time, bool asDirectory)