I have a timestamp that is supposed to be in EST:
2014-10-06T18:06:40-04:56
I understand this first part: 2014-10-06T18:06:40
, but not -04:56
.
What does -04:56
mean here?`
Here is how I got that timestamp:
import datetime start_time = datetime.datetime(year = 2014, month = 10, day = 6, hour = 18, tzinfo = pytz.timezone('US/Eastern')) end_time = start_time + datetime.timedelta(seconds=400)
And then:
end_time.isoformat()
returns:
2014-10-06T18:06:40-04:56
The problem is that pytz
:
… differs from the documented Python API for tzinfo implementations; if you want to create local wallclock times you need to use the
localize()
method documented in this document …
Further down, it says:
Unfortunately using the tzinfo argument of the standard datetime constructors "does not work" with pytz for many timezones.
>>> datetime(2002, 10, 27, 12, 0, 0, tzinfo=amsterdam).strftime(fmt) '2002-10-27 12:00:00 LMT+0020'
So, you need to do what the docs suggest—using normalize
, constructing UTC times and using astimezone
, etc. Which one you want depends on exactly what you're trying to do. For example:
>>> from datetime import datetime >>> from pytz import timezone >>> utc = timezone('UTC') >>> eastern = timezone('US/Eastern') >>> datetime(2014, 10, 6, 18, tzinfo=eastern).isoformat() '2014-10-06T18:00:00-04:56' >>> eastern.normalize(datetime(2014, 10, 6, 18, tzinfo=eastern)).isoformat() '2014-10-06T18:56:00-04:00' >>> datetime(2014, 10, 6, 18, tzinfo=utc).astimezone(eastern).isoformat() '2014-10-06T14:00:00-04:00' >>> eastern.localize(datetime(2014, 10, 6, 18)).isoformat() '2014-10-06T18:00:00-04:00'
I think it's the last you want. As the docs for localize
say:
Convert naive time to local time.
This method should be used to construct localtimes, rather than passing a tzinfo argument to a datetime constructor.
And I think constructing a local time is exactly what you wanted here.
If you're wondering why… well, if you look at the data that's in your Olson database, or just print out eastern._utcoffset
, you'll see -1 day, +68640 minutes. That's 19.0166+ hours, not 19. Why? Because every timezone is defined with its starting offset, with adjustments from there. Eastern is based on New York's timezone as of 1883 Nov 18 12:03:58, at which point it was -04:56:02 from GMT. There's an adjustment for dates starting in 1920 that subtracts the extra 00:03:58. And of course the yearly adjustments back and forth one hour for DST. So, as of right now, Eastern is -04:00, but without any idea of what date it's supposed to represent, it's -04:56. And, because datetime
just asks a timezone for its offset, rather than its offset at a particular time, that's what it gets.
One last thing: EST is Eastern Standard Time, which is -05:00. That's not the time zone of any location in the US on 6 October 2014, because in 2014, the US's daylight saving time goes to 2 November. (There used to be counties in Indiana that were on EST during the summer, but there no longer are.) What you're looking for is EDT, Eastern Daylight Time, which is -04:00. Or, of course, ET, which is EDT during the summer and EST during the winter, which is what you get from looking up 'US/Eastern'
or 'America/New_York'
.
What does -04:56 mean here?`
It means that the code that generates the input timestamp is broken in such a way that demonstrates the lack of understanding of how pytz
timezones work. You should not trust its results, if you think that only the UTC offset (-04:56
) is wrong but the date itself is the correct time in Eastern Time Zone then to parse the time, do:
#!/usr/bin/env python from datetime import datetime, timedelta import pytz tz = pytz.timezone('America/New_York') naive = datetime.strptime("2014-10-06T18:06:40-04:56"[:-6], "%Y-%m-%dT%H:%M:%S") start_time = tz.localize(naive, is_dst=None) end_time = tz.normalize(start_time + timedelta(seconds=400)) print(start_time.isoformat()) print(end_time.isoformat())
tz.localize()
instead of assigning tzinfo
attribute directlyis_dst=None
asserts that the input time exists and it is unambiguoustz.normalize()
is necessary if date arithmetics crosses DST boundaries2014-10-06T18:06:40-04:00 2014-10-06T18:13:20-04:00
Why you need localize()
, normalize()
is described in the pytz
docs (the part about localize()
/normalize()
is the very first note in the documentation).
-04:56
UTC offset is wrong in 2014
in Eastern Time ZoneUTC offset in the same place may be different at different times due to DST transitions or other reasons (such as war or because some politician thinks it is a good idea) e.g., here's possible values for US/Eastern:
>>> import pytz >>> pytz.timezone('US/Eastern') {(datetime.timedelta(-1, 72000), datetime.timedelta(0, 3600), 'EWT'): <DstTzInfo 'US/Eastern' EWT-1 day, 20:00:00 DST>, (datetime.timedelta(-1, 68640), datetime.timedelta(0), 'LMT'): <DstTzInfo 'US/Eastern' LMT-1 day, 19:04:00 STD>, (datetime.timedelta(-1, 72000), datetime.timedelta(0, 3600), 'EDT'): <DstTzInfo 'US/Eastern' EDT-1 day, 20:00:00 DST>, (datetime.timedelta(-1, 68400), datetime.timedelta(0), 'EST'): <DstTzInfo 'US/Eastern' EST-1 day, 19:00:00 STD>, (datetime.timedelta(-1, 72000), datetime.timedelta(0, 3600), 'EPT'): <DstTzInfo 'US/Eastern' EPT-1 day, 20:00:00 DST>}
Notice the UTC offset for LMT tzinfo: timedelta(-1, 68640)
== '-4:56:00'
. The way to get it is to use the incorrect code:
#XXX BROKEN, DO NOT DO IT >>> dt = datetime(2014, 10, 6, tzinfo=pytz.timezone('US/Eastern')) >>> dt datetime.datetime(2014, 10, 6, 0, 0, tzinfo=<DstTzInfo 'US/Eastern' LMT-1 day, 19:04:00 STD>) >>> dt.isoformat() 2014-10-06T00:00:00-04:56
Assigning tzinfo
directly doesn't allow pytz
to choose the correct tzinfo for the given time and some random tzinfo
object among available is used instead. You should always use tz.localize()
to attach the correct timezone info.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With