< Summary

Information
Class: Renci.SshNet.Sftp.SftpFileAttributes
Assembly: Renci.SshNet
File(s): \home\appveyor\projects\ssh-net\src\Renci.SshNet\Sftp\SftpFileAttributes.cs
Line coverage
89%
Covered lines: 268
Uncovered lines: 33
Coverable lines: 301
Total lines: 684
Line coverage: 89%
Branch coverage
86%
Covered branches: 86
Total branches: 100
Branch coverage: 86%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

\home\appveyor\projects\ssh-net\src\Renci.SshNet\Sftp\SftpFileAttributes.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Globalization;
 4using System.Linq;
 5
 6using Renci.SshNet.Common;
 7
 8namespace Renci.SshNet.Sftp
 9{
 10    /// <summary>
 11    /// Contains SFTP file attributes.
 12    /// </summary>
 13    public class SftpFileAttributes
 14    {
 15#pragma warning disable IDE1006 // Naming Styles
 16#pragma warning disable SA1310 // Field names should not contain underscore
 17        private const uint S_IFMT = 0xF000; // bitmask for the file type bitfields
 18        private const uint S_IFSOCK = 0xC000; // socket
 19        private const uint S_IFLNK = 0xA000; // symbolic link
 20        private const uint S_IFREG = 0x8000; // regular file
 21        private const uint S_IFBLK = 0x6000; // block device
 22        private const uint S_IFDIR = 0x4000; // directory
 23        private const uint S_IFCHR = 0x2000; // character device
 24        private const uint S_IFIFO = 0x1000; // FIFO
 25        private const uint S_ISUID = 0x0800; // set UID bit
 26        private const uint S_ISGID = 0x0400; // set-group-ID bit (see below)
 27        private const uint S_ISVTX = 0x0200; // sticky bit (see below)
 28        private const uint S_IRUSR = 0x0100; // owner has read permission
 29        private const uint S_IWUSR = 0x0080; // owner has write permission
 30        private const uint S_IXUSR = 0x0040; // owner has execute permission
 31        private const uint S_IRGRP = 0x0020; // group has read permission
 32        private const uint S_IWGRP = 0x0010; // group has write permission
 33        private const uint S_IXGRP = 0x0008; // group has execute permission
 34        private const uint S_IROTH = 0x0004; // others have read permission
 35        private const uint S_IWOTH = 0x0002; // others have write permission
 36        private const uint S_IXOTH = 0x0001; // others have execute permission
 37#pragma warning restore SA1310 // Field names should not contain underscore
 38#pragma warning restore IDE1006 // Naming Styles
 39
 40        private readonly DateTime _originalLastAccessTimeUtc;
 41        private readonly DateTime _originalLastWriteTimeUtc;
 42        private readonly long _originalSize;
 43        private readonly int _originalUserId;
 44        private readonly int _originalGroupId;
 45        private readonly uint _originalPermissions;
 46        private readonly IDictionary<string, string> _originalExtensions;
 47
 48        private bool _isBitFiledsBitSet;
 49        private bool _isUIDBitSet;
 50        private bool _isGroupIDBitSet;
 51        private bool _isStickyBitSet;
 52
 53        internal bool IsLastAccessTimeChanged
 54        {
 6337255            get { return _originalLastAccessTimeUtc != LastAccessTimeUtc; }
 56        }
 57
 58        internal bool IsLastWriteTimeChanged
 59        {
 6298260            get { return _originalLastWriteTimeUtc != LastWriteTimeUtc; }
 61        }
 62
 63        internal bool IsSizeChanged
 64        {
 6337265            get { return _originalSize != Size; }
 66        }
 67
 68        internal bool IsUserIdChanged
 69        {
 6337270            get { return _originalUserId != UserId; }
 71        }
 72
 73        internal bool IsGroupIdChanged
 74        {
 6299475            get { return _originalGroupId != GroupId; }
 76        }
 77
 78        internal bool IsPermissionsChanged
 79        {
 6337280            get { return _originalPermissions != Permissions; }
 81        }
 82
 83        internal bool IsExtensionsChanged
 84        {
 6337285            get { return _originalExtensions != null && Extensions != null && !_originalExtensions.SequenceEqual(Extensi
 86        }
 87
 88        /// <summary>
 89        /// Gets or sets the local time the current file or directory was last accessed.
 90        /// </summary>
 91        /// <value>
 92        /// The local time that the current file or directory was last accessed.
 93        /// </value>
 94        public DateTime LastAccessTime
 95        {
 96            get
 2897            {
 2898                return ToLocalTime(LastAccessTimeUtc);
 2899            }
 100
 101            set
 4102            {
 4103                LastAccessTimeUtc = ToUniversalTime(value);
 4104            }
 105        }
 106
 107        /// <summary>
 108        /// Gets or sets the local time when the current file or directory was last written to.
 109        /// </summary>
 110        /// <value>
 111        /// The local time the current file was last written.
 112        /// </value>
 113        public DateTime LastWriteTime
 114        {
 115            get
 40116            {
 40117                return ToLocalTime(LastWriteTimeUtc);
 40118            }
 119
 120            set
 4121            {
 4122                LastWriteTimeUtc = ToUniversalTime(value);
 4123            }
 124        }
 125
 126        /// <summary>
 127        /// Gets or sets the UTC time the current file or directory was last accessed.
 128        /// </summary>
 129        /// <value>
 130        /// The UTC time that the current file or directory was last accessed.
 131        /// </value>
 43816132        public DateTime LastAccessTimeUtc { get; set; }
 133
 134        /// <summary>
 135        /// Gets or sets the UTC time when the current file or directory was last written to.
 136        /// </summary>
 137        /// <value>
 138        /// The UTC time the current file was last written.
 139        /// </value>
 43713140        public DateTime LastWriteTimeUtc { get; set; }
 141
 142        /// <summary>
 143        /// Gets or sets the size, in bytes, of the current file.
 144        /// </summary>
 145        /// <value>
 146        /// The size of the current file in bytes.
 147        /// </value>
 44279148        public long Size { get; set; }
 149
 150        /// <summary>
 151        /// Gets or sets file user id.
 152        /// </summary>
 153        /// <value>
 154        /// File user id.
 155        /// </value>
 43805156        public int UserId { get; set; }
 157
 158        /// <summary>
 159        /// Gets or sets file group id.
 160        /// </summary>
 161        /// <value>
 162        /// File group id.
 163        /// </value>
 43679164        public int GroupId { get; set; }
 165
 166        /// <summary>
 167        /// Gets a value indicating whether file represents a socket.
 168        /// </summary>
 169        /// <value>
 170        /// <see langword="true"/> if file represents a socket; otherwise, <see langword="false"/>.
 171        /// </value>
 43734172        public bool IsSocket { get; private set; }
 173
 174        /// <summary>
 175        /// Gets a value indicating whether file represents a symbolic link.
 176        /// </summary>
 177        /// <value>
 178        /// <see langword="true"/> if file represents a symbolic link; otherwise, <see langword="false"/>.
 179        /// </value>
 43734180        public bool IsSymbolicLink { get; private set; }
 181
 182        /// <summary>
 183        /// Gets a value indicating whether file represents a regular file.
 184        /// </summary>
 185        /// <value>
 186        /// <see langword="true"/> if file represents a regular file; otherwise, <see langword="false"/>.
 187        /// </value>
 43997188        public bool IsRegularFile { get; private set; }
 189
 190        /// <summary>
 191        /// Gets a value indicating whether file represents a block device.
 192        /// </summary>
 193        /// <value>
 194        /// <see langword="true"/> if file represents a block device; otherwise, <see langword="false"/>.
 195        /// </value>
 43734196        public bool IsBlockDevice { get; private set; }
 197
 198        /// <summary>
 199        /// Gets a value indicating whether file represents a directory.
 200        /// </summary>
 201        /// <value>
 202        /// <see langword="true"/> if file represents a directory; otherwise, <see langword="false"/>.
 203        /// </value>
 43781204        public bool IsDirectory { get; private set; }
 205
 206        /// <summary>
 207        /// Gets a value indicating whether file represents a character device.
 208        /// </summary>
 209        /// <value>
 210        /// <see langword="true"/> if file represents a character device; otherwise, <see langword="false"/>.
 211        /// </value>
 43734212        public bool IsCharacterDevice { get; private set; }
 213
 214        /// <summary>
 215        /// Gets a value indicating whether file represents a named pipe.
 216        /// </summary>
 217        /// <value>
 218        /// <see langword="true"/> if file represents a named pipe; otherwise, <see langword="false"/>.
 219        /// </value>
 43734220        public bool IsNamedPipe { get; private set; }
 221
 222        /// <summary>
 223        /// Gets or sets a value indicating whether the owner can read from this file.
 224        /// </summary>
 225        /// <value>
 226        /// <see langword="true"/> if owner can read from this file; otherwise, <see langword="false"/>.
 227        /// </value>
 43735228        public bool OwnerCanRead { get; set; }
 229
 230        /// <summary>
 231        /// Gets or sets a value indicating whether the owner can write into this file.
 232        /// </summary>
 233        /// <value>
 234        /// <see langword="true"/> if owner can write into this file; otherwise, <see langword="false"/>.
 235        /// </value>
 43735236        public bool OwnerCanWrite { get; set; }
 237
 238        /// <summary>
 239        /// Gets or sets a value indicating whether the owner can execute this file.
 240        /// </summary>
 241        /// <value>
 242        /// <see langword="true"/> if owner can execute this file; otherwise, <see langword="false"/>.
 243        /// </value>
 43735244        public bool OwnerCanExecute { get; set; }
 245
 246        /// <summary>
 247        /// Gets or sets a value indicating whether the group members can read from this file.
 248        /// </summary>
 249        /// <value>
 250        /// <see langword="true"/> if group members can read from this file; otherwise, <see langword="false"/>.
 251        /// </value>
 43735252        public bool GroupCanRead { get; set; }
 253
 254        /// <summary>
 255        /// Gets or sets a value indicating whether the group members can write into this file.
 256        /// </summary>
 257        /// <value>
 258        /// <see langword="true"/> if group members can write into this file; otherwise, <see langword="false"/>.
 259        /// </value>
 43735260        public bool GroupCanWrite { get; set; }
 261
 262        /// <summary>
 263        /// Gets or sets a value indicating whether the group members can execute this file.
 264        /// </summary>
 265        /// <value>
 266        /// <see langword="true"/> if group members can execute this file; otherwise, <see langword="false"/>.
 267        /// </value>
 43735268        public bool GroupCanExecute { get; set; }
 269
 270        /// <summary>
 271        /// Gets or sets a value indicating whether the others can read from this file.
 272        /// </summary>
 273        /// <value>
 274        /// <see langword="true"/> if others can read from this file; otherwise, <see langword="false"/>.
 275        /// </value>
 43735276        public bool OthersCanRead { get; set; }
 277
 278        /// <summary>
 279        /// Gets or sets a value indicating whether the others can write into this file.
 280        /// </summary>
 281        /// <value>
 282        /// <see langword="true"/> if others can write into this file; otherwise, <see langword="false"/>.
 283        /// </value>
 43735284        public bool OthersCanWrite { get; set; }
 285
 286        /// <summary>
 287        /// Gets or sets a value indicating whether the others can execute this file.
 288        /// </summary>
 289        /// <value>
 290        /// <see langword="true"/> if others can execute this file; otherwise, <see langword="false"/>.
 291        /// </value>
 43735292        public bool OthersCanExecute { get; set; }
 293
 294        /// <summary>
 295        /// Gets the extensions.
 296        /// </summary>
 297        /// <value>
 298        /// The extensions.
 299        /// </value>
 22873300        public IDictionary<string, string> Extensions { get; private set; }
 301
 302        internal uint Permissions
 303        {
 304            get
 21281305            {
 21281306                uint permission = 0;
 307
 21281308                if (_isBitFiledsBitSet)
 6309                {
 6310                    permission |= S_IFMT;
 6311                }
 312
 21281313                if (IsSocket)
 38314                {
 38315                    permission |= S_IFSOCK;
 38316                }
 317
 21281318                if (IsSymbolicLink)
 44319                {
 44320                    permission |= S_IFLNK;
 44321                }
 322
 21281323                if (IsRegularFile)
 89324                {
 89325                    permission |= S_IFREG;
 89326                }
 327
 21281328                if (IsBlockDevice)
 48329                {
 48330                    permission |= S_IFBLK;
 48331                }
 332
 21281333                if (IsDirectory)
 84334                {
 84335                    permission |= S_IFDIR;
 84336                }
 337
 21281338                if (IsCharacterDevice)
 100339                {
 100340                    permission |= S_IFCHR;
 100341                }
 342
 21281343                if (IsNamedPipe)
 74344                {
 74345                    permission |= S_IFIFO;
 74346                }
 347
 21281348                if (_isUIDBitSet)
 82349                {
 82350                    permission |= S_ISUID;
 82351                }
 352
 21281353                if (_isGroupIDBitSet)
 72354                {
 72355                    permission |= S_ISGID;
 72356                }
 357
 21281358                if (_isStickyBitSet)
 80359                {
 80360                    permission |= S_ISVTX;
 80361                }
 362
 21281363                if (OwnerCanRead)
 93364                {
 93365                    permission |= S_IRUSR;
 93366                }
 367
 21281368                if (OwnerCanWrite)
 102369                {
 102370                    permission |= S_IWUSR;
 102371                }
 372
 21281373                if (OwnerCanExecute)
 85374                {
 85375                    permission |= S_IXUSR;
 85376                }
 377
 21281378                if (GroupCanRead)
 102379                {
 102380                    permission |= S_IRGRP;
 102381                }
 382
 21281383                if (GroupCanWrite)
 82384                {
 82385                    permission |= S_IWGRP;
 82386                }
 387
 21281388                if (GroupCanExecute)
 79389                {
 79390                    permission |= S_IXGRP;
 79391                }
 392
 21281393                if (OthersCanRead)
 116394                {
 116395                    permission |= S_IROTH;
 116396                }
 397
 21281398                if (OthersCanWrite)
 66399                {
 66400                    permission |= S_IWOTH;
 66401                }
 402
 21281403                if (OthersCanExecute)
 73404                {
 73405                    permission |= S_IXOTH;
 73406                }
 407
 21281408                return permission;
 21281409            }
 410            private set
 22453411            {
 22453412                _isBitFiledsBitSet = (value & S_IFMT) == S_IFMT;
 413
 22453414                IsSocket = (value & S_IFSOCK) == S_IFSOCK;
 415
 22453416                IsSymbolicLink = (value & S_IFLNK) == S_IFLNK;
 417
 22453418                IsRegularFile = (value & S_IFREG) == S_IFREG;
 419
 22453420                IsBlockDevice = (value & S_IFBLK) == S_IFBLK;
 421
 22453422                IsDirectory = (value & S_IFDIR) == S_IFDIR;
 423
 22453424                IsCharacterDevice = (value & S_IFCHR) == S_IFCHR;
 425
 22453426                IsNamedPipe = (value & S_IFIFO) == S_IFIFO;
 427
 22453428                _isUIDBitSet = (value & S_ISUID) == S_ISUID;
 429
 22453430                _isGroupIDBitSet = (value & S_ISGID) == S_ISGID;
 431
 22453432                _isStickyBitSet = (value & S_ISVTX) == S_ISVTX;
 433
 22453434                OwnerCanRead = (value & S_IRUSR) == S_IRUSR;
 435
 22453436                OwnerCanWrite = (value & S_IWUSR) == S_IWUSR;
 437
 22453438                OwnerCanExecute = (value & S_IXUSR) == S_IXUSR;
 439
 22453440                GroupCanRead = (value & S_IRGRP) == S_IRGRP;
 441
 22453442                GroupCanWrite = (value & S_IWGRP) == S_IWGRP;
 443
 22453444                GroupCanExecute = (value & S_IXGRP) == S_IXGRP;
 445
 22453446                OthersCanRead = (value & S_IROTH) == S_IROTH;
 447
 22453448                OthersCanWrite = (value & S_IWOTH) == S_IWOTH;
 449
 22453450                OthersCanExecute = (value & S_IXOTH) == S_IXOTH;
 22453451            }
 452        }
 453
 4454        private SftpFileAttributes()
 4455        {
 4456        }
 457
 22453458        internal SftpFileAttributes(DateTime lastAccessTimeUtc, DateTime lastWriteTimeUtc, long size, int userId, int gr
 22453459        {
 22453460            LastAccessTimeUtc = _originalLastAccessTimeUtc = lastAccessTimeUtc;
 22453461            LastWriteTimeUtc = _originalLastWriteTimeUtc = lastWriteTimeUtc;
 22453462            Size = _originalSize = size;
 22453463            UserId = _originalUserId = userId;
 22453464            GroupId = _originalGroupId = groupId;
 22453465            Permissions = _originalPermissions = permissions;
 22453466            Extensions = _originalExtensions = extensions;
 22453467        }
 468
 469        /// <summary>
 470        /// Sets the permissions.
 471        /// </summary>
 472        /// <param name="mode">The mode.</param>
 473        public void SetPermissions(short mode)
 1474        {
 1475            if (mode is < 0 or > 999)
 0476            {
 0477                throw new ArgumentOutOfRangeException(nameof(mode));
 478            }
 479
 1480            var modeBytes = mode.ToString(CultureInfo.InvariantCulture).PadLeft(3, '0').ToCharArray();
 481
 1482            var permission = ((modeBytes[0] & 0x0F) * 8 * 8) + ((modeBytes[1] & 0x0F) * 8) + (modeBytes[2] & 0x0F);
 483
 1484            OwnerCanRead = (permission & S_IRUSR) == S_IRUSR;
 1485            OwnerCanWrite = (permission & S_IWUSR) == S_IWUSR;
 1486            OwnerCanExecute = (permission & S_IXUSR) == S_IXUSR;
 487
 1488            GroupCanRead = (permission & S_IRGRP) == S_IRGRP;
 1489            GroupCanWrite = (permission & S_IWGRP) == S_IWGRP;
 1490            GroupCanExecute = (permission & S_IXGRP) == S_IXGRP;
 491
 1492            OthersCanRead = (permission & S_IROTH) == S_IROTH;
 1493            OthersCanWrite = (permission & S_IWOTH) == S_IWOTH;
 1494            OthersCanExecute = (permission & S_IXOTH) == S_IXOTH;
 1495        }
 496
 497        /// <summary>
 498        /// Returns a byte array representing the current <see cref="SftpFileAttributes"/>.
 499        /// </summary>
 500        /// <returns>
 501        /// A byte array representing the current <see cref="SftpFileAttributes"/>.
 502        /// </returns>
 503        public byte[] GetBytes()
 10562504        {
 10562505            using (var stream = new SshDataStream(4))
 10562506            {
 10562507                uint flag = 0;
 508
 10562509                if (IsSizeChanged && IsRegularFile)
 4510                {
 4511                    flag |= 0x00000001;
 4512                }
 513
 10562514                if (IsUserIdChanged || IsGroupIdChanged)
 63515                {
 63516                    flag |= 0x00000002;
 63517                }
 518
 10562519                if (IsPermissionsChanged)
 1520                {
 1521                    flag |= 0x00000004;
 1522                }
 523
 10562524                if (IsLastAccessTimeChanged || IsLastWriteTimeChanged)
 67525                {
 67526                    flag |= 0x00000008;
 67527                }
 528
 10562529                if (IsExtensionsChanged)
 0530                {
 0531                    flag |= 0x80000000;
 0532                }
 533
 10562534                stream.Write(flag);
 535
 10562536                if (IsSizeChanged && IsRegularFile)
 4537                {
 4538                    stream.Write((ulong) Size);
 4539                }
 540
 10562541                if (IsUserIdChanged || IsGroupIdChanged)
 63542                {
 63543                    stream.Write((uint) UserId);
 63544                    stream.Write((uint) GroupId);
 63545                }
 546
 10562547                if (IsPermissionsChanged)
 1548                {
 1549                    stream.Write(Permissions);
 1550                }
 551
 10562552                if (IsLastAccessTimeChanged || IsLastWriteTimeChanged)
 67553                {
 67554                    var time = (uint) ((LastAccessTimeUtc.ToFileTimeUtc() / 10000000) - 11644473600);
 67555                    stream.Write(time);
 67556                    time = (uint) ((LastWriteTimeUtc.ToFileTimeUtc() / 10000000) - 11644473600);
 67557                    stream.Write(time);
 67558                }
 559
 10562560                if (IsExtensionsChanged)
 0561                {
 0562                    foreach (var item in Extensions)
 0563                    {
 564                        /*
 565                         * TODO: we write as ASCII but read as UTF8 !!!
 566                         */
 567
 0568                        stream.Write(item.Key, SshData.Ascii);
 0569                        stream.Write(item.Value, SshData.Ascii);
 0570                    }
 0571                }
 572
 10562573                return stream.ToArray();
 574            }
 10562575        }
 576
 4577        internal static readonly SftpFileAttributes Empty = new SftpFileAttributes();
 578
 579        internal static SftpFileAttributes FromBytes(SshDataStream stream)
 22150580        {
 581            const uint SSH_FILEXFER_ATTR_SIZE = 0x00000001;
 582            const uint SSH_FILEXFER_ATTR_UIDGID = 0x00000002;
 583            const uint SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004;
 584            const uint SSH_FILEXFER_ATTR_ACMODTIME = 0x00000008;
 585            const uint SSH_FILEXFER_ATTR_EXTENDED = 0x80000000;
 586
 22150587            var flag = stream.ReadUInt32();
 588
 22150589            long size = -1;
 22150590            var userId = -1;
 22150591            var groupId = -1;
 22150592            uint permissions = 0;
 593            DateTime accessTime;
 594            DateTime modifyTime;
 22150595            Dictionary<string, string> extensions = null;
 596
 22150597            if ((flag & SSH_FILEXFER_ATTR_SIZE) == SSH_FILEXFER_ATTR_SIZE)
 10628598            {
 10628599                size = (long) stream.ReadUInt64();
 10628600            }
 601
 22150602            if ((flag & SSH_FILEXFER_ATTR_UIDGID) == SSH_FILEXFER_ATTR_UIDGID)
 10631603            {
 10631604                userId = (int) stream.ReadUInt32();
 605
 10631606                groupId = (int) stream.ReadUInt32();
 10631607            }
 608
 22150609            if ((flag & SSH_FILEXFER_ATTR_PERMISSIONS) == SSH_FILEXFER_ATTR_PERMISSIONS)
 10628610            {
 10628611                permissions = stream.ReadUInt32();
 10628612            }
 613
 22150614            if ((flag & SSH_FILEXFER_ATTR_ACMODTIME) == SSH_FILEXFER_ATTR_ACMODTIME)
 10631615            {
 616                // The incoming times are "Unix times", so they're already in UTC.  We need to preserve that
 617                // to avoid losing information in a local time conversion during the "fall back" hour in DST.
 10631618                var time = stream.ReadUInt32();
 10631619                accessTime = DateTime.FromFileTimeUtc((time + 11644473600) * 10000000);
 10631620                time = stream.ReadUInt32();
 10631621                modifyTime = DateTime.FromFileTimeUtc((time + 11644473600) * 10000000);
 10631622            }
 623            else
 11519624            {
 11519625                accessTime = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc);
 11519626                modifyTime = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc);
 11519627            }
 628
 22150629            if ((flag & SSH_FILEXFER_ATTR_EXTENDED) == SSH_FILEXFER_ATTR_EXTENDED)
 0630            {
 0631                var extendedCount = (int) stream.ReadUInt32();
 0632                extensions = new Dictionary<string, string>(extendedCount);
 0633                for (var i = 0; i < extendedCount; i++)
 0634                {
 0635                    var extensionName = stream.ReadString(SshData.Utf8);
 0636                    var extensionData = stream.ReadString(SshData.Utf8);
 0637                    extensions.Add(extensionName, extensionData);
 0638                }
 0639            }
 640
 22150641            return new SftpFileAttributes(accessTime, modifyTime, size, userId, groupId, permissions, extensions);
 22150642        }
 643
 644        internal static SftpFileAttributes FromBytes(byte[] buffer)
 0645        {
 0646            using (var stream = new SshDataStream(buffer))
 0647            {
 0648                return FromBytes(stream);
 649            }
 0650        }
 651
 652        private static DateTime ToLocalTime(DateTime value)
 68653        {
 654            DateTime result;
 655
 68656            if (value == DateTime.MinValue)
 0657            {
 0658                result = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Local);
 0659            }
 660            else
 68661            {
 68662                result = value.ToLocalTime();
 68663            }
 664
 68665            return result;
 68666        }
 667
 668        private static DateTime ToUniversalTime(DateTime value)
 8669        {
 670            DateTime result;
 671
 8672            if (value == DateTime.MinValue)
 0673            {
 0674                result = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc);
 0675            }
 676            else
 8677            {
 8678                result = value.ToUniversalTime();
 8679            }
 680
 8681            return result;
 8682        }
 683    }
 684}