Skip to content

Latest commit

 

History

54 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

bydate

Sorts files into folders, by date, using embedded or filesystem metadata.

Example

You type this:

bydate move src_dir dest_dir

This happens:

[1/3] src_dir/subdir1/IMG_0213.JPG -> dest_dir/2017/01/02/IMG_0213.JPG
[2/3] src_dir/subdir2/IMG_0215.JPG -> dest_dir/2017/01/08/IMG_0215.JPG
[3/3] src_dir/subdir3/Video112.mov -> dest_dir/2017/02/10/Video112.mov

Usage

Copy or move file(s) based on date:

bydate copy|move SOURCE_DIR_OR_FILE DEST_DIR

Just show where file(s) would be copied or moved to:

bydate show SOURCE_DIR_OR_FILE DEST_DIR

How it works

  • Source file(s) will be copied or moved into the destination, beneath a folder based on the year, month, and day of the file. For example, files whose date is Jan 1st 2017 will be copied or moved into dest_dir/2017/01/01/
  • To determine the authoritative date of the file, the metadata inside the file is examined first. The CreateDate EXIF tag is used as the primary source. Values in the canonical YYYY:MM:DD HH:MM:SS form are used as-is (they are now range-checked); a number of other unambiguous formats (ISO variants, ctime, and order-forced numeric dates -- see "Supported CreateDate formats") are normalized to the canonical form with a per-file note. If the tag is missing, zero, or cannot be parsed unambiguously, the file's filesystem modification time (mtime) is used instead.
  • After copying or moving it, the filesystem date will be set on the destination file according to the authoritative date as determined above.
  • Files will never be overwritten; if the destination file already exists, the source file will be left in place and the destination won't be touched.

Supported CreateDate formats

CreateDate values are trusted only when they can be parsed unambiguously into a real calendar date:

  • YYYY:MM:DD HH:MM:SS — the canonical exiftool form (range-checked).
  • YYYY-MM-DD, YYYY/MM/DD, with an optional time after a space or T (e.g. 2004-06-13, 2004/06/13 10:20:30).
  • ctime style: Dow Mon D HH:MM:SS YYYY (e.g. Sat Sep 4 16:12:41 2004); the weekday is recognized but not verified against the date.
  • A/B/YY or A/B/YYYY (slash or dash separated), with an optional time (HH:MM:SS, or H:MM[:SS] AM/PM). These are accepted only when some field is greater than 12, which forces the month/day order (28/01/97 -> 1997-01-28; 01/02/97 is ambiguous and is rejected). Two-digit years pivot at 69: 00-68 -> 20xx, 69-99 -> 19xx.

Every accepted value is range-checked (month 1-12, valid day for the month including leap years, hour <= 23, minute/second <= 59). Normalized values are converted to the canonical form with a per-file note; anything else is rejected with a warning and the file falls back to its filesystem mtime.

Installation

Make sure you have the dependencies:

  • You must have bash version 4 or greater.
  • exiftool must be in your PATH
  • jq must be in your path

On macos, these can all be installed via brew. On other OSes, you probably already have a new enough version of bash, and the other dependencies are pretty straightforward to install manually if your usual package repository doesn't have them.

Then you can put this script somewhere easy to execute, like /usr/local/bin/bydate, and make sure it's executable and in your PATH.

Wishlist

  • Improve speed by sending batches of files to exiftool, rather than one at a time.
  • Leverage the built-in renaming/moving file support in exiftool: https://www.sno.phy.queensu.ca/~phil/exiftool/filename.html
  • For each file, show whether EXIF or filesystem metadata was used to determine the date.

History

  • 2026-08-20

    • Treat CreateDates in non-ISO formats (e.g., 28/01/97, Sat Jun 12 19:16:03 2004) as invalid. Previously they produced garbage directory paths and a touch failure that aborted the run; now the file falls back to its filesystem mtime, with a warning.
    • Fixed macOS compatibility: the filesystem mtime fallback now uses BSD stat -f '%Sm' when GNU stat -c '%y' is unavailable, and test.sh sets fixture mtimes with portable touch -t instead of GNU-only touch -d.
    • Parse CreateDates in unambiguous non-ISO formats (ISO variants such as 2004-06-13T10:20:30, ctime such as Sat Sep 4 16:12:41 2004, and order-forced numeric dates such as 28/01/97 14:30:00) instead of rejecting them; see "Supported CreateDate formats". Canonical values are now range-checked. The fixture dates of Font.pfb, Font.afm, Font.pfa, FITS.fits, and PostScript.eps changed from the 1999-01-01 mtime fallback to their real dates, and eight new FITS fixtures plus test-parse.sh unit tests were added.
  • 2026-05-03

    • Fixed handling of invalid/zero CreateDate values (e.g., 0000:00:00) from exiftool, which previously caused errors and incorrect directory paths. Falls back to the file's filesystem modification time instead.
    • Rewrote date extraction to avoid bash word-splitting bugs with timestamp formats containing spaces. Now uses separate jq queries for each field.
    • Handle spaces in filenames and paths correctly throughout the script.
  • 2019-04-17

    • Switched main script from ruby to bash, simplifying deployment.
    • Now using exiftool for broader EXIF awareness (MOV, HEIC, etc.) This version is noticeably slower due to every file being examined by exiftool.
  • 2017-02-12

    • Original version, required ruby
    • Only supported EXIF metadata on JPEG files

About

Sorts files into folders, by date, using embedded or filesystem metadata

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages