CMPS 134
Calendar Date Conversion: One Example

There are several ways to express a calendar date. Among them are what here we will call the traditional format, exemplified by "September 29, 2023" and "July 7, 1865". In this format, a date is composed of these pieces:

Another format is Year-MONTH-Day (abbreviation YMD), exemplified by "2023-SEPTEMBER-29" and "1865-JULY-7". Its three components, the middle one preceded and followed by a single dash, are the year, month name (in all upper case letters), and the day number.

Consider the problem of converting a calendar date provided in the traditional format into the corresponding date expressed in the Year-MONTH-Day format. Here is the specification of a Java functional method that solves this problem:

/* Given (via the formal parameter) a calendar date expressed in the
** traditional format (e.g., "January 21, 1976"), returns the same date,
** but expressed in YMD format (e.g., "1976-JANUARY-21").
*/
public static String tradDateToYMD(String tradDate) { ... }

A calendar date has three components: month, day, and year. To convert from traditional format to YMD, it suffices to "extract" each of those components from the given traditional date and then to paste them back together in a different order, with dashes in between. (For the moment, ignore that the month name in YMD must be in all upper case letters.)

This realization leads us to this sketch of a solution:

public static String tradDateToYMD(String tradDate) { 
   String monthStr = that substring of tradDate identifying the month
   String dayStr = that substring of tradDate identifying the day
   String yearStr = that substring of tradDate identifying the year

   return yearStr + '-' + monthStr + '-' + dayStr;
}

What remains to be solved is how to obtain the three needed substrings of tradDate. As tools for accomplishing this, we have at our disposal all the methods in the java.lang.String class, including those described here.

Notice that the substring() method provides a way to "extract" a substring from a string. To specify which substring, we need only supply (as arguments) the beginning and ending positions of the substring within the larger string.

Consider the month name. In the traditional format, the month name is the prefix of the date up to, but not including, the first occurrence of a space character. But the indexOf() method is exactly the tool we need to find the position of the first occurrence of a given character. Code suitable for obtaining the month name from tradDate is thus

int posOf1stSpace = tradDate.indexOf(' ');
String monthStr = tradDate.substring(0, posOf1stSpace);

              +---+
posOf1stSpace | 7 | 
              +---+
         +-----------+
monthStr | "January" |
         +-----------+
Using "January 21, 1976" as an example, execution of these two assignment statements will result in the state of affairs shown to the right.

Now, what about the day? In the traditional format, the one- or two-digit numeral describing the day is sandwiched between the first occurrence of a space and the lone occurrence of a comma. It follows (assuming that the value of posOf1stSpace has already been established) that the correct value for dayStr can be found like this:

int posOfComma = tradDate.indexOf(',');
String dayStr = tradDate.substring(posOf1stSpace+1, posOfComma);

              +---+
posOf1stSpace | 7 | 
              +---+
         +-----------+
monthStr | "January" |
         +-----------+
           +----+
posOfComma | 10 | 
           +----+
       +------+
dayStr | "21" |
       +------+
Execution of these assignment statements will update the state of affairs to what is shown to the right.

Finally, if we assume that the year is necessarily four digits in length, we can assign to yearStr the suffix of tradDate of length four. The beginning position of that suffix is tradDate.length() - 4. It follows that a suitable statement by which to declare and then assign a value to yearStr is

String yearStr = tradDate.substring(tradDate.length() - 4);

Recall that if only one argument is passed to substring(), the result is the suffix of the target string (here, tradDate) beginning at the specified position.

Putting all this together, we get this completed method:

/* Given (via the formal parameter) a calendar date expressed in the
** traditional format (e.g., "January 21, 1976"), returns the same date,
** but expressed in YMD format (e.g., "1976-JANUARY-21").
*/
public static String tradDateToYMD(String tradDate) { 
   int posOf1stSpace = tradDate.indexOf(' ');
   String monthStr = tradDate.substring(0, posOf1stSpace);

   int posOfComma = tradDate.indexOf(',');
   String dayStr = tradDate.substring(posOf1stSpace+1, posOfComma);

   String yearStr = tradDate.substring(tradDate.length() - 4);

   return yearStr + '-' + monthStr + '-' + dayStr;
}

In one respect, this method produces incorrect results, because in the strings it returns the month name is identical to that in the input string, which is not guaranteed (or even expected) to be in all upper case letters. This is easily fixed, however, by utilizing the toUpperCase() method. Specifically, in the return statement, we can apply that method to monthStr:

return yearStr + '-' + monthStr.toUpperCase() + '-' + dayStr;

As it stands, our method is very brittle, meaning that, unless the input string is perfectly formed, the returned string is likely to be mal-formed, too. Consider the possibility that the input string included leading spaces, or trailing spaces, or "extra" spaces somewhere in the middle. One might hope that our conversion method would handle those anomalies, but it will not, unless we make some adjustments. Perhaps the best way to handle them is to apply the trim() method to tradDate, which produces a string identical to it except without any leading or trailing "whitespace" (spaces, tabs, or newline characters). This is only a partial fix, however, because there could be "extra" whitespace in the middle, too. Removing it is quite doable, but none of the methods in java.lang.String does it.