Как анализировать данные 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