Parsing datetimes in pandas converts date and timestamp text into dtypes that filtering, sorting, grouping, and time arithmetic can use. Use pd.to_datetime() after imports when a source column arrives as text, includes bad rows, or mixes local timestamps with offset-aware values.

The pd.to_datetime() function returns a datetime-like result for scalars, arrays, and Series. An explicit format makes fixed source strings easier to validate, errors=“coerce” turns invalid rows into NaT for review, and utc=True normalizes offset-aware timestamps into one UTC dtype.

Keep the original text column until failed parses and timezone assumptions have been reviewed. A successful conversion should show a datetime dtype, expected timestamp values, and a small failure table when coercion found rows that need correction.

Steps to parse pandas datetime columns:

  1. Save a datetime parser script.
    parse_order_dates.py
    import pandas as pd
     
     
    orders = pd.DataFrame(
        {
            "order_id": [1001, 1002, 1003, 1004],
            "ordered_at": [
                "2026-06-01 09:30",
                "2026-06-02 14:00",
                "not recorded",
                "2026-06-04 08:15",
            ],
            "shipped_at": [
                "2026-06-01T17:45:00+08:00",
                "2026-06-02T07:10:00Z",
                "bad timestamp",
                "2026-06-04T12:00:00+08:00",
            ],
        }
    )
     
    orders["ordered_at"] = pd.to_datetime(
        orders["ordered_at"],
        format="%Y-%m-%d %H:%M",
        errors="coerce",
    )
    orders["shipped_at_utc"] = pd.to_datetime(
        orders["shipped_at"],
        errors="coerce",
        utc=True,
    )
     
    print(f"pandas {pd.__version__}")
    print()
    print(orders.filter(["order_id", "ordered_at", "shipped_at_utc"]).to_string(index=False))
    print()
    print(orders.filter(["ordered_at", "shipped_at_utc"]).dtypes)
    print()
    print("parse failures")
    print(orders.loc[orders["ordered_at"].isna() | orders["shipped_at_utc"].isna(), ["order_id", "ordered_at", "shipped_at"]].to_string(index=False))

    Replace the sample DataFrame with the data already loaded in the working script. Keep the original text column in real projects when bad rows must be corrected instead of discarded.

  2. Run the parser script.
    $ python3 parse_order_dates.py
    pandas 3.0.3
    
     order_id          ordered_at            shipped_at_utc
         1001 2026-06-01 09:30:00 2026-06-01 09:45:00+00:00
         1002 2026-06-02 14:00:00 2026-06-02 07:10:00+00:00
         1003                 NaT                       NaT
         1004 2026-06-04 08:15:00 2026-06-04 04:00:00+00:00
    
    ordered_at             datetime64[us]
    shipped_at_utc    datetime64[us, UTC]
    dtype: object
    
    parse failures
     order_id ordered_at    shipped_at
         1003        NaT bad timestamp

    The parsed local timestamp column should show datetime64[us]. The offset-aware timestamp column should show datetime64[us, UTC] after utc=True normalizes each value to UTC.

  3. Preserve the raw timestamp text before parsing production rows.
    df["ordered_at_raw"] = df["ordered_at"]

    The raw copy gives reviewers the exact source value when errors=“coerce” produces NaT.

  4. Parse fixed-format timestamps with pd.to_datetime().
    df["ordered_at"] = pd.to_datetime(
        df["ordered_at_raw"],
        format="%Y-%m-%d %H:%M",
        errors="coerce",
    )

    Use format when the source uses one known pattern. Leave format unset for ordinary automatic parsing only when ambiguous day and month order cannot change the result.

  5. Parse offset-aware timestamps with utc=True.
    df["shipped_at_utc"] = pd.to_datetime(
        df["shipped_at"],
        errors="coerce",
        utc=True,
    )

    Mixed timezone offsets can raise an error or produce inconsistent results when parsed without utc=True. Normalize machine timestamps to UTC when later comparisons need one timeline.

  6. Verify the datetime dtypes and failed rows.
    print(df.filter(["ordered_at", "shipped_at_utc"]).dtypes)
    print(
        df.loc[
            df["ordered_at"].isna() | df["shipped_at_utc"].isna(),
            ["order_id", "ordered_at_raw", "shipped_at"],
        ]
    )

    Review every NaT row before using the parsed column for filters, joins, resampling, or date arithmetic.

  7. Remove the temporary parser script after the project code covers the same checks.
    $ rm parse_order_dates.py