如何解析EXIF日期时间数据

9

我正在编写一个C#程序,从JPEG文件中提取EXIF的DateTimeOriginal字段,如果该属性在数据中存在,并且我需要将其解析为DateTime值。

我的代码如下:

BitmapFrame src = decoder.Frames[ 0 ];

if ( src.Metadata != null ) {
    BitmapMetadata metaData = (BitmapMetadata) src.Metadata;

    if ( metaData.ContainsQuery( "/app1/{ushort=0}/{ushort=34665}/{ushort=36867}" ) ) {
        object o = metaData.GetQuery( "/app1/{ushort=0}/{ushort=34665}/{ushort=36867}" );

        if ( o != null && o is string ) {
            string originalDate = Convert.ToString( o );

            if ( originalDate != null ) {
                if ( !DateTime.TryParseExact( originalDate.Trim(), "yyyy:MM:dd hh:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out createDate ) ) {
                    // Sets createDate to a default value if the date doesn't parse.
                }
            }
        }
    }
}

然而,在调用 TryParseExact 时,格式字符串无法正常工作,因为代码执行了被注释掉的代码。
正确的写法是什么?DateTimeOriginal 属性中的值格式为 YYYY:MM:DD HH:MM:SS。在 YYYY、MM 和 DD 指示符之间的冒号让我很困惑。为什么要使用冒号? 编辑 我尝试将格式说明符字符串更改为 "yyyy\:MM\dd hh\:mm:\ss",但这也没有起作用。
4个回答

14

这是一个来自我电脑里的一个程序的代码片段,它做的事情与此非常相似:

string dateTakenText;
using (Image photo = Image.FromFile(file.Name))
{
    PropertyItem pi = photo.GetPropertyItem(Program.propertyTagExifDTOrig_);
    ASCIIEncoding enc = new ASCIIEncoding();
    dateTakenText = enc.GetString(pi.Value, 0, pi.Len - 1);
}
if (string.IsNullOrEmpty(dateTakenText))
{
    continue;
}
DateTime dateTaken;
if (!DateTime.TryParseExact(dateTakenText, "yyyy:MM:dd HH:mm:ss",
    CultureInfo.CurrentCulture, DateTimeStyles.None, out dateTaken))
{
    continue;
}

这段代码片段位于一个foreach循环内部,它解释了continue关键字的使用。

这是我在2002或2003年写的程序代码,自那以后我一直在经常使用它,它的运行相当可靠。


1
但是,嘿,var的男人,我的眼睛(和一个Encoding.ASCII):D计算机历史博物馆的东西 :) - Ruben Bartelink

3
感谢 Mark Seemann 和 Markus 的帮助,我终于明白了。EXIF 数据中的时间使用24小时制/军事时间表示。字符串中的“hh”格式说明符用于12小时制,包括上午/下午标识。我传递的时间是下午2:14,或者是24小时制的14:14。在12小时制中,“14”是无效的时间。
因此,正确的格式说明符应该是“yyyy:MM:dd HH:mm:ss”。

2

我最近重写了我在另一个回答中提到的应用程序,这次我用F#编写了它。负责工作的相关部分如下:

[<Literal>]
let private exifDateTaken = 0x0132

[<Literal>]
let private exifDateTimeOriginal = 0x9003

let private tryParseDate s =
    let res =
        DateTime.TryParseExact(
            s,
            "yyyy:MM:dd HH:mm:ss",
            CultureInfo.InvariantCulture,
            DateTimeStyles.None)
    match res with
    | true, dt -> Some dt
    | _ -> None

let extractDateTaken (fi : FileInfo) =
    let extractExif (img : Image) exif =
        if img.PropertyIdList |> Array.contains exif
        then
            let pi = img.GetPropertyItem exif
            Some (Encoding.ASCII.GetString(pi.Value, 0, pi.Len - 1))
        else None

    use photo = Image.FromFile fi.FullName

    [ exifDateTimeOriginal; exifDateTaken ]
    |> Seq.choose (extractExif photo)
    |> Seq.tryHead
    |> Option.bind tryParseDate

2

这个链接描述了一种解析字符串的方法,而不是使用DateTime.Parse进行解析:

/// <summary>
/// Returns the EXIF Image Data of the Date Taken.
/// </summary>
/// <param name="getImage">Image (If based on a file use Image.FromFile(f);)</param>
/// <returns>Date Taken or Null if Unavailable</returns>
public static DateTime? DateTaken(Image getImage)
{
    int DateTakenValue = 0x9003; //36867;

    if (!getImage.PropertyIdList.Contains(DateTakenValue))
        return null;

    string dateTakenTag = System.Text.Encoding.ASCII.GetString(getImage.GetPropertyItem(DateTakenValue).Value);
    string[] parts = dateTakenTag.Split(':', ' ');
    int year = int.Parse(parts[0]);
    int month = int.Parse(parts[1]);
    int day = int.Parse(parts[2]);
    int hour = int.Parse(parts[3]);
    int minute = int.Parse(parts[4]);
    int second = int.Parse(parts[5]);

    return new DateTime(year, month, day, hour, minute, second);
}

1
请注意,要使用 PropertyIdList.Contains(),您必须包含 using System.Linq - Doug S

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接