Skip to content

SFTP file timestamps in UTC shifts when daylight saving offset changes #415

Closed
@stefer

Description

@stefer

We have built a file synchronization service where we rely on the timestamps of files that we save in a DB.
We use SFTPClient.LastWriteTimeUtc to persist the date and time.

After Sweden changed to summertime, we experienced that files where considered as changed because they had a new time when calling SFTPFile.LastWriteTimeUtc. Files that had persisted 2012-12-01T11:51Z now got 2012-12-01T12:51Z.

It might be a problem with the server, CoreFTP, but I found a potential issue in SSH.NET code as well.

A look in https://github.com/sshnet/SSH.NET/blob/develop/src/Renci.SshNet/Sftp/SftpFileAttributes.cs:

            if ((flag & 0x00000008) == 0x00000008)   //  SSH_FILEXFER_ATTR_ACMODTIME
            {
                var time = stream.ReadUInt32();
                accessTime = DateTime.FromFileTime((time + 11644473600) * 10000000);
                time = stream.ReadUInt32();
                modifyTime = DateTime.FromFileTime((time + 11644473600) * 10000000);
            }

According to the RFC, the times here are unix timestamp, in UTC. These are decoded directly into DateTimes with DateTimeKind.Local in the code above. The accessTime and modifyTime should be in local time, as I understand code elsewhere

According to this article this is how that should be done properly:

public static DateTime UnixTimeStampToDateTime( double unixTimeStamp )
{
    // Unix timestamp is seconds past epoch
    System.DateTime dtDateTime = new DateTime(1970,1,1,0,0,0,0,System.DateTimeKind.Utc);
    dtDateTime = dtDateTime.AddSeconds( unixTimeStamp ).ToLocalTime();
    return dtDateTime;
}

Note that it creates a DateTime with DateTimeKind.Utc first and then converts that to local time.
This difference might be the cause of my problems.
The article also mentions DateTimeOffset.FromUnixTimeMilliSeconds (source) which can be used together with DateTimeOffset.UtcdateTime to parse these dates.

I also had a look in the ScpClient.NET.cs where I found this code:

    var mtime = long.Parse(match.Result("${mtime}"));
    var atime = long.Parse(match.Result("${atime}"));

   var zeroTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
   modifiedTime = zeroTime.AddSeconds(mtime);
   accessedTime = zeroTime.AddSeconds(atime);

In this case, the times are represented as UTC and this should be correct, I think.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions