diff --git a/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest_Success_MultiList_PartialSuccessLimitReachedFollowedBySuccessInAlternateBranch.cs b/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest_Success_MultiList_PartialSuccessLimitReachedFollowedBySuccessInAlternateBranch.cs index d60549e5f..16f08ba92 100644 --- a/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest_Success_MultiList_PartialSuccessLimitReachedFollowedBySuccessInAlternateBranch.cs +++ b/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest_Success_MultiList_PartialSuccessLimitReachedFollowedBySuccessInAlternateBranch.cs @@ -181,4 +181,4 @@ public void AuthenticateOnPublicKeyAuthenticationMethodShouldHaveBeenInvokedTwic PublicKeyAuthenticationMethodMock.Verify(p => p.Authenticate(SessionMock.Object), Times.Exactly(2)); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Common/BigIntegerTest.cs b/src/Renci.SshNet.Tests/Classes/Common/BigIntegerTest.cs index 8cca838d2..c99fb2a6e 100644 --- a/src/Renci.SshNet.Tests/Classes/Common/BigIntegerTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Common/BigIntegerTest.cs @@ -1506,10 +1506,12 @@ public void DefaultCtorWorks() Assert.AreEqual("0", a.ToString(), "#4"); a = new BigInteger(); +#pragma warning disable CS1718 // Comparison made to same variable Assert.AreEqual(true, a == a, "#5"); a = new BigInteger(); Assert.AreEqual(false, a < a, "#6"); +#pragma warning restore CS1718 // Comparison made to same variable a = new BigInteger(); Assert.AreEqual(true, a < 10L, "#7"); diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelExtendedDataMessageTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelExtendedDataMessageTest.cs index e50f5fa48..469908ea3 100644 --- a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelExtendedDataMessageTest.cs +++ b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelExtendedDataMessageTest.cs @@ -30,7 +30,7 @@ public void ChannelExtendedDataMessageConstructorTest() [Ignore] // placeholder public void ChannelExtendedDataMessageConstructorTest1() { - uint localChannelNumber = 0; // TODO: Initialize to an appropriate value + //uint localChannelNumber = 0; // TODO: Initialize to an appropriate value //ChannelExtendedDataMessage target = new ChannelExtendedDataMessage(localChannelNumber, null, null); Assert.Inconclusive("TODO: Implement code to verify target"); } diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTestBase.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTestBase.cs index fe73f9a6d..2f2f1bbf0 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTestBase.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTestBase.cs @@ -38,7 +38,8 @@ public void SetUp() protected static SftpFileAttributes CreateSftpFileAttributes(long size) { - return new SftpFileAttributes(default(DateTime), default(DateTime), size, default(int), default(int), default(uint), null); + var utcDefault = DateTime.SpecifyKind(default(DateTime), DateTimeKind.Utc); + return new SftpFileAttributes(utcDefault, utcDefault, size, default(int), default(int), default(uint), null); } protected static byte[] CreateByteArray(Random random, int length) diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeAppend_FileAccessWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeAppend_FileAccessWrite.cs index d8c4904cc..871830670 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeAppend_FileAccessWrite.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeAppend_FileAccessWrite.cs @@ -34,8 +34,8 @@ protected override void SetupData() _readBufferSize = (uint) _random.Next(5, 1000); _writeBufferSize = (uint) _random.Next(5, 1000); _handle = GenerateRandom(_random.Next(1, 10), _random); - _fileAttributes = new SftpFileAttributesBuilder().WithLastAccessTime(DateTime.Now.AddSeconds(_random.Next())) - .WithLastWriteTime(DateTime.Now.AddSeconds(_random.Next())) + _fileAttributes = new SftpFileAttributesBuilder().WithLastAccessTime(DateTime.UtcNow.AddSeconds(_random.Next())) + .WithLastWriteTime(DateTime.UtcNow.AddSeconds(_random.Next())) .WithSize(_random.Next()) .WithUserId(_random.Next()) .WithGroupId(_random.Next()) diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthGreatherThanPosition.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthGreatherThanPosition.cs index aee4b2762..59a0135f0 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthGreatherThanPosition.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthGreatherThanPosition.cs @@ -53,8 +53,8 @@ protected override void SetupData() _fileAttributes = new SftpFileAttributesBuilder().WithExtension("X", "ABC") .WithExtension("V", "VValue") .WithGroupId(random.Next()) - .WithLastAccessTime(DateTime.Now.AddSeconds(random.Next())) - .WithLastWriteTime(DateTime.Now.AddSeconds(random.Next())) + .WithLastAccessTime(DateTime.UtcNow.AddSeconds(random.Next())) + .WithLastWriteTime(DateTime.UtcNow.AddSeconds(random.Next())) .WithPermissions((uint) random.Next()) .WithSize(_length + 100) .WithUserId(random.Next()) @@ -201,4 +201,4 @@ public void WriteShouldStartFromSamePositionAsBeforeSetLength() SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(4)); } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthLessThanPosition.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthLessThanPosition.cs index ae3952cc6..a61163392 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthLessThanPosition.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthLessThanPosition.cs @@ -50,8 +50,8 @@ protected override void SetupData() _fileAttributes = new SftpFileAttributesBuilder().WithExtension("X", "ABC") .WithExtension("V", "VValue") .WithGroupId(random.Next()) - .WithLastAccessTime(DateTime.Now.AddSeconds(random.Next())) - .WithLastWriteTime(DateTime.Now.AddSeconds(random.Next())) + .WithLastAccessTime(DateTime.UtcNow.AddSeconds(random.Next())) + .WithLastWriteTime(DateTime.UtcNow.AddSeconds(random.Next())) .WithPermissions((uint)random.Next()) .WithSize(_length + 100) .WithUserId(random.Next()) diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthGreatherThanPosition.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthGreatherThanPosition.cs index 5a19aa3ef..c71b43c85 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthGreatherThanPosition.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthGreatherThanPosition.cs @@ -54,8 +54,8 @@ protected override void SetupData() _fileAttributes = new SftpFileAttributesBuilder().WithExtension("X", "ABC") .WithExtension("V", "VValue") .WithGroupId(random.Next()) - .WithLastAccessTime(DateTime.Now.AddSeconds(random.Next())) - .WithLastWriteTime(DateTime.Now.AddSeconds(random.Next())) + .WithLastAccessTime(DateTime.UtcNow.AddSeconds(random.Next())) + .WithLastWriteTime(DateTime.UtcNow.AddSeconds(random.Next())) .WithPermissions((uint)random.Next()) .WithSize(_length + 100) .WithUserId(random.Next()) diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthLessThanPosition.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthLessThanPosition.cs index cfcf23155..96ad14b18 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthLessThanPosition.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthLessThanPosition.cs @@ -54,8 +54,8 @@ protected override void SetupData() _fileAttributes = new SftpFileAttributesBuilder().WithExtension("X", "ABC") .WithExtension("V", "VValue") .WithGroupId(random.Next()) - .WithLastAccessTime(DateTime.Now.AddSeconds(random.Next())) - .WithLastWriteTime(DateTime.Now.AddSeconds(random.Next())) + .WithLastAccessTime(DateTime.UtcNow.AddSeconds(random.Next())) + .WithLastWriteTime(DateTime.UtcNow.AddSeconds(random.Next())) .WithPermissions((uint) random.Next()) .WithSize(_length + 100) .WithUserId(random.Next()) diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_SessionOpen_FIleAccessReadWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_SessionOpen_FIleAccessReadWrite.cs index 0fde319a1..ef89483b3 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_SessionOpen_FIleAccessReadWrite.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_SessionOpen_FIleAccessReadWrite.cs @@ -48,8 +48,8 @@ protected void Arrange() _writeBufferSize = (uint) random.Next(0, 1000); _length = random.Next(); - _fileAttributesLastAccessTime = DateTime.Now.AddSeconds(random.Next()); - _fileAttributesLastWriteTime = DateTime.Now.AddSeconds(random.Next()); + _fileAttributesLastAccessTime = DateTime.UtcNow.AddSeconds(random.Next()); + _fileAttributesLastWriteTime = DateTime.UtcNow.AddSeconds(random.Next()); _fileAttributesSize = random.Next(); _fileAttributesUserId = random.Next(); _fileAttributesGroupId = random.Next(); diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_SessionOpen_FIleAccessWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_SessionOpen_FIleAccessWrite.cs index ce2619a5b..73b5a9536 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_SessionOpen_FIleAccessWrite.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_SessionOpen_FIleAccessWrite.cs @@ -48,8 +48,8 @@ protected void Arrange() _writeBufferSize = (uint) random.Next(0, 1000); _length = random.Next(); - _fileAttributesLastAccessTime = DateTime.Now.AddSeconds(random.Next()); - _fileAttributesLastWriteTime = DateTime.Now.AddSeconds(random.Next()); + _fileAttributesLastAccessTime = DateTime.UtcNow.AddSeconds(random.Next()); + _fileAttributesLastWriteTime = DateTime.UtcNow.AddSeconds(random.Next()); _fileAttributesSize = random.Next(); _fileAttributesUserId = random.Next(); _fileAttributesGroupId = random.Next(); diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Write_SessionOpen_CountGreatherThanTwoTimesTheWriteBufferSize.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Write_SessionOpen_CountGreatherThanTwoTimesTheWriteBufferSize.cs index 68020d553..e7d537069 100644 --- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Write_SessionOpen_CountGreatherThanTwoTimesTheWriteBufferSize.cs +++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Write_SessionOpen_CountGreatherThanTwoTimesTheWriteBufferSize.cs @@ -119,8 +119,8 @@ public void PositionShouldBeNumberOfBytesWrittenToFileAndNUmberOfBytesInBuffer() [TestMethod] public void LengthShouldFlushBufferAndReturnSizeOfFile() { - var lengthFileAttributes = new SftpFileAttributes(DateTime.Now, - DateTime.Now, + var lengthFileAttributes = new SftpFileAttributes(DateTime.UtcNow, + DateTime.UtcNow, _random.Next(), _random.Next(), _random.Next(), diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest.cs index 3edd7c7b7..f562f66bb 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest.cs @@ -1076,7 +1076,9 @@ public void SetLastAccessTimeTest() SftpClient target = new SftpClient(connectionInfo); // TODO: Initialize to an appropriate value string path = string.Empty; // TODO: Initialize to an appropriate value DateTime lastAccessTime = new DateTime(); // TODO: Initialize to an appropriate value +#pragma warning disable CS0618 // Type or member is obsolete target.SetLastAccessTime(path, lastAccessTime); +#pragma warning restore CS0618 // Type or member is obsolete Assert.Inconclusive("A method that does not return a value cannot be verified."); } @@ -1091,7 +1093,9 @@ public void SetLastAccessTimeUtcTest() SftpClient target = new SftpClient(connectionInfo); // TODO: Initialize to an appropriate value string path = string.Empty; // TODO: Initialize to an appropriate value DateTime lastAccessTimeUtc = new DateTime(); // TODO: Initialize to an appropriate value +#pragma warning disable CS0618 // Type or member is obsolete target.SetLastAccessTimeUtc(path, lastAccessTimeUtc); +#pragma warning restore CS0618 // Type or member is obsolete Assert.Inconclusive("A method that does not return a value cannot be verified."); } @@ -1106,7 +1110,9 @@ public void SetLastWriteTimeTest() SftpClient target = new SftpClient(connectionInfo); // TODO: Initialize to an appropriate value string path = string.Empty; // TODO: Initialize to an appropriate value DateTime lastWriteTime = new DateTime(); // TODO: Initialize to an appropriate value +#pragma warning disable CS0618 // Type or member is obsolete target.SetLastWriteTime(path, lastWriteTime); +#pragma warning restore CS0618 // Type or member is obsolete Assert.Inconclusive("A method that does not return a value cannot be verified."); } @@ -1121,7 +1127,9 @@ public void SetLastWriteTimeUtcTest() SftpClient target = new SftpClient(connectionInfo); // TODO: Initialize to an appropriate value string path = string.Empty; // TODO: Initialize to an appropriate value DateTime lastWriteTimeUtc = new DateTime(); // TODO: Initialize to an appropriate value +#pragma warning disable CS0618 // Type or member is obsolete target.SetLastWriteTimeUtc(path, lastWriteTimeUtc); +#pragma warning restore CS0618 // Type or member is obsolete Assert.Inconclusive("A method that does not return a value cannot be verified."); } @@ -1403,4 +1411,4 @@ private class TestInfo public SftpDownloadAsyncResult DownloadResult { get; set; } } } -} \ No newline at end of file +} diff --git a/src/Renci.SshNet.Tests/Common/Extensions.cs b/src/Renci.SshNet.Tests/Common/Extensions.cs index 911f34139..4e88a7900 100644 --- a/src/Renci.SshNet.Tests/Common/Extensions.cs +++ b/src/Renci.SshNet.Tests/Common/Extensions.cs @@ -49,8 +49,8 @@ internal static SftpFileAttributes Clone(this SftpFileAttributes value) clonedExtensions = null; } - return new SftpFileAttributes(value.LastAccessTime, - value.LastWriteTime, + return new SftpFileAttributes(value.LastAccessTimeUtc, + value.LastWriteTimeUtc, value.Size, value.UserId, value.GroupId, diff --git a/src/Renci.SshNet.Tests/Common/SftpFileAttributesBuilder.cs b/src/Renci.SshNet.Tests/Common/SftpFileAttributesBuilder.cs index 1f2d33240..2cf1b11a8 100644 --- a/src/Renci.SshNet.Tests/Common/SftpFileAttributesBuilder.cs +++ b/src/Renci.SshNet.Tests/Common/SftpFileAttributesBuilder.cs @@ -64,9 +64,15 @@ public SftpFileAttributesBuilder WithExtension(string name, string value) public SftpFileAttributes Build() { if (_lastAccessTime == null) - _lastAccessTime = DateTime.MinValue; + _lastAccessTime = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc); + else if (_lastAccessTime.Value.Kind != DateTimeKind.Utc) + _lastAccessTime = _lastAccessTime.Value.ToUniversalTime(); + if (_lastWriteTime == null) - _lastWriteTime = DateTime.MinValue; + _lastWriteTime = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc); + else if (_lastWriteTime.Value.Kind != DateTimeKind.Utc) + _lastWriteTime = _lastWriteTime.Value.ToUniversalTime(); + if (_size == null) _size = 0; if (_userId == null) diff --git a/src/Renci.SshNet/Sftp/SftpFile.cs b/src/Renci.SshNet/Sftp/SftpFile.cs index de729e130..cfcc37a2f 100644 --- a/src/Renci.SshNet/Sftp/SftpFile.cs +++ b/src/Renci.SshNet/Sftp/SftpFile.cs @@ -99,11 +99,11 @@ public DateTime LastAccessTimeUtc { get { - return Attributes.LastAccessTime.ToUniversalTime(); + return Attributes.LastAccessTimeUtc; } set { - Attributes.LastAccessTime = value.ToLocalTime(); + Attributes.LastAccessTimeUtc = value; } } @@ -117,11 +117,11 @@ public DateTime LastWriteTimeUtc { get { - return Attributes.LastWriteTime.ToUniversalTime(); + return Attributes.LastWriteTimeUtc; } set { - Attributes.LastWriteTime = value.ToLocalTime(); + Attributes.LastWriteTimeUtc = value; } } diff --git a/src/Renci.SshNet/Sftp/SftpFileAttributes.cs b/src/Renci.SshNet/Sftp/SftpFileAttributes.cs index d99dfc2df..573deb92a 100644 --- a/src/Renci.SshNet/Sftp/SftpFileAttributes.cs +++ b/src/Renci.SshNet/Sftp/SftpFileAttributes.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Globalization; using Renci.SshNet.Common; +using System.Diagnostics; namespace Renci.SshNet.Sftp { @@ -11,7 +12,7 @@ namespace Renci.SshNet.Sftp /// public class SftpFileAttributes { - #region Bitmask constats + #region Bitmask constants private const uint S_IFMT = 0xF000; // bitmask for the file type bitfields @@ -60,8 +61,8 @@ public class SftpFileAttributes private bool _isGroupIDBitSet; private bool _isStickyBitSet; - private readonly DateTime _originalLastAccessTime; - private readonly DateTime _originalLastWriteTime; + private readonly DateTime _originalLastAccessTimeUtc; + private readonly DateTime _originalLastWriteTimeUtc; private readonly long _originalSize; private readonly int _originalUserId; private readonly int _originalGroupId; @@ -70,12 +71,12 @@ public class SftpFileAttributes internal bool IsLastAccessTimeChanged { - get { return _originalLastAccessTime != LastAccessTime; } + get { return _originalLastAccessTimeUtc != LastAccessTimeUtc; } } internal bool IsLastWriteTimeChanged { - get { return _originalLastWriteTime != LastWriteTime; } + get { return _originalLastWriteTimeUtc != LastWriteTimeUtc; } } internal bool IsSizeChanged @@ -104,20 +105,58 @@ internal bool IsExtensionsChanged } /// - /// Gets or sets the time the current file or directory was last accessed. + /// Gets or sets the local time the current file or directory was last accessed. /// /// - /// The time that the current file or directory was last accessed. + /// The local time that the current file or directory was last accessed. /// - public DateTime LastAccessTime { get; set; } + public DateTime LastAccessTime + { + get + { + return ToLocalTime(this.LastAccessTimeUtc); + } + + set + { + this.LastAccessTimeUtc = ToUniversalTime(value); + } + } + + /// + /// Gets or sets the local time when the current file or directory was last written to. + /// + /// + /// The local time the current file was last written. + /// + public DateTime LastWriteTime + { + get + { + return ToLocalTime(this.LastWriteTimeUtc); + } + + set + { + this.LastWriteTimeUtc = ToUniversalTime(value); + } + } + + /// + /// Gets or sets the UTC time the current file or directory was last accessed. + /// + /// + /// The UTC time that the current file or directory was last accessed. + /// + public DateTime LastAccessTimeUtc { get; set; } /// - /// Gets or sets the time when the current file or directory was last written to. + /// Gets or sets the UTC time when the current file or directory was last written to. /// /// - /// The time the current file was last written. + /// The UTC time the current file was last written. /// - public DateTime LastWriteTime { get; set; } + public DateTime LastWriteTimeUtc { get; set; } /// /// Gets or sets the size, in bytes, of the current file. @@ -395,10 +434,10 @@ private SftpFileAttributes() { } - internal SftpFileAttributes(DateTime lastAccessTime, DateTime lastWriteTime, long size, int userId, int groupId, uint permissions, IDictionary extensions) + internal SftpFileAttributes(DateTime lastAccessTimeUtc, DateTime lastWriteTimeUtc, long size, int userId, int groupId, uint permissions, IDictionary extensions) { - LastAccessTime = _originalLastAccessTime = lastAccessTime; - LastWriteTime = _originalLastWriteTime = lastWriteTime; + LastAccessTimeUtc = _originalLastAccessTimeUtc = lastAccessTimeUtc; + LastWriteTimeUtc = _originalLastWriteTimeUtc = lastWriteTimeUtc; Size = _originalSize = size; UserId = _originalUserId = userId; GroupId = _originalGroupId = groupId; @@ -491,9 +530,9 @@ public byte[] GetBytes() if (IsLastAccessTimeChanged || IsLastWriteTimeChanged) { - var time = (uint)(LastAccessTime.ToFileTime() / 10000000 - 11644473600); + var time = (uint)(LastAccessTimeUtc.ToFileTimeUtc() / 10000000 - 11644473600); stream.Write(time); - time = (uint)(LastWriteTime.ToFileTime() / 10000000 - 11644473600); + time = (uint)(LastWriteTimeUtc.ToFileTimeUtc() / 10000000 - 11644473600); stream.Write(time); } @@ -521,8 +560,8 @@ internal static SftpFileAttributes FromBytes(SshDataStream stream) var userId = -1; var groupId = -1; uint permissions = 0; - var accessTime = DateTime.MinValue; - var modifyTime = DateTime.MinValue; + DateTime accessTime; + DateTime modifyTime; IDictionary extensions = null; if ((flag & 0x00000001) == 0x00000001) // SSH_FILEXFER_ATTR_SIZE @@ -544,10 +583,17 @@ internal static SftpFileAttributes FromBytes(SshDataStream stream) if ((flag & 0x00000008) == 0x00000008) // SSH_FILEXFER_ATTR_ACMODTIME { + // The incoming times are "Unix times", so they're already in UTC. We need to preserve that + // to avoid losing information in a local time conversion during the "fall back" hour in DST. var time = stream.ReadUInt32(); - accessTime = DateTime.FromFileTime((time + 11644473600) * 10000000); + accessTime = DateTime.FromFileTimeUtc((time + 11644473600) * 10000000); time = stream.ReadUInt32(); - modifyTime = DateTime.FromFileTime((time + 11644473600) * 10000000); + modifyTime = DateTime.FromFileTimeUtc((time + 11644473600) * 10000000); + } + else + { + accessTime = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc); + modifyTime = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc); } if ((flag & 0x80000000) == 0x80000000) // SSH_FILEXFER_ATTR_EXTENDED @@ -572,5 +618,37 @@ internal static SftpFileAttributes FromBytes(byte[] buffer) return FromBytes(stream); } } + + private static DateTime ToLocalTime(DateTime value) + { + DateTime result; + + if (value == DateTime.MinValue) + { + result = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Local); + } + else + { + result = value.ToLocalTime(); + } + + return result; + } + + private static DateTime ToUniversalTime(DateTime value) + { + DateTime result; + + if (value == DateTime.MinValue) + { + result = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc); + } + else + { + result = value.ToUniversalTime(); + } + + return result; + } } }