From 6771c218dd1ea5850d045d062b2ec860965e9c2a Mon Sep 17 00:00:00 2001 From: Bill Menees Date: Wed, 13 Dec 2017 09:28:20 -0600 Subject: [PATCH 1/3] Improved SFTP file UTC time handling --- .editorconfig | 98 ++++++++++++++++ ...nected_KeepAliveInterval_NotNegativeOne.cs | 2 - ...achedFollowedBySuccessInAlternateBranch.cs | 3 +- .../Classes/Common/BigIntegerTest.cs | 2 + .../ChannelExtendedDataMessageTest.cs | 2 +- .../Classes/SftpClientTest.cs | 10 +- src/Renci.SshNet/Sftp/SftpFile.cs | 8 +- src/Renci.SshNet/Sftp/SftpFileAttributes.cs | 106 +++++++++++++++--- 8 files changed, 204 insertions(+), 27 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..eb77c27f3 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,98 @@ +# http://EditorConfig.org +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference + +# top-most EditorConfig file +root = true + +# Code files +[*.{cs,csx,scs,sql,vb,vbx}] +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +# Xml and Json files +[*.{xml,xsd,csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj,props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct,json}] +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +# Dotnet code style settings: +[*.{cs,scs,vb}] +# Sort using and Import directives with System.* appearing first +dotnet_sort_system_directives_first = true + +# Use language keywords instead of framework type names for type references +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +# Suggest more modern language features when available +dotnet_style_object_initializer = true:none +dotnet_style_collection_initializer = true:none +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion + +# CSharp code style settings: +[*.{cs,scs}] +# Never prefer "var" +csharp_style_var_for_built_in_types = false:none +csharp_style_var_when_type_is_apparent = false:none +csharp_style_var_elsewhere = false:none + +# Prefer method-like constructs to have an expression-body +csharp_style_expression_bodied_methods = true:none +csharp_style_expression_bodied_constructors = true:none +csharp_style_expression_bodied_operators = true:none + +# Prefer property-like constructs to have an expression-body +csharp_style_expression_bodied_properties = true:none +csharp_style_expression_bodied_indexers = true:none +csharp_style_expression_bodied_accessors = true:none + +# Suggest more modern language features when available +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion + +# Newline settings +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = flush_left + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = do_not_ignore +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false diff --git a/src/Renci.SshNet.Tests/Classes/BaseClientTest_Disconnected_KeepAliveInterval_NotNegativeOne.cs b/src/Renci.SshNet.Tests/Classes/BaseClientTest_Disconnected_KeepAliveInterval_NotNegativeOne.cs index aceba42fe..588bfd833 100644 --- a/src/Renci.SshNet.Tests/Classes/BaseClientTest_Disconnected_KeepAliveInterval_NotNegativeOne.cs +++ b/src/Renci.SshNet.Tests/Classes/BaseClientTest_Disconnected_KeepAliveInterval_NotNegativeOne.cs @@ -14,7 +14,6 @@ public class BaseClientTest_Disconnected_KeepAliveInterval_NotNegativeOne private BaseClient _client; private ConnectionInfo _connectionInfo; private TimeSpan _keepAliveInterval; - private int _keepAliveCount; [TestInitialize] public void Setup() @@ -38,7 +37,6 @@ private void SetupData() { _connectionInfo = new ConnectionInfo("host", "user", new PasswordAuthenticationMethod("user", "pwd")); _keepAliveInterval = TimeSpan.FromMilliseconds(50d); - _keepAliveCount = 0; } private void CreateMocks() 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 8c6413ef0..16f08ba92 100644 --- a/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest_Success_MultiList_PartialSuccessLimitReachedFollowedBySuccessInAlternateBranch.cs +++ b/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest_Success_MultiList_PartialSuccessLimitReachedFollowedBySuccessInAlternateBranch.cs @@ -35,7 +35,6 @@ public class ClientAuthenticationTest_Success_MultiList_PartialSuccessLimitReach { private int _partialSuccessLimit; private ClientAuthentication _clientAuthentication; - private SshAuthenticationException _actualException; protected override void SetupData() { @@ -182,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/SftpClientTest.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest.cs index 6f4ec9644..dd0982d45 100644 --- a/src/Renci.SshNet.Tests/Classes/SftpClientTest.cs +++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest.cs @@ -942,7 +942,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."); } @@ -957,7 +959,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."); } @@ -972,7 +976,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."); } @@ -987,7 +993,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."); } @@ -1269,4 +1277,4 @@ private class TestInfo public SftpDownloadAsyncResult DownloadResult { get; set; } } } -} \ No newline at end of file +} 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..2a66bcd98 100644 --- a/src/Renci.SshNet/Sftp/SftpFileAttributes.cs +++ b/src/Renci.SshNet/Sftp/SftpFileAttributes.cs @@ -11,7 +11,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 +60,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 +70,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 +104,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. @@ -397,8 +435,8 @@ private SftpFileAttributes() internal SftpFileAttributes(DateTime lastAccessTime, DateTime lastWriteTime, long size, int userId, int groupId, uint permissions, IDictionary extensions) { - LastAccessTime = _originalLastAccessTime = lastAccessTime; - LastWriteTime = _originalLastWriteTime = lastWriteTime; + LastAccessTimeUtc = _originalLastAccessTimeUtc = ToUniversalTime(lastAccessTime); + LastWriteTimeUtc = _originalLastWriteTimeUtc = ToUniversalTime(lastWriteTime); Size = _originalSize = size; UserId = _originalUserId = userId; GroupId = _originalGroupId = groupId; @@ -491,9 +529,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); } @@ -544,10 +582,12 @@ 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); } if ((flag & 0x80000000) == 0x80000000) // SSH_FILEXFER_ATTR_EXTENDED @@ -572,5 +612,37 @@ internal static SftpFileAttributes FromBytes(byte[] buffer) return FromBytes(stream); } } + + internal static DateTime ToLocalTime(DateTime value) + { + DateTime result; + + if (value == DateTime.MinValue) + { + result = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Local); + } + else + { + result = value.ToLocalTime(); + } + + return result; + } + + internal static DateTime ToUniversalTime(DateTime value) + { + DateTime result; + + if (value == DateTime.MinValue) + { + result = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc); + } + else + { + result = value.ToUniversalTime(); + } + + return result; + } } } From e621113e2d66a27f36ed732af9b267bc831c6ecf Mon Sep 17 00:00:00 2001 From: Bill Menees <13545825+menees@users.noreply.github.com> Date: Sun, 12 Jul 2020 09:11:38 -0500 Subject: [PATCH 2/3] Delete .editorconfig Removed .editorconfig from PR at [@drieseng's request](https://github.com/sshnet/SSH.NET/pull/356#issuecomment-657189591). --- .editorconfig | 98 --------------------------------------------------- 1 file changed, 98 deletions(-) delete mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index eb77c27f3..000000000 --- a/.editorconfig +++ /dev/null @@ -1,98 +0,0 @@ -# http://EditorConfig.org -# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference - -# top-most EditorConfig file -root = true - -# Code files -[*.{cs,csx,scs,sql,vb,vbx}] -indent_size = 4 -indent_style = space -insert_final_newline = true -trim_trailing_whitespace = true - -# Xml and Json files -[*.{xml,xsd,csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj,props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct,json}] -indent_size = 2 -indent_style = space -insert_final_newline = true -trim_trailing_whitespace = true - -# Dotnet code style settings: -[*.{cs,scs,vb}] -# Sort using and Import directives with System.* appearing first -dotnet_sort_system_directives_first = true - -# Use language keywords instead of framework type names for type references -dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion -dotnet_style_predefined_type_for_member_access = true:suggestion - -# Suggest more modern language features when available -dotnet_style_object_initializer = true:none -dotnet_style_collection_initializer = true:none -dotnet_style_coalesce_expression = true:suggestion -dotnet_style_null_propagation = true:suggestion -dotnet_style_explicit_tuple_names = true:suggestion - -# CSharp code style settings: -[*.{cs,scs}] -# Never prefer "var" -csharp_style_var_for_built_in_types = false:none -csharp_style_var_when_type_is_apparent = false:none -csharp_style_var_elsewhere = false:none - -# Prefer method-like constructs to have an expression-body -csharp_style_expression_bodied_methods = true:none -csharp_style_expression_bodied_constructors = true:none -csharp_style_expression_bodied_operators = true:none - -# Prefer property-like constructs to have an expression-body -csharp_style_expression_bodied_properties = true:none -csharp_style_expression_bodied_indexers = true:none -csharp_style_expression_bodied_accessors = true:none - -# Suggest more modern language features when available -csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion -csharp_style_pattern_matching_over_as_with_null_check = true:suggestion -csharp_style_inlined_variable_declaration = true:suggestion -csharp_style_throw_expression = true:suggestion -csharp_style_conditional_delegate_call = true:suggestion - -# Newline settings -csharp_new_line_before_open_brace = all -csharp_new_line_before_else = true -csharp_new_line_before_catch = true -csharp_new_line_before_finally = true -csharp_new_line_before_members_in_object_initializers = true -csharp_new_line_before_members_in_anonymous_types = true - -# Indentation preferences -csharp_indent_block_contents = true -csharp_indent_braces = false -csharp_indent_case_contents = true -csharp_indent_switch_labels = true -csharp_indent_labels = flush_left - -# Space preferences -csharp_space_after_cast = false -csharp_space_after_colon_in_inheritance_clause = true -csharp_space_after_comma = true -csharp_space_after_dot = false -csharp_space_after_keywords_in_control_flow_statements = true -csharp_space_after_semicolon_in_for_statement = true -csharp_space_around_binary_operators = before_and_after -csharp_space_around_declaration_statements = do_not_ignore -csharp_space_before_colon_in_inheritance_clause = true -csharp_space_before_comma = false -csharp_space_before_dot = false -csharp_space_before_open_square_brackets = false -csharp_space_before_semicolon_in_for_statement = false -csharp_space_between_empty_square_brackets = false -csharp_space_between_method_call_empty_parameter_list_parentheses = false -csharp_space_between_method_call_name_and_opening_parenthesis = false -csharp_space_between_method_call_parameter_list_parentheses = false -csharp_space_between_method_declaration_empty_parameter_list_parentheses = false -csharp_space_between_method_declaration_name_and_open_parenthesis = false -csharp_space_between_method_declaration_parameter_list_parentheses = false -csharp_space_between_parentheses = false -csharp_space_between_square_brackets = false From b5e439a4d481582c0f578496df5f4e3a6156a9c0 Mon Sep 17 00:00:00 2001 From: Bill Menees Date: Thu, 16 Jul 2020 10:19:14 -0500 Subject: [PATCH 3/3] Code review changes for SftpFileAttributes plus required test changes --- .../Classes/Sftp/SftpFileReaderTestBase.cs | 3 ++- ...est_Ctor_FileModeAppend_FileAccessWrite.cs | 4 ++-- ...eadBuffer_NewLengthGreatherThanPosition.cs | 6 +++--- ...aInReadBuffer_NewLengthLessThanPosition.cs | 4 ++-- ...iteBuffer_NewLengthGreatherThanPosition.cs | 4 ++-- ...InWriteBuffer_NewLengthLessThanPosition.cs | 4 ++-- ...tLength_SessionOpen_FIleAccessReadWrite.cs | 4 ++-- ...t_SetLength_SessionOpen_FIleAccessWrite.cs | 4 ++-- ...tGreatherThanTwoTimesTheWriteBufferSize.cs | 4 ++-- src/Renci.SshNet.Tests/Common/Extensions.cs | 4 ++-- .../Common/SftpFileAttributesBuilder.cs | 10 ++++++++-- src/Renci.SshNet/Sftp/SftpFileAttributes.cs | 20 ++++++++++++------- 12 files changed, 42 insertions(+), 29 deletions(-) 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/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/SftpFileAttributes.cs b/src/Renci.SshNet/Sftp/SftpFileAttributes.cs index 2a66bcd98..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 { @@ -433,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) { - LastAccessTimeUtc = _originalLastAccessTimeUtc = ToUniversalTime(lastAccessTime); - LastWriteTimeUtc = _originalLastWriteTimeUtc = ToUniversalTime(lastWriteTime); + LastAccessTimeUtc = _originalLastAccessTimeUtc = lastAccessTimeUtc; + LastWriteTimeUtc = _originalLastWriteTimeUtc = lastWriteTimeUtc; Size = _originalSize = size; UserId = _originalUserId = userId; GroupId = _originalGroupId = groupId; @@ -559,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 @@ -589,6 +590,11 @@ internal static SftpFileAttributes FromBytes(SshDataStream stream) time = stream.ReadUInt32(); 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 { @@ -613,7 +619,7 @@ internal static SftpFileAttributes FromBytes(byte[] buffer) } } - internal static DateTime ToLocalTime(DateTime value) + private static DateTime ToLocalTime(DateTime value) { DateTime result; @@ -629,7 +635,7 @@ internal static DateTime ToLocalTime(DateTime value) return result; } - internal static DateTime ToUniversalTime(DateTime value) + private static DateTime ToUniversalTime(DateTime value) { DateTime result;