{"id":55533,"date":"2008-05-20T01:44:00","date_gmt":"2008-05-20T01:44:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2008\/05\/20\/hey-scripting-guy-how-can-i-sort-digital-files-by-the-date-each-picture-was-taken\/"},"modified":"2008-05-20T01:44:00","modified_gmt":"2008-05-20T01:44:00","slug":"hey-scripting-guy-how-can-i-sort-digital-files-by-the-date-each-picture-was-taken","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/hey-scripting-guy-how-can-i-sort-digital-files-by-the-date-each-picture-was-taken\/","title":{"rendered":"Hey, Scripting Guy! How Can I Sort Digital Files By the Date Each Picture Was Taken?"},"content":{"rendered":"<p><img decoding=\"async\" class=\"nearGraphic\" title=\"Hey, Scripting Guy! Question\" height=\"34\" alt=\"Hey, Scripting Guy! Question\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/q-for-powertip.jpg\" width=\"34\" align=\"left\" border=\"0\" \/> <\/p>\n<p>Hey, Scripting Guy! I read your article on <a href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/resources\/qanda\/mar08\/hey0327.mspx\"><b>renaming image files<\/b><\/a> so that the pictures could then be sorted in chronological order. When I ran your script, however, it didn\u2019t quite work for me: the files were renamed, but the pictures weren\u2019t sorted the way I had hoped they would be. I think that\u2019s because the pictures were being renamed based on the day I downloaded them to my computer rather than the date that the picture was taken. Is there a way to rename these files based on the date the picture was taken?<br \/>&#8212; JH<\/p>\n<p><img decoding=\"async\" height=\"5\" alt=\"Spacer\" src=\"https:\/\/devblogs.microsoft.com\/scripting\/wp-content\/uploads\/sites\/29\/2019\/05\/spacer.gif\" width=\"5\" border=\"0\" \/><img decoding=\"async\" class=\"nearGraphic\" title=\"Hey, Scripting Guy! Answer\" height=\"34\" alt=\"Hey, Scripting Guy! Answer\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/a-for-powertip.jpg\" width=\"34\" align=\"left\" border=\"0\" \/><a href=\"http:\/\/go.microsoft.com\/fwlink\/?linkid=68779&amp;clcid=0x409\"><img decoding=\"async\" class=\"farGraphic\" title=\"Script Center\" height=\"288\" alt=\"Script Center\" src=\"http:\/\/img.microsoft.com\/library\/media\/1033\/technet\/images\/scriptcenter\/ad.jpg\" width=\"120\" align=\"right\" border=\"0\" \/><\/a> <\/p>\n<p>Hey, JH. You know, back in 1975, in the Bahamas, a man riding a moped was killed when he collided with a taxi. One year later the man\u2019s brother was killed in the exact same way: he was riding a moped when he collided with a taxi. Kind of spooky? Just wait; you haven\u2019t heard <i>anything<\/i> yet. For one thing, the man was riding the exact same moped his brother had been riding. For another, he crashed into the exact same taxi, driven by the same cab driver and ferrying the very same passenger!<\/p>\n<p>And here\u2019s the <i>really<\/i> weird part: neither of the two brothers had the least interest in writing scripts that can be used to rename image files! <\/p>\n<p>Well, OK, maybe that last part isn\u2019t all <i>that<\/i> weird. But hey, we had to find a way to explain what this anecdote has to do with a daily column devoted to system administration.<\/p>\n<p>Oh, wait: <i>here\u2019s<\/i> the really weird part: we actually <i>do<\/i> have a way to connect this anecdote to a daily column devoted to system administration. In our <a href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/resources\/qanda\/may08\/hey0516.mspx\"><b>last column<\/b><\/a>, a column that dealt with the horizontal and vertical sizes of a series of .JPG files, we came up with a solution that required us to use the <b>Shell<\/b> object and the <b>GetDetailsOf<\/b> method, an object and method we hadn\u2019t relied on in quite some time. And now, in this very next column, we\u2019re going to use the Shell object and the GetDetailsOf method again! <\/p>\n<p>Oh, and did we mention that today\u2019s column was written by the <i>exact same Scripting Guy who wrote the last column<\/i>!?! <i>(And edited by the exact same Scripting <\/i><i>Ed<\/i><i>itor.)<\/i> Amazing, but true.<\/p>\n<p>And yes, this <i>is<\/i> getting a little creepy, isn\u2019t it?<\/p>\n<p>Here\u2019s something a little more fun that you can try when you have a spare moment. Go to <a href=\"http:\/\/www.angio.net\/pi\/bigpi.cgi\" target=\"_blank\"><b>this Web site<\/b><\/a> and enter your date of birth; an application connected to the site will then search the value of pi to see if those exact digits appear anywhere in that value. For example, the Scripting Editor was born on July 12, 1896 so we entered the following value:<\/p>\n<pre class=\"codeSample\">07121896\n<\/pre>\n<p>Here\u2019s what we got back: <\/p>\n<pre class=\"codeSample\">The string 07121896 occurs at position 97,674,811 counting from the first digit after the decimal point.\n<\/pre>\n<p>Try it yourself. There\u2019s a better than 80 percent chance that any such birthdate can be found <i>somewhere<\/i> in the value of pi.<\/p>\n<table class=\"dataTable\" id=\"EDF\" cellSpacing=\"0\" cellPadding=\"0\">\n<thead><\/thead>\n<tbody>\n<tr class=\"record\" vAlign=\"top\">\n<td class=\"\">\n<p class=\"lastInCell\"><b>Note<\/b>. Was the Scripting Editor <i>really<\/i> born on July 12, 1896? Well, that\u2019s hard to say; after all, record-keeping was spotty, at best, back in those days. But as far as we know, yes.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<div class=\"dataTableBottomMargin\"><\/div>\n<p>But here\u2019s the most amazing thing of all: not only does today\u2019s column contain irrelevant ramblings about coincidences, but it also includes a script that can rename image files based on the date that the picture was taken! In fact, today\u2019s column just happens to include <i>this<\/i> script:<\/p>\n<pre class=\"codeSample\">Const adVarChar = 200\nConst MaxCharacters = 255\n\nSet DataList = CreateObject(\"ADOR.Recordset\")\nDataList.Fields.Append \"FileName\", adVarChar, MaxCharacters\nDataList.Fields.Append \"FileDate\", adVarChar, MaxCharacters\nDataList.Open\n\nSet objFSO = CreateObject(\"Scripting.FileSystemObject\")\n\nSet objShell = CreateObject (\"Shell.Application\")\nSet objFolder = objShell.Namespace (\"C:\\Images\")\n\nFor Each objFile in objFolder.Items\n    DataList.AddNew\n    DataList(\"FileName\") = objFile.Path\n    DataList(\"FileDate\") = objFolder.GetDetailsOf(objFile, 25)\n    DataList.Update\nNext\n\nDataList.Sort = \"FileDate\"\nDataList.MoveFirst\n\ni = 1\n\nDo Until DataList.EOF\n    If i &lt; 10 Then\n        x = CStr(\"000\" &amp; i)\n    ElseIf i &lt; 100 Then\n        x = CStr(\"00\" &amp; i)\n    ElseIf i &lt; 1000 Then\n        x = CStr(\"0\" &amp; i)\n    Else\n        x = i\n    End If\n\n    strNewName = \"C:\\Images\\NY_\" &amp; x &amp; \".jpg\"\n    objFSO.MoveFile DataList.Fields.Item(\"FileName\"), strNewName\n\n    i = i + 1\n    DataList.MoveNext\nLoop\n<\/pre>\n<p>As you can see, we start things out by creating a pair of constants, adVarChar (with a value of 200) and MaxCharacters (with a value of 255). Is it just a coincidence that we create <i>two<\/i> constants at the beginning of this script? Well, no, not really: we need adVarChar to create a pair of variant fields in our disconnected recordset, and we need MaxCharacters to set the maximum number of characters that can be stored in these fields to 255.<\/p>\n<p>Oh, by the way: we\u2019re going to use a disconnected recordset in our script. If you aren\u2019t familiar with disconnected recordsets, they\u2019re essentially database tables that exist only in memory; they aren\u2019t tied to an actual database. We\u2019re using a disconnected recordset because it provides an easy place for us to store data, and an even easier way to sort data.<\/p>\n<table class=\"dataTable\" id=\"E4F\" cellSpacing=\"0\" cellPadding=\"0\">\n<thead><\/thead>\n<tbody>\n<tr class=\"record\" vAlign=\"top\">\n<td class=\"\">\n<p class=\"lastInCell\"><b>Note<\/b>. Not a detailed enough explanation for you? Then take a look at <a href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/guide\/sas_ent_piij.mspx\"><b>this section<\/b><\/a> of the <i>Microsoft Windows 2000 Scripting Guide<\/i> for more details.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<div class=\"dataTableBottomMargin\"><\/div>\n<p>Speaking of disconnected recordsets, by a remarkable coincidence the next four lines of code in our script actually create a disconnected recordset (amazing, but true):<\/p>\n<pre class=\"codeSample\">Set DataList = CreateObject(\"ADOR.Recordset\")\nDataList.Fields.Append \"FileName\", adVarChar, MaxCharacters\nDataList.Fields.Append \"FileDate\", adVarChar, MaxCharacters\nDataList.Open\n<\/pre>\n<p>In line 1, we create an instance of the <b>ADOR.Recordset<\/b> object; the object that \u2013 yes, that\u2019s right, the object that creates a disconnected recordset. (Believe it or not, we were just about to say that exact same thing.) In the next two lines we create two fields, FileName and FileDate, fields that we\u2019ll use to store the file name and the date that each digital photo was taken. As for line 4, well, that\u2019s the best line of all: that\u2019s the line we use to open the recordset so we can begin to add data to it.<\/p>\n<p>We always save the best for last.<\/p>\n<p>Once the recordset is open we create an instance of the <b>Scripting.FileSystemObject<\/b>, an object we\u2019ll then set aside for future use. (This is the object we\u2019ll use to actually rename the files.) We then create an instance of the <b>Shell.Application<\/b> object, the object that lets us mine the metadata of a file or folder (metadata like, say, the day that a digital photo was taken). After that we use this line of code to bind to the folder C:\\Images, the folder where all of our digital photos are stored:<\/p>\n<pre class=\"codeSample\">Set objFolder = objShell.Namespace (\"C:\\Images\")\n<\/pre>\n<p>Ah, but wait. Maybe that\u2019s <i>currently<\/i> the only place where our digital photos are stored, but in just a second we\u2019re going to store those photos (or at least selected information about those photos) in a second spot: our disconnected recordset. In fact, that\u2019s what this block of code is for:<\/p>\n<pre class=\"codeSample\">For Each objFile in objFolder.Items\n    DataList.AddNew\n    DataList(\"FileName\") = objFile.Path\n    DataList(\"FileDate\") = objFolder.GetDetailsOf(objFile, 25)\n    DataList.Update\nNext\n<\/pre>\n<p>So what are we doing here? Well, to begin with, we\u2019re looping through all the digital photos (that is, all the objects in the C:\\Images folder\u2019s <b>Items<\/b> collection). For each photo in the collection we call the <b>AddNew<\/b> method to create a new, blank record in our recordset. We use the next line of code to assign the complete file <b>Path<\/b> to the new record\u2019s FileName property, then call the <b>GetDetailsOf<\/b> method in order to retrieve the date that the picture was taken and assign <i>that<\/i> value to the FileDate field:<\/p>\n<pre class=\"codeSample\">DataList(\"FileDate\") = objFolder.GetDetailsOf(objFile, 25)\n<\/pre>\n<table class=\"dataTable\" id=\"E1H\" cellSpacing=\"0\" cellPadding=\"0\">\n<thead><\/thead>\n<tbody>\n<tr class=\"record\" vAlign=\"top\">\n<td class=\"\">\n<p><b>Note<\/b>. See our <a href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/resources\/qanda\/may08\/hey0516.mspx\"><b>previous column<\/b><\/a> for more information about the GetDetailsOf method. Note, too this particular script will not work as-is on Windows Vista; that\u2019s because, on Windows Vista, the date that a picture was taken doesn\u2019t have an integer value of 25. Instead, on Windows Vista you need to use an integer value of 12 to get the date that a digital photo was taken:<\/p>\n<pre class=\"codeSample\">DataList(\"FileDate\") = objFolder.GetDetailsOf(objFile, 12)\n<\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<div class=\"dataTableBottomMargin\"><\/div>\n<p>After that we call the <b>Update<\/b> method to add the record to the recordset, then go back to the top of the loop and repeat the process with the next digital photo in the collection.<\/p>\n<p>As soon as we finish adding information about each file to the recordset we call the <b>Sort<\/b> method to sort the files by the date and time that each photo was taken:<\/p>\n<pre class=\"codeSample\">DataList.Sort = \"FileDate\"\n<\/pre>\n<table class=\"dataTable\" id=\"EYAAC\" cellSpacing=\"0\" cellPadding=\"0\">\n<thead><\/thead>\n<tbody>\n<tr class=\"record\" vAlign=\"top\">\n<td class=\"\">\n<p class=\"lastInCell\"><b>Note<\/b>. <i>Why<\/i> do we sort the files by the date and time each photo was taken? That\u2019s easy: we want to incrementally number the photos, with the first picture taken being photo 1, the second picture taken being photo 2, etc. The easiest way to determine which photo was taken first is to sort all the files by the date the picture was taken.<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<div class=\"dataTableBottomMargin\"><\/div>\n<p>Once we have a sorted list we use the <b>MoveFirst<\/b> method to move to the first record in the recordset, then use this line of code to set the value of a counter variable named i to 1:<\/p>\n<pre class=\"codeSample\">i = 1\n<\/pre>\n<p>What do we need a counter variable for? Believe it or not, we were just about to tell you that.<\/p>\n<p>As it turns out, we\u2019re now ready to start renaming files. To do that, we start by setting up a Do Until loop designed to run until the recordset\u2019s <b>EOF<\/b> (end-of-file) property is True. (In other words, we want to keep grabbing items out of the recordset \u2013 and renaming files \u2013 until we run out of records.) The first thing we do inside the loop? This:<\/p>\n<pre class=\"codeSample\">If i &lt; 10 Then\n    x = CStr(\"000\" &amp; i)\nElseIf i &lt; 100 Then\n    x = CStr(\"00\" &amp; i)\nElseIf i &lt; 1000 Then\n    x = CStr(\"0\" &amp; i)\nElse\n    x= i\nEnd If\n<\/pre>\n<p>What we\u2019re doing here is adding leading zeros to our counter variable as needed. For example, suppose i is less than 10 (the first condition in our If Then statement). In that case, we\u2019re going to assign three leading zeroes (000) plus the value of i to the variable x. What does that mean? That means that, if i is equal to 1 (as it will be the first time through the loop), then x will be equal to this:<\/p>\n<pre class=\"codeSample\">0001\n<\/pre>\n<p>See how that works? Now, what if i is greater than 9 but less than 100? In that case, we\u2019ll add two leading zeroes. If i is greater than 99 but less than 999 we\u2019ll add one leading zero. And if i is greater than 999 we\u2019ll just assign x the value of i. Depending on the number of files in the folder that will give us values similar to this:<\/p>\n<pre class=\"codeSample\">0009\n0099\n0999\n9999\n<\/pre>\n<p>Why do we bother with all that? Because it gives us a nice, neat directory listing that looks like this:<\/p>\n<pre class=\"codeSample\">NY_2008_0009.jpg\nNY_2007_0099.jpg\nNY_2007_0999.jpg\nNY_2007_9999.jpg\n<\/pre>\n<p>After we\u2019ve assigned a value to x we construct a new file name using this line of code:<\/p>\n<pre class=\"codeSample\">strNewName = \"C:\\Images\\NY_2008_\" &amp; x &amp; \".jpg\"\n<\/pre>\n<p>As you can see, there\u2019s nothing very fancy here: we\u2019re simply taking the string <i>C:\\Images\\NY_2008<\/i>, tacking on the value of x, then wrapping things up by adding the file extension <i>.jpg<\/i>. In case you\u2019re wondering, C:\\Images is, of course, the folder where our digital files are stored. As for NY_2008, well, that\u2019s just a prefix we\u2019re attaching to the beginning of each file name.<\/p>\n<p>The first time through the loop that\u2019s going to give us a file path that looks like this:<\/p>\n<pre class=\"codeSample\">C:\\Images\\NY_2008_0001.jpg\n<\/pre>\n<p>We can then rename the first file in the collection (<b>DataList.Fields.Item(&#8220;FileName&#8221;)<\/b>) using a single line of code, just like we said we could:<\/p>\n<pre class=\"codeSample\">objFSO.MoveFile DataList.Fields.Item(\"FileName\"), strNewName\n<\/pre>\n<p>From there we increment the value of i by 1, call the <b>MoveNext<\/b> method to move to the next file in the recordset, and then repeat the process. When we\u2019re done we should have a folder filled with files similar to these:<\/p>\n<pre class=\"codeSample\">NY_2008_0001.jpg\nNY_2008_0002.jpg\nNY_2008_0003.jpg\nNY_2008_0004.jpg\n<\/pre>\n<p>Best of all, NY_2008_0001.jpg is the very first picture that was taken; NY_2008_0002.jpg is the second digital photo taken; etc., etc.<\/p>\n<p>So there you have it, JH. Is it just a coincidence that you asked a question and the Scripting Guys actually answered it? Well, as a matter of fact it is. But let\u2019s not tell anyone else that, OK?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hey, Scripting Guy! I read your article on renaming image files so that the pictures could then be sorted in chronological order. When I ran your script, however, it didn\u2019t quite work for me: the files were renamed, but the pictures weren\u2019t sorted the way I had hoped they would be. I think that\u2019s because [&hellip;]<\/p>\n","protected":false},"author":595,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[19,146,122,123,3,5],"class_list":["post-55533","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-activex-data-objects-ado","tag-databases","tag-graphics","tag-multimedia","tag-scripting-guy","tag-vbscript"],"acf":[],"blog_post_summary":"<p>Hey, Scripting Guy! I read your article on renaming image files so that the pictures could then be sorted in chronological order. When I ran your script, however, it didn\u2019t quite work for me: the files were renamed, but the pictures weren\u2019t sorted the way I had hoped they would be. I think that\u2019s because [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/55533","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/595"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=55533"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/55533\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=55533"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=55533"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=55533"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}