Android Get Photo Taken Date

Apr 3, 2020
MediaStore.MediaColumns.DATE_TAKEN, ExifInterface.TAG_DATETIME_ORIGINAL or Filename Date

You can list albums on your Android phone, and you should access image files via scoped storage.

To find photos on Android device, refer to Android Find/Search Photos/Images in Album.

Code to find photos

fun findImagesInAlbum(albumId: String) {    val contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI    val projections = arrayOf(        MediaStore.Images.ImageColumns._ID,        MediaStore.Images.ImageColumns.DATE_TAKEN,        MediaStore.Images.ImageColumns.DISPLAY_NAME,    )    val selection = "${MediaStore.Images.ImageColumns.BUCKET_ID} == ?"    val selectionArgs = arrayOf(        albumId    )    contentResolver.query(contentUri, projections, selection, selectionArgs, "${MediaStore.Images.ImageColumns.DATE_TAKEN} ASC")?.use { cursor ->        if (cursor.moveToFirst()) {            val idIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns._ID)            val dateTakenIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.DATE_TAKEN)            val displayNameIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.DISPLAY_NAME)            do {                val mediaId = cursor.getLong(idIndex)                val filename = cursor.getString(displayNameIndex)                val millis = cursor.getLong(dateTakenIndex)                // scoped storage - access file via uri instead of filepath + filename                var uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mediaId)                // read file                contentResolver.openInputStream(uri).use { stream ->                    val exif = ExifInterface(stream)                })            }        }    }}

Option 1: MediaStore.MediaColumns.DATE_TAKEN

You can get DATE_TAKEN which is in epoch milli seconds.

Indexed value of MediaMetadataRetriever#METADATA_KEY_DATE or ExifInterface#TAG_DATETIME_ORIGINAL extracted from this media item.

Note that images must define both ExifInterface#TAG_DATETIME_ORIGINAL and ExifInterface#TAG_OFFSET_TIME_ORIGINAL to reliably determine this value in relation to the epoch. Value is a non-negative timestamp measured as the number of milliseconds since 1970-01-01T00:00:00Z.

NOTE: DATE_TAKEN might be miscalculated if some photos have ExifInterface#TAG_OFFSET_TIME_ORIGINAL or GPS info, while others photos doesn't have such info.

val millis = cursor.getLong(dateTakenIndex)// convert epoch millis to device timezone dateval date = LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.systemDefault())// convert epoch millis to UTC dateval dateUtc = LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneOffset.UTC)

NOTE: If you need to convert millis to DateTime, you need to specify a timezone. Using UTC would gain the most persistence result, or you can guess the timezone of the photo.

Option 2: ExifInterface.TAG_DATETIME_ORIGINAL

Read TAG_DATETIME_ORIGINAL

The date and time when the original image data was generated. For a DSC the date and time the picture was taken are recorded. The format is "YYYY:MM:DD HH:MM:SS" with time shown in 24-hour format

// scoped storage - access file via uri instead of filepath + filenamevar uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mediaId)// read filevar exifDate: LocalDateTime? = nullcontentResolver.openInputStream(uri)?.use { stream ->    val exif = ExifInterface(stream)    val exifDateFormatter = DateTimeFormatter.ofPattern("yyyy:MM:dd HH:mm:ss")    var exifDateString = exif.getAttribute(ExifInterface.TAG_DATETIME_ORIGINAL)    if (exifDateString == null) {        exifDateString = exif.getAttribute(ExifInterface.TAG_DATETIME)    }    if (exifDateString != null) {        exifDate = LocalDateTime.parse(exifDateString, exifDateFormatter)    }}

NOTE: if MediaStore.MediaColumns.DATE_TAKEN gives you epoch millis seconds, ExifInterface.TAG_DATETIME_ORIGINAL gives you the date and time of the device when it was taken.

Option 3: Extra Date from Filename

Filename usually comes in the format of IMG_20190907_143933.jpg, which gives you a clue of the date. There is no guarantee though.

val filenameRegex = """(\w|-|_|\s)(?<year>\d{4})(?<month>\d{2})(?<day>\d{2})(-|_|\s)(?<hour>\d{2})(?<minute>\d{2})(?<second>\d{2})""".toRegex() // (-|_|\s|\.|$)fun filenameToDate(filename: String): LocalDateTime? {    // IMG_20190907_143933.jpg    val m = filenameRegex.find(filename)    if (m != null) {        val (_, year, month, day, _, hour, minute, second) = m.destructured        return LocalDateTime.of(year.toInt(), month.toInt(), day.toInt(), hour.toInt(), minute.toInt(), second.toInt())    }    return null}

Usage

val filename = cursor.getString(displayNameIndex)val filenameDate = filenameToDate(filename)

NOTE: The filename date usually is the same as the exif date.

Timezone?

Refer Android Get Photo Timezone.

❤️ Is this article helpful?

Buy me a coffee ☕ or support my work via PayPal to keep this space 🖖 and ad-free.

Do send some 💖 to @d_luaz or share this article.

✨ By Desmond Lua

A dream boy who enjoys making apps, travelling and making youtube videos. Follow me on @d_luaz

👶 Apps I built

Travelopy - discover travel places in Malaysia, Singapore, Taiwan, Japan.