Summary:

Renaming folders from a date format DD/MM/YY to YYYY/MM/DD using PowerShell

  • Listing files/folders
  • Regular Expressions
  • Split function
  • Replace function
  • Renaming files/folders

Issue

When using the date format DD/MM/YY you will find your folders do not appear in date order when sorting by folder name, in order to resolve this issue i need to convert my content to the syntax YYYY/MM/DD.

Since I have a rather large back catalogue of folders using the older format, this presented a interesting scenario to try and achieve in PowerShell.

Resolution

Please note this is a pretty quick draft of code, there are many improvements that could be made and feel free to tweak as appropriate.

In order to test various scenarios i have created a set of test folders:

PowerShell_ReformatDate_TestFolders_03052015

The basic steps involved are:

  1. Get a list of folder names for processing
  2. Check folder name contains the old format
  3. Extract the Date (DD), Month (MM) and Year (YY) values for processing
  4. Re-order date values as required, i.e. YYYY_MM_DD
  5. Replace current DDMMYY values with the newly formatted YYYYMMDD
  6. Commit changes

Implementation

Code Explained

Lets break down the code into functional parts so we can align with the above basic steps involved

  1. Get a list of folder names for processing

    The target is the root folder containing all the sub folders you wish to process.
    By default the target will be set to whatever folder the script itself resides in, this is defined by

    $strDirectoryPath = split-path -parent $MyInvocation.MyCommand.Definition	# Root of target folder

    Getting a list of folders is achieved using a very useful commandlet Get-ChildItem,we set the type of object we are looking for to -Directory to ensure no files are listed:

    $aFolderList = Get-ChildItem -Path $strDirectoryPath –Directory
  2. Check folder name contains old format

    In order to extract the date components we will first need to ensure that they are present in the current folder, we achieve this using a regular expression using a pattern match:

    $strRegEx = "^\d{2}_\d{2}_\d{2}_"

    This allows us to avoid processing invalid folders as well as giving the ability to target a specific value set.
    The Regular Expression (RegEx) is pretty simple

    ^

    Anchor point for start of expression

    \d{2}

    Look 2 sequential digits (numbers)

    _

    Underscore, Replace this with whatever delimiter is being used to match the syntax of the target folder names.

    A good guide on regular expressions can be found here, it is well worth investing some time in understanding how to construct basic expressions.

    In order to avoid duplication of parameters we can use $strDelimiter to make up the expression:

    $strRegEx = "^\d{2}"+$strDelimiter+"\d{2}"+$strDelimiter+"\d{2}"+$strDelimiter

    Now simply check if the folder name of the current object matches the above RegEx

    IF ($objFolder.Name -match $strRegEx)
  3. Extract the Date (DD), Month (MM) and Year (YY) values for processing

    There are many ways to achieve this, but since we know the target formatting already a simple solution is to use a built-in method .Split.

    $aSplitFileName = $objFolder.Name.Split($strDelimiter)

    At this point if it was preferable the values could be put into meaningful variables for easier reading of the code. i.e.

    $strDay = $aSplitFileName[0]
    $strMonth = $aSplitFileName[1]
    $strYear = "20" + $aSplitFileName[2]
    

    However as above since we already know what the target syntax will be, as well as using a RegEx to filter out entries that do not conform to the standard we can use the values directly.

  4. Re-order the values are required, i.e. YYYY_MM_DD

  5. Replace current DDMMYY values with the newly formatted

    Steps 4 and 5 can be achieved in the same line of code.
    Since we have built a RegEx to target objects with a specific syntax we can use the same search pattern to simply –replace the old values with new.

    $strNewFolderName = $objFolder.Name -replace $strRegEx,("20" + $aSplitFileName[2] + $strDelimiter + $aSplitFileName[1] + $strDelimiter + $aSplitFileName[0] + $strDelimiter)
  6.   The changes are commited using another built-in commandlet Rename-Item

    Rename-Item -path $strDirectoryPath\$objFolder -newName $strNewFolderName

And there we have it, while the script is not the prettiest and by far a complete product it does touch on some very common tools you will be using when working with PowerShell.

Before After
PowerShell_ReformatDate_TestFolders_03052015 PowerShell_ReformatDate_TestFoldersRenamed_03052015

As you can see folders not satisfying the regular expression are skipped and any others reformatted

Processing  01_01_15_01_01_15_DoubleRegEx
New folder name:  2015_01_01_01_01_15_DoubleRegEx
=================
Processing  01_01_15_NoSpaces
New folder name:  2015_01_01_NoSpaces
=================
Processing  01_02_14_UnderScores_In_Folder_NAME
New folder name:  2014_02_01_UnderScores_In_Folder_NAME
=================
Processing  02_11_10_Spaces In Folder Name
New folder name:  2010_11_02_Spaces In Folder Name
=================
Processing  03_03_2011_RegExFail
03_03_2011_RegExFail  doesn't match the RegEx and will be skipped
=================
Processing  2010_01_03_AlreadyFormatted
2010_01_03_AlreadyFormatted  doesn't match the RegEx and will be skipped
=================
Processing  NameBeforeDate_02_01_10
NameBeforeDate_02_01_10  doesn't match the RegEx and will be skipped
=================

Script

Usage Instructions

# Adjust the following parameters to your needs below:
# $strDirectoryPath	# Root of target folder, default is directory where script
#			  is located.
# $strDelimiter 	# Delimiter to split date by (e.g. "/")
# $strRegEx 		# RegEx for date format ("##_##_##_" e.g. "01_01_15_")
# $strLogPath		# Path to log file, default is directory where script is located.

Source Code

# Script to re-format date syntax for folders
# Copyright (C) 2015  Adil Dean
# Script Name: ReformatFolderDates.ps1
# Script Version: 1.0
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
#(at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see .
#
# ============================================================================
# Change History:
# 0.0.1 - 03/05/2015 - Adil Dean
#	- Script created
# 0.0.2 - 03/05/2015 - Adil Dean
#	- Add rename folders functionlity
#	- Add transaction logging
#	- Clean up messages to log
# 0.0.3 - 03/05/2015 - Adil Dean
#	- Add conformation prompt
# 1.0 - 03/05/2015 - Adil Dean
#	- Release version
#	- Added $strDelimiter to regex contstruction
#	- Updated usage instructions to include log
#
# ============================================================================
# Known Bugs/Feature requests:
# - No validation of parameters,invalid entries will cause script to fail
# - Script only caters for years starting with 20
# - Subfolders are not traversed
# ============================================================================
# Requirements:
# - Script is not signed (requires necessary execution policy)
# Usage Instructions:
# Adjust the following parameters to your needs below:
# $strDirectoryPath # Root of target folder, default is directory where script
#					  is located.
# $strDelimiter # Delimiter to split date by (e.g. "/")
# $strRegEx 	# RegEx for date format ("##_##_##_" e.g. "01_01_15_")
# $strLogPath	# Path to log file, default is directory where script is located.
#
# ============================================================================

# Initialize Parameters/Variables
$strDirectoryPath = split-path -parent $MyInvocation.MyCommand.Definition	# Root of target folder
$strDelimiter = "_"									# Delimiter to split date by (e.g. "/")

$strRegEx = "^\d{2}"+$strDelimiter+"\d{2}"+$strDelimiter+"\d{2}"+$strDelimiter	# RegEx for date format ("##_##_##_" e.g. "01_01_15_")
$strLogPath = $strDirectoryPath + "\RenameLog.log"	# Path to log file

$aFolderList = $null
$aSplitFileName = @()
$strNewDate = $null
$strNewFolderName = $null

Clear-Host

Write-Host "##################################################################"
Write-Host "Please check the following information is correct before proceding"
Write-Host "Root of target folders is: "$strDirectoryPath -ForegroundColor Yellow -BackgroundColor Blue
Write-Host "RegEx being applied is: "$strRegEx -ForegroundColor Yellow -BackgroundColor Blue
Write-Host "Delimiter to split date is: "$strDelimiter -ForegroundColor Yellow -BackgroundColor Blue
Write-Host "Log will be saved to: "$strLogPath -ForegroundColor Yellow -BackgroundColor Blue
Write-Host "##################################################################"
Write-Host "If the above information is not correct press Ctrl+C to quit" -ForegroundColor Yellow -BackgroundColor Blue
Write-Host "or the enter key to continue" -ForegroundColor Yellow -BackgroundColor Blue
PAUSE

Start-Transcript $strLogPath
# Get folder list
$aFolderList = Get-ChildItem -Path $strDirectoryPath -Directory

FOREACH ($objFolder in $aFolderList){
	Write-Host "Processing "$objFolder.Name
	IF ($objFolder.Name -match $strRegEx){
		$aSplitFileName = $objFolder.Name.Split($strDelimiter)
		$strNewFolderName = $objFolder.Name -replace $strRegEx,("20" + $aSplitFileName[2] + $strDelimiter + $aSplitFileName[1] + $strDelimiter + $aSplitFileName[0] + $strDelimiter)
		Write-Host "New folder name: "$strNewFolderName
		
		Rename-Item -path $strDirectoryPath\$objFolder -newName $strNewFolderName

	} ELSE {
		Write-Host $objFolder.Name" doesn't match the RegEx and will be skipped" -BackgroundColor Red
	}
	Write-Host "================="
	
	# Clear variables for next pass
	$objFolder = @()
	$aSplitFileName = @()
	$strNewFolderName = $null
}		

Stop-Transcript