Humanizing Differences: Making Time Intervals More Readable with Pendulum
Raw time differences like timedelta(days=45, hours=12) are hard for humans to understand quickly. In data science, showing "3 days ago", "2 weeks from now", or "5 months old" makes reports, dashboards, and logs far more intuitive. Pendulum’s humanizing features turn technical time deltas into natural, readable language with almost zero effort.
TL;DR — Humanizing Time with Pendulum
.diff()→ calculates difference.in_words()→ a duration in words ("1 month 3 days").diff_for_humans()→ relative wording ("3 days ago", "in 2 weeks").in_words(locale=...)→ supports multiple languages- Works on both past and future intervals
1. Basic Humanizing
import pendulum
now = pendulum.now("UTC")
# Past
past = pendulum.parse("2026-02-15 10:00:00", tz="UTC")
print(past.diff(now).in_words()) # a duration, e.g. "8 months 2 weeks ..."
print(past.diff_for_humans()) # relative, e.g. "8 months ago"
# Future
future = now.add(days=14)
print(now.diff(future).in_words()) # "2 weeks"
print(future.diff_for_humans()) # "in 2 weeks"
2. Real-World Data Science Examples
import pandas as pd
import pendulum
# Small inline sample so this example runs as-is.
# In a real project you would load it with pd.read_csv("sales_data.csv")
df = pd.DataFrame({
"customer_id": [101, 102, 103, 104, 105, 106],
"customer_name": ["Ana", "Ben", "Chen", "Dana", "Eli", "Fay"],
"order_date": ["2026-01-05", "2026-01-12", "2026-02-03",
"2026-02-18", "2026-03-01", "2026-03-09"],
"amount": [1250.75, 890.50, 2340.00, 1780.25, 2615.40, 450.00],
"quantity": [3, 2, 5, 4, 6, 1],
"profit": [312.69, 222.63, 585.00, 445.06, 653.85, 112.50],
"region": ["North", "South", "East", "North", "West", "South"],
"category": ["Electronics", "Books", "Electronics", "Home", "Home", "Books"],
"rating": [5, 4, None, 3, 5, 4],
"year": [2025, 2025, 2026, 2026, 2026, 2026],
"description": ["fast laptop", "python book", "noise cancelling headphones",
"desk lamp", "standing desk", "data science book"],
})
df["order_date"] = pd.to_datetime(df["order_date"]).dt.tz_localize("UTC") # make the dates UTC-aware
# Current time
now = pendulum.now("UTC")
# Human-readable freshness
df["time_ago"] = df["order_date"].apply(
lambda x: pendulum.instance(x).diff_for_humans(now) if pd.notna(x) else "Unknown"
)
df["days_ago"] = df["order_date"].apply(
lambda x: (now - pendulum.instance(x)).in_words() if pd.notna(x) else "Unknown"
)
print(df[["order_date", "time_ago"]].head())
3. Advanced Humanizing Features
# Custom granularity
diff = pendulum.parse("2025-12-01").diff(now)
print(diff.in_words()) # duration, e.g. "11 months ..."
print(diff.in_words(locale="fr")) # same duration in French
print(pendulum.parse("2025-12-01").diff_for_humans(locale="fr")) # e.g. "il y a 11 mois"
# Relative time in reports
for row in df.itertuples():
print(f"Order {row.customer_id} was placed {pendulum.instance(row.order_date).diff_for_humans()}")
4. Best Practices in 2026
- Use
.diff_for_humans()for "ago / from now" labels and.in_words()for durations - Always parse with explicit timezone before calculating differences
- Use
localeparameter for international applications - Combine with pandas
.apply()for batch humanization - Keep raw datetime columns for calculations and add humanized columns for display
Duration Words vs Relative Words
Pendulum offers two different kinds of human-readable output, and mixing them up is the most common error with this feature. interval.in_words() describes a length of time — "1 month 3 days 14 hours" — with no direction. dt.diff_for_humans(other) describes a position in time relative to another moment and rounds to the largest sensible unit: "8 months ago", "in 2 weeks", or "1 month before" when you pass an explicit reference. Both accept a locale argument.
Worked Example: Deterministic Labels
import pendulum
ref = pendulum.datetime(2026, 3, 19, 12, 0, tz="UTC") # fixed "now" for reproducible output
events = {
"signup": ref.subtract(months=1, days=3, hours=14),
"last_login": ref.subtract(minutes=5),
"renewal": ref.add(days=14),
}
for name, when in events.items():
gap = when.diff(ref) # absolute interval
print(f"{name:10} | {gap.in_words():24} | {when.diff_for_humans(ref)}")
# signup | 1 month 3 days 14 hours | 1 month before
# last_login | 5 minutes | 5 minutes before
# renewal | 2 weeks | 2 weeks after
print(ref.subtract(days=3).diff_for_humans(ref, locale="fr")) # 3 jours avant
With an explicit reference, the wording is "before/after"; without one, pendulum compares against the current time and says "ago/in". Fixing the reference makes reports and tests reproducible.
Common Mistakes
- Expecting
in_words()to say "ago": it only describes the duration - Calling
pendulum.parse()on pandas Timestamps: usependulum.instance() - Using humanised strings for sorting or maths: keep the raw datetimes
Quick Summary
in_words()= how long;diff_for_humans()= when, relative to now- Pass a reference datetime for reproducible labels
- Use
locale=for other languages
Conclusion
Pendulum’s ability to humanize time differences turns technical timedelta objects into natural language that users actually understand. In 2026 data science projects, features like “3 days ago”, “in 2 weeks”, or “5 months old” make dashboards, alerts, and reports far more user-friendly and professional. Add Pendulum’s humanizing methods to your toolkit and watch your time-based insights become instantly more accessible.
Next steps:
- Add a human-readable “time ago” column to one of your datasets using Pendulum’s
.diff_for_humans()