LeetCode 1507 - Reformat Date

The problem gives a date string in a human readable format such as "20th Oct 2052" and asks us to convert it into the st

LeetCode Problem 1507

Difficulty: 🟢 Easy
Topics: String

Solution

LeetCode 1507 - Reformat Date

Problem Understanding

The problem gives a date string in a human readable format such as "20th Oct 2052" and asks us to convert it into the standardized ISO style format YYYY-MM-DD.

The input string always contains exactly three parts:

  1. A day value with an ordinal suffix such as "1st", "2nd", "3rd", "4th", and so on.
  2. A three letter month abbreviation such as "Jan" or "Oct".
  3. A four digit year.

The output must always follow the exact structure:

  • Four digits for the year
  • Two digits for the month
  • Two digits for the day

This means that single digit months and days must include a leading zero. For example:

  • "6th Jun 1933" becomes "1933-06-06"
  • "1st Jan 2000" becomes "2000-01-01"

The constraints are very small. The problem guarantees that all dates are valid, so we do not need to perform any error checking or validation. This immediately tells us that the main challenge is simply parsing and formatting strings correctly.

One subtle edge case is handling single digit days like "1st" or "6th". A naive implementation might accidentally produce "1933-06-6" instead of "1933-06-06". Another important detail is removing the ordinal suffix correctly from the day component. The suffix may be "st", "nd", "rd", or "th".

Since the date format is guaranteed to be valid and highly structured, a direct parsing approach is the most natural solution.

Approaches

Brute Force Approach

A brute force style solution would manually inspect every character of the input string and build the output piece by piece. For example, we could iterate through the string character by character, identify where spaces occur, separate the day, month, and year manually, then process each segment individually.

For the day portion, we would scan until we encounter a non digit character. For the month, we would compare against all twelve possible month names using conditional statements. Finally, we would concatenate the formatted pieces together.

This approach works correctly because the input format is fixed and predictable. However, the implementation becomes unnecessarily verbose and harder to maintain. The repeated conditional logic for months also makes the code less elegant.

Optimal Approach

The key observation is that the format is already well structured. We can simply split the string into three components using spaces.

Once we have:

  • day
  • month
  • year

we can process each independently:

  • Remove the suffix from the day by taking only the numeric part.
  • Use a hash map to convert the month abbreviation into its numeric representation.
  • Concatenate everything in YYYY-MM-DD format.

This approach is clean, efficient, and easy to understand.

Approach Time Complexity Space Complexity Notes
Brute Force O(n) O(1) Manual parsing with many conditionals
Optimal O(n) O(1) Split string and use hash map lookup

Here, n is the length of the input string, which is very small.

Algorithm Walkthrough

  1. Split the input string using spaces.

This gives us three parts:

  • day
  • month
  • year

For example:

"20th Oct 2052"

becomes:

["20th", "Oct", "2052"]
  1. Create a mapping from month abbreviation to month number.

We use a hash map because it provides constant time lookup. For example:

"Jan" -> "01"
"Feb" -> "02"
...
"Dec" -> "12"
  1. Extract the numeric portion of the day.

The last two characters are always the ordinal suffix. Therefore:

  • "20th" becomes "20"
  • "6th" becomes "6"

We can remove the suffix by taking all characters except the last two. 4. Ensure the day has two digits.

If the day contains only one digit, prepend a leading zero.

Examples:

  • "6" becomes "06"
  • "1" becomes "01"
  1. Look up the numeric month value from the hash map.

Example:

  • "Oct" becomes "10"
  1. Combine the year, month, and day using the required format.

Example:

"2052" + "-" + "10" + "-" + "20"

becomes:

"2052-10-20"

Why it works

The algorithm works because the input format is guaranteed to be consistent. Every date always contains exactly three space separated components, and every month abbreviation maps uniquely to a numeric month value. By removing the final two suffix characters from the day and padding with zeros when necessary, we always produce a correctly formatted ISO style date string.

Python Solution

class Solution:
    def reformatDate(self, date: str) -> str:
        month_map = {
            "Jan": "01",
            "Feb": "02",
            "Mar": "03",
            "Apr": "04",
            "May": "05",
            "Jun": "06",
            "Jul": "07",
            "Aug": "08",
            "Sep": "09",
            "Oct": "10",
            "Nov": "11",
            "Dec": "12",
        }

        day, month, year = date.split()

        # Remove ordinal suffix
        day_number = day[:-2]

        # Ensure two digits
        day_number = day_number.zfill(2)

        return f"{year}-{month_map[month]}-{day_number}"

The implementation begins by defining a dictionary that maps each three letter month abbreviation to its two digit numeric representation. This allows constant time conversion from names like "Oct" to "10".

Next, the input string is split into three components using split(). Since the problem guarantees the format is always valid, we can safely unpack the result directly into day, month, and year.

The day suffix is removed using slicing. Since the suffix always contains exactly two characters, day[:-2] extracts only the numeric portion.

The zfill(2) method ensures the day always contains two digits. This automatically handles single digit days correctly.

Finally, the formatted string is constructed using an f-string.

Go Solution

package main

import (
	"fmt"
	"strings"
)

func reformatDate(date string) string {
	monthMap := map[string]string{
		"Jan": "01",
		"Feb": "02",
		"Mar": "03",
		"Apr": "04",
		"May": "05",
		"Jun": "06",
		"Jul": "07",
		"Aug": "08",
		"Sep": "09",
		"Oct": "10",
		"Nov": "11",
		"Dec": "12",
	}

	parts := strings.Split(date, " ")

	day := parts[0]
	month := parts[1]
	year := parts[2]

	// Remove ordinal suffix
	dayNumber := day[:len(day)-2]

	// Add leading zero if needed
	if len(dayNumber) == 1 {
		dayNumber = "0" + dayNumber
	}

	return fmt.Sprintf("%s-%s-%s", year, monthMap[month], dayNumber)
}

The Go implementation follows the exact same logic as the Python version. One small difference is that Go does not have a built in equivalent of Python's zfill, so we manually prepend "0" when the day contains only one digit.

Another difference is string formatting. Go uses fmt.Sprintf instead of Python f-strings.

Since the constraints are tiny, there are no concerns about integer overflow or memory usage.

Worked Examples

Example 1

Input:

"20th Oct 2052"

Step 1: Split the string

Variable Value
day "20th"
month "Oct"
year "2052"

Step 2: Remove suffix

Operation Result
"20th"[:-2] "20"

Step 3: Zero padding

Original After zfill
"20" "20"

Step 4: Month lookup

Month Numeric
"Oct" "10"

Final Result

"2052-10-20"

Example 2

Input:

"6th Jun 1933"

Step 1: Split the string

Variable Value
day "6th"
month "Jun"
year "1933"

Step 2: Remove suffix

Operation Result
"6th"[:-2] "6"

Step 3: Zero padding

Original After zfill
"6" "06"

Step 4: Month lookup

Month Numeric
"Jun" "06"

Final Result

"1933-06-06"

Example 3

Input:

"26th May 1960"

Step 1: Split the string

Variable Value
day "26th"
month "May"
year "1960"

Step 2: Remove suffix

Operation Result
"26th"[:-2] "26"

Step 3: Zero padding

Original After zfill
"26" "26"

Step 4: Month lookup

Month Numeric
"May" "05"

Final Result

"1960-05-26"

Complexity Analysis

Measure Complexity Explanation
Time O(n) We scan the string a constant number of times
Space O(1) Only a fixed size month map is stored

The input string length is extremely small and bounded. The month mapping always contains exactly twelve entries, so it occupies constant space. All operations such as splitting, slicing, and dictionary lookup are linear or constant time relative to the small input size.

Test Cases

class Solution:
    def reformatDate(self, date: str) -> str:
        month_map = {
            "Jan": "01",
            "Feb": "02",
            "Mar": "03",
            "Apr": "04",
            "May": "05",
            "Jun": "06",
            "Jul": "07",
            "Aug": "08",
            "Sep": "09",
            "Oct": "10",
            "Nov": "11",
            "Dec": "12",
        }

        day, month, year = date.split()
        day_number = day[:-2].zfill(2)

        return f"{year}-{month_map[month]}-{day_number}"

sol = Solution()

# Provided examples
assert sol.reformatDate("20th Oct 2052") == "2052-10-20"  # standard two digit day
assert sol.reformatDate("6th Jun 1933") == "1933-06-06"   # single digit day
assert sol.reformatDate("26th May 1960") == "1960-05-26"  # regular conversion

# Boundary days
assert sol.reformatDate("1st Jan 2000") == "2000-01-01"   # smallest day
assert sol.reformatDate("31st Dec 2099") == "2099-12-31"  # largest day

# Different suffixes
assert sol.reformatDate("2nd Feb 2020") == "2020-02-02"   # nd suffix
assert sol.reformatDate("3rd Mar 2021") == "2021-03-03"   # rd suffix
assert sol.reformatDate("4th Apr 2022") == "2022-04-04"   # th suffix

# Leading zero checks
assert sol.reformatDate("9th Sep 1999") == "1999-09-09"   # single digit month/day

# All month mappings
assert sol.reformatDate("15th Nov 2015") == "2015-11-15"  # November
assert sol.reformatDate("10th Aug 1980") == "1980-08-10"  # August
Test Why
"20th Oct 2052" Validates normal conversion
"6th Jun 1933" Ensures single digit day padding
"26th May 1960" Confirms standard formatting
"1st Jan 2000" Tests smallest valid day
"31st Dec 2099" Tests largest valid day
"2nd Feb 2020" Verifies nd suffix handling
"3rd Mar 2021" Verifies rd suffix handling
"4th Apr 2022" Verifies th suffix handling
"9th Sep 1999" Confirms leading zero behavior
"15th Nov 2015" Confirms month mapping correctness

Edge Cases

One important edge case is a single digit day such as "6th Jun 1933". A buggy implementation might remove the suffix correctly but fail to prepend a leading zero. The solution handles this safely using zfill(2) in Python or manual zero padding in Go.

Another edge case involves the different ordinal suffixes. Days may end in "st", "nd", "rd", or "th". A naive parser might try to explicitly handle each suffix separately. This implementation avoids unnecessary branching entirely by simply removing the final two characters, which works for all valid suffixes.

A third important edge case is ensuring month conversion is always correct. Since month names are abbreviations rather than numbers, using a fixed mapping guarantees accurate conversion. This prevents mistakes such as incorrect ordering or accidental string parsing errors.