Beginning javascript tutorial: Parsing a date from a sequenced jpeg filename

The other day I ran across another post at aenhancers.com. (aenhancers is a really great AE scripting forum by the way). The poster, Dan wrote:

I have shot thousands of stills
that I am now wanting to make
a time lapse movie out of. Each
still is named with the date and time
it was taken. For example
0507270505011.jpg would have been
taken yy/mm/dd/hh/mm/ss/(camera ID).
...
is there a way to parse the filename
to convert it into something more
legible? So 0507230004461.jpg would
translate into July 23, 2005 - 12:10 AM?...


The more I thought about it, this is exactly the kind of task I’m hoping to cover here at Creative Workflow Hacks. So in addition to posting a usable solution at aenhancers I decided to do a tutorial here with the problem solving steps involved. You know, teach a guy to fish and all.

I’m also going to try something new here. From the feedback I’m getting from users, the skill level and interest in DIY varies a lot. Some of you are hoping for beginners tutorials, some are looking for more advanced ideas and inspiration, and some of you are looking for solutions that are already thought through where you can download a bit of software, do your work and forget about it. So, I’m going to try labeling categories with beginner, intermediate, advanced and shrinkwrapped (although not that much software is actually shrinkwrapped anymore) depending on the skill level and the degree of involvement necessary to get the solution working. Send some feedback if you find it useful or not.

Back to our date parsing problem. Like almost all computer problems, the key here is to break down the big problem into a series of smaller problems. Today we are going to write a function to do our bit of business and return the parsed date. A function allows us to organize these smaller tasks into one section of a program that does one function. Later, when we talk about Object Oriented Programming we will refer to a function as a method, but no matter which name is used they do similar things.

A function has a signature. It is all of the component parts of a function placed correctly so that the program interpreter will know what to do with the parts of the function. An interpreter is a program running behind the scenes that takes all the parts of the function and convert it it into instructions the computer and program will understand. If you type something the interpreter doesn’t understand you will get a syntax error that indicates you made a mistake.

In javascript, our function signature looks something like this:

function doSomething(withThis){     
         return thisResult; 
}


Javascript has it’s roots in a C style syntax. C is a programming language that has been around over 30 years, which is several centuries in programming dog years. Because it has been so popular for so long, a lot of programming languages (Java, C#, C++, Javascript, etc.) have adopted it’s syntax. Some of the notable features are the use of the ( ), { }, and ; characters.

The ; character is used to define the end of a statement. A statement corresponds to a declarative sentence in english. You tell the interpreter something about the value of something or how something corresponds to something else.
The parentheses characters are used to group an evaluation. When we are testing for a value we use parentheses. This is called a conditional.
The brace characters define a block of statements. It helps the interpreter understand the flow of your program. We’ll talk more about all of these ideas in just a bit.

So we’ll start with the function signature and intitial variables for our date parsing function.

var jpgName = '0507230004461.jpg';   
var dateStringParsed;   
dateStringParsed = parseDateString(jpgName);    

function parseDateString(jpgName){            
   var evaluatedDateString = '';            
   return evaluatedDateString;  
}


First we set a variable for our jpeg name. A variable is a holder for our values. We can set values and manipulate values and keep them in a variable while we are doing so. Javascript is a typeless language. Meaning a variable can contain any kind of value. In this case it holds a string value of characters. Javascript has functions to convert between types like numbers and strings when we know we need a particular type of variable. We’ll go over one of these later.

We then set up a variable to hold our parsed date string. We declare that variable by putting the var keyword in front of it. Javascript is pretty loose about declaring variables. The var keyword helps us define the scope of the variable. We’ll cover scope in a later tutorial, but scope helps us organize whether we can use a variable anywhere from a global scope or only within a function in a local scope. Just file that away so you can say, “ahh, yes. I remember something about that”, later.

After we have a container variable, we call our dateParseString function so we can place the value calculated in the function in our variable. Notice how the function is on the right side of the variable? In javascript, we evaluate the right side of the statement and place that in the left side of the statement, separated by the = in this case. So, in english, we call the parseDateFunction and place the return value of the function in our variable. Notice the return keyword in our function? It indicates that we will return that value back to the function callee which in this case is our dateStringParsed variable. We also set up our dateString variable that we will calculate at this point.

We now have the function skeleton setup. Let’s do the calculating. We’ll start with gathering some initial values from the variable jpgName

var jpgName = '0507230004461.jpg';   
var dateStringParsed;   
dateStringParsed = parseDateString(jpgName);    
function parseDateString(jpgName){               
     var evaluatedDateString = '';       
     var year =jpgName.substring(0,2);    
     var month = jpgName.substring(2,4);    
     var day = jpgName.substring(4,6);   
     var hour = jpgName.substring(6,8);    
     var minutes =jpgName.substring(8,10);   
     var seconds = jpgName.substring(10,12);            

}


Dan told us that the jpg name followed a very specific pattern. He said: Each still is named with the date and time it was taken. For example 0507270505011.jpg would have been taken yy/mm/dd/hh/mm/ss/(camera ID). This is the kind of task a script is really great with. We figure that every 2 characters based on location in the name will represent a certain value. We can than assign values to our variables based on this knowledge. . How do we get those values? We use a built in function that javascript assigns to every string. We are going to use the substring function. The substring function allows us to call that function from a string variable and return a string that starts at the first location called and ends at the last.var year = jpgName.substring(0,2); would then return ’05’. Notice how the first called value is 0? A lot of beginners get tripped up with using 0 as the first value of a container. In general, javascript containers begin with 0 as the first value, so it is important to get your head around that. Programming is a lot like math, but if you remember a bit of algebra it can seem like what you remember as functions, etc. don’t work like you remember them. As you get familiar with programming it will seem less strange.

Let’s move on. We’ve assigned variables from our parsed jpeg and now we need to format them into a useful date. Here is the next part of our function:

var jpgName = '0507230004461.jpg';   
var dateStringParsed;   
dateStringParsed = parseDateString(jpgName);    
alert(dateStringParsed);

function parseDateString(jpgName){                
	var evaluatedDateString = '';       
	var year =jpgName.substring(0,2);    
	var month = jpgName.substring(2,4);    
	var day = jpgName.substring(4,6);    
	var hour = jpgName.substring(6,8);    
	var minutes =jpgName.substring(8,10);   
	var seconds = jpgName.substring(10,12);     
   
   switch (month)
   {
      case "01":
         month = 'January';
         break;
      case "02":
         month = 'February';
         break;
      case "03":
         month = 'March';
         break;
      case "04":
         month = 'April';
         break;
      case "05":
         month = 'May';
         break;
      case "06":
         month = 'June';
         break;
      case "07":
         month = 'July';
         break;
      case "08":
         month = 'August';
         break;
      case "09":
         month = 'September';
         break;
      case "10":
         month = 'October';
         break;
      case "11":
         month = 'November';
         break;
      case "12":
         month = 'December';
         break;
      
   }
 }


Here we use a switch statement to convert our numeric month to a month string. A switch statement is another version of an if..else conditional statement. if..else allows us to test for a condition and either match a checked value or continue on to the next statement. In some cases, a switch statment is easier to read. In checking for a month, the switch statement allows us to stack the month variables one on top of each other and at first glance you can tell what we’re trying to do.

In programming, it is almost as important to be able to read the program easily as it is for the function to work correctly. This is so when another programmer reads your program it is clear what you were trying to accomplish. Even if you don’t work with other programmers, if you try to read your own code a few months from now you’ll wonder what the guy or gal who wrote this code was thinking about. It’s a weird thing the first time you read your own code and wonder who wrote this ****. Another important component of understanding code is to use comments. Comments allow you to set up the scenario for your code. We’ll go over comments in a future article.

There is one tricky part in our switch statement. Notice how we check for ’01’, ’02’? If we test for 1, 2, etc. the test will fail. Why is that? Remember how we talked about types earlier. This is a case where we are checking for a string value and not an integer. Most of the time that flexibility is useful, but if you find yourself not getting the values you expected, one of the first things to check is some kind of type mismatch.

The next part of our function converts our two digit date into a full date string. Remember the Y2K bug? We recreate it right here in our function. In our case we’ll assume the date is post 2000, but that is a big assumption in large bulky systems. Luckily for us, this isn’t a large bulky system. Btw, the //assume 2000+ for year is an example of a comment, nice of us to tell them we are going to break their system isn’t it?

//assume 2000+ for year    
year = "20" + year;


The last part of our function is a really great example of using conditionals. We check and format our minutes variable. We also check and format to AM or PM based on what we know about the date. This is also a very simple example of an algorithm. If we were going to be fancy, it’s also a heuristic algorithm, because we take what we know about the problem and work out a system that returns the correct value without relying on a mathematical proof to generate that value. In scripting, we do this a lot. We find a way to get the values we are interested in without concerning our self too much about the theory behind it. Of course that opens ourself up into outlying cases that can produce bugs and it certainly is better to understand the whole problem if it is of a complex nature. In this case we’ll just get our work done.

The minutes and AM/PM logic:

if(minutes > 0){
      minuteFormat = ":" + minutes;
   }else{
      minuteFormat = ":00";
   }   
      
   if(hour == 0){
      timeFormat = "12" + minuteFormat + " AM";
   }else if(hour < 12){
      
      timeFormat = hour + minuteFormat + " AM";
      
   }else if(hour > 12 && hour != 24){
      hour = hour - 12;
      
      timeFormat = hour + minuteFormat + " PM";
   }else if(hour == 12){
      timeFormat = hour + minuteFormat + " PM";
   }else if(hour == 24){
      timeFormat = "12" + minuteFormat + " AM";
   }


Notice these two conditional checks, if(minutes > 0){ and if(hour == 0){. These are examples of where javascript is doing it’s own type coercion. Type coercion is when the interpreter makes an assumption about the types necessary to perform a task. Since we are checking for greater than value for example, it makes the assumption that we want to compare the string as an integer. Most of the time this works, but if we wanted to explicitly change the value to a number, we could use a built in javascript function called parseInt() to give the interpreter the information that this was definitely an integer
This is also where things sort of look like the math you remember, “hey, I know > means “greater than”, but what’s with the != and ==?”. When we do a comparison using if..else we use comparison operators to do that comparison. The == characters indicate we are asking does the value of the left side equal the value of the right side. If we used != we are asking the interpreter does the value on the left not equal the value on the right.
When we use && above, we are using a logical operator. When we write, else if(hour > 12 && hour != 24){ we are asking the interpreter to evaluate whether hour is greater than 12 AND hour is not equal to 24. The && is a logical operator that means AND. Javascript also has the || OR logical operator and the ! NOT logical operator.
If I run our final function from within a browser, Flash (change the alert(dateStringParsed);to trace(dateStringParsed);), or the scripts menu in After Effects:

var jpgName = '0507230004461.jpg';   
var dateStringParsed;   
dateStringParsed = parseDateString(jpgName);    
alert(dateStringParsed);

function parseDateString(jpgName){                
	var evaluatedDateString = '';       
	var year =jpgName.substring(0,2);    
	var month = jpgName.substring(2,4);    
	var day = jpgName.substring(4,6);    
	var hour = jpgName.substring(6,8);    
	var minutes =jpgName.substring(8,10);   
	var seconds = jpgName.substring(10,12);     
   
   switch (month)
   {
      case "01":
         month = 'January';
         break;
      case "02":
         month = 'February';
         break;
      case "03":
         month = 'March';
         break;
      case "04":
         month = 'April';
         break;
      case "05":
         month = 'May';
         break;
      case "06":
         month = 'June';
         break;
      case "07":
         month = 'July';
         break;
      case "08":
         month = 'August';
         break;
      case "09":
         month = 'September';
         break;
      case "10":
         month = 'October';
         break;
      case "11":
         month = 'November';
         break;
      case "12":
         month = 'December';
         break;
      
   }
   
   
   //assume 2000+ for year
   
   year = "20" + year;
   
   if(minutes > 0){
      minuteFormat = ":" + minutes;
   }else{
      minuteFormat = ":00";
   }   
      
   if(hour == 0){
      timeFormat = "12" + minuteFormat + " AM";
   }else if(hour < 12){
      
      timeFormat = hour + minuteFormat + " AM";
      
   }else if(hour > 12 && hour != 24){
      hour = hour - 12;
      
      timeFormat = hour + minuteFormat + " PM";
   }else if(hour == 12){
      timeFormat = hour + minuteFormat + " PM";
   }else if(hour == 24){
      timeFormat = "12" + minuteFormat + " AM";
   }
   
   evaluatedDateString = month + " " + day + ", " + year + "  - " + timeFormat;
   
   return evaluatedDateString;
} 


I get July 23, 2005 – 12:04 AM, which seems to pass the test of what we are trying to get our script to do.If this is an actual production script I would encourage you to try a bunch of different values and also to check and return for invalid values so that you don’t break other parts of your system. I’ve got tutorials on user and unit testing as well as debugging coming up.

Don’t be intimidated by scripting. Dive right in. The worst thing that can happen is you might be confused for a bit, but by being diligent and doing some tutorials you’ll get useful stuff happening before too long. I especially wouldn’t worry too much about the more heavy computer science ideas. While I don’t question the importance of the theory behind the math and programming you might use, it tends to make us feel like we have to ask permission to try things out and that’s simply not true.

2 Comments

  1. Carl said

    How does the script read the filenames without the user typing in a fixed ‘jpgName’?

  2. Dale said

    Hi Carl,

    In this case I was referefing to using Javascript in Adode After Effects which has a file browser built into the program, if you were using the javascript in a web app you’d probably want to use a form submit in conjunction with a server if you needed additional information about the photo.

    Hope that helps,

    Dale

RSS feed for comments on this post

Comments are closed.