How to resolve created events' time mismatches due to Calendar API upgrade? - python

For reference, my timezone is Eastern - New York.
I am inserting events from a PostgreSQL database to a Google Calendar. I have been using UTC-4 since early June, when I finally got my app moved from v2 to v3, and for a couple of years in v2. Up until the August 18 that has worked giving me the correct time. On August 18 the time was off by one hour so I changed the setting to UTC-5. That worked for about 2 hours and then I have had to reset it back to UTC-4.
Now today, August 21, it is off an hour again and I have set the UTC back to -5. The events are getting inserted as they should with the exception of an event being an hour off and the UTC needing to be changed sometimes. The system time is correct on my server.
Any ideas on what is happening?
Some of my code snippets:
#get an event from a PostgreSQL database to insert into a Google Calendar
curs.execute("SELECT c_event_title,c_name,c_event_date,c_event_starttime,c_event_endtime,c_department,seat_arrange,c_attendee_count from sched_421 where sched_id_421=%i;" %recnum)
mit=curs.fetchall() # mit IS NOW ALL THE RESULTS OF THE QUERY
for myrec in mit: # FOR THE ONE RECORD (EVENT) IN THE QUERY RESULTS
myend_time = time.strftime("%I:%M %p", time.strptime(str(myrec[4]),"%H:%M:%S"))
if myend_time[0]=='0': # Remove leading zero for 01:00 - 09:00
myend_time = myend_time[1:]
title = ' - %s %s - Group:%s' %(myend_time,myrec[0],myrec[5])
mycontent = myrec[0]+' - '+ myrec[5]
content = mycontent
where = where_dict[room_calendar]
# THIS IS WHERE THE UTC IS, SOMETIMES 4 WORKS SOMETIMES 5 WORKS
start_time = '%sT%s-05:00' %(myrec[2],myrec[3]) # Google format
end_time = '%sT%s-05:00' %(myrec[2],myrec[4]) # Google format
myend_time = '%s' %myrec[4] # User format (am/pm)
seat_arrange = '\nSeating - %s' %str(myrec[6])
attendee_count = '\nNumber of participants: %s' %str(myrec[7])
descript = str(myrec[0]) + ' ' + seat_arrange + attendee_count+ "\n Created By: me#somewhere.com"
# upload the event to the calendar
created_event = service.events().insert(calendarId=calendar_dict[room_calendar], body=event).execute()

Are the dates you are looking at on different sides of the daylight savings switch?
Eastern Time Zone is UTC-4:00 from March to November and UTC-5:00 from November to March.
Hard-coding the TZ Offset like that is a bad idea, especially in a TZ that uses daylight savings. It would be best to store all the times as UTC and just apply the TZ information at the endpoints (data input and data display).
At the very least, you will want to have something calculate the correct TZ offset, based on the date, like a helper function or some block of logic.
I'm not sure how much control you have over the data in the database, so that would dictate which path you choose.
Ideally, you could change the 3 fields (date, start time, end time) in the database into 2 (start datetime UTC, end datetime UTC)

I have had to change this code:
# THIS IS WHERE THE UTC IS, SOMETIMES 4 WORKS SOMETIMES 5 WORKS
start_time = '%sT%s-05:00' %(myrec[2],myrec[3]) # Google format
end_time = '%sT%s-05:00' %(myrec[2],myrec[4]) # Google format
to (check to see if the event is in daylight savings time or not, this was not necessary with v2)
if bool (pytz.timezone('America/New_York').dst(datetime.datetime(myrec[2].year,myrec[2].month,myrec[2].day), is_dst=None)):
utc_offset = '4'
else:
utc_offset = '5'
start_time = '%sT%s-0%s:00' %(myrec[2],myrec[3],utc_offset)
end_time = '%sT%s-0%s:00' %(myrec[2],myrec[4],utc_offset)

Related

Convert timestamp to time only

I'm getting the calendar results from outlook, fetching only the Start time and the Subject of each calendar item.
import win32com, win32com.client
import datetime, time, pytz
def getCalendarEntries():
Outlook = win32com.client.Dispatch("Outlook.Application")
appointments = Outlook.GetNamespace("MAPI").GetDefaultFolder(9).Items
appointments.Sort("[Start]");
appointments.IncludeRecurrences = "True"
today = datetime.datetime.today().date().strftime("%Y-%d-%m")
tomorrow = (datetime.date.today() + datetime.timedelta(days=1)).strftime("%Y-%d-%m")
appointments = appointments.Restrict("[Start] >= '" +today+"' AND [Start] < '"+tomorrow+"'");
events={'Start':[],'Subject':[]}
for a in appointments:
events['Start' ].append(a.Start );
events['Subject'].append(a.Subject)
return events
calendar = getCalendarEntries();
n=len(calendar['Start']);
i=0;
while( n ):
print(
calendar['Start'][i] ,
calendar['Subject'][i]
);
n-=1;
i+=1;
This is the result, and it is correct:
$ py test_outlook.py
2019-12-06 10:00:00+00:00 test apointment
What I need now is to manipule this data above to get only the time: 10:00, so that I can do calculations and find out how much time there is until the event starts... like if it's 10min away, 1h away, etc.
I really have no idea on how to do it... anyone has any idea?
Uri Goren seems to have answered the question here: https://stackoverflow.com/a/38992623/8678978
You need to use strptime with the datetime format to get a date object, and then you can extract the time portion.
dateString = '2019-12-06 10:00:00+00:00'
dateObject = datetime.datetime.strptime(str[0:19], '%Y-%m-%d %H:%M:%S')
Now you have a date object and can get the time parts using:
dateObject.hour
dateObject.minute
dateObject.second
I am not sure what type getCalendarEntries returns. You can find out by adding an additional temporary line in your program:
print(type(calendar['Start'][i]))
If it is a datetime object, you can simply query the hour attribute:
hours = calendar['Start'][i].hour
If getCalendarEntries returns a POSIX timestamp, you can first convert it to a Python datetime object and then query the hour
dt = datetime.fromtimestamp(calendar['Start'][i])
hours = dt.hour
If it is a string, you can parse it using datetime.fromisoformat:
dt = datetime.datetime.fromisoformat(calendar['Start'][i])
hours = dt.hour

How to check if a date is earlier than today's date

I have a program that runs a Windows "net user" command to get the expiration date of a user. For example: "net user justinb /domain"
The program grabs the expiration date. I am taking that expiration date and setting it as variable datd
In the example below, we'll pretend the expiration date given was 1/10/2018. (Note: Windows does not put a 0 in front of the month)
import time
datd = "1/10/2018"
# places a 0 in front of the month if only 1 digit received for month
d = datd.split("/")
if len(d[0])==1:
datd="0"+datd
print("Changed to: " + datd)
myDate = (time.strftime("%m/%d/%Y"))
print ("This is today's date: " + myDate)
if datd <= myDate: # if datd is is earlier than todays date
print (" Password expired. ")
else:
print (" Password not expired. ")
input(" Press Enter to exit.")
Right now, it gives me correct information if datd equals a date in 2017. I am getting a problem with the year 2018 and on though. It is telling me that 1/10/2018 comes before today's date of 10/24/2017.
Is there a way I change the format of the dates properly so that they are read correctly in this program?
I want the output to say that the "Password is not expired" if datd = 1/10/2018
from datetime import datetime
string_input_with_date = "25/10/2017"
past = datetime.strptime(string_input_with_date, "%d/%m/%Y")
present = datetime.now()
past.date() < present.date()
This should do the job for you! Both handling day in format with leading 0 and without it.
Use .date() to make comparision of datetimes just to extent of daily date.
Warning: if you would like to be 100% correct and purist take care about timezone related issues. Make sure what time zone is assumed in your windows domain etc.
Reference:
datetime strptime - https://docs.python.org/3/library/datetime.html#strftime-strptime-behavior

How to compute the time difference between two time zones in python?

How can I compute the time differential between two time zones in Python? That is, I don't want to compare TZ-aware datetime objects and get a timedelta; I want to compare two TimeZone objects and get an offset_hours. Nothing in the datetime library handles this, and neither does pytz.
Here is a solution using the Python library Pytz which solves the issue of ambiguous times at the end of daylight saving time.
from pytz import timezone
import pandas as pd
def tz_diff(date, tz1, tz2):
'''
Returns the difference in hours between timezone1 and timezone2
for a given date.
'''
date = pd.to_datetime(date)
return (tz1.localize(date) -
tz2.localize(date).astimezone(tz1))\
.seconds/3600
The examples below calculate the difference in hours between UTC and Australia time for the first of January and first of June respectively. Notice how daylight savings are taken into consideration.
utc = timezone('UTC')
aus = timezone('Australia/Sydney')
tz_diff('2017-01-01', utc, aus)
# 11.0
tz_diff('2017-06-01', utc, aus)
# 10.0
Thanks
The first thing you have to know is that the offset between two time zones depends not only on the time zones in question, but on the date you're asking about. For example, the dates on which Daylight Savings Time began and ended changed in the US in 2007. While fundamental time zone logistics change only infrequently in any single location, the rate of change globally is impossible to ignore. Therefore, you have to incorporate the date in question into your function.
Having completed the necessary preface, the actual function isn't too hard to write if you take advantage of the pendulum library. It should look something like this:
import pendulum
def tz_diff(home, away, on=None):
"""
Return the difference in hours between the away time zone and home.
`home` and `away` may be any values which pendulum parses as timezones.
However, recommended use is to specify the full formal name.
See https://gist.github.com/pamelafox/986163
As not all time zones are separated by an integer number of hours, this
function returns a float.
As time zones are political entities, their definitions can change over time.
This is complicated by the fact that daylight savings time does not start
and end on the same days uniformly across the globe. This means that there are
certain days of the year when the returned value between `Europe/Berlin` and
`America/New_York` is _not_ `6.0`.
By default, this function always assumes that you want the current
definition. If you prefer to specify, set `on` to the date of your choice.
It should be a `Pendulum` object.
This function returns the number of hours which must be added to the home time
in order to get the away time. For example,
```python
>>> tz_diff('Europe/Berlin', 'America/New_York')
-6.0
>>> tz_diff('Europe/Berlin', 'Asia/Kabul')
2.5
```
"""
if on is None:
on = pendulum.today()
diff = (on.set(tz=home) - on.set(tz=away)).total_hours()
# what about the diff from Tokyo to Honolulu? Right now the result is -19.0
# it should be 5.0; Honolulu is naturally east of Tokyo, just not so around
# the date line
if abs(diff) > 12.0:
if diff < 0.0:
diff += 24.0
else:
diff -= 24.0
return diff
As stated in the documentation, you may not get a stable result for this between any two given locations as you sweep across the days of the year. However, implementing a variant which chooses the median result over the days of the current year is an exercise left for the reader.
Here's another solution:
from datetime import datetime
from pytz import timezone
from dateutil.relativedelta import relativedelta
utcnow = timezone('utc').localize(datetime.utcnow()) # generic time
here = utcnow.astimezone(timezone('US/Eastern')).replace(tzinfo=None)
there = utcnow.astimezone(timezone('Asia/Ho_Chi_Minh')).replace(tzinfo=None)
offset = relativedelta(here, there)
offset.hours
Here what we're doing is converting a time to two different time zones. Then, we remove the time zone information so that when you calculate the difference between the two using relativedelta, we trick it into thinking that these are two different moments in time instead of the same moment in different time zones.
The above result will return -11, however this amount can change throughout the year since US/Eastern observes DST and Asia/Ho_Chi_Minh does not.
I created two functions to deal with timezone.
import datetime
import pytz
def diff_hours_tz(from_tz_name, to_tz_name, negative=False):
"""
Returns difference hours between timezones
res = diff_hours_tz("UTC", "Europe/Paris") : 2
"""
from_tz = pytz.timezone(from_tz_name)
to_tz = pytz.timezone(to_tz_name)
utc_dt = datetime.datetime.now(datetime.timezone.utc)
dt_from = dt_to = datetime.datetime.utcnow()
dt_from = from_tz.localize(dt_from)
dt_to = to_tz.localize(dt_to)
from_d = dt_from - utc_dt
if from_d.days < 0:
return diff_hours_tz(to_tz_name, from_tz_name, True)
dt_delta = dt_from - dt_to
negative_int = -1 if negative else 1
return int(dt_delta.seconds/3600)*negative_int
def dt_tz_to_tz(dt, from_tz_name, to_tz_name):
"""
Apply difference hours between timezones to a datetime object
dt_new = dt_tz_to_tz(datetime.datetime.now(), "UTC", "Europe/Paris")
"""
hours = diff_hours_tz(from_tz_name, to_tz_name)
return dt+datetime.timedelta(hours=hours)
# Usage example
res = diff_hours_tz("Europe/Paris", "America/New_York")
# Result : -6
res = diff_hours_tz("UTC", "Europe/Paris")
# Result : 2
now = datetime.datetime.now()
# Result : 2019-06-18 15:10:31.720105
dt_new = dt_tz_to_tz(now, "UTC", "Europe/Paris")
# Result : 2019-06-18 17:10:31.720105
dt_new = dt_tz_to_tz(now, "Europe/Paris", "America/New_York")
# Result : 2019-06-18 09:10:31.720105
dt_new = dt_tz_to_tz(now, "America/New_York", "Europe/Paris")
# Result : 2019-06-18 21:10:31.720105
I hope it will help !
Here is a code snippet to get the difference between UTC and US/Eastern, but it should work for any two timezones.
# The following algorithm will work no matter what is the local timezone of the server,
# but for the purposes of this discussion, let's assume that the local timezone is UTC.
local_timestamp = datetime.now()
# Assume that utc_timestamp == 2019-01-01 12:00.
utc_timestamp = pytz.utc.localize(local_timestamp)
# If it was 12:00 in New York, it would be 20:00 in UTC. So us_eastern_timestamp is a UTC
# timestamp with the value of 2019-01-01 20:00.
us_eastern_timestamp = timezone("US/Eastern").localize(local_timestamp).astimezone(pytz.utc)
# delta is a Python timedelta object representing the interval between the two timestamps,
# which, in our example, is -8 hours.
delta = utc_timestamp - us_eastern_timestamp
# In the last line, we convert the timedelta into an integer representing the number of
# hours.
print round(delta.total_seconds() / 60.0 / 60.0)
(tz_from.localize(date) - tz_to.localize(date)).seconds/3600.0
Where tz_from and tz_to are the starting and ending timezones. You must specify a particular date.
from datetime import datetime
from zoneinfo import ZoneInfo
dt = datetime.now() # 2020-09-13
tz0, tz1 = "Europe/Berlin", "US/Eastern" # +2 vs. -4 hours rel. to UTC
utcoff0, utcoff1 = dt.astimezone(ZoneInfo(tz0)).utcoffset(), dt.astimezone(ZoneInfo(tz1)).utcoffset()
print(f"hours offset between {tz0} -> {tz1} timezones: {(utcoff1-utcoff0).total_seconds()/3600}")
>>> hours offset between Europe/Berlin -> US/Eastern timezones: -6.0
a way to do this with Python 3.9's standard library.

Customizing timezones shown to users with pytz, datetime and Python 3

I have a list of users:
tom, 0530, eastern
dick, 0745, pacific
harry, 0915, central
ect...
I need to send them an email at that specific time (or within 5 minutes) for their timezone. I've been working on a python script that I would run with cron every 5 minutes.
## pseudo code:
for user in users:
if difference of usertime and utctime is <= 5 minutes:
send email
I'm getting close, but doing the math with the converted timezones is where I'm stuck now.
code:
import datetime
import pytz
today = datetime.datetime.now().date()
user1 = pytz.timezone('US/Eastern').localize(datetime.datetime(today.year, today.month, today.day, 5, 30))
now_utc = datetime.datetime.now(pytz.timezone('UTC'))
user1_converted = user1.astimezone(pytz.timezone('UTC'))
now_utc - user1_converted
##maths
We call this "making a mountain out of a mole hill" :)
It sounds like you have user preferences for the time they would like to be emailed, and the time zone they are part of.
E.g. tom = ('US/Eastern', "09:15")
When your script runs the first thing it must do is establish the current time in UTC.
from datetime import datetime
current_time = datetime.utcnow()
Then you need to convert the user's current time, and preference time, into UTC time. From this question.
tz = timezone('US/Pacific')
def toUTC(d):
return tz.normalize(tz.localize(d)).astimezone(pytz.utc)
After you have done this you can subtract one from the other and compare to your set threshold (5 minutes) to see if it's the right time to send the email.

In Python, using win32api doesn't set the correct date

I am trying to set system date (not time) using following code. I want to set the current time to the new date. Following is a sample code and I found the time is not correct after change.
day = 20
month = 3
year = 2010
timetuple = time.localtime()
print timetuple
print timetuple[3], timetuple[4], timetuple[5]
win32api.SetSystemTime(year, month, timetuple[6]+1,
day, timetuple[3], timetuple[4], timetuple[5], 1)
You are setting the system time from the localtime timestamp. The latter is adjusted for the local timezone, while SetSystemTime requires you to use the UTC timezone.
Use time.gmtime() instead:
tt = time.gmttime()
win32api.SetSystemTime(year, month, 0, day,
tt.tm_hour, tt.tt_min, tt.tt_sec, 0)
You then also avoid having to deal with whether or not you are in summer time (DST) now, vs. March when you would be in winter time.
Alternatively you can use a datetime.datetime.utcnow() call and get the millisecond parameter as a bonus:
import datetime
tt = datetime.datetime.utcnow().time()
win32api.SetSystemTime(year, month, 0, day,
tt.hour, tt.minute, tt.second, tt.microsecond//1000)
Note that I left the weekday item set to 0 in both examples; it is ignored when calling SetSystemTime. If it was not ignored, then your code example had the value wrong; the Python value ranges from 0 to 6 for Monday through to Sunday, while the Win32 API wants 1 through to 7 for Sunday through to Saturday. You'd have to add 2 and use modulo 7:
win32_systemtime_weekday = (python_weekday + 2) % 7)

Categories

Resources