Facebook Pixel

1507. Reformat Date

EasyString
Leetcode Link

Problem Description

This problem asks you to convert a date from one format to another.

You're given a date string in the format Day Month Year, where:

  • Day is a number with an ordinal suffix like "1st", "2nd", "3rd", "4th", "5th", ..., "30th", "31st"
  • Month is a three-letter abbreviation: "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", or "Dec"
  • Year is a 4-digit number between 1900 and 2100

Your task is to convert this date string into the ISO format YYYY-MM-DD, where:

  • YYYY is the 4-digit year
  • MM is the 2-digit month (with leading zero if needed, like "01" for January)
  • DD is the 2-digit day (with leading zero if needed, like "05" for the 5th)

For example:

  • Input: "20th Oct 2052" → Output: "2052-10-20"
  • Input: "6th Jun 1933" → Output: "1933-06-06"
  • Input: "26th May 1960" → Output: "1960-05-26"

The solution works by:

  1. Splitting the input string into its three components
  2. Reversing the order to get [Year, Month, Day]
  3. Converting the month abbreviation to its numeric value (Jan=01, Feb=02, etc.) using string indexing
  4. Removing the ordinal suffix from the day (like "20th" → "20") and padding with zeros if needed
  5. Joining the components with hyphens to form the final date string
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is that we need to transform three pieces of information from one format to another. The input format is human-readable but inconsistent (ordinal numbers, month names), while the output format is standardized and sortable (numeric values with fixed widths).

We can break down the transformation into three independent conversions:

  1. The year is already in the correct format - we just need to reposition it
  2. The month needs to be mapped from text to a number
  3. The day needs its suffix removed and zero-padding added

Since the output format is YYYY-MM-DD and the input is Day Month Year, we notice the order is reversed. This suggests we can split the string, reverse the components, and then process each part.

For the month conversion, instead of using a dictionary or multiple if-statements, we can leverage a clever string indexing technique. By creating a string " JanFebMarAprMayJunJulAugSepOctNovDec" where each month abbreviation occupies exactly 3 characters, we can find any month's position using index() and divide by 3 to get its month number. The leading space ensures January maps to index 3, which divided by 3 gives us 0, so adding 1 gives us month 1.

For the day, we observe that all ordinal suffixes ("st", "nd", "rd", "th") are exactly 2 characters long. So we can simply slice off the last 2 characters using [:-2] to get the numeric part.

The zfill() method elegantly handles the zero-padding requirement for both month and day, ensuring single-digit values become two digits (e.g., "6" becomes "06").

Solution Approach

Let's walk through the implementation step by step:

  1. Split the input string: We use date.split() to break the input into three components. For example, "20th Oct 2052" becomes ["20th", "Oct", "2052"].

  2. Reverse the array: Since we need to output YYYY-MM-DD but receive Day Month Year, we reverse the array using s.reverse(). Our array ["20th", "Oct", "2052"] becomes ["2052", "Oct", "20th"].

  3. Convert month to number: We create a lookup string months = " JanFebMarAprMayJunJulAugSepOctNovDec". Each month abbreviation occupies exactly 3 characters in this string:

    • The space at the beginning is crucial for proper indexing
    • s[1] contains the month abbreviation (e.g., "Oct")
    • months.index(s[1]) finds the starting position of the month (e.g., "Oct" is at index 27)
    • Dividing by 3 gives us the month's zero-based index: 27 // 3 = 9
    • Adding 1 converts to 1-based month numbering: 9 + 1 = 10
    • zfill(2) ensures two digits: "10" stays "10", but "6" becomes "06"
  4. Process the day:

    • s[2] contains the day with its ordinal suffix (e.g., "20th")
    • s[2][:-2] removes the last 2 characters to get just the number ("20")
    • zfill(2) adds a leading zero if needed ("5" becomes "05")
  5. Join with hyphens: Finally, "-".join(s) combines the three components with hyphens as separators, producing the desired format "2052-10-20".

The entire solution is elegant and concise, avoiding complex conditionals or mappings by using string manipulation techniques effectively.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's trace through the solution with the input "6th Jun 1933":

Step 1: Split the input string

  • Input: "6th Jun 1933"
  • After splitting: ["6th", "Jun", "1933"]

Step 2: Reverse the array

  • Before: ["6th", "Jun", "1933"]
  • After reversing: ["1933", "Jun", "6th"]
  • Now we have: s[0] = "1933", s[1] = "Jun", s[2] = "6th"

Step 3: Process the year (s[0])

  • The year "1933" is already in the correct 4-digit format
  • No conversion needed

Step 4: Convert month to number (s[1])

  • We have the lookup string: " JanFebMarAprMayJunJulAugSepOctNovDec"
  • Find "Jun" in this string: it starts at index 15
    • Position 0: " " (space)
    • Position 1-3: "Jan"
    • Position 4-6: "Feb"
    • Position 7-9: "Mar"
    • Position 10-12: "Apr"
    • Position 13-15: "May"
    • Position 16-18: "Jun" ← Found at index 15
  • Calculate month number: 15 // 3 = 5, then add 1 → month = 6
  • Apply zfill(2): "6" becomes "06"
  • Replace s[1] with "06"

Step 5: Process the day (s[2])

  • Start with "6th"
  • Remove last 2 characters: "6th"[:-2] = "6"
  • Apply zfill(2): "6" becomes "06"
  • Replace s[2] with "06"

Step 6: Join with hyphens

  • Array is now: ["1933", "06", "06"]
  • Join with "-": "1933-06-06"

Final Output: "1933-06-06"

Solution Implementation

1class Solution:
2    def reformatDate(self, date: str) -> str:
3        # Split the date string into components (day, month, year)
4        date_parts = date.split()
5      
6        # Reverse the list to get [year, month, day] order
7        date_parts.reverse()
8      
9        # Month abbreviations lookup string (each month takes 3 characters)
10        # Starting with a space to make January index 3 (position 1 * 3)
11        month_lookup = " JanFebMarAprMayJunJulAugSepOctNovDec"
12      
13        # Convert month abbreviation to month number (01-12)
14        # Find the index of the month string and divide by 3 to get month number
15        month_index = month_lookup.index(date_parts[1])
16        month_number = month_index // 3 + 1
17        date_parts[1] = str(month_number).zfill(2)
18      
19        # Extract day number by removing the suffix (st, nd, rd, th)
20        # and pad with zero if single digit
21        day_number = date_parts[2][:-2]
22        date_parts[2] = day_number.zfill(2)
23      
24        # Join the parts with hyphen to form YYYY-MM-DD format
25        return "-".join(date_parts)
26
1class Solution {
2    public String reformatDate(String date) {
3        // Split the input date string into components (day, month, year)
4        String[] dateComponents = date.split(" ");
5      
6        // Define month abbreviations for index-based lookup
7        // Each month abbreviation is 3 characters, starting from index 1
8        String monthAbbreviations = " JanFebMarAprMayJunJulAugSepOctNovDec";
9      
10        // Extract day by removing the suffix (st, nd, rd, th) from the first component
11        String dayWithSuffix = dateComponents[0];
12        int day = Integer.parseInt(dayWithSuffix.substring(0, dayWithSuffix.length() - 2));
13      
14        // Calculate month number by finding the index of month abbreviation
15        // Divide by 3 since each abbreviation is 3 characters long, then add 1 for 1-based indexing
16        String monthAbbreviation = dateComponents[1];
17        int month = monthAbbreviations.indexOf(monthAbbreviation) / 3 + 1;
18      
19        // Extract year from the third component
20        String year = dateComponents[2];
21      
22        // Format and return the date in YYYY-MM-DD format
23        return String.format("%s-%02d-%02d", year, month, day);
24    }
25}
26
1class Solution {
2public:
3    string reformatDate(string date) {
4        // Map of month abbreviations for conversion (3 chars each)
5        // Starting with space to make January index 1
6        string monthsMap = " JanFebMarAprMayJunJulAugSepOctNovDec";
7      
8        // Create string stream to parse the input date
9        stringstream ss(date);
10      
11        // Variables to store parsed date components
12        string year;
13        string monthStr;
14        string dayWithSuffix;
15      
16        // Parse the date string: "15th May 2020"
17        // Note: >> operator for int extracts only digits, leaving suffix
18        int day;
19        ss >> day >> dayWithSuffix >> monthStr >> year;
20      
21        // Convert month abbreviation to month number
22        // find() returns position, divide by 3 to get month index, add 1 for 1-based months
23        int monthNum = monthsMap.find(monthStr) / 3 + 1;
24        string monthFormatted = to_string(monthNum);
25      
26        // Build the formatted date string in "YYYY-MM-DD" format
27        // Add leading zeros where necessary for month and day
28        string formattedMonth = (monthFormatted.size() == 1) ? "0" + monthFormatted : monthFormatted;
29        string formattedDay = (day < 10) ? "0" + to_string(day) : to_string(day);
30      
31        return year + "-" + formattedMonth + "-" + formattedDay;
32    }
33};
34
1/**
2 * Reformats a date string from format "Day Month Year" to "YYYY-MM-DD"
3 * @param date - Input date string (e.g., "20th Oct 2052")
4 * @returns Formatted date string in ISO format (e.g., "2052-10-20")
5 */
6function reformatDate(date: string): string {
7    // Split the input date string into components [day, month, year]
8    const dateComponents: string[] = date.split(' ');
9  
10    // Month abbreviations lookup string (each month takes 3 characters)
11    const monthsLookup: string = ' JanFebMarAprMayJunJulAugSepOctNovDec';
12  
13    // Extract day number by removing the ordinal suffix (st, nd, rd, th)
14    const dayString: string = dateComponents[0].substring(0, dateComponents[0].length - 2);
15    const day: number = parseInt(dayString);
16  
17    // Find month number by searching for the month abbreviation position
18    const monthAbbreviation: string = dateComponents[1];
19    const monthIndex: number = monthsLookup.indexOf(monthAbbreviation);
20    const month: number = Math.floor(monthIndex / 3) + 1;
21  
22    // Extract year from the third component
23    const year: string = dateComponents[2];
24  
25    // Format month and day with leading zeros if needed
26    const formattedMonth: string = month.toString().padStart(2, '0');
27    const formattedDay: string = day.toString().padStart(2, '0');
28  
29    // Return the formatted date in YYYY-MM-DD format
30    return `${year}-${formattedMonth}-${formattedDay}`;
31}
32

Time and Space Complexity

Time Complexity: O(1)

The time complexity is constant because:

  • date.split() splits the string into exactly 3 parts (day, month, year), which takes O(1) time since the input format is fixed
  • s.reverse() reverses a list of 3 elements, which is O(1)
  • months.index(s[1]) searches for a month abbreviation in a fixed-size string of 36 characters, which is O(1)
  • Integer division, addition, and str() conversion are all O(1) operations
  • zfill(2) pads strings to at most 2 characters, which is O(1)
  • String slicing s[2][:-2] removes the last 2 characters from a fixed-length string, which is O(1)
  • "-".join(s) joins exactly 3 strings, which is O(1)

Space Complexity: O(1)

The space complexity is constant because:

  • s is a list containing exactly 3 string elements regardless of input
  • The months string is a fixed constant of 36 characters
  • Temporary strings created during operations (str(), zfill(), slicing) are all bounded by constant size
  • The final joined string has a fixed format "YYYY-MM-DD" with constant length

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Incorrect Month Index Calculation

A common mistake is forgetting why the space at the beginning of the month lookup string is necessary. Without it, the index calculation breaks:

Incorrect:

month_lookup = "JanFebMarAprMayJunJulAugSepOctNovDec"  # No leading space
month_index = month_lookup.index(date_parts[1])
month_number = month_index // 3 + 1  # Wrong calculation!

For "Jan", the index would be 0, giving 0 // 3 + 1 = 1 (correct by chance), but for "Feb", the index would be 3, giving 3 // 3 + 1 = 2 (correct), but "Mar" at index 6 gives 6 // 3 + 1 = 3 (should be 3, correct). However, without proper alignment, later months fail.

Correct approach: Include the leading space so each month starts at a position divisible by 3.

2. Assuming Fixed Day Suffix Length

The code uses [:-2] to remove the suffix, which works for "st", "nd", "rd", "th". However, if you accidentally try to handle the suffix differently or forget that all suffixes are exactly 2 characters, you might write:

Incorrect:

# Trying to be clever with suffix detection
if date_parts[2].endswith("st"):
    day_number = date_parts[2][:-2]
elif date_parts[2].endswith("nd"):
    day_number = date_parts[2][:-2]
# ... unnecessary complexity

Better: Simply use [:-2] since all ordinal suffixes in this problem are exactly 2 characters long.

3. Forgetting to Pad Single-Digit Values

Without zfill(2), single-digit days and months won't have leading zeros:

Incorrect:

date_parts[1] = str(month_number)  # "6" instead of "06"
date_parts[2] = date_parts[2][:-2]  # "5" instead of "05"

This would produce "1933-6-6" instead of "1933-06-06".

Solution: Always use zfill(2) to ensure two-digit formatting for both month and day.

4. Using Dictionary When String Indexing Suffices

While a dictionary works, it's overkill for this problem:

Overcomplicated:

month_map = {"Jan": "01", "Feb": "02", "Mar": "03", ...}
date_parts[1] = month_map[date_parts[1]]

Better: The string indexing approach is more elegant and requires less code, though the dictionary approach is arguably more readable for some developers.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What's the output of running the following function using input 56?

1KEYBOARD = {
2    '2': 'abc',
3    '3': 'def',
4    '4': 'ghi',
5    '5': 'jkl',
6    '6': 'mno',
7    '7': 'pqrs',
8    '8': 'tuv',
9    '9': 'wxyz',
10}
11
12def letter_combinations_of_phone_number(digits):
13    def dfs(path, res):
14        if len(path) == len(digits):
15            res.append(''.join(path))
16            return
17
18        next_number = digits[len(path)]
19        for letter in KEYBOARD[next_number]:
20            path.append(letter)
21            dfs(path, res)
22            path.pop()
23
24    res = []
25    dfs([], res)
26    return res
27
1private static final Map<Character, char[]> KEYBOARD = Map.of(
2    '2', "abc".toCharArray(),
3    '3', "def".toCharArray(),
4    '4', "ghi".toCharArray(),
5    '5', "jkl".toCharArray(),
6    '6', "mno".toCharArray(),
7    '7', "pqrs".toCharArray(),
8    '8', "tuv".toCharArray(),
9    '9', "wxyz".toCharArray()
10);
11
12public static List<String> letterCombinationsOfPhoneNumber(String digits) {
13    List<String> res = new ArrayList<>();
14    dfs(new StringBuilder(), res, digits.toCharArray());
15    return res;
16}
17
18private static void dfs(StringBuilder path, List<String> res, char[] digits) {
19    if (path.length() == digits.length) {
20        res.add(path.toString());
21        return;
22    }
23    char next_digit = digits[path.length()];
24    for (char letter : KEYBOARD.get(next_digit)) {
25        path.append(letter);
26        dfs(path, res, digits);
27        path.deleteCharAt(path.length() - 1);
28    }
29}
30
1const KEYBOARD = {
2    '2': 'abc',
3    '3': 'def',
4    '4': 'ghi',
5    '5': 'jkl',
6    '6': 'mno',
7    '7': 'pqrs',
8    '8': 'tuv',
9    '9': 'wxyz',
10}
11
12function letter_combinations_of_phone_number(digits) {
13    let res = [];
14    dfs(digits, [], res);
15    return res;
16}
17
18function dfs(digits, path, res) {
19    if (path.length === digits.length) {
20        res.push(path.join(''));
21        return;
22    }
23    let next_number = digits.charAt(path.length);
24    for (let letter of KEYBOARD[next_number]) {
25        path.push(letter);
26        dfs(digits, path, res);
27        path.pop();
28    }
29}
30

Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More