pyutils.datetimes package

This subpackage contains code for parsing and manipulating dates, times and datetimes.

Submodules

pyutils.datetimes.constants module

Universal date/time constants.

pyutils.datetimes.dateparse_utils module

Parse dates / datetimes in a variety of formats. Some examples:

today
tomorrow
yesterday
21:30
12:01am
12:01pm
last Wednesday
this Wednesday
next Wed
this coming Tues
this past Mon
4 days ago
4 Mondays ago
4 months ago
3 days back
13 weeks from now
1 year from now
4 weeks from now
3 saturdays ago
4 months from today
5 years from yesterday
6 weeks from tomorrow
april 15, 2005
april 21
9:30am on last Wednesday
2005/apr/15
2005 apr 15
the 1st wednesday in may
the last sun of june
this easter
last xmas
Christmas, 1999
next MLK day
Halloween, 2020
5 work days after independence day
50 working days from last wed
25 working days before xmas
today +1 week
sunday -3 weeks
3 weeks before xmas, 1999
3 days before new years eve, 2000
july 4th
the ides of march
the nones of april
the kalends of may
4 sundays before veterans’ day
xmas eve
this friday at 5pm
presidents day
memorial day, 1921
thanksgiving
2 sun in jun
easter -40 days
2 days before last xmas at 3:14:15.92a
3 weeks after xmas, 1995 at midday
4 months before easter, 1992 at midnight
5 months before halloween, 1995 at noon
4 days before last wednesday
44 months after today
44 years before today
44 weeks ago
15 minutes to 3am
quarter past 4pm
half past 9
4 seconds to midnight
4 seconds to midnight, tomorrow
2021/apr/15T21:30:44.55
2021/apr/15 at 21:30:44.55
2021/4/15 at 21:30:44.55
2021/04/15 at 21:30:44.55Z
2021/04/15 at 21:30:44.55EST
13 days after last memorial day at 12 seconds before 4pm

This code is used by other code in the pyutils library such as pyutils.argparse_utils.valid_datetime(), pyutils.argparse_utils.valid_date(), pyutils.string_utils.to_datetime() and pyutils.string_utils.to_date(). This means any of these are also able to accept and recognize this larger set of date expressions.

See the unittest for more examples and the grammar for more details.

class pyutils.datetimes.dateparse_utils.DateParser(*, override_now_for_test_purposes=None)[source]

A class to parse dates expressed in human language (English). Example usage:

d = DateParser()
d.parse('next wednesday')
dt = d.get_datetime()
print(dt)
Wednesday 2022/10/26 00:00:00.000000

Note that the interface is somewhat klunky here because this class is conforming to interfaces auto-generated by ANTLR as it parses the grammar. See also pyutils.string_utils.to_date().

Construct a parser.

Parameters:

override_now_for_test_purposes – passing a value to override_now_for_test_purposes can be used to force this parser instance to use a custom date/time for its idea of “now” so that the code can be more easily unittested. Leave as None for real use cases.

get_date() date | None[source]
Returns:

The date part of the last parse() operation again or None.

Return type:

date | None

get_datetime(*, tz: tzinfo | None = None) datetime | None[source]

Get the datetime of the last parse() operation again ot None.

Parameters:

tz (tzinfo | None) – the timezone to set on output times. By default we return timezone-naive datetime objects.

Returns:

the same datetime that parse() last did, optionally overriding the timezone. Returns None of no calls to parse() have yet been made.

Return type:

datetime | None

Note

Parsed date expressions without any time part return hours = minutes = seconds = microseconds = 0 (i.e. at midnight that day). Parsed time expressions without any date part default to date = today.

get_time() time | None[source]
Returns:

The time part of the last parse() operation again or None.

Return type:

time | None

parse(date_string: str) datetime | None[source]

Parse a ~free form date/time expression and return a timezone agnostic datetime on success. Also sets self.datetime, self.date and self.time which can each be accessed other methods on the class: get_datetime(), get_date() and get_time(). Raises a PyUtilsDateParseException with a helpful(?) message on parse error or confusion.

This is the main entrypoint to this class for caller code.

To get an idea of what expressions can be parsed, check out the unittest and the grammar.

Parameters:

date_string (str) – the string to parse

Returns:

A datetime.datetime representing the parsed date/time or None on error.

Raises:

PyUtilsDateParseException – an exception happened during parsing

Return type:

datetime | None

Note

Parsed date expressions without any time part return hours = minutes = seconds = microseconds = 0 (i.e. at midnight that day). Parsed time expressions without any date part default to date = today.

Example usage:

txt = '3 weeks before last tues at 9:15am'
dp = DateParser()
dt1 = dp.parse(txt)
dt2 = dp.get_datetime(tz=pytz.timezone('US/Pacific'))

# dt1 and dt2 will be identical other than the fact that
# the latter's tzinfo will be set to PST/PDT.

pyutils.datetimes.datetime_utils module

Utilities related to dates, times, and datetimes.

class pyutils.datetimes.datetime_utils.TimeUnit(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)[source]

Bases: IntEnum

An enum to represent units with which we can compute deltas.

DAYS = 10
FRIDAYS = 4
HOURS = 9
MINUTES = 8
MONDAYS = 0
MONTHS = 13
SATURDAYS = 5
SECONDS = 7
SUNDAYS = 6
THURSDAYS = 3
TUESDAYS = 1
WEDNESDAYS = 2
WEEKS = 12
WORKDAYS = 11
YEARS = 14
classmethod is_valid(value: Any)[source]
Parameters:

value (Any) – a value to be checked

Returns:

True is input value is a valid TimeUnit, False otherwise.

pyutils.datetimes.datetime_utils.add_timezone(dt: datetime, tz: tzinfo) datetime[source]

Adds a timezone to a timezone naive datetime.

Parameters:
  • dt (datetime) – the datetime to insert a timezone onto

  • tz (tzinfo) – the timezone to insert

Return type:

datetime

See also replace_timezone(), strip_timezone(), translate_timezone().

Returns:

A datetime identical to dt, the input datetime, except for that a timezone has been added.

Raises:

ValueError – if dt is already a timezone aware datetime.

Parameters:
  • dt (datetime) –

  • tz (tzinfo) –

Return type:

datetime

Warning

This doesn’t change the hour, minute, second, day, month, etc… of the input timezone. It simply adds a timezone to it. Adding a timezone this way will likely change the instant to which the datetime refers. See examples.

>>> now = datetime.datetime.now()
>>> is_timezone_aware(now)
False
>>> now_pacific = add_timezone(now, pytz.timezone('US/Pacific'))
>>> is_timezone_aware(now_pacific)
True
>>> "US/Pacific" in now_pacific.tzinfo.__repr__()
True
>>> now.hour == now_pacific.hour
True
>>> now.minute == now_pacific.minute
True
pyutils.datetimes.datetime_utils.date_and_time_to_datetime(date: date, time: time) datetime[source]

Given a date and time, merge them and return a datetime.

Parameters:
  • date (date) – the date component

  • time (time) – the time component

Returns:

A datetime with the time component set from time and the date component set from date.

Return type:

datetime

>>> import datetime
>>> d = datetime.date(2021, 12, 25)
>>> t = datetime.time(12, 30, 0, 0)
>>> date_and_time_to_datetime(d, t)
datetime.datetime(2021, 12, 25, 12, 30)
pyutils.datetimes.datetime_utils.date_to_datetime(date: date) datetime[source]

Given a date, return a datetime with hour/min/sec zero (midnight)

Arg:

date: the date desired

Returns:

A datetime with the same month, day, and year as the input date and hours, minutes, seconds set to 12:00:00am.

Parameters:

date (date) –

Return type:

datetime

>>> import datetime
>>> date_to_datetime(datetime.date(2021, 12, 25))
datetime.datetime(2021, 12, 25, 0, 0)
pyutils.datetimes.datetime_utils.datetime_to_date(dt: datetime) date[source]

Return just the date part of a datetime.

Parameters:

dt (datetime) – the datetime

Returns:

A datetime.date with month, day and year set from input dt.

Return type:

date

>>> import datetime
>>> dt = datetime.datetime(2021, 12, 25, 12, 30)
>>> datetime_to_date(dt)
datetime.date(2021, 12, 25)
pyutils.datetimes.datetime_utils.datetime_to_date_and_time(dt: datetime) Tuple[date, time][source]

Return the component date and time objects of a datetime in a Tuple given a datetime.

Parameters:

dt (datetime) – the datetime to decompose

Returns:

A tuple whose first element contains a datetime.date that holds the day, month, year, etc… from the input dt and whose second element contains a datetime.time with hour, minute, second, micros, and timezone set from the input dt.

Return type:

Tuple[date, time]

>>> import datetime
>>> dt = datetime.datetime(2021, 12, 25, 12, 30)
>>> (d, t) = datetime_to_date_and_time(dt)
>>> d
datetime.date(2021, 12, 25)
>>> t
datetime.time(12, 30)
pyutils.datetimes.datetime_utils.datetime_to_minute_number(dt: datetime) MinuteOfDay[source]

Convert a datetime’s time component into a minute number (of the day). Note that this ignores the date part of the datetime and only uses the time part.

Parameters:

dt (datetime) – the datetime whose time is to be converted

Returns:

The minute number (of the day) that represents the input datetime’s time.

Return type:

MinuteOfDay

>>> d = string_to_datetime(
...                        "2021/09/10 11:24:51AM-0700",
...                       )[0]
>>> datetime_to_minute_number(d)
684
pyutils.datetimes.datetime_utils.datetime_to_string(dt: datetime, *, date_time_separator: str = ' ', include_timezone: bool = True, include_dayname: bool = False, use_month_abbrevs: bool = False, include_seconds: bool = True, include_fractional: bool = False, twelve_hour: bool = True) str[source]

A nice way to convert a datetime into a string; arguably better than just printing it and relying on it __repr__().

Parameters:
  • dt (datetime) – the datetime to represent

  • date_time_separator (str) – the character or string to separate the date and time pieces of the representation.

  • include_timezone (bool) – should we include a timezone in the representation?

  • include_dayname (bool) – should we include the dayname (e.g. Mon) in the representation or omit it?

  • use_month_abbrevs (bool) – should we name the month briefly (e.g. Jan) or spell it out fully (e.g. January) in the representation?

  • include_seconds (bool) – should we include seconds in the time?

  • include_fractional (bool) – should we include micros in the time?

  • twelve_hour (bool) – should we use twelve or twenty-four hour time format?

Return type:

str

>>> d = string_to_datetime(
...                        "2021/09/10 11:24:51AM-0700",
...                       )[0]
>>> d
datetime.datetime(2021, 9, 10, 11, 24, 51, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=61200)))
>>> datetime_to_string(d)
'2021/09/10 11:24:51AM-0700'
>>> datetime_to_string(d, include_dayname=True, include_seconds=False)
'Fri/2021/09/10 11:24AM-0700'
pyutils.datetimes.datetime_utils.datetime_to_time(dt: datetime) time[source]

Return just the time part of a datetime.

Parameters:

dt (datetime) – the datetime

Returns:

A datetime.time with hour, minute, second, micros, and timezone set from the input dt.

Return type:

time

>>> import datetime
>>> dt = datetime.datetime(2021, 12, 25, 12, 30)
>>> datetime_to_time(dt)
datetime.time(12, 30)
pyutils.datetimes.datetime_utils.describe_duration(seconds: int, *, include_seconds: bool = False) str[source]

Describe a duration represented as a count of seconds nicely.

Parameters:
  • seconds (int) – the number of seconds in the duration to be represented.

  • include_seconds (bool) – should we include or drop the seconds part in the representation?

Return type:

str

Note

Of course if we drop the seconds part the result is not precise. See examples.

>>> describe_duration(182)
'3 minutes'
>>> describe_duration(182, include_seconds=True)
'3 minutes, and 2 seconds'
>>> describe_duration(100, include_seconds=True)
'1 minute, and 40 seconds'

describe_duration(1303200) ‘15 days, 2 hours’

pyutils.datetimes.datetime_utils.describe_duration_briefly(seconds: int, *, include_seconds: bool = False) str[source]

Describe a duration briefly.

Parameters:
  • seconds (int) – the number of seconds in the duration to describe.

  • include_seconds (bool) – should we include seconds in our description or omit?

Returns:

A string describing the duration represented by the input seconds briefly.

Return type:

str

Note

Of course if we drop the seconds part the result is not precise. See examples.

>>> describe_duration_briefly(182)
'3m'
>>> describe_duration_briefly(182, include_seconds=True)
'3m 2s'
>>> describe_duration_briefly(100, include_seconds=True)
'1m 40s'

describe_duration_briefly(1303200) ‘15d 2h’

pyutils.datetimes.datetime_utils.describe_timedelta(delta: timedelta) str[source]

Describe a duration represented by a timedelta object.

Parameters:

delta (timedelta) – the timedelta object that represents the duration to describe.

Returns:

A string representation of the input duration.

Return type:

str

Warning

Milliseconds are never included in the string representation of durations even through they may be represented by an input datetime.timedelta. Not for use when this level of precision is needed.

>>> d = datetime.timedelta(1, 600)
>>> describe_timedelta(d)
'1 day, and 10 minutes'
pyutils.datetimes.datetime_utils.describe_timedelta_briefly(delta: timedelta, *, include_seconds: bool = False) str[source]

Describe a duration represented by a timedelta object.

Parameters:
  • delta (timedelta) – the timedelta to describe briefly

  • include_seconds (bool) – should we include the second delta?

Returns:

A string description of the input timedelta object.

Return type:

str

Warning

Milliseconds are never included in the string representation of durations even through they may be represented by an input datetime.timedelta. Not for use when this level of precision is needed.

>>> d = datetime.timedelta(1, 600)
>>> describe_timedelta_briefly(d)
'1d 10m'
pyutils.datetimes.datetime_utils.easter(year: int, method: int = 3)[source]

This method was ported from the work done by GM Arts, on top of the algorithm by Claus Tondering, which was based in part on the algorithm of Ouding (1940), as quoted in “Explanatory Supplement to the Astronomical Almanac”, P. Kenneth Seidelmann, editor.

This algorithm implements three different Easter calculation methods:

  1. Original calculation in Julian calendar, valid in dates after 326 AD

  2. Original method, with date converted to Gregorian calendar, valid in years 1583 to 4099

  3. Revised method, in Gregorian calendar, valid in years 1583 to 4099 as well

These methods are represented by the constants:

  • EASTER_JULIAN   = 1

  • EASTER_ORTHODOX = 2

  • EASTER_WESTERN  = 3

The default method is method 3.

More about the algorithm may be found at:

GM Arts: Easter Algorithms

and

The Calendar FAQ: Easter

Raises:

ValueError if method argument is invalid

Parameters:
  • year (int) –

  • method (int) –

pyutils.datetimes.datetime_utils.get_format_string(*, date_time_separator: str = ' ', include_timezone: bool = True, include_dayname: bool = False, use_month_abbrevs: bool = False, include_seconds: bool = True, include_fractional: bool = False, twelve_hour: bool = True) str[source]

Helper to return a format string without looking up the documentation for strftime.

Parameters:
  • date_time_separator (str) – character or string to use between the date and time outputs.

  • include_timezone (bool) – whether or not the result should include a timezone

  • include_dayname (bool) – whether or not the result should incude the dayname (e.g. Monday, Wednesday, etc…)

  • use_month_abbrevs (bool) – whether or not to abbreviate (e.g. Jan) or spell out (e.g. January) month names.

  • include_seconds (bool) – whether or not to include seconds in time.

  • include_fractional (bool) – whether or not to include micros in time output.

  • twelve_hour (bool) – use twelve hour (with am/pm) or twenty four hour time format?

Returns:

The format string for use with strftime that follows the given requirements.

Return type:

str

>>> get_format_string()
'%Y/%m/%d %I:%M:%S%p%z'
>>> get_format_string(date_time_separator='@')
'%Y/%m/%d@%I:%M:%S%p%z'
>>> get_format_string(include_dayname=True)
'%a/%Y/%m/%d %I:%M:%S%p%z'
>>> get_format_string(include_dayname=True, twelve_hour=False)
'%a/%Y/%m/%d %H:%M:%S%z'
pyutils.datetimes.datetime_utils.is_timezone_aware(dt: datetime) bool[source]

Checks whether a datetime is timezone aware or naive. See: https://docs.python.org/3/library/datetime.html#determining-if-an-object-is-aware-or-naive

Parameters:

dt (datetime) – the datetime to check for timezone awareness

Returns:

True if the datetime argument is timezone aware or False if not.

Return type:

bool

>>> is_timezone_aware(datetime.datetime.now())
False
>>> is_timezone_aware(now_pacific())
True
pyutils.datetimes.datetime_utils.is_timezone_naive(dt: datetime) bool[source]

Inverse of is_timezone_aware(). See: https://docs.python.org/3/library/datetime.html#determining-if-an-object-is-aware-or-naive

Parameters:

dt (datetime) – the datetime to check

Returns:

True if the dt argument is timezone naive, False otherwise

Return type:

bool

>>> is_timezone_naive(datetime.datetime.now())
True
>>> is_timezone_naive(now_pacific())
False
pyutils.datetimes.datetime_utils.minute_number(hour: int, minute: int) MinuteOfDay[source]

Convert hour:minute into minute number from start of day. That is, if you imagine a day as a sequence of minutes from minute #0 up to minute #1439, what minute number is, e.g., 6:52am?

Parameters:
  • hour (int) – the hour to convert (0 <= hour <= 23)

  • minute (int) – the minute to convert (0 <= minute <= 59)

Returns:

The minute number requested. Raises ValueError on bad input.

Raises:

ValueError – invalid hour or minute input argument

Return type:

MinuteOfDay

>>> minute_number(0, 0)
0
>>> minute_number(9, 15)
555
>>> minute_number(23, 59)
1439
pyutils.datetimes.datetime_utils.minute_number_to_time_string(minute_num: MinuteOfDay) str[source]

Convert minute number from start of day into hour:minute am/pm string.

Parameters:

minute_num (MinuteOfDay) – the minute number to convert into a string

Returns:

MM[a|p]” that represents the time that the input minute_num refers to.

Return type:

A string of the format “HH

>>> minute_number_to_time_string(315)
' 5:15a'
>>> minute_number_to_time_string(684)
'11:24a'
pyutils.datetimes.datetime_utils.n_timeunits_from_base(count: int, unit: TimeUnit, base: datetime) datetime[source]

Return a datetime that is N units before/after a base datetime. For example:

  • 3 Wednesdays from base datetime,

  • 2 weeks from base date,

  • 10 years before base datetime,

  • 13 minutes after base datetime, etc…

Parameters:
  • count (int) – signed number that indicates N units before/after the base.

  • unit (TimeUnit) – the timeunit that we are counting by.

  • base (datetime) – a datetime representing the base date the result should be relative to.

Raises:

ValueError – unit is invalid

Returns:

A datetime that is count units before of after the base datetime.

Return type:

datetime

Note

To indicate before/after the base date, use a positive or negative count.

>>> base = string_to_datetime("2021/09/10 11:24:51AM-0700")[0]

The next (1) Monday from the base datetime:

>>> n_timeunits_from_base(+1, TimeUnit.MONDAYS, base)
datetime.datetime(2021, 9, 13, 11, 24, 51, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=61200)))

Ten (10) years after the base datetime:

>>> n_timeunits_from_base(10, TimeUnit.YEARS, base)
datetime.datetime(2031, 9, 10, 11, 24, 51, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=61200)))

Fifty (50) working days (M..F, not counting holidays) after base datetime:

>>> n_timeunits_from_base(50, TimeUnit.WORKDAYS, base)
datetime.datetime(2021, 11, 23, 11, 24, 51, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=61200)))

Fifty (50) days (including weekends and holidays) after base datetime:

>>> n_timeunits_from_base(50, TimeUnit.DAYS, base)
datetime.datetime(2021, 10, 30, 11, 24, 51, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=61200)))

Fifty (50) months before (note negative count) base datetime:

>>> n_timeunits_from_base(-50, TimeUnit.MONTHS, base)
datetime.datetime(2017, 7, 10, 11, 24, 51, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=61200)))

Fifty (50) hours after base datetime:

>>> n_timeunits_from_base(50, TimeUnit.HOURS, base)
datetime.datetime(2021, 9, 12, 13, 24, 51, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=61200)))

Fifty (50) minutes before base datetime:

>>> n_timeunits_from_base(-50, TimeUnit.MINUTES, base)
datetime.datetime(2021, 9, 10, 10, 34, 51, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=61200)))

Fifty (50) seconds from base datetime:

>>> n_timeunits_from_base(50, TimeUnit.SECONDS, base)
datetime.datetime(2021, 9, 10, 11, 25, 41, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=61200)))

Next month corner case – it will try to make Feb 31, 2022 then count backwards.

>>> base = string_to_datetime("2022/01/31 11:24:51AM-0700")[0]
>>> n_timeunits_from_base(1, TimeUnit.MONTHS, base)
datetime.datetime(2022, 2, 28, 11, 24, 51, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=61200)))

Last month with the same corner case

>>> base = string_to_datetime("2022/03/31 11:24:51AM-0700")[0]
>>> n_timeunits_from_base(-1, TimeUnit.MONTHS, base)
datetime.datetime(2022, 2, 28, 11, 24, 51, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=61200)))
pyutils.datetimes.datetime_utils.now() datetime[source]

What time is it? Result is a timezone naive datetime.

Return type:

datetime

pyutils.datetimes.datetime_utils.now_pacific() datetime[source]

What time is it? Result in US/Pacific time (PST/PDT)

Return type:

datetime

pyutils.datetimes.datetime_utils.parse_duration(duration: str, raise_on_error: bool = False) int[source]

Parse a duration in string form into a delta seconds.

Parameters:
  • duration (str) – a string form duration, see examples.

  • raise_on_error (bool) – should we raise on invalid input or just return a zero duration?

Returns:

A count of seconds represented by the input string.

Raises:

ValueError – bad duration and raise_on_error is set.

Return type:

int

>>> parse_duration('15 days, 2 hours')
1303200
>>> parse_duration('15d 2h')
1303200
>>> parse_duration('100s')
100
>>> parse_duration('3min 2sec')
182
>>> parse_duration('recent')
0
>>> parse_duration('recent', raise_on_error=True)
Traceback (most recent call last):
...
ValueError: recent is not a valid duration.
pyutils.datetimes.datetime_utils.replace_time_timezone(t: time, tz: tzinfo) time[source]

Replaces the timezone on a datetime.time directly without performing any translation.

Parameters:
  • t (time) – the time to change the timezone on

  • tz (tzinfo) – the new timezone desired

Returns:

A time with hour, minute, second, etc… identical to the input time but with timezone replaced.

Return type:

time

Warning

This code isn’t changing the hour, minute, second, etc… of the time. It’s just messing with the timezone. Changing the timezone without changing the time causes the instant to which the datetime refers to change. For example, if passed 7:01pm PST and asked to make it EST, the result will be 7:01pm EST. See examples.

>>> t = datetime.time(8, 15, 12, 0, pytz.UTC)
>>> t.tzname()
'UTC'
>>> t = replace_time_timezone(t, pytz.timezone('US/Pacific'))
>>> t.tzname()
'US/Pacific'
pyutils.datetimes.datetime_utils.replace_timezone(dt: datetime, tz: tzinfo | None) datetime[source]

Replaces the timezone on a timezone aware datetime object directly (leaving the year, month, day, hour, minute, second, micro, etc… alone). The same as calling strip_timezone() followed by add_timezone().

Works with timezone aware and timezone naive dts but for the latter it is probably better to use add_timezone() or just create it with a tz parameter. Using this can have weird side effects like UTC offsets that are not an even multiple of an hour, etc…

Parameters:
  • dt (datetime) – the datetime whose timezone should be changed

  • tz (tzinfo | None) – the new timezone

Returns:

The resulting datetime. Hour, minute, second, etc… are unmodified. See warning below.

Return type:

datetime

See also add_timezone(), strip_timezone(), translate_timezone().

Warning

This code isn’t changing the hour, minute, second, day, month, etc… of the datetime. It’s just messing with the timezone. Changing the timezone without changing the time causes the instant to which the datetime refers to change. For example, if passed 7:01pm PST and asked to make it EST, the result will be 7:01pm EST. See examples.

>>> from pytz import UTC
>>> d = now_pacific()
>>> d.tzinfo.tzname(d)[0]     # Note: could be PST or PDT
'P'
>>> h = d.hour
>>> o = replace_timezone(d, UTC)
>>> o.tzinfo.tzname(o)
'UTC'
>>> o.hour == h
True
pyutils.datetimes.datetime_utils.seconds_to_timedelta(seconds: int) timedelta[source]
Parameters:

seconds (int) – a count of seconds

Returns:

A datetime.timedelta representing that count of seconds.

Return type:

timedelta

pyutils.datetimes.datetime_utils.string_to_datetime(txt: str, *, date_time_separator: str = ' ', include_timezone: bool = True, include_dayname: bool = False, use_month_abbrevs: bool = False, include_seconds: bool = True, include_fractional: bool = False, twelve_hour: bool = True) Tuple[datetime, str][source]

A nice way to convert a string into a datetime. Returns both the datetime and the format string used to parse it. Also consider pyutils.datetimes.dateparse_utils for a full parser alternative.

Parameters:
  • txt (str) – the string to be converted into a datetime

  • date_time_separator (str) – the character or string between the time and date portions.

  • include_timezone (bool) – does the string include a timezone?

  • include_dayname (bool) – does the string include a dayname?

  • use_month_abbrevs (bool) – is the month abbreviated in the string (e.g. Feb) or spelled out completely (e.g. February)?

  • include_seconds (bool) – does the string’s time include seconds?

  • include_fractional (bool) – does the string’s time include micros?

  • twelve_hour (bool) – is the string’s time in twelve or twenty-four hour format?

Returns:

A tuple containing the datetime parsed from string and the formatting string used to parse it.

Return type:

Tuple[datetime, str]

>>> d = string_to_datetime(
...                        "2021/09/10 11:24:51AM-0700",
...                       )
>>> d
(datetime.datetime(2021, 9, 10, 11, 24, 51, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=61200))), '%Y/%m/%d %I:%M:%S%p%z')
pyutils.datetimes.datetime_utils.strip_timezone(dt: datetime) datetime[source]

Remove the timezone from a datetime. Silently ignores datetimes which are already timezone naive.

Parameters:

dt (datetime) – the datetime to remove timezone from

Returns:

A datetime identical to dt, the input argument, except for that the timezone has been removed.

Return type:

datetime

See also add_timezone(), replace_timezone(), translate_timezone().

Warning

This does not change the hours, minutes, seconds, months, days, years, etc… Thus, the instant to which this timestamp refers will change when the timezone is added. See examples.

>>> now = now_pacific()
>>> now.tzinfo == None
False
>>> "US/Pacific" in now.tzinfo.__repr__()
True
>>> dt = strip_timezone(now)
>>> dt == now
False
>>> dt.tzinfo == None
True
>>> dt.hour == now.hour
True
pyutils.datetimes.datetime_utils.time_to_datetime_today(time: time) datetime[source]

Given a time, returns that time as a datetime with a date component set based on the current date. If the time passed is timezone aware, the resulting datetime will also be (and will use the same tzinfo). If the time is timezone naive, the datetime returned will be too.

Parameters:

time (time) – the time desired

Returns:

datetime with hour, minute, second, timezone set to time and day, month, year set to “today”.

Return type:

datetime

>>> t = datetime.time(13, 14, 0)
>>> d = now_pacific().date()
>>> dt = time_to_datetime_today(t)
>>> dt.date() == d
True
>>> dt.time() == t
True
>>> dt.tzinfo == t.tzinfo
True
>>> dt.tzinfo == None
True
>>> t = datetime.time(8, 15, 12, 0, pytz.UTC)
>>> t.tzinfo == None
False
>>> dt = time_to_datetime_today(t)
>>> dt.tzinfo == None
False
pyutils.datetimes.datetime_utils.time_to_minute_number(t: time) MinuteOfDay[source]

Convert a datetime.time into a minute number.

Parameters:

t (time) – a datetime.time to convert into a minute number.

Returns:

The minute number (of the day) of the input time.

Return type:

MinuteOfDay

>>> t = datetime.time(5, 15)
>>> time_to_minute_number(t)
315
pyutils.datetimes.datetime_utils.time_to_string(dt: datetime, *, include_seconds: bool = True, include_fractional: bool = False, include_timezone: bool = False, twelve_hour: bool = True) str[source]

A nice way to convert a datetime into a time (only) string. This ignores the date part of the datetime completely.

Parameters:
  • dt (datetime) – the datetime whose time to represent

  • include_seconds (bool) – should seconds be included in the output?

  • include_fractional (bool) – should micros be included in the output?

  • include_timezone (bool) – should timezone be included in the output?

  • twelve_hour (bool) – use twelve or twenty-four hour format?

Returns:

A string representing the time of the input datetime.

Return type:

str

>>> d = string_to_datetime(
...                        "2021/09/10 11:24:51AM-0700",
...                       )[0]
>>> d
datetime.datetime(2021, 9, 10, 11, 24, 51, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=61200)))
>>> time_to_string(d)
'11:24:51AM'
>>> time_to_string(d, include_seconds=False)
'11:24AM'
>>> time_to_string(d, include_seconds=False, include_timezone=True)
'11:24AM-0700'
pyutils.datetimes.datetime_utils.timestamp() str[source]
Returns:

A timestamp for right now in Pacific timezone.

Return type:

str

pyutils.datetimes.datetime_utils.timezone_abbrev_to_canonical_name(abbrev: str, country_code: str | None = 'US') str | None[source]

Return the full, unique timezone name given a timezone abbreviation and an optional country code denoting which country the timezone is located in.

Parameters:
  • abbrev (str) – the timezone abbreviation, e.g. “PDT”

  • country_code (str | None) – optional two-letter ISO 3166-1 alpha-2 country code

Returns:

The expanded timezone, on None if nothing found.

Return type:

str | None

Note

This method has a predisposition to returning US-based timezones since the :param:country_code argument defaults to ‘US’.

>>> timezone_abbrev_to_canonical_name('PDT')
'America/Boise'
>>> timezone_abbrev_to_canonical_name('PST')
'America/Boise'
>>> timezone_abbrev_to_canonical_name('AKST')
'America/Nome'
>>> timezone_abbrev_to_canonical_name('+0600')
'Etc/GMT+600'
>>> timezone_abbrev_to_canonical_name('-0600')
'Etc/GMT-600'
>>> timezone_abbrev_to_canonical_name('abracadabra')
pyutils.datetimes.datetime_utils.timezone_abbrev_to_tz(abbrev: str, country_code: str | None = 'US') DstTzInfo | StaticTzInfo | None[source]

Given a timezone abbreviation and optional country code, return a timezone from pytz that cooresponds to the given abbreviation.

Parameters:
  • abbrev (str) – the timezone abbreviation, e.g. “EST”

  • country_code (str | None) – optional

Returns:

A timezone from the pytz library or None on error.

Return type:

DstTzInfo | StaticTzInfo | None

Note

This method has a predisposition to returning US-based timezones. To override this, pass an explicit country_code instead of relying on the default.

>>> timezone_abbrev_to_tz('EST')
<StaticTzInfo 'EST'>
>>> timezone_abbrev_to_tz('CST')
<DstTzInfo 'America/Chicago' LMT-1 day, 18:09:00 STD>
>>> timezone_abbrev_to_tz('CST', 'CN')
<DstTzInfo 'Asia/Shanghai' LMT+8:06:00 STD>
>>> timezone_abbrev_to_tz('xyzzy')
pyutils.datetimes.datetime_utils.translate_timezone(dt: datetime, tz: tzinfo) datetime[source]

Translates dt into a different timezone by adjusting the year, month, day, hour, minute, second, micro, etc… appropriately. The returned dt is the same instant in another timezone.

Parameters:
  • dt (datetime) – the datetime whose timezone should be translated.

  • tz (tzinfo) – the desired timezone

Returns:

A new datetime object that represents the same instant as the input datetime but in the desired timezone. Modifies hour, minute, seconds, day, etc… as necessary for the instant to be preserved. For example, if you pass 11:01pm PST in and ask for it to be translated to EST you would get 2:01am the next day EST back out.

Return type:

datetime

See also replace_timezone(), strip_timezone().

>>> import pytz
>>> d = now_pacific()
>>> d.tzinfo.tzname(d)[0]     # Note: could be PST or PDT
'P'
>>> h = d.hour
>>> o = translate_timezone(d, pytz.timezone('US/Eastern'))
>>> o.tzinfo.tzname(o)[0]     # Again, could be EST or EDT
'E'
>>> o.hour == h
False
>>> expected = h + 3          # Three hours later in E?T than P?T
>>> expected = expected % 24  # Handle edge case
>>> expected == o.hour
True

Module contents