Cambiare data alle foto con iMac

Ho già creato il medesimo articolo dedicato a Linux ma, essendo lo script che esegue l’operazione, piuttosto diverso nella versione iMac ho, per praticità, preferito crearlo ex-novo.

Il problema: cambiare la data ad un insieme di foto (ma, volendo, a qualsiasi file) con uno strumento sufficientemente potente, che mi consenta di stabilire se eseguire addizioni/sottrazioni alle date, piuttosto che impostare la data odierna o comunque una forzata.

So che, per iMac, esistono soluzioni a pagamento su AppStore che eseguono l’operazione ma: perché pagare per un’applicazione che sono capace di crearmi anche da solo ?

Mi rendo conto che, per gli utenti iMac, la soluzione proposta sia decisamente meno fruibile che non per quelli Linux, dal momento che sono meno avvezzi all’uso del terminale, tuttavia, mi permetto di proporla ugualmente per chi vuole comunque – e in tempi rapidi ed economici – una soluzione al problema di cui sopra, che si presenta piuttosto puntualmente nel momento in cui si vanno ad importare fotografie da una fotocamera digitale. La data sballata, soprattutto nel momento in cui si esegue l’importazione in iPhoto, è un problema fastidioso che va risolto prima, perché con quest’ultima applicazione non è altrettanto pratico ed efficace.

Come prima cosa dobbiamo creare lo script bash (sostanzialmente un programma interpretato run-time dal sistema Mac OS X) che esegue l’operazione; i passaggi sono i seguenti:

  1. Si apre un Terminale; dal Finder si va in Applicazioni -> Utility -> Terminale
  2. Si devono ottenere i privilegi di amministratore (in questo modo è possibile salvare lo script in una cartella di sistema, accessibile da qualsiasi utente); per farlo si digita il comando:
    sudo bash

    e si preme INVIO per eseguirlo (da ora in poi, premere sempre INVIO quando suggerisco l’esecuzione di un comando a terminale). Affinché il comando possa essere eseguito è necessario introdurre la password che si utilizza per fare login con l’utente attuale.

  3. Si rende corrente la cartella dove andremo a collocare lo script con il comando:
    cd /usr/local/bin

    e si apre la creazione dello script con questi 2 comandi:

    vi uda_change_time
    i
  4. A questo punto bisogna fare un copia/incolla del seguente codice:
    #!/bin/bash
    
    # Script per cambiare la data ai files specificati.
    
    # Versione iMac 2012.12.26.a
    # By UDA'Software 2012
    
    ################################################################################
    #_______________________________________________________________________________
    function print_help() {
        printf 'Sintassi: %s [--help] <ora> <minuti> <secondi> <giorni> <mesi> <anni> [OPTIONS]\n' "${0}"
    
        cat<<EOF
    
    Scopo dello script e' quello di modificare la data e l'ora dei files
    specificati,aggiungendo o sottraendo "tempo".
    
    Il "tempo" viene specificato con 6 parametri, che possono essere
    positivi o negativi - aggiungendo il segno "-" (meno) -, rappresentati
    l'entità che si vuole andare, rispettivamente, ad aggiungere o
    togliere alla data corrente dei files che verranno elaborati.
    
    Ad esempio:
    
    ${0} 5 -30 20 4 -3 10 miafoto.jpg
    
    produce come effetto che alla data di creazione del file "miafoto.jpg"
    saranno aggiunte 5 ore, sottratti 30 minuti, aggiunti 20 secondi,
    aggiunti 4 giorni, sottratti 3 mesi e aggiunti 10 anni.
    
    Volendo elaborare piu' files contemporaneamente e' possibile
    utilizzare i caratteri jolly; ad esempio:
    
    ${0} 5 -30 20 4 -3 10 '*.jpg'
    
    OPZIONI
    
    E' possibile utilizzare delle opzioni che modificano il comportamento
    base dello script, ovvero:
    
        --test
            Modalita' di simulazione; non esegue nessuna modifica,
            aiuta solo a capire come la data dei files specificati
            verra' modificata.
    
        --ask
            Modalita' di richiesta; prima di procedere alla
            modifica dei files specificati, chiede conferma. Con
            la risposta "a" si ottiene l'interruzione definitiva.
    
        --current
            Ignora i parametri - relativi al tempo di modifica - e
            utilizza la data corrente per stabilire quella che
            dovranno assumere i files specificati.
    
        --force
            Anziche' utilizzare i parametri come modificatori della
            data corrente dei files da elaborare, vengono
            utilizzati per stabilirla esattamente; questo determina
            che il parametro "anni", anziche' essere trattato come
            la quantita' di anni da aggiungere o sottrarre, deve
            essere a 4 cifre e rappresentare direttamente l'anno
            che si intende configurare.
    
    EOF
        exit 0;
    }
    
    ################################################################################
    #_______________________________________________________________________________
    function calcola_max_giorni() {
        case ${file_mese} in
            1) # Gennaio
                max_giorni=31
                ;;
            2) # Febbraio
                if (( (anno % 400) == 0 )) || (( (anno % 4) == 0 && (anno % 100) != 0 )); then
                    max_giorni=29
                else
                    max_giorni=28
                fi
                ;;
            3) # Marzo
                max_giorni=31
                ;;
            4) # Aprile
                max_giorni=30
                ;;
            5) # Maggio
                max_giorni=31
                ;;
            6) # Giugno
                max_giorni=30
                ;;
            7) # Luglio
                max_giorni=31
                ;;
            8) # Agosto
                max_giorni=31
                ;;
            9) # Settembre
                max_giorni=30
                ;;
            10) # Ottobre
                max_giorni=31
                ;;
            11) # Novembre
                max_giorni=30
                ;;
            12) # Dicembre
                max_giorni=31
                ;;
        esac
    }
    
    ################################################################################
    #_______________________________________________________________________________
    ora="${1:-0}"
    minuti="${2:-0}"
    secondi="${3:-0}"
    giorno="${4:-0}"
    mese="${5:-0}"
    anno="${6:-0}"
    files="${7}"
    
    if [ "${1}" == "--help" ]; then
        print_help
    fi
    
    if [ "${files}" == "" ]; then
        printf 'ERRORE: Non hai specificato i paramatri correttamente.\nUsare: %s --help\n' "${0}"
    
        exit 1;
    fi
    
    while [ "$8" != "" ]; do
        case ${8} in
            "--test")
                flag_test=1
                printf '(Attivata la modalita simulazione)\n'
                ;;
            "--ask")
                flag_ask=1
                printf '(Attivata la modalita richiesta di modifica)\n'
                ;;
            "--current")
                flag_current=1
                printf '(Attivata la modalita data corrente)\n'
                ;;
            "--force")
                flag_force=1
                if [ ${ora} -lt 0 ] || [ ${ora} -gt 23 ]; then
                    printf 'ERRORE: Hai specificato una data non valida; l ora non deve essere inferiore a 0 e superiore a 23.\n'
                    exit 1;
                fi
                if [ ${minuti} -lt 0 ] || [ ${minuti} -gt 59 ]; then
                    printf 'ERRORE: Hai specificato una data non valida; i minuti non devono essere inferiori a 0 e superiori a 59.\n'
                    exit 1;
                fi
                if [ ${secondi} -lt 0 ] || [ ${secondi} -gt 59 ]; then
                    printf 'ERRORE: Hai specificato una data non valida; i secondi non devono essere inferiori a 0 e superiori a 59.\n'
                    exit 1;
                fi
                if [ ${mese} -lt 1 ] || [ ${mese} -gt 12 ]; then
                    printf 'ERRORE: Hai specificato una data non valida; i mesi non devono essere inferiori a 1 e superiori a 12.\n'
                    exit 1;
                fi
                if [ ${anno} -lt 1970 ] || [ ${anno} -gt 9999 ]; then
                    printf 'ERRORE: Hai specificato una data non valida; l anno non deve essere inferiore al 1970.\n'
                    exit 1;
                fi
    
                file_mese=${mese}
                calcola_max_giorni
    
                if [ ${giorno} -lt 1 ] || [ ${giorno} -gt ${max_giorni} ]; then
                    printf 'ERRORE: Hai specificato una data non valida; i giorni non devono essere inferiori a 1 e superiori a %d.\n' ${max_giorni}
                    exit 1;
                fi
                printf '(Attivata la modalita data forzata [%02d:%02d:%02d %02d/%02d/%02d])\n' ${ora} ${minuti} ${secondi} ${giorno} ${mese} ${anno}
                ;;
        esac
    
        shift
    done
    
    ################################################################################
    #_______________________________________________________________________________
    if [ ${flag_current:-0} == 0 ] && [ ${flag_force:-0} == 0 ]; then
        if [ ${ora} -lt 0 ]; then
            printf '* All orario saranno sottratte %d ore\n' ${ora/-/}
        else
            printf '* All orario saranno aggiunte %d ore\n' ${ora/-/}
        fi
    
        if [ ${minuti} -lt 0 ]; then
            printf '* All orario saranno sottratte %d minuti\n' ${minuti/-/}
        else
            printf '* All orario saranno aggiunte %d minuti\n' ${minuti/-/}
        fi
    
        if [ ${secondi} -lt 0 ]; then
            printf '* All orario saranno sottratte %d secondi\n' ${secondi/-/}
        else
            printf '* All orario saranno aggiunte %d secondi\n' ${secondi/-/}
        fi
    
        if [ ${giorno} -lt 0 ]; then
            printf '* Alla data saranno sottratti %d giorni\n' ${giorno/-/}
        else
            printf '* Alla data saranno aggiunti %d giorni\n' ${giorno/-/}
        fi
    
        if [ ${mese} -lt 0 ]; then
            printf '* Alla data saranno sottratti %d mesi\n' ${mese/-/}
        else
            printf '* Alla data saranno aggiunti %d mesi\n' ${mese/-/}
        fi
    
        if [ ${anno} -lt 0 ]; then
            printf '* Alla data saranno sottratti %d anni\n' ${anno/-/}
        else
            printf '* Alla data saranno aggiunti %d anni\n' ${anno/-/}
        fi
    fi
    
    for file in ${files}; do
        if [ -f ${file} ]; then
            printf 'Elaborazione del file: %s\n' "${file}"
    
            file_timestamp=$(stat -f %m ${file})
    
            file_ora=$(date -r ${file_timestamp} +%H | sed 's@^0@@')
            file_minuti=$(date -r ${file_timestamp} +%M | sed 's@^0@@')
            file_secondi=$(date -r ${file_timestamp} +%S | sed 's@^0@@')
            file_giorno=$(date -r ${file_timestamp} +%d | sed 's@^0@@')
            file_mese=$(date -r ${file_timestamp} +%m | sed 's@^0@@')
            file_anno=$(date -r ${file_timestamp} +%Y | sed 's@^0@@')
    
            printf '  Data corrente   : %02d:%02d:%02d %02d/%02d/%04d\n' ${file_ora} ${file_minuti} ${file_secondi} ${file_giorno} ${file_mese} ${file_anno}
    
            if [ ${flag_current:-0} == 0 ]; then
                if [ ${flag_force:-0} == 0 ]; then
                    delta_timestamp=$(($((${ora} * 3600)) + $((${minuti} * 60)) + ${secondi}))
                    file_timestamp=$((${file_timestamp} + ${delta_timestamp}))
    
                    file_ora=$(date -r ${file_timestamp} +%H | sed 's@^0@@')
                    file_minuti=$(date -r ${file_timestamp} +%M | sed 's@^0@@')
                    file_secondi=$(date -r ${file_timestamp} +%S | sed 's@^0@@')
    
                    file_anno=$(($(date -r ${file_timestamp} +%Y | sed 's@^0@@') + ${anno}))
                    file_mese=$(($(date -r ${file_timestamp} +%m | sed 's@^0@@') + ${mese}))
                    if [ ${file_mese} -gt 12 ]; then
                        file_mese=1
                        file_anno=$((${file_anno} + 1))
                    elif [ ${file_mese} -lt 1 ]; then
                        file_mese=12
                        file_anno=$((${file_anno} - 1))
                    fi
    
                    calcola_max_giorni
    
                    file_giorno=$(($(date -r ${file_timestamp} +%d | sed 's@^0@@') + ${giorno}))
                    if [ ${file_giorno} -gt ${max_giorni} ]; then
                        file_giorno=1
                        file_mese=$((${file_mese} + 1))
    
                        if [ ${file_mese} -gt 12 ]; then
                            file_mese=1
                            file_anno=$((${file_anno} + 1))
                        fi
                    elif [ ${file_giorno} -lt 1 ]; then
                        file_mese=$((${file_mese} - 1))
                        if [ ${file_mese} -lt 1 ]; then
                            file_mese=12
                            file_anno=$((${file_anno} - 1))
                        fi
    
                        calcola_max_giorni
    
                        file_giorno=${max_giorni}
                    fi
                else
                    file_ora=$(echo "${ora}" | grep -o '[0-9]*' | sed 's@^00@0@g')
                    file_minuti=$(echo "${minuti}" | grep -o '[0-9]*' | sed 's@^00@0@g')
                    file_secondi=$(echo "${secondi}" | grep -o '[0-9]*' | sed 's@^00@0@g')
                    file_giorno=$(echo "${giorno}" | grep -o '[0-9]*' | sed 's@^00@0@g')
                    file_mese=$(echo "${mese}" | grep -o '[0-9]*' | sed 's@^00@0@g')
                    file_anno=$(echo "${anno}" | grep -o '[0-9]*' | sed 's@^00@0@g')
                fi
            else
                file_ora=$(date +%H)
                file_minuti=$(date +%M)
                file_secondi=$(date +%S)
                file_giorno=$(date +%d)
                file_mese=$(date +%m)
                file_anno=$(date +%Y)
            fi
    
            if [ ${flag_test:-0} == 0 ]; then
                if [ ${flag_ask:-0} == 0 ]; then
                    answer="S"
                else
                    printf '  Data prevista   : %02d:%02d:%02d %02d/%02d/%04d\n' ${file_ora} ${file_minuti} ${file_secondi} ${file_giorno} ${file_mese} ${file_anno}
                    printf 'Vuoi modificare la data del file  %s  ? (S/n/a)\n'
                    read answer
    
                    if [ "${answer}" == "a" ] || [ "${answer}" == "A" ]; then
                        printf '\nABORTITO !\n'
    
                        exit 0;
                    fi
                fi
    
                if [ "${answer}" != "n" ] && [ "${answer}" != "N" ]; then
                    printf '  Data modificata : %02d:%02d:%02d %02d/%02d/%04d\n' ${file_ora} ${file_minuti} ${file_secondi} ${file_giorno} ${file_mese} ${file_anno}
    
                    file_ora=$(printf '%02d\n' ${file_ora})
                    file_minuti=$(printf '%02d\n' ${file_minuti})
                    file_secondi=$(printf '%02d\n' ${file_secondi})
                    file_giorno=$(printf '%02d\n' ${file_giorno})
                    file_mese=$(printf '%02d\n' ${file_mese})
                    file_anno=$(printf '%04d\n' ${file_anno})
    
                    /usr/bin/SetFile -d "${file_mese}/${file_giorno}/${file_anno} ${file_ora}:${file_minuti}:${file_secondi}" "${file}"
    		if [ ${?} != 0 ]; then
    		    printf 'ERRORE: Nel cambiare la data di creazione si e verificato un problema con codice %d \n' ${?}
    		fi
    		
                    /usr/bin/SetFile -m "${file_mese}/${file_giorno}/${file_anno} ${file_ora}:${file_minuti}:${file_secondi}" "${file}"
    		if [ ${?} != 0 ]; then
    		    printf 'ERRORE: Nel cambiare la data di modifica si e verificato un problema con codice %d \n' ${?}
    		fi
                fi
            else
                printf '  Data prevista   : %02d:%02d:%02d %02d/%02d/%04d\n' ${file_ora} ${file_minuti} ${file_secondi} ${file_giorno} ${file_mese} ${file_anno}
            fi
        else
            printf 'ERRORE: File  %s  non trovato !\n' "${file}"
        fi
    done
    
    ################################################################################
    #_______________________________________________________________________________
  5. Ora dobbiamo salvare il codice incollato premendo il tasto ESC della tastiera e il comando:
    ZZ

    (facendo attenzione a rispettare le maiuscole)

  6. Bene, è arrivato il momento di rendere eseguibile lo script con il comando:
    chmod +x uda_change_time

    e uscire dallo stato di amministratore con il comando:

    exit

Ok, fino a qui, sono le operazioni – da eseguirsi solo una volta – necessarie per creare lo script e renderlo eseguibile; adesso passiamo alla modifica dei file in oggetto !

Supponiamo di avere una SD-Card con le foto, la inseriamo nell’apposito slot, l’iMac monta la card e, sul desktop, ci mostra la relativa icona. Il nostro problema è ora, da terminale, rendere corrente la cartella contenente le foto o comunque i files – da sistemare.

N.B.: Quando si inserisce una SD-Card o comunque si collega una fotocamera, facile che iPhoto parta in automatico. Non è il momento opportuno per importare le foto, la data va sistemata prima, non dopo, quindi chiuderlo.

Utilizzare l’icona sul desktop per aprire la finestra nel Finder, navigare nelle cartelle presenti sul dispositivo al fine di trovare le foto da modificare; a questo punto, nel terminale, scrivere il comando (senza premere INVIO):

cd

(notare che, dopo “cd”, c’è uno spazio, rispettarlo !) e, dalla finestra del Finder dove sono presenti le foto, cliccare – nel titolo della finestra – sull’icona della cartella finale – antecedente il nome della stessa – e trascinarla nella finestra del Terminale e lì rilasciare. Si ottiene che Mac OS X copia in automatico, nel terminale, il percorso corretto, facendo in modo che il comando che si è precedentemente iniziato a scrivere diventi simile al seguente:

cd /Volumes/Untitled/DCIM/101SSCAM

Ecco, ora, si può premere INVIO. Per verificare che tutto sia stato eseguito correttamente, usare il comando:

ls

per elencare i files presenti; mi aspetto siano elencati dei file con estensione “.JPG”, tuttavia, i comandi che andrò a proporre di seguito servono per modificare qualsiasi file sia presente nella cartella corrente.

Ora è giunto il momento di passare alla modifica della data/ora. Sono presenti più metodi:

  • Aggiungere o sottrarre del “tempo” alla data/ora corrente dei file.
  • Impostare a tutti i file la data e l’ora corrente.
  • Impostare a tutti i file la data e l’ora desiderate.

Notare che solo la prima soluzione – che è quella consigliata – consente di impostare la data e l’ora che i file dovevano avere già in origine, le altre due la forzano ad una ben precisa.

Il mio consiglio è quello di partire da un comando di test che non modifica nulla, ma consente di elencare i file con la loro data corrente, in modo da rendersi conto dell’entità di tempo da usare per la modifica. Il comando è il seguente:

uda_change_time 0 0 0 0 0 0 '*' --test

N.B.: Il Terminale di Mac OS X (ma quelli UNIX in generale) sono strumenti molto potenti; tra le varie cose è possibile ripristinare i comandi dati in precedenza usando i tasti freccia della tastiera. E’ anche possibile farsi aiutare nel completamento dei comandi da eseguire utilizzando il tasto “->|” (tabulazione); ad esempio, se si inizia a scrivere “uda” e si preme il tasto tabulazione , si ottiene che viene automaticamente scritto “uda_time_change”, risparmiando fatica e la possibilità di errori.

Nel comando dato in precedenza bisogna porre attenzione ai sei parametri numerici “0” (zero) che, rispettivamente, rappresentano: ora, minuti, secondi, giorni, mesi e anni di modifica.

Ad esempio, supponiamo che l’ora/data delle nostre foto sia sballata in maniera che, per ripristinarla, sia necessario: togliere 3 ore, aggiungere 10 minuti, togliere 30 secondi, togliere 6 giorni, aggiungere 2 mesi e aggiungere 4 anni.

Il comando che – ancora in fase di test – andremo a dare sarà il seguente:

uda_change_time -3 10 -30 -6 2 4 '*' --test

così da renderci conto se l’operazione avrà gli effetti desiderati; se ciò si verifica, possiamo procedere effettivamente alla modifica (togliendo l’opzione “–test”) con:

uda_change_time -3 10 -30 -6 2 4 '*'

Per una guida completa dello script, usare:

uda_change_time --help

Una volta terminata la modifica della data, possiamo uscire dal terminale con il comando:

exit

e la combinazione di tasti cmd+q .

Non ultimo: Non mi assumo nessuna responsabilità per qualsiasi danno possa eventualmente derivare direttamente o indirettamente da quanto descritto in questo articolo e dall’uso proprio o improprio dello script.

Autore: BuDuS

Disegnatore Meccanico, con la passione per l'Informatica. Come hobby, produco piccoli software di (in)utilità.