Skip to content

Commit ed57cfd

Browse files
authored
Merge pull request #803 from DannyBoyk/802_Add_UnixTimeExtraField_Support_Zips
Add support for the UnixTimeExtraField in Zip files
2 parents 396717e + d69559e commit ed57cfd

File tree

3 files changed

+142
-2
lines changed

3 files changed

+142
-2
lines changed

src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs

+30
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,36 @@ internal override void Read(BinaryReader reader)
8585
RelativeOffsetOfEntryHeader = zip64ExtraData.RelativeOffsetOfEntryHeader;
8686
}
8787
}
88+
89+
var unixTimeExtra = Extra.FirstOrDefault(u => u.Type == ExtraDataType.UnixTimeExtraField);
90+
91+
if (unixTimeExtra is not null)
92+
{
93+
// Tuple order is last modified time, last access time, and creation time.
94+
var unixTimeTuple = ((UnixTimeExtraField)unixTimeExtra).UnicodeTimes;
95+
96+
if (unixTimeTuple.Item1.HasValue)
97+
{
98+
var dosTime = Utility.DateTimeToDosTime(unixTimeTuple.Item1.Value);
99+
100+
LastModifiedDate = (ushort)(dosTime >> 16);
101+
LastModifiedTime = (ushort)(dosTime & 0x0FFFF);
102+
}
103+
else if (unixTimeTuple.Item2.HasValue)
104+
{
105+
var dosTime = Utility.DateTimeToDosTime(unixTimeTuple.Item2.Value);
106+
107+
LastModifiedDate = (ushort)(dosTime >> 16);
108+
LastModifiedTime = (ushort)(dosTime & 0x0FFFF);
109+
}
110+
else if (unixTimeTuple.Item3.HasValue)
111+
{
112+
var dosTime = Utility.DateTimeToDosTime(unixTimeTuple.Item3.Value);
113+
114+
LastModifiedDate = (ushort)(dosTime >> 16);
115+
LastModifiedTime = (ushort)(dosTime & 0x0FFFF);
116+
}
117+
}
88118
}
89119

90120
internal ushort Version { get; private set; }

src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs

+30
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,36 @@ internal override void Read(BinaryReader reader)
6464
UncompressedSize = zip64ExtraData.UncompressedSize;
6565
}
6666
}
67+
68+
var unixTimeExtra = Extra.FirstOrDefault(u => u.Type == ExtraDataType.UnixTimeExtraField);
69+
70+
if (unixTimeExtra is not null)
71+
{
72+
// Tuple order is last modified time, last access time, and creation time.
73+
var unixTimeTuple = ((UnixTimeExtraField)unixTimeExtra).UnicodeTimes;
74+
75+
if (unixTimeTuple.Item1.HasValue)
76+
{
77+
var dosTime = Utility.DateTimeToDosTime(unixTimeTuple.Item1.Value);
78+
79+
LastModifiedDate = (ushort)(dosTime >> 16);
80+
LastModifiedTime = (ushort)(dosTime & 0x0FFFF);
81+
}
82+
else if (unixTimeTuple.Item2.HasValue)
83+
{
84+
var dosTime = Utility.DateTimeToDosTime(unixTimeTuple.Item2.Value);
85+
86+
LastModifiedDate = (ushort)(dosTime >> 16);
87+
LastModifiedTime = (ushort)(dosTime & 0x0FFFF);
88+
}
89+
else if (unixTimeTuple.Item3.HasValue)
90+
{
91+
var dosTime = Utility.DateTimeToDosTime(unixTimeTuple.Item3.Value);
92+
93+
LastModifiedDate = (ushort)(dosTime >> 16);
94+
LastModifiedTime = (ushort)(dosTime & 0x0FFFF);
95+
}
96+
}
6797
}
6898

6999
internal ushort Version { get; private set; }

src/SharpCompress/Common/Zip/Headers/LocalEntryHeaderExtraFactory.cs

+82-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Buffers.Binary;
33
using System.Text;
44

@@ -13,7 +13,8 @@ internal enum ExtraDataType : ushort
1313
// Third Party Mappings
1414
// -Info-ZIP Unicode Path Extra Field
1515
UnicodePathExtraField = 0x7075,
16-
Zip64ExtendedInformationExtraField = 0x0001
16+
Zip64ExtendedInformationExtraField = 0x0001,
17+
UnixTimeExtraField = 0x5455
1718
}
1819

1920
internal class ExtraData
@@ -145,6 +146,84 @@ ushort diskNumber
145146
public uint VolumeNumber { get; private set; }
146147
}
147148

149+
internal sealed class UnixTimeExtraField : ExtraData
150+
{
151+
public UnixTimeExtraField(ExtraDataType type, ushort length, byte[] dataBytes)
152+
: base(type, length, dataBytes) { }
153+
154+
/// <summary>
155+
/// The unix modified time, last access time, and creation time, if set.
156+
/// </summary>
157+
/// <remarks>Must return Tuple explicitly due to net462 support.</remarks>
158+
internal Tuple<DateTime?, DateTime?, DateTime?> UnicodeTimes
159+
{
160+
get
161+
{
162+
// There has to be at least 5 byte for there to be a timestamp.
163+
// 1 byte for flags and 4 bytes for a timestamp.
164+
if (DataBytes is null || DataBytes.Length < 5)
165+
{
166+
return Tuple.Create<DateTime?, DateTime?, DateTime?>(null, null, null);
167+
}
168+
169+
var flags = DataBytes[0];
170+
var isModifiedTimeSpecified = (flags & 0x01) == 1;
171+
var isLastAccessTimeSpecified = (flags & 0x02) == 1;
172+
var isCreationTimeSpecified = (flags & 0x04) == 1;
173+
var currentIndex = 1;
174+
DateTime? modifiedTime = null;
175+
DateTime? lastAccessTime = null;
176+
DateTime? creationTime = null;
177+
178+
if (isModifiedTimeSpecified)
179+
{
180+
var modifiedEpochTime = BinaryPrimitives.ReadInt32LittleEndian(
181+
DataBytes.AsSpan(currentIndex, 4)
182+
);
183+
184+
currentIndex += 4;
185+
modifiedTime = DateTimeOffset.FromUnixTimeSeconds(modifiedEpochTime).UtcDateTime;
186+
}
187+
188+
if (isLastAccessTimeSpecified)
189+
{
190+
if (currentIndex + 4 > DataBytes.Length)
191+
{
192+
throw new ArchiveException("Invalid UnicodeExtraTime field");
193+
}
194+
195+
var lastAccessEpochTime = BinaryPrimitives.ReadInt32LittleEndian(
196+
DataBytes.AsSpan(currentIndex, 4)
197+
);
198+
199+
currentIndex += 4;
200+
lastAccessTime = DateTimeOffset
201+
.FromUnixTimeSeconds(lastAccessEpochTime)
202+
.UtcDateTime;
203+
}
204+
205+
if (isCreationTimeSpecified)
206+
{
207+
if (currentIndex + 4 > DataBytes.Length)
208+
{
209+
throw new ArchiveException("Invalid UnicodeExtraTime field");
210+
}
211+
212+
var creationTimeEpochTime = BinaryPrimitives.ReadInt32LittleEndian(
213+
DataBytes.AsSpan(currentIndex, 4)
214+
);
215+
216+
currentIndex += 4;
217+
creationTime = DateTimeOffset
218+
.FromUnixTimeSeconds(creationTimeEpochTime)
219+
.UtcDateTime;
220+
}
221+
222+
return Tuple.Create(modifiedTime, lastAccessTime, creationTime);
223+
}
224+
}
225+
}
226+
148227
internal static class LocalEntryHeaderExtraFactory
149228
{
150229
internal static ExtraData Create(ExtraDataType type, ushort length, byte[] extraData) =>
@@ -154,6 +233,7 @@ internal static ExtraData Create(ExtraDataType type, ushort length, byte[] extra
154233
=> new ExtraUnicodePathExtraField(type, length, extraData),
155234
ExtraDataType.Zip64ExtendedInformationExtraField
156235
=> new Zip64ExtendedInformationExtraField(type, length, extraData),
236+
ExtraDataType.UnixTimeExtraField => new UnixTimeExtraField(type, length, extraData),
157237
_ => new ExtraData(type, length, extraData)
158238
};
159239
}

0 commit comments

Comments
 (0)