Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Time zone field in isoformat

Tags:

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 
like image 431
Josh Avatar asked Oct 08 '14 19:10

Josh


2 Answers

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'.

like image 171
abarnert Avatar answered Sep 27 '22 18:09

abarnert


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()) 
  • you should use tz.localize() instead of assigning tzinfo attribute directly
  • is_dst=None asserts that the input time exists and it is unambiguous
  • tz.normalize() is necessary if date arithmetics crosses DST boundaries

Output

2014-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).

Why -04:56 UTC offset is wrong in 2014 in Eastern Time Zone

UTC 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.

like image 23
jfs Avatar answered Sep 27 '22 17:09

jfs