+ if is_timezone_naive(dt):
+ return dt
+ return replace_timezone(dt, None)
+
+
+def add_timezone(dt: datetime.datetime, tz: datetime.tzinfo) -> datetime.datetime:
+ """
+ Adds a timezone to a timezone naive datetime. This does not
+ change the instant to which the timestamp refers. See also:
+ replace_timezone.
+
+ >>> now = datetime.datetime.now()
+ >>> is_timezone_aware(now)
+ False
+
+ >>> now_pacific = add_timezone(now, pytz.timezone('US/Pacific'))
+ >>> is_timezone_aware(now_pacific)
+ True
+
+ >>> now.hour == now_pacific.hour
+ True
+ >>> now.minute == now_pacific.minute
+ True
+
+ """
+
+ # This doesn't work, tz requires a timezone naive dt. Two options
+ # here:
+ # 1. Use strip_timezone and try again.
+ # 2. Replace the timezone on your dt object via replace_timezone.
+ # Be aware that this changes the instant to which the dt refers
+ # and, further, can introduce weirdness like UTC offsets that
+ # are weird (e.g. not an even multiple of an hour, etc...)
+ if is_timezone_aware(dt):
+ if dt.tzinfo == tz:
+ return dt
+ raise Exception(
+ f'{dt} is already timezone aware; use replace_timezone or translate_timezone '
+ + 'depending on the semantics you want.'
+ )
+ return dt.replace(tzinfo=tz)
+
+
+def replace_timezone(dt: datetime.datetime, tz: Optional[datetime.tzinfo]) -> datetime.datetime:
+ """Replaces the timezone on a timezone aware datetime object directly
+ (leaving the year, month, day, hour, minute, second, micro,
+ etc... alone).
+
+ 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...
+