Как анализировать данные EXIF ​​Date Time

Я пишу программу на C#, которая извлекает поле EXIF ​​DateTimeOriginal из файла JPEG, если это свойство содержится в данных, и мне нужно проанализировать его в 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. Почему они используют двоеточия?

РЕДАКТИРОВАТЬ

Я попытался изменить строку спецификатора формата на "гггг \: ММ \ дд чч \: мм:\ сс", но это тоже не сработало.

4 ответа

Решение

Вот фрагмент кода из старой программы, которая у меня лежит, которая делает что-то очень похожее на это:

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 году, и с тех пор я регулярно ее использую; это работает довольно надежно.

Благодаря Марку Симанну и Маркусу я наконец понял это. Время в данных EXIF ​​указано в 24 часовом / военном времени. Спецификатор формата "чч" в строке предназначен для 12-часового времени с AM/PM. Время прохождения было в 14:14 или 14:14. В 12 часов, "14" является недействительным временем.

Таким образом, правильный спецификатор формата - "гггг: ММ: дд ЧЧ: мм: сс".

Эта ссылка описывает способ, который анализирует отдельные части строки вместо ее анализа с использованием 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);
}

Я недавно переписал приложение, которое я упоминаю в моем другом ответе. На этот раз я написал это на 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
Другие вопросы по тегам