A Ruby JPEG example - Use Ruby to get the date and time from a JPEG file

A few weeks ago when I upgraded my iPhone 3G to iOS 4, I hooked up my iPhone to my old Windows computer, where it was originally sync'd. Downloading my iPhone photos onto my Windows XP computer was a pain, and worse than that, in the process, the iPhone photos were pulled off the phone in some random order, so all the photos are completely out of order when I look at them in the Windows Explorer or the Mac Finder.

Wanting to sort these files in order by date (like they were on my iPhone before I downloaded them to my Windows computer), I created a Ruby script to help me rename all my iPhone photos in date sequence. In short, if a JPEG file with this name:

Poopy-Windows-Filename.jpg

actually has a date and time of April 1, 2009 at 13:51:16 stored in the JPEG file, my Ruby script will rename this JPEG file to the following name:

20090401-135116.jpg

While this new JPEG filename isn't too exciting, it does put all of my JPEG image files back into date/time sorted order in the Mac Finder. (I'm here to tell you that when you have more than 1,000 JPEG images, it sure is nice to have them in sorted order by date; otherwise pictures all the way from Virginia Beach to California, through Canada, and into Alaska all end up in a big jumbled mess).

My Ruby JPEG date/time script

Here's the current version of my Ruby JPEG timestamp script that helped me rename all these files. I just used this script to successfully rename over 1,000 JPG files based on the date and timestamp of the JPEG file.

#!/usr/bin/ruby

# a ruby script to get the date and timestamp from a jpeg file.
# this script prints a string, something like '20080801.224705'.
# created by alvin alexander, http://devdaily.com.
# released here under the creative commons share-alike license:
# http://creativecommons.org/licenses/by-sa/3.0/

require 'rubygems'
require 'exifr'

input_file = ARGV[0]

# get the date/time info from the JPEG file
d1 = EXIFR::JPEG.new(input_file).date_time

# convert to the format we need
#d = 'Wed Apr 01 13:51:16 -0800 2009'
a,b,d,hms,poop,y = d1.to_s.split
d2 = "#{a} #{b} #{d} #{hms} #{y}"

# create a real date
d3 = DateTime::strptime(d2, '%a %b %d %H:%M:%S %Y')

# get this date in the desired format
s = d3.strftime('%Y%m%d.%H%M%S')
puts s

As you can see, this Ruby script is meant to work with one filename at a time. I call it from a Unix shell script which feeds it all the JPEG filenames. I'll show that shell script shortly, but first, a discussion of this Ruby JPG script.

This Ruby JPEG date/time filename script

There are several lines of code here that can be improved with a little research. I jump through several hoops in the code because I don't know how to skip the "-0800" portion of the JPEG date string with the strptime method. I don't care about that offset, which is returned by the EXIFR::JPEG.new method call. If I knew how to skip that "junk" in that first string, or read it properly, I could skip the creation of the 'd1' and 'd2' objects and parse the first string directly into a Ruby Date or DateTime object.

A Unix shell script wrapper

As mentioned, I call this Ruby script from a Unix shell script. The shell script (a) gets the name of every JPG file in the current directory, (b) passes each name to the Ruby script one at a time, and (c) renames the file based on the string the Ruby script returns. Here's the source code for the Unix shell script, with the "mv" part commented out to help prevent you from deleting all your files without testing it first:

#!/bin/sh

# a unix shell script to help rename a collection of jpeg files.
# this script calls a ruby script to get the new name for each jpeg
# file, where the new name is based on the date/timestamp of the jpeg file.
# my ruby script was named jpgdate.rb.

# this script is created by alvin alexander, http://devdaily.com.
# released here under the creative commons share-alike license:
# http://creativecommons.org/licenses/by-sa/3.0/

IFS=$'\n'

for infile in $(ls -1 *.jpg)
do

  newbase=`./jpgdate.rb $infile`
  outfile="$newbase.jpg"

  echo "Moving \"$infile\" --> \"$outfile\" ..."

  # remove the comment below if and only if you have run this script once with just
  # the 'echo' output, and you've confirmed everything looks okay.
  # also, make sure you have a backup of your
  # files in another directory or on another system before
  # running this 'mv' command. you have been warned.
  # mv "$infile" "$outfile"

done

While my Ruby script would surely run faster if I did all this work inside of it, I like the Unix concept of a script doing one thing, and doing it well, so you can easily reuse it in other scripts, so that's why I have the Ruby JPG script and this Unix shell script.

I'll come back and update this Ruby JPEG date/time article as I learn more, but for now, if you just need to know how to get the date and time out of a JPEG file, I hope this Ruby example is helpful.