@@ -10,7 +10,7 @@ namespace CameraUtility.CameraFiles
10
10
/// <summary>
11
11
/// Jpeg (Android and Canon) or Cr2 raw Canon photo.
12
12
/// </summary>
13
- internal sealed class ImageFile
13
+ public sealed class ImageFile
14
14
: AbstractCameraFile , ICameraFile
15
15
{
16
16
/// <summary>
@@ -19,11 +19,11 @@ internal sealed class ImageFile
19
19
private const int DateTimeOriginalTagType = 0x9003 ;
20
20
21
21
/// <summary>
22
- /// Some older cameras don't use the 0x9003. We will try to read it from 0x0132 tag.
22
+ /// Some older cameras don't use the 0x9003. We will try to read it from 0x0132 tag (ModifyDate) .
23
23
/// </summary>
24
24
private const int FallbackDateTimeTagType = 0x0132 ;
25
25
26
- private const int SubSecondTagType = 0x9291 ;
26
+ private const int SubSecTimeOriginalTagType = 0x9291 ;
27
27
28
28
private ImageFile (
29
29
CameraFilePath fullName ,
@@ -36,7 +36,7 @@ private ImageFile(
36
36
public override DateTime Created { get ; }
37
37
public override string DestinationNamePrefix => "IMG_" ;
38
38
39
- internal static Result < ICameraFile > Create (
39
+ public static Result < ICameraFile > Create (
40
40
CameraFilePath fullName ,
41
41
IEnumerable < ITag > exifTags )
42
42
{
@@ -54,14 +54,13 @@ internal static Result<ICameraFile> Create(
54
54
55
55
return new ImageFile (
56
56
fullName ,
57
- parsedDateTimeResult . Value . AddMilliseconds ( FindSubSeconds ( enumeratedExifTags ) ) ) ;
57
+ parsedDateTimeResult . Value . Add ( FindSubSeconds ( enumeratedExifTags ) ) ) ;
58
58
}
59
59
60
60
private static Result < ITag > FindCreatedDateTimeTag (
61
61
IList < ITag > exifTags )
62
62
{
63
63
var tag = exifTags . FirstOrDefault ( t => t . Type == DateTimeOriginalTagType )
64
- /* Try fallback tag, if not found then an exception will be thrown */
65
64
?? exifTags . FirstOrDefault ( t => t . Type == FallbackDateTimeTagType ) ;
66
65
return
67
66
tag is not null
@@ -84,17 +83,28 @@ private static Result<DateTime> ParseCreatedDateTime(
84
83
return Result . Failure < DateTime > ( "Invalid metadata" ) ;
85
84
}
86
85
87
- private static int FindSubSeconds (
86
+ private static TimeSpan FindSubSeconds (
88
87
IEnumerable < ITag > exifTags )
89
88
{
90
- var subSeconds = exifTags . FirstOrDefault ( t => t . Type == SubSecondTagType ) ;
91
- return subSeconds is null ? 0 : ToMilliseconds ( int . Parse ( subSeconds . Value ) ) ;
89
+ var subSeconds = exifTags . FirstOrDefault ( t => t . Type == SubSecTimeOriginalTagType ) ;
90
+ return subSeconds is null ? TimeSpan . Zero : ToMilliseconds ( subSeconds . Value ) ;
92
91
}
93
92
94
- private static int ToMilliseconds (
95
- int subSeconds )
93
+ /// <summary>
94
+ /// EXIF specifies that SubSecOriginal tag contains "fractions" of a second. Depending on length of the
95
+ /// value a different fractional unit can be used, e.g. "042" is 42 milliseconds (0.042 of a second) but
96
+ /// "42" is 420 milliseconds (0.42 of a second).
97
+ /// </summary>
98
+ private static TimeSpan ToMilliseconds (
99
+ string subSeconds )
96
100
{
97
- return subSeconds * 10 ;
101
+ if ( int . TryParse ( subSeconds , out var tagIntValue ) is false )
102
+ {
103
+ return TimeSpan . Zero ;
104
+ }
105
+ var subSecondDenominator = Math . Pow ( 10 , subSeconds . Trim ( ) . Length ) ;
106
+ var millisecondMultiplier = 1000 / subSecondDenominator ;
107
+ return TimeSpan . FromMilliseconds ( tagIntValue * millisecondMultiplier ) ;
98
108
}
99
109
}
100
110
}
0 commit comments