Pandas to_timedelta(): Convert Durations and Check Deadlines

pd.to_timedelta() converts duration text or numeric measurements into elapsed time. Add or subtract durations from timestamps, compare response times with a target, or report total hours. Use calendar offsets when the requirement is a number of months or years.

Input or requirementApproach
Numeric minutespd.to_timedelta(values, unit="min")
Duration textpd.to_timedelta("1 days 02:30:00")
Invalid inputerrors="coerce" gives NaT; default raise stops
Full elapsed secondsseries.dt.total_seconds()
Calendar month or yearpd.DateOffset(months=1) or pd.DateOffset(years=1)

Convert a Duration String or Series

Use pd.to_timedelta() for elapsed time, and to_datetime() for a point in time. A scalar input returns a Timedelta, a list returns a TimedeltaIndex, and a Series returns a Series. Duration resolution can differ by input and Pandas version.

import pandas as pd

duration = pd.to_timedelta("1 days 05:22:34.006")
print(duration)
series = pd.to_timedelta(pd.Series(["30 min", "2 h", "1 day"]))
print(series)
assert duration == pd.Timedelta(days=1, hours=5, minutes=22, seconds=34, milliseconds=6)
assert isinstance(series, pd.Series)

Expected output

1 days 05:22:34.006000
0   0 days 00:30:00
1   0 days 02:00:00
2   1 days 00:00:00
dtype: timedelta64[us]

Numeric Values Need an Explicit Unit

Numeric values default to nanoseconds. For a source measured in minutes, specify unit="min". For strings, include units in the text and omit the unit argument. Use lowercase h, min, s, ms, us and ns for clock units. Months and years are not fixed durations.

print("Two without a unit:", pd.to_timedelta(2))
print("Two hours:", pd.to_timedelta(2, unit="h"))
print(pd.to_timedelta([15, 30, 90], unit="min"))
assert pd.to_timedelta(2) == pd.Timedelta(nanoseconds=2)

Expected output

Two without a unit: 0 days 00:00:00.000000002
Two hours: 0 days 02:00:00
TimedeltaIndex(['0 days 00:15:00', '0 days 00:30:00', '0 days 01:30:00'], dtype='timedelta64[s]', freq=None)

Add 10 Days

Add a fixed elapsed duration to a timestamp. The fixed base makes the output repeatable. Use pd.Timestamp.now(tz="UTC") as the base to calculate from the current UTC time.

base = pd.Timestamp("2020-04-13 13:35:44.877832")
change = pd.to_timedelta(10, unit="D")
future = base + change
print("Base:", base)
print("Duration:", change)
print("Future:", future)
assert future - base == change

Expected output

Base: 2020-04-13 13:35:44.877832
Duration: 10 days 00:00:00
Future: 2020-04-23 13:35:44.877832

Add 2 Weeks

Add a fixed elapsed duration to a timestamp. The fixed base makes the output repeatable. Use pd.Timestamp.now(tz="UTC") as the base to calculate from the current UTC time.

base = pd.Timestamp("2020-04-13 13:35:44.877832")
change = pd.to_timedelta(2, unit="W")
future = base + change
print("Base:", base)
print("Duration:", change)
print("Future:", future)
assert future - base == change

Expected output

Base: 2020-04-13 13:35:44.877832
Duration: 14 days 00:00:00
Future: 2020-04-27 13:35:44.877832

Add 2 Hours

Add a fixed elapsed duration to a timestamp. The fixed base makes the output repeatable. Use pd.Timestamp.now(tz="UTC") as the base to calculate from the current UTC time.

base = pd.Timestamp("2020-04-13 13:35:44.877832")
change = pd.to_timedelta(2, unit="h")
future = base + change
print("Base:", base)
print("Duration:", change)
print("Future:", future)
assert future - base == change

Expected output

Base: 2020-04-13 13:35:44.877832
Duration: 0 days 02:00:00
Future: 2020-04-13 15:35:44.877832

Add 10 Minutes

Add a fixed elapsed duration to a timestamp. The fixed base makes the output repeatable. Use pd.Timestamp.now(tz="UTC") as the base to calculate from the current UTC time.

base = pd.Timestamp("2020-04-13 13:35:44.877832")
change = pd.to_timedelta(10, unit="min")
future = base + change
print("Base:", base)
print("Duration:", change)
print("Future:", future)
assert future - base == change

Expected output

Base: 2020-04-13 13:35:44.877832
Duration: 0 days 00:10:00
Future: 2020-04-13 13:45:44.877832

Add 10 Seconds

Add a fixed elapsed duration to a timestamp. The fixed base makes the output repeatable. Use pd.Timestamp.now(tz="UTC") as the base to calculate from the current UTC time.

base = pd.Timestamp("2020-04-13 13:35:44.877832")
change = pd.to_timedelta(10, unit="s")
future = base + change
print("Base:", base)
print("Duration:", change)
print("Future:", future)
assert future - base == change

Expected output

Base: 2020-04-13 13:35:44.877832
Duration: 0 days 00:00:10
Future: 2020-04-13 13:35:54.877832

Add 100 Milliseconds

Add a fixed elapsed duration to a timestamp. The fixed base makes the output repeatable. Use pd.Timestamp.now(tz="UTC") as the base to calculate from the current UTC time.

base = pd.Timestamp("2020-04-13 13:35:44.877832")
change = pd.to_timedelta(100, unit="ms")
future = base + change
print("Base:", base)
print("Duration:", change)
print("Future:", future)
assert future - base == change

Expected output

Base: 2020-04-13 13:35:44.877832
Duration: 0 days 00:00:00.100000
Future: 2020-04-13 13:35:44.977832

Add 100 Microseconds

Add a fixed elapsed duration to a timestamp. The fixed base makes the output repeatable. Use pd.Timestamp.now(tz="UTC") as the base to calculate from the current UTC time.

base = pd.Timestamp("2020-04-13 13:35:44.877832")
change = pd.to_timedelta(100, unit="us")
future = base + change
print("Base:", base)
print("Duration:", change)
print("Future:", future)
assert future - base == change

Expected output

Base: 2020-04-13 13:35:44.877832
Duration: 0 days 00:00:00.000100
Future: 2020-04-13 13:35:44.877932

Add Calendar Months and Years with DateOffset

A month or year does not have a fixed number of seconds. Use DateOffset rather than unit="M" or unit="Y". Month-end and leap-day results follow calendar rules; define the rule your schedule needs.

base = pd.Timestamp("2020-04-13 13:35:44")
print("Four months:", base + pd.DateOffset(months=4))
print("Four years:", base + pd.DateOffset(years=4))
print("Month end:", pd.Timestamp("2024-01-31") + pd.DateOffset(months=1))
print("Leap day plus one year:", pd.Timestamp("2024-02-29") + pd.DateOffset(years=1))
assert pd.Timestamp("2024-01-31") + pd.DateOffset(months=1) == pd.Timestamp("2024-02-29")

Expected output

Four months: 2020-08-13 13:35:44
Four years: 2024-04-13 13:35:44
Month end: 2024-02-29 00:00:00
Leap day plus one year: 2025-02-28 00:00:00

Use a Custom Datetime and a Combined Duration

Parse a timestamp with an explicit format. Calendar years preserve the time of day here. A duration string can combine days, hours, minutes and fractional seconds.

base = pd.to_datetime("12252020:233445", format="%m%d%Y:%H%M%S")
print("Five calendar years later:", base + pd.DateOffset(years=5))
delta = pd.to_timedelta("1 days 5 hours 22 minute 34.006 second")
start = pd.to_datetime("2001-11-27 16:20:05", format="%Y-%m-%d %H:%M:%S")
print("Combined duration:", delta)
print("After addition:", start + delta)
assert base + pd.DateOffset(years=5) == pd.Timestamp("2025-12-25 23:34:45")
assert start + delta == pd.Timestamp("2001-11-28 21:42:39.006")

Expected output

Five calendar years later: 2025-12-25 23:34:45
Combined duration: 1 days 05:22:34.006000
After addition: 2001-11-28 21:42:39.006000

Create a Series of Daily Durations

Store Timedelta values in a Series and add them to a timestamp. Arithmetic between two Series aligns by index labels; verify labels when combining independently collected columns.

durations = pd.Series([pd.Timedelta(days=day) for day in range(6)])
print(durations)
print("Dtype:", durations.dtype)
print(pd.Timestamp("2024-01-01") + durations)
assert durations.iloc[-1] == pd.Timedelta(days=5)

Expected output

0   0 days
1   1 days
2   2 days
3   3 days
4   4 days
5   5 days
dtype: timedelta64[us]
Dtype: timedelta64[us]
0   2024-01-01
1   2024-01-02
2   2024-01-03
3   2024-01-04
4   2024-01-05
5   2024-01-06
dtype: datetime64[us]

errors: Raise or Coerce to NaT

errors="raise" stops on invalid input. errors="coerce" gives NaT. Current Pandas supports raise and coerce; errors="ignore" is not supported. Preserve raw text and distinguish invalid nonblank values from missing input.

raw = pd.Series(["30 min", "bad duration", "", None], dtype="string")
try:
    pd.to_timedelta(raw, errors="raise")
except ValueError:
    print("Invalid duration found; inspect raw input.")
parsed = pd.to_timedelta(raw, errors="coerce")
missing = raw.isna() | raw.str.strip().eq("").fillna(False)
invalid = ~missing & parsed.isna()
print(pd.DataFrame({"raw": raw, "duration": parsed, "missing": missing, "invalid": invalid}))
assert missing.sum() == 2 and invalid.sum() == 1

Expected output

Invalid duration found; inspect raw input.
            raw        duration  missing  invalid
0        30 min 0 days 00:30:00    False    False
1  bad duration             NaT    False     True
2                           NaT     True    False
3          <NA>             NaT     True    False

Total Seconds versus the Seconds Component

For a duration longer than one day, .seconds gives the remaining seconds component. Use total_seconds for the complete elapsed time. A Series uses the .dt accessor; dividing by a one-hour Timedelta gives total hours.

duration = pd.to_timedelta("1 days 02:30:00")
print("Seconds component:", duration.seconds)
print("Total seconds:", duration.total_seconds())
values = pd.to_timedelta(pd.Series(["1 days 02:30:00", "00:30:00"]))
print("Total hours:")
print(values / pd.Timedelta(hours=1))
print("Total seconds:")
print(values.dt.total_seconds())
assert duration.total_seconds() == 95400
assert duration.seconds == 9000

Expected output

Seconds component: 9000
Total seconds: 95400.0
Total hours:
0    26.5
1     0.5
dtype: float64
Total seconds:
0    95400.0
1     1800.0
dtype: float64

Subtract Durations and Interpret Negative Results

Subtracting a duration moves a timestamp earlier. Negative durations display normalized days and a positive clock component. Inspect total_seconds when the sign matters. A negative response time needs review rather than conversion to its absolute value.

base = pd.Timestamp("2024-01-01 10:00:00")
print("Thirty minutes earlier:", base - pd.to_timedelta(30, unit="min"))
negative = pd.to_timedelta(-30, unit="min")
print("Negative duration:", negative)
print("Total seconds:", negative.total_seconds())
assert negative.total_seconds() == -1800

Expected output

Thirty minutes earlier: 2024-01-01 09:30:00
Negative duration: -1 days +23:30:00
Total seconds: -1800.0

Elapsed Days versus Calendar Days across Daylight Saving

A Timedelta day is 24 elapsed hours. A DateOffset day follows the calendar and can span 23 or 25 elapsed hours across a daylight-saving change. This example uses a timezone with a spring clock change; select the rule that matches your deadline policy.

base = pd.Timestamp("2024-03-09 12:00:00", tz="America/New_York")
elapsed = base + pd.to_timedelta(1, unit="D")
calendar = base + pd.DateOffset(days=1)
print("24 elapsed hours:", elapsed)
print("Next calendar day:", calendar)
assert elapsed.hour == 13 and calendar.hour == 12
assert (calendar - base).total_seconds() == 23 * 3600

Expected output

24 elapsed hours: 2024-03-10 13:00:00-04:00
Next calendar day: 2024-03-10 12:00:00-04:00

Practical Data: Calculate Response Durations

This synthetic support dataset includes a slow response, a missing finish time and a reversed timestamp. Parse UTC timestamps, calculate elapsed time and create a 90-minute threshold with to_timedelta. Keep unknown or negative durations in a review group.

def ticket_data():
    return pd.DataFrame({"ticket_id": [101, 102, 103, 104, 105],
                         "start": ["2024-01-01 09:00:00"] * 5,
                         "finish": ["2024-01-01 09:30:00", "2024-01-01 10:30:00", "2024-01-01 11:00:00", None, "2024-01-01 08:45:00"]})

tickets = ticket_data()
for column in ["start", "finish"]:
    tickets[column] = pd.to_datetime(tickets[column], format="%Y-%m-%d %H:%M:%S", utc=True, errors="coerce")
tickets["elapsed"] = tickets["finish"] - tickets["start"]
threshold = pd.to_timedelta(90, unit="min")
tickets["needs_review"] = tickets["elapsed"].isna() | tickets["elapsed"].lt(pd.Timedelta(0))
tickets["status"] = "review"
valid = ~tickets["needs_review"]
tickets.loc[valid & tickets["elapsed"].le(threshold), "status"] = "within target"
tickets.loc[valid & tickets["elapsed"].gt(threshold), "status"] = "over target"
tickets["total_minutes"] = tickets["elapsed"].dt.total_seconds() / 60
print(tickets[["ticket_id", "elapsed", "total_minutes", "status"]].to_string(index=False))
assert tickets["status"].tolist() == ["within target", "within target", "over target", "review", "review"]

Expected output

 ticket_id           elapsed  total_minutes        status
       101   0 days 00:30:00           30.0 within target
       102   0 days 01:30:00           90.0 within target
       103   0 days 02:00:00          120.0   over target
       104               NaT            NaN        review
       105 -1 days +23:45:00          -15.0        review

Report the Denominator and Export Numeric Durations

A response of exactly 90 minutes meets this inclusive target. Exclude review records from the target rate and disclose their count. Missing finish times are not zero. For portable CSV output, export total minutes with explicit review labels.

from pathlib import Path

valid_tickets = tickets.loc[~tickets["needs_review"]]
within = valid_tickets["elapsed"].le(threshold).sum()
print("Valid responses:", len(valid_tickets))
print("Within target:", within)
print("Review records:", tickets["needs_review"].sum())
print("Target rate:", round(100 * within / len(valid_tickets), 1))
assert len(valid_tickets) + tickets["needs_review"].sum() == len(tickets)
assert valid_tickets["total_minutes"].sum() == 240
folder = Path("pandas_timedelta_practice")
folder.mkdir(exist_ok=True)
path = folder / "response_minutes.csv"
tickets[["ticket_id", "total_minutes", "status"]].to_csv(path, index=False)
restored = pd.read_csv(path)
assert len(restored) == 5 and restored["total_minutes"].isna().sum() == 1
assert restored["status"].tolist() == tickets["status"].tolist()
print("Verified CSV:", path.as_posix())

Expected output

Valid responses: 3
Within target: 2
Review records: 2
Target rate: 66.7
Verified CSV: pandas_timedelta_practice/response_minutes.csv

Common Duration Conversion Problems

Unexpected tiny value: specify the unit for numeric input. Months or years rejected: use DateOffset for calendar changes. NaT appeared: inspect raw values and missing-input flags. Total is too small: use total_seconds rather than the seconds component. Negative time: check timestamp order and source timezones. Different wall-clock deadline: choose elapsed versus calendar arithmetic. Label mismatch: Series arithmetic aligns indexes. For further practice, combine Excel imports, data cleaning and time-based summaries.

Exercise: Convert a Minutes Column

Convert 30, 90, a missing value and -5 into durations. Flag unknown or negative durations for review. With an inclusive 60-minute target, predict one within-target record, one over-target record and two review records.

Exercise Solution

Check validity before comparing durations with a threshold. Missing comparisons must not silently count as successful responses. A nullable integer column preserves whole-minute measurements and missing values.

minutes = pd.Series([30, 90, None, -5], dtype="Int64")
durations = pd.to_timedelta(minutes, unit="min")
review = durations.isna() | durations.lt(pd.Timedelta(0))
within = ~review & durations.le(pd.to_timedelta(60, unit="min"))
over = ~review & durations.gt(pd.to_timedelta(60, unit="min"))
print(pd.DataFrame({"minutes": minutes, "duration": durations, "review": review, "within": within, "over": over}))
assert within.sum() == over.sum() == 1
assert review.sum() == 2

Expected output

   minutes          duration  review  within   over
0       30   0 days 00:30:00   False    True  False
1       90   0 days 01:30:00   False   False   True
2     <NA>               NaT    True   False  False
3       -5 -1 days +23:55:00    True   False  False

Practice in Google Colab

Open in Google Colab View on GitHub
All synthetic data is included. Run the duration examples, inspect review records and try the exercise. Save a copy in Drive before editing.

Continue with Pandas date and time, date_range(), period_range() and Excel export. Reference: Pandas to_timedelta documentation.




Subscribe to our YouTube Channel here



plus2net.com







Python Video Tutorials
Python SQLite Video Tutorials
Python MySQL Video Tutorials
Python Tkinter Video Tutorials
✖
We use cookies to improve your browsing experience. . Learn more
HTML MySQL PHP JavaScript ASP Photoshop Articles Contact us
© 2000-2026 plus2net.com All rights reserved worldwide Privacy Policy Disclaimer