Have you encountered the issue in Google Photos where a holiday album with images from different devices, such as a phone and DSLR, appear out of order with no option to sort them correctly? This post examines the cause of the problem and provides a solution that may help.

The Issue

After returning from trips abroad, I developed a habit of organising all my photos into a single album to share with friends and family. These photos came from various devices: my phone, a Sony mirrorless camera, a DJI Pocket, and my partner’s phone. Some devices—typically those with Wi-Fi—adjusted their capture times automatically based on the local time, while others had to be manually updated. If I overlooked this step, I could batch-correct the timestamps in Adobe Lightroom, syncing all devices to the precise minute and second.

On my computer, the photos appeared perfectly ordered. However, once uploaded to Google Photos, the album descended into chaos, with the photo order completely scrambled. While Google Photos does support sorting by “Oldest first” or “Newest first,” albums could still appear out of order despite the correct capture times (as illustrated below):

alt text

The reason this is happening is because Google Photos uses another part of the EXIF data to further sort them: OffsetTime. Looking at the image details on Google Photos shows this offset value, which is recorded based on the location data in the image EXIF:

alt text

Google is trying to be helpful by adding the offset hours to the capture time when ordering the photos, but this causes photos without location/timezone/offset data to no longer appear next to the other photos captured at the same time. This OffsetTime value also cannot be edited in Google Photos or Lightroom, so we must explore other methods.

Solution #1 - ExifTool

First ensure that all images are correctly ordered on your computer with their capture times properly adjusted. For this task I personally use Lightroom, but any photo editing tool capable of timestamp adjustments will suffice.

Next, you’ll need ExifTool, a great utility for editing image metadata. Follow these steps:

  1. Download ExifTool from the official website.
  2. Extract the executable (named exiftool(-k).exe by default).
  3. Rename it to exiftool.exe.
  4. Add it to your system’s PATH to make it accessible from any directory in the terminal.

Once ExifTool is ready, you can use the below Python script to streamline the process of modifying metadata for an entire album. The script will automate the adjustments needed to ensure proper sorting in Google Photos. Here’s the Python code to get started:


import os

# pip install pyexiftool
import exiftool

def adjust_timezone_offset(image_path, new_offset, dryrun=False):
    new_offset_str = f"{new_offset:+03d}:00"
    try:
        with exiftool.ExifTool() as et:
            # Get current metadata
            current_metadata = et.execute(
                f"-j -OffsetTimeOriginal -OffsetTimeDigitized -OffsetTime {image_path}".encode("utf-8")
            )

            if dryrun:
                print(f"Dry Run - {image_path}:")
                print(f"  Current Metadata: {current_metadata}")
                print(f"  New Offset: {new_offset_str}")
                return

            # Update timezone offsets
            et.execute(
                f"-OffsetTimeOriginal={new_offset_str}".encode("utf-8"),
                f"-OffsetTimeDigitized={new_offset_str}".encode("utf-8"),
                f"-OffsetTime={new_offset_str}".encode("utf-8"),
                f"-overwrite_original".encode("utf-8"),
                image_path.encode("utf-8")
            )

            print(f"Updated {image_path} with timezone offset {new_offset_str}.")
    except Exception as e:
        print(f"Error processing {image_path}: {e}")

def process_images_in_directory(directory, new_offset, dryrun=False):
    for filename in os.listdir(directory):
        if filename.lower().endswith(('.jpg', '.jpeg', '.tiff', '.png')):
            image_path = os.path.join(directory, filename)
            adjust_timezone_offset(image_path, new_offset, dryrun=dryrun)

if __name__ == "__main__":
    
    target_directory = "C:\\path\\to\\your\\images"
    new_offset = 0
    
    # Set to True for a dry run, False to apply changes
    dryrun = False

    process_images_in_directory(target_directory, new_offset, dryrun=dryrun)

Save the above to a file called process_images_exiftool.py, edit the target_directory to be the folder containing your images, then open the folder where this file was saved in the terminal / command-prompt, then run python process_images_exiftool.py.

alt text

Solution #2 - piexif

While developing the solution to this problem my first attempt used the piexif package, which seemed to be an incomplete solution as it was not able to transfer over the “tags” had added to the images from Lightroom. It also had some unexpected behaviour of reducing the filesize to about 25% of the original size with no loss in image quality.


import os

# pip install pillow
from PIL import Image

# pip install piexif
import piexif

def adjust_timezone_offset(image_path, new_offset, dryrun=False):
    try:
        # Load the image and its EXIF data
        img = Image.open(image_path)
        exif_data = img.info.get('exif', b'')  # Preserve existing EXIF data
        exif_dict = piexif.load(exif_data) if exif_data else {"0th": {}, "Exif": {}, "GPS": {}, "1st": {}, "thumbnail": None}

        # Determine the current offset
        current_offset = None
        if piexif.ExifIFD.OffsetTimeOriginal in exif_dict['Exif']:
            current_offset = exif_dict['Exif'][piexif.ExifIFD.OffsetTimeOriginal].decode("utf-8")
        elif piexif.ExifIFD.OffsetTimeDigitized in exif_dict['Exif']:
            current_offset = exif_dict['Exif'][piexif.ExifIFD.OffsetTimeDigitized].decode("utf-8")
        elif piexif.ExifIFD.OffsetTime in exif_dict['Exif']:
            current_offset = exif_dict['Exif'][piexif.ExifIFD.OffsetTime].decode("utf-8")

        new_offset_str = f"{new_offset:+03d}:00"  # Format as "+02:00" or "-05:00"

        # Print dry run information
        if dryrun:
            print(f"Dry Run - {image_path}:")
            print(f"  Current Offset: {current_offset if current_offset else 'Not Set'}")
            print(f"  New Offset: {new_offset_str}")
            return

        # Update the timezone offset EXIF tags
        if piexif.ExifIFD.OffsetTimeOriginal in exif_dict['Exif']:
            exif_dict['Exif'][piexif.ExifIFD.OffsetTimeOriginal] = new_offset_str.encode("utf-8")
        if piexif.ExifIFD.OffsetTimeDigitized in exif_dict['Exif']:
            exif_dict['Exif'][piexif.ExifIFD.OffsetTimeDigitized] = new_offset_str.encode("utf-8")
        if piexif.ExifIFD.OffsetTime in exif_dict['Exif']:
            exif_dict['Exif'][piexif.ExifIFD.OffsetTime] = new_offset_str.encode("utf-8")

        # Save the new EXIF data back to the image
        exif_bytes = piexif.dump(exif_dict)
        img.save(image_path, exif=exif_bytes)

        print(f"Updated {image_path} with timezone offset {new_offset_str}.")
    except Exception as e:
        print(f"Error processing {image_path}: {e}")

def process_images_in_directory(directory, new_offset, dryrun=False):
    for filename in os.listdir(directory):
        if filename.lower().endswith(('.jpg', '.jpeg', '.tiff', '.png')):
            image_path = os.path.join(directory, filename)
            adjust_timezone_offset(image_path, new_offset, dryrun=dryrun)

if __name__ == "__main__":
    # Define variables directly in the script
    target_directory = "C:\\path\\to\\your\\images"
    new_offset = 0

      # Set to True for a dry run, False to apply changes
    dryrun = False

    # Process images in the specified directory
    process_images_in_directory(target_directory, new_offset, dryrun=dryrun)

As before, save the above to another file called process_images_piexif.py then then run python process_images_piexif.py. You’ll see when comparing to the original images that the filesize is much lower, but the tags I was using have been removed:

alt text

The piexif package does not support metadata stored in XMP or IPTC which may be what is being lost in this process. I am opting to use this approach despite the missing metadata as I prefer the large reduction in file size. As I cannot read this metadata easily either it concerns me slightly what could be included there (I should look into this).

Conclusion

Using either method to process the images before uploading to Google Photos will now show the images in the correct order:

alt text