1
- using System ;
1
+ using System ;
2
2
using System . Buffers . Binary ;
3
3
using System . Text ;
4
4
@@ -13,7 +13,8 @@ internal enum ExtraDataType : ushort
13
13
// Third Party Mappings
14
14
// -Info-ZIP Unicode Path Extra Field
15
15
UnicodePathExtraField = 0x7075 ,
16
- Zip64ExtendedInformationExtraField = 0x0001
16
+ Zip64ExtendedInformationExtraField = 0x0001 ,
17
+ UnixTimeExtraField = 0x5455
17
18
}
18
19
19
20
internal class ExtraData
@@ -145,6 +146,84 @@ ushort diskNumber
145
146
public uint VolumeNumber { get ; private set ; }
146
147
}
147
148
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
+
148
227
internal static class LocalEntryHeaderExtraFactory
149
228
{
150
229
internal static ExtraData Create ( ExtraDataType type , ushort length , byte [ ] extraData ) =>
@@ -154,6 +233,7 @@ internal static ExtraData Create(ExtraDataType type, ushort length, byte[] extra
154
233
=> new ExtraUnicodePathExtraField ( type , length , extraData ) ,
155
234
ExtraDataType . Zip64ExtendedInformationExtraField
156
235
=> new Zip64ExtendedInformationExtraField ( type , length , extraData ) ,
236
+ ExtraDataType . UnixTimeExtraField => new UnixTimeExtraField ( type , length , extraData ) ,
157
237
_ => new ExtraData ( type , length , extraData )
158
238
} ;
159
239
}
0 commit comments