I get the following output. Is this the intended behavior of pytz? I live in US/Eastern timezone, by the way. Why is EST giving -04:56 as the timezone offset?
import datetime
import pytz
a = datetime.datetime.now()
tz_est = pytz.timezone("US/Eastern")
a = a.replace(tzinfo=tz_est)
print("EST")
print(a)
print("\n")
b = datetime.datetime.now(pytz.timezone("US/Pacific"))
print("PST - version 1")
print(b)
print("\n")
tz_pst = pytz.timezone('US/Pacific')
c = tz_pst.normalize(a)
print("PST - version 2")
print(c)
print("\n")
EST
2017-03-16 22:52:27.616000-04:56
PST - version 1
2017-03-16 19:52:27.617000-07:00
PST - version 2
2017-03-16 20:48:27.616000-07:00
import datetime
import pytz
a = datetime.datetime.now(pytz.timezone("US/Eastern"))
b = datetime.datetime.now()
pacific = pytz.timezone("US/Pacific")
c = pacific.localize(b)
d = pacific.normalize(a)
print(c)
print(d)
Use zoneinfo instead of pytz to get the expected behavior.
https://docs.python.org/3/library/zoneinfo.html
from zoneinfo import ZoneInfo
dt = datetime(2020, 10, 31, 12, tzinfo=ZoneInfo("America/Los_Angeles"))
Related
I am having issues converting a datetime string of this format "%d %b %Y %X %Z" to "%Y-%m-%dT%X%z". The timezone information is stripped out. For example:
>> import datetime
>> datetime_string_raw = "18 Nov 2022 08:57:04 EST"
>> datetime_utc = datetime.datetime.strptime(datetime_string_raw, "%d %b %Y %X %Z").strftime("%Y-%m-%dT%X%z")
>> print(datetime_utc)
2022-11-18T08:57:04
How can I get it to print the UTC offset? Why doesn't the %Z and %z have any effect? Thanks!
Using dateutil's parser and a definition which abbreviated names should resemble which time zone:
import datetime
import dateutil # pip install python-dateutil
tzinfos = {"EST": dateutil.tz.gettz("America/New_York"),
"EDT": dateutil.tz.gettz("America/New_York")}
datetime_string_raw = "18 Nov 2022 08:57:04 EST"
datetime_ny = dateutil.parser.parse(datetime_string_raw, tzinfos=tzinfos)
print(datetime_ny)
# 2022-11-18 08:57:04-05:00
datetime_utc = datetime_ny.astimezone(datetime.timezone.utc)
print(datetime_utc)
# 2022-11-18 13:57:04+00:00
You can do basically the same using only the standard library, but it requires some pre-processing of the date/time string. Ex:
import datetime
import zoneinfo # Python >= 3.9
def parse_dt_with_tz(dt_string: str, fmt: str, tzinfos: dict) -> datetime.datetime:
"""Parse date/time string with abbreviated time zone name to aware datetime."""
parts = dt_string.split(" ")
tz = tzinfos.get(parts[-1]) # last element is the tz name
if not tz:
raise ValueError(f"no entry found for {parts[-1]} in tzinfos")
return datetime.datetime.strptime(" ".join(parts[:-1]), fmt).replace(tzinfo=tz)
# usage
tzinfos = {"EST": zoneinfo.ZoneInfo("America/New_York"),
"EDT": zoneinfo.ZoneInfo("America/New_York")}
s = "18 Nov 2022 08:57:04 EST"
dt = parse_dt_with_tz(s, "%d %b %Y %H:%M:%S", tzinfos)
print(dt, repr(dt))
# 2022-11-18 08:57:04-05:00 datetime.datetime(2022, 11, 18, 8, 57, 4, tzinfo=zoneinfo.ZoneInfo(key='America/New_York'))
Can someone help me to unit test this line of code?
from datetime import datetime, timedelta, timezone
def get_timestamp_plus_100_year():
return int((datetime.now(timezone.utc) + timedelta(days=100 * 365)).timestamp())
I try this, but I don't know how to assign the values:
#patch("src.shared.utils.timedelta")
#patch("src.shared.utils.datetime")
def test_get_timestamp_now_plus_100_years(self, mock_datetime, mock_timedelta):
mock_datetime.now.return_value = 2021-09-14 15:54:25.284087+00:00
mock_timedelta.return_value = 36500 days, 0:00:00
self.assertEqual(
get_timestamp_plus_100_year(),
int((mock_datetime.now.return_value
+ mock_timedelta.return_value).timestamp_return_value ),
)
Correct the implementation first. As pointed out by #MrFuppes, not all years are 365 days. Assuming today is 2021-9-15, your original implementation would result to:
>>> datetime.now(timezone.utc) + timedelta(days=100 * 365)
datetime.datetime(2121, 8, 22, 9, 19, 30, 468735, tzinfo=datetime.timezone.utc)
Here, you can see that instead of the expected 2121-9-15, what we got was 2121-8-22.
Option 1: Using datetime.replace() to replace the year with year+100
>>> (dt := datetime.now(timezone.utc)).replace(year=dt.year + 100)
datetime.datetime(2121, 9, 15, 9, 24, 52, 139984, tzinfo=datetime.timezone.utc)
Option 2: Using dateutil.relativedelta.relativedelta to add +100 years. This requires pip install python-dateutil.
>>> datetime.now(timezone.utc) + relativedelta(years=100)
datetime.datetime(2121, 9, 15, 9, 28, 16, 789807, tzinfo=datetime.timezone.utc)
Then, you don't need to mock the timedelta (or relativedelta in our corrected code) since its value will always be 100 years. You just need to mock the current date via datetime.now() since it is the base of the addition and we need to assert the result.
Assuming this is the file tree:
.
├── src.py
└── test_src.py
src.py
from datetime import datetime, timezone
from dateutil.relativedelta import relativedelta
def get_timestamp_plus_100_year():
return int((datetime.now(timezone.utc) + relativedelta(years=100)).timestamp())
Solution 1:
from datetime import datetime
import unittest
from unittest.mock import patch
from dateutil.relativedelta import relativedelta
from src import get_timestamp_plus_100_year
class TestDates(unittest.TestCase):
#patch("src.datetime")
def test_get_timestamp_now_plus_100_years(self, mock_datetime):
frozen_dt = datetime(year=2021, month=9, day=15)
mock_datetime.now.return_value = frozen_dt
# Solution 1.1
self.assertEqual(
get_timestamp_plus_100_year(),
int((frozen_dt + relativedelta(years=100)).timestamp()),
)
# Solution 1.2
frozen_dt_100 = datetime(year=2121, month=9, day=15) # Since we already know the value of +100 years, we can just define it here
self.assertEqual(
get_timestamp_plus_100_year(),
int(frozen_dt_100.timestamp()),
)
Solution 2
This requires freezegun via pip install freezegun
from datetime import datetime
import unittest
from freezegun import freeze_time
from src import get_timestamp_plus_100_year
#freeze_time("2021-09-15") # Either here
class TestDates(unittest.TestCase):
#freeze_time("2021-09-15") # Or here
def test_get_timestamp_now_plus_100_years_2(self):
frozen_dt_100 = datetime(year=2121, month=9, day=15) # Since we already know the value of +100 years, we can just define it here
self.assertEqual(
get_timestamp_plus_100_year(),
int(frozen_dt_100.timestamp()),
)
Using Python 2.7, how can I convert a given time from one offset to another? My solution below treats offsets as a time and ignores the sign (+/-) leading to incorrect conversions.
import time
from datetime import datetime
import re
# inputs (cannot change)
from_time_str = '13:45'
from_offset = '-0700'
to_offset = '-0100'
# conversion
from_time = time.strptime(from_time_str, '%H:%M')
from_offset_time = time.strptime(from_offset, '-%H%M')
to_offset_time = time.strptime(to_offset, '-%H%M')
offset_diff = abs(time.mktime(to_offset_time) - time.mktime(from_offset_time))
to_timestamp = offset_diff + time.mktime(from_time)
to_datetime = datetime.fromtimestamp(to_timestamp)
print to_datetime.strftime('%H:%M')
Output:
19:45
+/-:
from_time_str = '13:45'
from_offset = '-0700'
to_offset = '+0700'
to_offset_time = time.strptime(to_offset, '+%H%M')
Output:
13:45
if you're open to using the dateutil library, this seems to work:
from dateutil.parser import parse
from dateutil.relativedelta import relativedelta
# inputs (cannot change)
from_time_str = '13:45'
from_offset = '-0700'
to_offset = '-0100'
if from_offset[0]=='-':
non_offset = parse(from_time_str)+relativedelta(hours=int(from_offset[1:3]), minutes=int(from_offset[3:]))
else:
non_offset = parse(from_time_str)-relativedelta(hours=int(from_offset[1:3]), minutes=int(from_offset[3:]))
if to_offset[0]=='-':
to_offset_time = non_offset-relativedelta(hours=int(to_offset[1:3]), minutes=int(to_offset[3:]))
else:
to_offset_time = non_offset+relativedelta(hours=int(to_offset[1:3]), minutes=int(to_offset[3:]))
print to_offset_time.strftime('%H:%M')
I'm sure there is a more pythonic way, but it does seem to work!
I use this code to format my time but the time that comes out is 5 hours wrong. I should be 06 something in calcutta now and it formats the time now as 01... something. What is wrong with the code?
def datetimeformat_viewad(to_format, locale='en', timezoneinfo='Asia/Calcutta'):
tzinfo = timezone(timezoneinfo)
month = MONTHS[to_format.month - 1]
input = pytz.timezone(timezoneinfo).localize(
datetime(int(to_format.year), int(to_format.month), int(to_format.day), int(to_format.hour), int(to_format.minute)))
date_str = '{0} {1}'.format(input.day, _(month))
time_str = format_time(input, 'H:mm', tzinfo=tzinfo, locale=locale)
return "{0} {1}".format(date_str, time_str)
Update
This code worked which was according to the answer below.
def datetimeformat_viewad(to_format, locale='en', timezoneinfo='Asia/Calcutta'):
import datetime as DT
import pytz
utc = pytz.utc
to_format = DT.datetime(int(to_format.year), int(to_format.month), int(to_format.day), int(to_format.hour), int(to_format.minute))
utc_date = utc.localize(to_format)
tzone = pytz.timezone(timezoneinfo)
tzone_date = utc_date.astimezone(tzone)
month = MONTHS[int(tzone_date.month) - 1]
time_str = format_time(tzone_date, 'H:mm')
date_str = '{0} {1}'.format(tzone_date.day, _(month))
return "{0} {1}".format(date_str, time_str)
It sounds like to_format is a naive datetime in UTC time.
You want to convert it to Calcutta time.
To do this, you localize to_format to UTC time1, and then use astimezone to convert that timezone-aware time to Calcutta time:
import datetime as DT
import pytz
utc = pytz.utc
to_format = DT.datetime(2015,7,17,1,0)
print(to_format)
# 2015-07-17 01:00:00
utc_date = utc.localize(to_format)
print(utc_date)
# 2015-07-17 01:00:00+00:00
timezoneinfo = 'Asia/Calcutta'
tzone = pytz.timezone(timezoneinfo)
tzone_date = utc_date.astimezone(tzone)
print(tzone_date)
# 2015-07-17 06:30:00+05:30
1The tzone.localize method does not convert between timezones. It
interprets the given localtime as one given in tzone. So if to_format is
meant to be interpreted as a UTC time, then use utc.localize to convert the
naive datetime to a timezone-aware UTC time.
How to increment the day of a datetime?
for i in range(1, 35)
date = datetime.datetime(2003, 8, i)
print(date)
But I need pass through months and years correctly? Any ideas?
date = datetime.datetime(2003,8,1,12,4,5)
for i in range(5):
date += datetime.timedelta(days=1)
print(date)
Incrementing dates can be accomplished using timedelta objects:
import datetime
datetime.datetime.now() + datetime.timedelta(days=1)
Look up timedelta objects in the Python docs: http://docs.python.org/library/datetime.html
All of the current answers are wrong in some cases as they do not consider that timezones change their offset relative to UTC. So in some cases adding 24h is different from adding a calendar day.
Proposed solution
The following solution works for Samoa and keeps the local time constant.
def add_day(today):
"""
Add a day to the current day.
This takes care of historic offset changes and DST.
Parameters
----------
today : timezone-aware datetime object
Returns
-------
tomorrow : timezone-aware datetime object
"""
today_utc = today.astimezone(datetime.timezone.utc)
tz = today.tzinfo
tomorrow_utc = today_utc + datetime.timedelta(days=1)
tomorrow_utc_tz = tomorrow_utc.astimezone(tz)
tomorrow_utc_tz = tomorrow_utc_tz.replace(hour=today.hour,
minute=today.minute,
second=today.second)
return tomorrow_utc_tz
Tested Code
# core modules
import datetime
# 3rd party modules
import pytz
# add_day methods
def add_day(today):
"""
Add a day to the current day.
This takes care of historic offset changes and DST.
Parameters
----------
today : timezone-aware datetime object
Returns
-------
tomorrow : timezone-aware datetime object
"""
today_utc = today.astimezone(datetime.timezone.utc)
tz = today.tzinfo
tomorrow_utc = today_utc + datetime.timedelta(days=1)
tomorrow_utc_tz = tomorrow_utc.astimezone(tz)
tomorrow_utc_tz = tomorrow_utc_tz.replace(hour=today.hour,
minute=today.minute,
second=today.second)
return tomorrow_utc_tz
def add_day_datetime_timedelta_conversion(today):
# Correct for Samoa, but dst shift
today_utc = today.astimezone(datetime.timezone.utc)
tz = today.tzinfo
tomorrow_utc = today_utc + datetime.timedelta(days=1)
tomorrow_utc_tz = tomorrow_utc.astimezone(tz)
return tomorrow_utc_tz
def add_day_dateutil_relativedelta(today):
# WRONG!
from dateutil.relativedelta import relativedelta
return today + relativedelta(days=1)
def add_day_datetime_timedelta(today):
# WRONG!
return today + datetime.timedelta(days=1)
# Test cases
def test_samoa(add_day):
"""
Test if add_day properly increases the calendar day for Samoa.
Due to economic considerations, Samoa went from 2011-12-30 10:00-11:00
to 2011-12-30 10:00+13:00. Hence the country skipped 2011-12-30 in its
local time.
See https://stackoverflow.com/q/52084423/562769
A common wrong result here is 2011-12-30T23:59:00-10:00. This date never
happened in Samoa.
"""
tz = pytz.timezone('Pacific/Apia')
today_utc = datetime.datetime(2011, 12, 30, 9, 59,
tzinfo=datetime.timezone.utc)
today_tz = today_utc.astimezone(tz) # 2011-12-29T23:59:00-10:00
tomorrow = add_day(today_tz)
return tomorrow.isoformat() == '2011-12-31T23:59:00+14:00'
def test_dst(add_day):
"""Test if add_day properly increases the calendar day if DST happens."""
tz = pytz.timezone('Europe/Berlin')
today_utc = datetime.datetime(2018, 3, 25, 0, 59,
tzinfo=datetime.timezone.utc)
today_tz = today_utc.astimezone(tz) # 2018-03-25T01:59:00+01:00
tomorrow = add_day(today_tz)
return tomorrow.isoformat() == '2018-03-26T01:59:00+02:00'
to_test = [(add_day_dateutil_relativedelta, 'relativedelta'),
(add_day_datetime_timedelta, 'timedelta'),
(add_day_datetime_timedelta_conversion, 'timedelta+conversion'),
(add_day, 'timedelta+conversion+dst')]
print('{:<25}: {:>5} {:>5}'.format('Method', 'Samoa', 'DST'))
for method, name in to_test:
print('{:<25}: {:>5} {:>5}'
.format(name,
test_samoa(method),
test_dst(method)))
Test results
Method : Samoa DST
relativedelta : 0 0
timedelta : 0 0
timedelta+conversion : 1 0
timedelta+conversion+dst : 1 1
Here is another method to add days on date using dateutil's relativedelta.
from datetime import datetime
from dateutil.relativedelta import relativedelta
print 'Today: ',datetime.now().strftime('%d/%m/%Y %H:%M:%S')
date_after_month = datetime.now()+ relativedelta(day=1)
print 'After a Days:', date_after_month.strftime('%d/%m/%Y %H:%M:%S')
Output:
Today: 25/06/2015 20:41:44
After a Days: 01/06/2015 20:41:44
Most Simplest solution
from datetime import timedelta, datetime
date = datetime(2003,8,1,12,4,5)
for i in range(5):
date += timedelta(days=1)
print(date)
This was a straightforward solution for me:
from datetime import timedelta, datetime
today = datetime.today().strftime("%Y-%m-%d")
tomorrow = datetime.today() + timedelta(1)
You can also import timedelta so the code is cleaner.
from datetime import datetime, timedelta
date = datetime.now() + timedelta(seconds=[delta_value])
Then convert to date to string
date = date.strftime('%Y-%m-%d %H:%M:%S')
Python one liner is
date = (datetime.now() + timedelta(seconds=[delta_value])).strftime('%Y-%m-%d %H:%M:%S')
A short solution without libraries at all. :)
d = "8/16/18"
day_value = d[(d.find('/')+1):d.find('/18')]
tomorrow = f"{d[0:d.find('/')]}/{int(day_value)+1}{d[d.find('/18'):len(d)]}".format()
print(tomorrow)
# 8/17/18
Make sure that "string d" is actually in the form of %m/%d/%Y so that you won't have problems transitioning from one month to the next.