Get the Current Date and Time in Python (datetime.now)

datetime.now() is how Python hands you the current date and time, in a single object:

from datetime import datetime

now = datetime.now()        # 2026-09-29 10:16:12.051584

For the date on its own, date.today() is shorter. To control how it looks, pass a pattern to strftime().

One thing has changed recently and is worth knowing: datetime.utcnow() is deprecated. The replacement is covered below, along with formatting, timezones and timestamps. Output is from a real run on Python 3.12.5.

Diagram comparing datetime.now, date.today, time and strftime output for the current date and time in Python
Four ways to ask for the current moment, and what each returns.

Get the current date and time with datetime.now()

One call gives you everything:

from datetime import datetime

now = datetime.now()

print("datetime.now() ->", now)
print("type           ->", type(now).__name__)
print()
print("the date and the time, down to microseconds, in one object.")
print()
print("pull out the parts you want:")
print("  year   ->", now.year)
print("  month  ->", now.month)
print("  day    ->", now.day)
print("  hour   ->", now.hour)
print("  minute ->", now.minute)
print("  second ->", now.second)

Output:

datetime.now() -> 2026-09-29 10:21:28.007947
type           -> datetime

the date and the time, down to microseconds, in one object.

pull out the parts you want:
  year   -> 2026
  month  -> 9
  day    -> 29
  hour   -> 10
  minute -> 21
  second -> 28
Command Prompt showing Python datetime.now returning the current date and time with its parts
datetime.now() returns a single object holding both date and time.

Every part is available as an attribute, so there is no parsing to do. The microseconds are real precision, not padding, though the operating system clock may not update that finely.

Note the import. datetime is both a module and a class inside it, which is why from datetime import datetime looks odd the first time you write it.

Get the current date or time on its own

Two shorter routes when you do not need both halves:

from datetime import datetime, date

print("just the date:")
print("  date.today()          ->", date.today())
print("  datetime.now().date() ->", datetime.now().date())
print()
print("just the time:")
print("  datetime.now().time() ->", datetime.now().time())
print()
print("date.today() is the direct route when the time is irrelevant.")
print("There is no time.today(), so the time comes from a datetime.")

Output:

just the date:
  date.today()          -> 2026-09-29
  datetime.now().date() -> 2026-09-29

just the time:
  datetime.now().time() -> 10:21:28.088303

date.today() is the direct route when the time is irrelevant.
There is no time.today(), so the time comes from a datetime.

date.today() is the direct one. There is no matching time.today(), so the time comes from a datetime with .time() called on it.

Once you have a date object, formatting and arithmetic work exactly as they do on a datetime. Counting days between two dates builds on that.

Format the date and time with strftime

The default text is rarely the text you want:

from datetime import datetime

now = datetime.now()

print("the raw object prints in a fixed way:")
print(" ", now)
print()
print("strftime turns it into whatever text you want:")
for pattern in ("%Y-%m-%d", "%d/%m/%Y", "%H:%M:%S",
                "%Y-%m-%d %H:%M:%S", "%B %d, %Y", "%I:%M %p", "%A"):
    print(f"  {pattern:20} -> {now.strftime(pattern)}")

print()
print("an f-string does the same without calling strftime:")
print(f'  f"{{now:%Y-%m-%d %H:%M}}" -> {now:%Y-%m-%d %H:%M}')
print()
print("and the standard machine-readable form:")
print("  now.isoformat() ->", now.isoformat())

Output:

the raw object prints in a fixed way:
  2026-09-29 10:21:28.159803

strftime turns it into whatever text you want:
  %Y-%m-%d             -> 2026-09-29
  %d/%m/%Y             -> 29/09/2026
  %H:%M:%S             -> 10:21:28
  %Y-%m-%d %H:%M:%S    -> 2026-09-29 10:21:28
  %B %d, %Y            -> September 29, 2026
  %I:%M %p             -> 10:21 AM
  %A                   -> Tuesday

an f-string does the same without calling strftime:
  f"{now:%Y-%m-%d %H:%M}" -> 2026-09-29 10:21

and the standard machine-readable form:
  now.isoformat() -> 2026-09-29T10:21:28.159803
Command Prompt showing Python strftime formatting the current date and time in several patterns
strftime() turns a datetime into whatever text you need.
CodeMeansExample
%YFour digit year2026
%mMonth as a number09
%BMonth nameSeptember
%dDay of the month29
%AWeekday nameTuesday
%H / %IHour, 24 or 12 clock10
%M / %SMinute and second16 / 12
%pAM or PMAM

An f-string accepts the same codes directly, which saves a method call in the middle of a message. Going the other way, turning text back into a date uses the matching codes.

Why datetime.utcnow() is deprecated

This one has changed, and older tutorials have not caught up:

import warnings
from datetime import datetime, timezone

with warnings.catch_warnings(record=True) as caught:
    warnings.simplefilter("always")
    value = datetime.utcnow()
    print("datetime.utcnow() ->", value)
    print("its tzinfo        ->", value.tzinfo, " <- no timezone attached")
    for w in caught:
        print()
        print(f"{w.category.__name__}:")
        print(str(w.message))

print()
print("the replacement keeps the timezone:")
modern = datetime.now(timezone.utc)
print("  datetime.now(timezone.utc) ->", modern)
print("  its tzinfo                 ->", modern.tzinfo)

Output:

datetime.utcnow() -> 2026-09-29 04:51:28.223799
its tzinfo        -> None  <- no timezone attached

DeprecationWarning:
datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).

the replacement keeps the timezone:
  datetime.now(timezone.utc) -> 2026-09-29 04:51:28.223799+00:00
  its tzinfo                 -> UTC
Command Prompt showing the Python DeprecationWarning that datetime.utcnow is scheduled for removal
utcnow() warns, and the value it returns carries no timezone.

The problem is visible in the output. utcnow() hands back a datetime holding UTC values with tzinfo=None, so nothing in the object records that it is UTC. Anything that reads it later has to be told separately.

datetime.now(timezone.utc) returns the same instant with the timezone attached, so the value carries its own meaning. Switch to it now rather than waiting for the removal.

Naive and aware datetimes

The distinction the deprecation is pushing you towards:

from datetime import datetime, timezone

naive = datetime.now()
aware = datetime.now(timezone.utc)

print("naive  ->", naive, "  tzinfo:", naive.tzinfo)
print("aware  ->", aware, "tzinfo:", aware.tzinfo)
print()

try:
    naive - aware
except TypeError as err:
    print("naive - aware -> TypeError:", err)

print()
print("a naive datetime does not know what it means. Two of them from")
print("different machines cannot be compared safely.")
print()
print("attaching a timezone to a naive value you KNOW is UTC:")
print(" ", naive.replace(tzinfo=timezone.utc))

Output:

naive  -> 2026-09-29 10:21:28.293028   tzinfo: None
aware  -> 2026-09-29 04:51:28.293028+00:00 tzinfo: UTC

naive - aware -> TypeError: can't subtract offset-naive and offset-aware datetimes

a naive datetime does not know what it means. Two of them from
different machines cannot be compared safely.

attaching a timezone to a naive value you KNOW is UTC:
  2026-09-29 10:21:28.293028+00:00
Command Prompt showing the Python TypeError when subtracting a naive datetime from an aware one
Mixing the two raises rather than producing a wrong answer.

A naive datetime has no timezone, so it means whatever the machine that produced it happened to mean. Two of them from different servers cannot be compared safely.

Python refuses to mix the two rather than guessing. When you know a naive value is UTC, .replace(tzinfo=timezone.utc) labels it without shifting the clock.

Current time in a specific timezone

zoneinfo has been in the standard library since Python 3.9:

from datetime import datetime, timezone
from zoneinfo import ZoneInfo

print("the same instant, shown in three places:")
print("  UTC       ->", datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S %Z"))
print("  New York  ->", datetime.now(ZoneInfo("America/New_York")).strftime("%Y-%m-%d %H:%M:%S %Z%z"))
print("  Tokyo     ->", datetime.now(ZoneInfo("Asia/Tokyo")).strftime("%Y-%m-%d %H:%M:%S %Z%z"))
print()
print("zoneinfo ships with Python and handles daylight saving for you,")
print("which a fixed offset such as timezone(timedelta(hours=-5)) cannot.")
print()
print("converting rather than re-reading the clock:")
utc_now = datetime.now(timezone.utc)
print("  UTC       ->", utc_now.strftime("%H:%M:%S %Z"))
print("  as Tokyo  ->", utc_now.astimezone(ZoneInfo("Asia/Tokyo")).strftime("%H:%M:%S %Z"))

Output:

the same instant, shown in three places:
  UTC       -> 2026-09-29 04:51:28 UTC
  New York  -> 2026-09-29 00:51:28 EDT-0400
  Tokyo     -> 2026-09-29 13:51:28 JST+0900

zoneinfo ships with Python and handles daylight saving for you,
which a fixed offset such as timezone(timedelta(hours=-5)) cannot.

converting rather than re-reading the clock:
  UTC       -> 04:51:28 UTC
  as Tokyo  -> 13:51:28 JST
Command Prompt showing the current time in UTC New York and Tokyo using Python zoneinfo
Named timezones handle daylight saving; a fixed offset does not.

Use a named zone such as America/New_York rather than a fixed offset. New York is four hours behind UTC in summer and five in winter, and only the named zone knows which applies today.

.astimezone() converts a moment you already have instead of reading the clock again, which matters when several values must line up. Converting local time to UTC covers the other direction.

Get the current timestamp

Seconds since the start of 1970, which is what most APIs and databases want:

import time
from datetime import datetime

print("seconds since 1 January 1970:")
print("  time.time()              ->", time.time())
print("  datetime.now().timestamp() ->", datetime.now().timestamp())
print()
print("turning one back into a datetime:")
stamp = datetime.now().timestamp()
print("  datetime.fromtimestamp(stamp) ->", datetime.fromtimestamp(stamp))
print()
print("a whole number, which is what most APIs want:")
print("  int(time.time()) ->", int(time.time()))
print()
print("milliseconds:")
print("  int(time.time() * 1000) ->", int(time.time() * 1000))

Output:

seconds since 1 January 1970:
  time.time()              -> 1790657488.5116405
  datetime.now().timestamp() -> 1790657488.51164

turning one back into a datetime:
  datetime.fromtimestamp(stamp) -> 2026-09-29 10:21:28.512647

a whole number, which is what most APIs want:
  int(time.time()) -> 1790657488

milliseconds:
  int(time.time() * 1000) -> 1790657488512

time.time() and .timestamp() give the same number as a float. Wrap it in int() for a whole number of seconds, or multiply by 1000 for milliseconds.

datetime.fromtimestamp() turns one back into a datetime. Converting a datetime to a Unix timestamp goes through the edge cases.

Adding and subtracting time

timedelta handles the arithmetic:

from datetime import datetime, timedelta

now = datetime.now()

print("now          ->", now.strftime("%Y-%m-%d %H:%M"))
print("tomorrow     ->", (now + timedelta(days=1)).strftime("%Y-%m-%d %H:%M"))
print("a week ago   ->", (now - timedelta(weeks=1)).strftime("%Y-%m-%d %H:%M"))
print("in 90 minutes->", (now + timedelta(minutes=90)).strftime("%Y-%m-%d %H:%M"))
print()

earlier = now - timedelta(hours=3, minutes=20)
gap = now - earlier
print("subtracting two datetimes gives a timedelta:")
print("  gap              ->", gap)
print("  type             ->", type(gap).__name__)
print("  gap.total_seconds() ->", gap.total_seconds())
print()
print("timedelta has no months or years, because neither has a fixed length.")

Output:

now          -> 2026-09-29 10:21
tomorrow     -> 2026-09-30 10:21
a week ago   -> 2026-09-22 10:21
in 90 minutes-> 2026-09-29 11:51

subtracting two datetimes gives a timedelta:
  gap              -> 3:20:00
  type             -> timedelta
  gap.total_seconds() -> 12000.0

timedelta has no months or years, because neither has a fixed length.

Subtracting two datetimes gives a timedelta, and total_seconds() flattens it into a single number for comparisons.

There is no months or years argument, because neither has a fixed length. Adding a month means deciding what happens on the 31st, which is a choice the library refuses to make for you.

Reading a date back from text

strptime is the reverse of strftime:

from datetime import datetime

text = "2026-09-29 14:30:00"
parsed = datetime.strptime(text, "%Y-%m-%d %H:%M:%S")
print("strptime reads text back into a datetime:")
print("  ", repr(text), "->", parsed)
print()

print("the pattern has to match the text exactly:")
try:
    datetime.strptime("29/09/2026", "%Y-%m-%d")
except ValueError as err:
    print("  ValueError:", err)
print()

print("for ISO text there is a shortcut that needs no pattern:")
print("  datetime.fromisoformat('2026-09-29T14:30:00') ->",
      datetime.fromisoformat("2026-09-29T14:30:00"))
print()
print("strftime writes text OUT, strptime reads text IN.")
print("The p stands for parse.")

Output:

strptime reads text back into a datetime:
   '2026-09-29 14:30:00' -> 2026-09-29 14:30:00

the pattern has to match the text exactly:
  ValueError: time data '29/09/2026' does not match format '%Y-%m-%d'

for ISO text there is a shortcut that needs no pattern:
  datetime.fromisoformat('2026-09-29T14:30:00') -> 2026-09-29 14:30:00

strftime writes text OUT, strptime reads text IN.
The p stands for parse.

The pattern must describe the text exactly, or you get a ValueError naming both. That message is the fastest way to spot a day and month the wrong way round.

For ISO text, fromisoformat() needs no pattern at all. Converting a string to a datetime covers the awkward formats.

Current date and time quick reference

  • datetime.now() for the date and time together.
  • date.today() for the date alone.
  • datetime.now(timezone.utc) for UTC, never utcnow().
  • now.strftime("%Y-%m-%d"), or an f-string with the same codes, to format it.
  • ZoneInfo("Area/City") for a named timezone that understands daylight saving.
  • time.time() or .timestamp() for seconds since 1970.
  • timedelta to add or subtract; subtracting two datetimes gives one back.

Related date and time guides:

Frequently asked questions

How do I get the current date and time in Python?

Import the class and call it: from datetime import datetime then datetime.now(). That returns one object holding both the date and the time.

How do I get only the current date?

from datetime import date then date.today(). Calling datetime.now().date() gives the same result.

Why is datetime.utcnow() deprecated?

Because it returns a datetime with tzinfo=None, so nothing in the value records that it is UTC. Use datetime.now(timezone.utc), which attaches the timezone.

How do I format the current date in Python?

Call strftime with a pattern, such as now.strftime("%Y-%m-%d"). An f-string accepts the same codes: f"{now:%Y-%m-%d}".

How do I get the current time in another timezone?

datetime.now(ZoneInfo("Asia/Tokyo")), importing ZoneInfo from zoneinfo. A named zone handles daylight saving; a fixed offset does not.

What is the difference between a naive and an aware datetime?

A naive one has no timezone and means whatever the machine meant. An aware one carries its offset. Python raises TypeError rather than letting you mix them.

How do I get the current timestamp in Python?

time.time() or datetime.now().timestamp() give seconds since 1970 as a float. The full set of format codes is in the Python strftime format codes documentation.