Convert EST timestamp to GMT taking into account Daylight Savings - python

I have a timestamp that represents milliseconds since 1970 1432202088224 which translates to Thursday, May 21, 2015 5:54:48 AM EDT. I'd like to write a python function that converts that timestamp to milliseconds in GMT. I can't naively add four hours (3600000 milliseconds) to the existing timestamp because half the year i'll be off by one hour.
I've tried writing a function using datetime and pytz
def convert_mills_GMT(milliseconds):
converted_raw = datetime.datetime.fromtimestamp(milliseconds/1000.0)
date_eastern = eastern.localize(converted_raw, is_dst=True)
date_utc = date_eastern.astimezone(utc)
return int(date_utc.strftime("%s")) * 1000
using the input of 1432202088224 this function returns 1432220088000 which is Thursday, May 21, 2015 10:54:48 AM EDT when what I want is 9:54 AM. what am I missing?

There is no such thing as "EST timestamp". If you need "GMT timestamp" then you already have it.
To get UTC time from a POSIX timestamp given as number of milliseconds:
>>> from datetime import datetime, timedelta
>>> timestamp = 1432202088224
>>> utc_time = datetime(1970, 1, 1) + timedelta(milliseconds=timestamp)
>>> utc_time.strftime('%A, %B %d, %Y %H:%M:%S %p UTC')
'Thursday, May 21, 2015 09:54:48 AM UTC'
We can check that the result is correct by converting the UTC time back to "EST" timezone:
>>> import pytz # $ pip install pytz
>>> est = utc_time.replace(tzinfo=pytz.utc).astimezone(pytz.timezone('US/Eastern'))
>>> est.strftime('%A, %B %d, %Y %H:%M:%S %p %Z')
'Thursday, May 21, 2015 05:54:48 AM EDT'

Don't use .strftime("%s"). It is not supported, and may silently fail. Instead, to convert a UTC datetime to a timestamp use one of the methods shown here depending on your version of Python:
Python 3.3+:
timestamp = dt.timestamp()
Python3 (< 3.3):
epoch = datetime(1970, 1, 1, tzinfo=timezone.utc)
timestamp = (dt - epoch) / timedelta(seconds=1)
Python 2.7+:
timestamp = (dt.replace(tzinfo=None) - datetime(1970, 1, 1)).total_seconds()
Python2 (< 2.7):
def totimestamp(dt, epoch=datetime(1970,1,1)):
td = dt - epoch
# return td.total_seconds()
return (td.microseconds + (td.seconds + td.days * 86400) * 10**6) / 10**6
timestamp = totimestamp(dt.replace(tzinfo=None))
Therefore, your convert_mills_GMT should look like
def convert_mills_GMT(milliseconds,
utc=pytz.utc,
eastern=pytz.timezone('US/Eastern')
):
converted_raw = DT.datetime.fromtimestamp(milliseconds/1000.0)
date_eastern = eastern.localize(converted_raw, is_dst=True)
date_utc = date_eastern.astimezone(utc)
timestamp = ...
return int(timestamp) * 1000
For example, with Python2.7,
import datetime as DT
import pytz
def convert_mills_GMT(milliseconds,
utc=pytz.utc,
eastern=pytz.timezone('US/Eastern')
):
converted_raw = DT.datetime.fromtimestamp(milliseconds/1000.0)
date_eastern = eastern.localize(converted_raw, is_dst=True)
date_utc = date_eastern.astimezone(utc)
timestamp = ((date_utc.replace(tzinfo=None) - DT.datetime(1970, 1, 1))
.total_seconds())
return int(timestamp) * 1000
print(DT.datetime.utcfromtimestamp(convert_mills_GMT(1432202088224)/1000.0))
prints
2015-05-21 09:54:48

Related

Compare time with timezone to now

I have a string time coming from a third party (external to my python program), and I need to compare that time to right now. How long ago was that time?
How can I do this?
I've looked at the datetime and time libraries, as well as pytz, and can't find an obvious way to do this. It should automatically incorporate DST because the third party doesn't explicitly state its offset, only the timezone (US/Eastern).
I've tried this, and it fails:
dt = datetime.datetime.strptime('June 10, 2016 12:00PM', '%B %d, %Y %I:%M%p')
dtEt = dt.replace(tzinfo=pytz.timezone('US/Eastern'))
now = datetime.datetime.now()
now - dtEt
TypeError: can't subtract offset-naive and offset-aware datetimes
Good question Zack! I've had this problem myself.
Here's some code to do so:
from datetime import datetime
import time
import calendar
import pytz
def howLongAgo(thirdPartyString, timeFmt):
# seconds since epoch
thirdPartySeconds = calendar.timegm(time.strptime(thirdPartyString, timeFmt))
nowSecondsUTC = time.time()
# hour difference with DST
nowEastern = datetime.now(pytz.timezone('US/Eastern'))
nowUTC = datetime.now(pytz.timezone('UTC'))
timezoneOffset = (nowEastern.day - nowUTC.day)*24 + (nowEastern.hour - nowUTC.hour) + (nowEastern.minute - nowUTC.minute)/60.0
thirdPartySecondsUTC = thirdPartySeconds - (timezoneOffset * 60 * 60)
return nowSecondsUTC - thirdPartySecondsUTC
howLongAgo('June 09, 2016 at 06:22PM', '%B %d, %Y at %I:%M%p')
# first argument always provided in ET, either EDT or EST
TypeError: can't subtract offset-naive and offset-aware datetimes
To fix the TypeError, use timezone aware datetime objects:
#!/usr/bin/env python
from datetime import datetime
import pytz # $ pip install pytz
tz = pytz.timezone('US/Eastern')
now = datetime.now(tz) # the current time (it works even during DST transitions)
then_naive = datetime.strptime('June 10, 2016 12:00PM', '%B %d, %Y %I:%M%p')
then = tz.localize(then_naive, is_dst=None)
time_difference_in_seconds = (now - then).total_seconds()
is_dst=None causes an exception for ambiguous/non-existing times. You could also use is_dst=False (default) or is_dst=True, see python converting string in localtime to UTC epoch timestamp.

How to convert python timestamp string to epoch?

I have the following string:
mytime = "2009-03-08T00:27:31.807Z"
How do I convert it to epoch in python?
I tried:
import time
p = '%Y-%m-%dT%H:%M:%S'
int(time.mktime(time.strptime(s, p)))
But it does not work with the 31.807Z.
There are two parts:
Convert the time string into a broken-down time. See How to parse ISO formatted date in python?
Convert the UTC time to "seconds since the Epoch" (POSIX timestamp).
#!/usr/bin/env python
from datetime import datetime
utc_time = datetime.strptime("2009-03-08T00:27:31.807Z", "%Y-%m-%dT%H:%M:%S.%fZ")
epoch_time = (utc_time - datetime(1970, 1, 1)).total_seconds()
# -> 1236472051.807
If you are sure that you want to ignore fractions of a second and to get an integer result:
#!/usr/bin/env python
import time
from calendar import timegm
utc_time = time.strptime("2009-03-08T00:27:31.807Z", "%Y-%m-%dT%H:%M:%S.%fZ")
epoch_time = timegm(utc_time)
# -> 1236472051
To support timestamps that correspond to a leap second such as Wed July 1 2:59:60 MSK 2015, you could use a combination of time.strptime() and datetime (if you care about leap seconds you should take into account the microseconds too).
You are missing .%fZ from your format string.
p = '%Y-%m-%dT%H:%M:%S.%fZ'
The correct way to convert to epoch is to use datetime:
from datetime import datetime
p = '%Y-%m-%dT%H:%M:%S.%fZ'
mytime = "2009-03-08T00:27:31.807Z"
epoch = datetime(1970, 1, 1)
print((datetime.strptime(mytime, p) - epoch).total_seconds())
Or call int if you want to ignore fractions.
dateutil has recently been added back to python packages, it's an easy one liner that handles formatting on its own.
from dateutil import parser
strtime = '2009-03-08T00:27:31.807Z'
epoch = parser.parse(strtime).timestamp()
dateutil is the only library i have found that correctly deals with the timezone offset identitifier (Z)
pip install python-dateutil
then
from dateutil.parser import parse as date_parse
print date_parse("2009-03-08T00:27:31.807Z")
#get timestamp
import calendar
dt = date_parse("2009-03-08T00:27:31.807Z")
timestamp1 = calendar.timegm(dt.timetuple())
Code:
import datetime
epoch = datetime.datetime(1970, 1, 1)
mytime = "2009-03-08T00:27:31.807Z"
myformat = "%Y-%m-%dT%H:%M:%S.%fZ"
mydt = datetime.datetime.strptime(mytime, myformat)
val = (mydt - epoch).total_seconds()
print(val)
> 1236472051.81
repr(val)
> '1236472051.807'
Notes:
When using time.strptime(), the returned time.struct_time does not support sub-second precision.
The %f format is for microseconds. When parsing it need not be the full 6 digits.
Python 3.7+ The string format in question can be parsed by strptime:
from datetime import datetime
datetime.strptime("2009-03-08T00:27:31.807Z", '%Y-%m-%dT%H:%M:%S.%f%z')
>>> datetime.datetime(2009, 3, 8, 0, 27, 31, 807000, tzinfo=datetime.timezone.utc)
Another option using the built-in datetime.fromisoformat(): As mentioned in this thread linked by #jfs, fromisoformat() doesn't parse the 'Z' character to UTC although this is part of the RFC3339 definitions. A little work-around can make it work - some will consider this nasty but it's efficient after all.
from datetime import datetime
mytime = "2009-03-08T00:27:31.807Z"
datetime.fromisoformat(mytime.replace("Z", "+00:00")).timestamp()
>>> 1236472051.807
This code works in Python 3.6 to convert a datetime string to epoch in UTC or local timezone.
from datetime import datetime, timedelta
from dateutil.tz import tzutc, tzlocal
mydate = '2020-09-25'
mytime = '06:00:00'
epoch1970 = datetime(1970, 1, 1, 0, 0, 0, tzinfo=tzutc())
myepochutc = int((datetime.strptime(mydate + ' ' + mytime, "%Y-%m-%d %H:%M:%S").replace(tzinfo=tzutc()) - epoch1970).total_seconds()*1000)
myepochlocal = int((datetime.strptime(mydate + ' ' + mytime, "%Y-%m-%d %H:%M:%S").replace(tzinfo=tzlocal()) - epoch1970).total_seconds()*1000)
#epoch will be in milliseconds
print(myepochutc) #if mydate/mytime was in utc
print(myepochlocal) #if mydate/mytime was in local timezone

Calculate time in a different timezone in Python

I'm working with a dataset with timestamps from two different time zones. Below is what I am trying to do:
1.Create a time object t1 from a string;
2.Set the timezone for t1;
3.Infer the time t2 at a different timezone.
import time
s = "2014-05-22 17:16:15"
t1 = time.strptime(s, "%Y-%m-%d %H:%M:%S")
#Set timezone for t1 as US/Pacific
#Based on t1, calculate the time t2 in a different time zone
#(e.g, Central European Time(CET))
Any answers/comments will be appreciated..!
Use datetime and pytz
import datetime
import pytz
pac=pytz.timezone('US/Pacific')
cet=pytz.timezone('CET')
s = "2014-05-22 17:16:15"
t1 = datetime.datetime.strptime(s, "%Y-%m-%d %H:%M:%S")
pacific = pac.localize(t1)
cet_eur = pacific.astimezone(cet)
print pacific
print cet_eur
2014-05-22 17:16:15-07:00
2014-05-23 02:16:15+02:00
I think you want datetime.timetuple
Return a time.struct_time such as returned by time.localtime(). The hours, minutes and seconds are 0, and the DST flag is -1. d.timetuple() is equivalent to time.struct_time((d.year, d.month, d.day, 0, 0, 0, d.weekday(), yday, -1)), where yday = d.toordinal() - date(d.year, 1, 1).toordinal() + 1 is the day number within the current year starting with 1 for January 1st.
print datetime.date.timetuple(t1)
time.struct_time(tm_year=2014, tm_mon=5, tm_mday=22, tm_hour=17, tm_min=16, tm_sec=15, tm_wday=3, tm_yday=142, tm_isdst=-1)
That is a quick draft but the pytz docs have lots of good and clear examples

python - datetime with timezone to epoch

In the code below, I am calculating now epoch and beginning of current day epoch.
import time
import pytz
from datetime import datetime
tz1 = pytz.timezone('CST6CDT')
utc = pytz.timezone('UTC')
now = pytz.UTC.localize(datetime.utcnow())
now_tz = now.astimezone(tz1)
print now_tz
print now_tz.strftime('%s')
begin_day = now_tz.replace(hour=0, minute=0, second=0)
print begin_day
print begin_day.strftime('%s')
print statements:
2012-08-28 13:52:21.595718-05:00
1346187141
2012-08-28 00:00:00.595718-05:00
1346137200
Converting epochs to timestamp with CDT timezone:
1346187141 - Aug 28 2012 15:52:21,
1346137200 - Aug 28 2012 02:00:00
I'd like the second epoch to be beginning of the day but it's 2 am. It looks like it is still using local timezone PST when converting to epoch.
What am I doing wrong ? or can this be done a different way?
Thanks!
To convert a datetime with timezone to epoch (POSIX timestamp):
from datetime import datetime
import pytz
tz = pytz.timezone('CST6CDT')
# a datetime with timezone
dt_with_tz = tz.localize(datetime(2012, 8, 28, 19, 33, 50), is_dst=None)
# get timestamp
ts = (dt_with_tz - datetime(1970, 1, 1, tzinfo=pytz.utc)).total_seconds()
# -> 1346200430.0
It is how datetime.timestamp method is implemented for timezone-aware datetime objects in Python 3.
To get "now epoch":
from datetime import datetime
now_epoch = (datetime.utcnow() - datetime(1970, 1, 1)).total_seconds()
Or (assuming time uses POSIX epoch):
import time
now_epoch = time.time()
Getting "beginning of current day epoch" is more complex because current day may be different in different timezones:
from datetime import datetime, time
import pytz
tz = pytz.timezone('CST6CDT')
# get current date in given timezone
today = datetime.now(tz).date()
# -> datetime.date(2013, 6, 22)
# get beginning of current day in given timezone as a datetime with timezone
midnight = tz.localize(datetime.combine(today, time(0, 0)), is_dst=None)
# -> datetime.datetime(2013, 6, 22, 0, 0, tzinfo=<DstTzInfo 'CST6CDT'...>)
# get timestamp
ts = (midnight - datetime(1970, 1, 1, tzinfo=pytz.utc)).total_seconds()
# -> 1371877200.0
See How do I get the UTC time of “midnight” for a given timezone?.
To get "beginning of current day epoch" assuming UTC date:
from datetime import datetime, date
# get current date in UTC
utc_date = datetime.utcnow().date()
# -> datetime.date(2013, 6, 23)
# get timestamp
ts = (utc_date - date(1970, 1, 1)).days * 86400
# -> 1371945600
See Converting datetime.date/datetime.datetime to UTC timestamp in Python.
NOTE: My answer is flat-out wrong. (I'd like to delete it, but am unable to do so until the accept flag is removed.)
Please see J.F.Sebastian's answer.
Here is code demonstrating a value of now_tz for which our two methods produce different results.
import calendar
import pytz
import datetime as dt
tz1 = pytz.timezone('US/Eastern')
utc = pytz.timezone('UTC')
now = utc.localize(dt.datetime(2002, 10, 28), is_dst=None)
now_tz = now.astimezone(tz1)
now_epoch = calendar.timegm(now_tz.utctimetuple())
begin_day = tz1.normalize(now_tz.replace(hour=0, minute=0, second=0))
midnight = tz1.localize(dt.datetime.combine(now_tz, dt.time(0, 0)), is_dst=None)
if begin_day != midnight:
print(begin_day)
# 2002-10-27 01:00:00-04:00 # my result -- is not midnight
print(midnight)
# 2002-10-27 00:00:00-04:00 # J.F.Sebastian's result is correct
(Original answer redacted)
the latest release of simple-date (version 0.2 on pypi) will manage the details for you:
>>> from simpledate import *
>>> now_utc = SimpleDate(tz='UTC')
>>> now_tz = now_utc.convert(tz='CST6CDT')
>>> begin_day = now_tz.replace(hour=0, minute=0, second=0, microsecond=0)
>>> now_utc.timestamp
1371950295.777453
>>> now_tz.timestamp
1371950295.777453
>>> begin_day.timestamp
1371877200.0
we can go backwards to check the timestamps (although it's clear above that switching timezone didn't change the epoch, while moving to start of day did):
>>> SimpleDate(1371877200.0, tz='CST6CDT')
SimpleDate('2013-06-22 00:00:00.000000 CDT', tz='CST6CDT')
>>> SimpleDate(1371877200.0, tz='UTC')
SimpleDate('2013-06-22 05:00:00.000000 UTC')

Python time to age

I'm trying to convert a date string into an age.
The string is like: "Mon, 17 Nov 2008 01:45:32 +0200" and I need to work out how many days old it is.
I have sucessfully converted the date using:
>>> time.strptime("Mon, 17 Nov 2008 01:45:32 +0200","%a, %d %b %Y %H:%M:%S +0200")
(2008, 11, 17, 1, 45, 32, 0, 322, -1)
For some reason %z gives me an error for the +0200 but it doesn't matter that much.
I can get the current time using:
>>> time.localtime()
(2009, 2, 3, 19, 55, 32, 1, 34, 0)
but how can I subtract one from the other without going though each item in the list and doing it manually?
You need to use the module datetime and the object datetime.timedelta
from datetime import datetime
t1 = datetime.strptime("Mon, 17 Nov 2008 01:45:32 +0200","%a, %d %b %Y %H:%M:%S +0200")
t2 = datetime.now()
tdelta = t2 - t1 # actually a datetime.timedelta object
print tdelta.days
In Python, datetime objects natively support subtraction:
from datetime import datetime
age = datetime.now() - datetime.strptime(...)
print age.days
The result is a timedelta object.
from datetime import datetime, timedelta
datetime.now()
datetime.datetime(2009, 2, 3, 15, 17, 35, 156000)
datetime.now() - datetime(1984, 6, 29 )
datetime.timedelta(8985, 55091, 206000)
datetime.now() - datetime(1984, 6, 29 )
datetime.timedelta(8985, 55094, 198000) # my age...
timedelta(days[, seconds[, microseconds[, milliseconds[, minutes[, hours[, weeks]]]]]]])
If you don't want to use datetime (e.g. if your Python is old and you don't have the module), you can just use the time module.
s = "Mon, 17 Nov 2008 01:45:32 +0200"
import time
import email.utils # Using email.utils means we can handle the timezone.
t = email.utils.parsedate_tz(s) # Gets the time.mktime 9-tuple, plus tz
d = time.time() - time.mktime(t[:9]) + t[9] # Gives the difference in seconds.
Thanks guys, I ended up with the following:
def getAge( d ):
""" Calculate age from date """
delta = datetime.now() - datetime.strptime(d, "%a, %d %b %Y %H:%M:%S +0200")
return delta.days + delta.seconds / 86400.0 # divide secs into days
Giving:
>>> getAge("Mon, 17 Nov 2008 01:45:32 +0200")
78.801319444444445
Since Python 3.2, datetime.strptime() returns an aware datetime object if %z directive is provided:
#!/usr/bin/env python3
from datetime import datetime, timezone, timedelta
s = "Mon, 17 Nov 2008 01:45:32 +0200"
birthday = datetime.strptime(s, '%a, %d %b %Y %H:%M:%S %z')
age = (datetime.now(timezone.utc) - birthday) / timedelta(1) # age in days
print("%.0f" % age)
On older Python versions the correct version of #Tony Meyer's answer could be used:
#!/usr/bin/env python
import time
from email.utils import parsedate_tz, mktime_tz
s = "Mon, 17 Nov 2008 01:45:32 +0200"
ts = mktime_tz(parsedate_tz(s)) # seconds since Epoch
age = (time.time() - ts) / 86400 # age in days
print("%.0f" % age)
Both code examples produce the same result.

Categories