Created
April 18, 2015 19:47
-
-
Save zed/ff4e35df3887c1f82002 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python | |
| """Test POSIX definition of "seconds since Epoch". | |
| Assert that posix_time(), calendar.timegm(), and timegm() (mktime() in | |
| UTC) behave the same. Check `datetime.timestamp()` and the explicit | |
| formula (d - epoch) too. | |
| """ | |
| import calendar | |
| import itertools | |
| import os | |
| import time | |
| from contextlib import contextmanager | |
| from datetime import datetime, date, time as datetime_time | |
| try: | |
| from datetime import timezone | |
| except ImportError: | |
| timezone = None | |
| def posix_time(utc_time_tuple): | |
| """seconds since Epoch as defined by POSIX.""" | |
| tm_year = utc_time_tuple.tm_year - 1900 | |
| tm_yday = utc_time_tuple.tm_yday - 1 | |
| tm_hour = utc_time_tuple.tm_hour | |
| tm_min = utc_time_tuple.tm_min | |
| tm_sec = utc_time_tuple.tm_sec | |
| # http://pubs.opengroup.org/stage7tc1/basedefs/V1_chap04.html#tag_04_15 | |
| return (tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 + | |
| (tm_year-70)*31536000 + ((tm_year-69)//4)*86400 - | |
| ((tm_year-1)//100)*86400 + ((tm_year+299)//400)*86400) | |
| @contextmanager | |
| def local_timezone(zonename): | |
| #NOTE: it manipulates global state | |
| oldname = os.environ.get('TZ') | |
| try: | |
| os.environ['TZ'] = zonename | |
| time.tzset() | |
| yield | |
| finally: | |
| if oldname is None: | |
| del os.environ['TZ'] | |
| else: | |
| os.environ['TZ'] = oldname | |
| time.tzset() | |
| def timegm(utc_time_tuple): | |
| with local_timezone('UTC'): | |
| return time.mktime(utc_time_tuple) | |
| def eq(a, b, err): | |
| return abs(a - b) < err | |
| if __name__ == "__main__": | |
| seconds_since_epoch = time.time() #NOTE: assume POSIX epoch (fail with TZ=right/UTC) | |
| tt = time.gmtime(seconds_since_epoch) | |
| print(posix_time(tt)) | |
| assert posix_time(tt) == int(seconds_since_epoch) | |
| assert calendar.timegm(tt) == int(seconds_since_epoch) | |
| assert timegm(tt) == int(seconds_since_epoch) | |
| if timezone: | |
| aware_dt = datetime.fromtimestamp(seconds_since_epoch, timezone.utc) | |
| assert eq(aware_dt.timestamp(), seconds_since_epoch, 1e-6) | |
| for year, month in itertools.product(range(1970, 1970+401), range(1, 13)): | |
| utc_d = date(year, month, 1) | |
| seconds_since_epoch = (utc_d - date(1970, 1, 1)).days*86400 | |
| tt = utc_d.timetuple() | |
| assert posix_time(tt) == seconds_since_epoch | |
| assert calendar.timegm(tt) == seconds_since_epoch | |
| assert timegm(tt) == seconds_since_epoch | |
| if timezone: | |
| aware_dt = datetime.combine(utc_d, datetime_time(tzinfo=timezone.utc)) | |
| assert aware_dt.timestamp() == seconds_since_epoch |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment