From string to datetime, looking at the current (local) time - python

Is there any function, in python, that extracts time information from a string to get the time from the current local one?
For example:
I have a column with time information in the past compared to the current time (let's say UK time, approximately 20:00)
Time
10 hours # ago
6 hours # ago
12 hours # ago
2 days # ago
1 day # ago
I would like to have, looking at the current UK time, (i.e., approximately 20:00):
New_Time
10:00
14:00
8:00
48:00
24:00
The column above comes from the current local time minus the number of hours (from string to numerical) in Time column.
Is it something that I might do with a function? The New_Time is the output that I will need.
In case there might not be a function in python, my approach would be:
use something to detect the current time (to save it in a variable)
consider hours as 60 min and 1 day as 24 hours ... in order to run some mathematical operations, once transformed strings into numerical
subtract from the current time the amount of minutes/hours to go back in time

You can make use of the dateparser package (some explanation in comments):
import datetime
import dateparser # pip install dateparser
# a function to format timedelta to string needed below,
# see https://stackoverflow.com/q/538666/10197418
def timedelta2str(sec):
hours, remainder = divmod(sec, 3600)
return f'{int(hours):02}:{int(remainder//60):02}'
def formatTimeAgo(string, reftime):
# try to parser... if successful, you get a datetime object
dtobj = dateparser.parse(string, settings={'RELATIVE_BASE': reftime})
if isinstance(dtobj, datetime.datetime):
# calculate a timedelta object against reference time
td = reftime - dtobj
# now format output based on input (days or hours..)
if td >= datetime.timedelta(1):
return timedelta2str(td.total_seconds())
else:
return (reftime-td).strftime('%H:%M')
else:
return "N/A"
# exemplary input
t = ("10 hours", "6 hours", "2 days", "1 day")
# a reference time
reftime = datetime.datetime(2021,4,10,20)
for elem in t:
print(elem, '->', formatTimeAgo(elem, reftime))
# giving you
>>> 10 hours -> 10:00
>>> 6 hours -> 14:00
>>> 2 days -> 48:00
>>> 1 day -> 24:00
Now you can adjust that to be used on pandas.Series:
import pandas as pd
df = pd.DataFrame({'Time': ["10 hours", "6 hours", "12 hours", "2 days", "1 day"]})
df['New_Time'] = df['Time'].apply(formatTimeAgo, args=(reftime,))
df['New_Time']
0 10:00
1 14:00
2 08:00
3 48:00
4 24:00
Name: New_Time, dtype: object

You can create a method that would return the time that it was a few hours/days/... ago. For example, two methods that will respectively return what day it was x days ago and what hour it was x hours ago :
def getTimeDaysAgo(x):
today = datetime.datetime.now()
value = today.day - x
previousMonth = today.month - 1
previousMonthDuration = 30
if previousMonth == 2:
if today.year % 4 == 0:
previousMonthDuration = 29
else:
previousMonthDuration = 28
if previousMonth % 2 == 1:
previousMonthDuration = 31
return value if value >= 0 else value + previousMonthDuration
def getTimeHoursAgo(x):
value = datetime.datetime.now().hour - x
return value if value >= 0 else value + 24
I'm not sure why you want to get "48:00" as a result of 2 days ago and "10:00" as a result of 10 hours ago at the same time. Feel free to ask for more infos, I'd be glad to help you out.

Related

Efficiently Finding the next Friday The 13th

I have been trying to calculate the date for next Friday the 13th in Python 3, but Idk how to make it more efficient.
from datetime import date, timedelta
from calendar import monthrange
def is_friday_13th(date):
# Returns bool value for the given date if it is Friday The 13th
return date.day == 13 and date.weekday() == 4
def max_days(date):
# Returns Number of Days for the given month
return monthrange(date.year, date.month)[1]
def friday_the_13th():
# Returns next Friday the 13th
# If today is Friday the 13th, returns today's date.
today = date.today()
result = today
if result.day < 13:
result += timedelta(days=13-result.day)
found = is_friday_13th(result)
while not found:
result += timedelta(days=max_days(result) - result.day)
result += timedelta(days=13)
found = is_friday_13th(result)
return f"{result.strftime('%Y-%m-%d')}"
if __name__ == "__main__":
print(friday_the_13th())
I feel like using the monthrange function of the calendar module makes it less efficient and this problem can be solved without it but I'm struggling to do it.
I read the solutions written by others and I don't understand how to solve this problem more efficiently and write more Pythonically.
How about this
from datetime import date, timedelta
def friday13s(from_date=date.today()):
d = from_date + timedelta(13 - from_date.day) # clamp date to the 13th
def increment_month(d):
mm = 1 if d.month == 12 else d.month + 1
yy = d.year + 1 if mm == 1 else d.year
return date(yy, mm, d.day)
if from_date > d:
d = increment_month(d)
while True:
if d.weekday() == 4:
yield d
d = increment_month(d)
usage:
from itertools import islice
for d in islice(friday13s(), 10):
print(d)
prints
2021-08-13
2022-05-13
2023-01-13
2023-10-13
2024-09-13
2024-12-13
2025-06-13
2026-02-13
2026-03-13
2026-11-13
Note that increment_month() is not a general-purpose way to increment the month in a date. It works fine if the day is the 13th, but it will fail if the day is >= 29th. It's only fit for the purpose of the particular task it is used in here.
There are more elegant ways of incrementing the month with libraries like dateutil, but when using only built-ins from the Python standard library, doing some legwork is necessary. Luckily, the date arithmetic for this task is not complicated. Adding a whole extra library just to reduce the above by two or three lines seems excessive.

Python get date from weekday

Let's say I have 11 Sessions for myself to complete. I haven't set dates for these sessions but rather just weekdays where one session would take place. Let's say when scheduling these sessions, I chose MON, TUE and WED. This means that after today, I want the dates to 11 my sessions which would be 4 Mondays, 4 Tuesdays and 3 Wednesdays from now after which my sessions will be completed.
I want to automatically get the dates for these days until there are 11 dates in total.
I really hope this makes sense... Please help me. I've been scratching my head over this for 3 hours straight.
Thanks,
You can use pd.date_range and the CustomBusinessDay object to do this very easily.
You can use the CustomBusinessDay to specify your "business days" and create your date range from it:
import pandas
from datetime import date
session_days = pd.offset.CustomBusinessDay(weekmask="Mon Tue Wed")
dates = pd.date_range(date.today(), freq=session_days, periods=11)
I figured it out a while ago but my internet died. All it took was Dunhill and some rest.
import datetime
def get_dates():
#This is the max number of dates you want. In my case, sessions.
required_sessions = 11
#These are the weekdays you want these sessions to be
days = [1,2,3]
#An empty list to store the dates you get
dates = []
#Initialize a variable for the while loop
current_sessions = 0
#I will start counting from today but you can choose any date
now = datetime.datetime.now()
#For my use case, I don't want a session on the same day I run this function.
#I will start counting from the next day
if now.weekday() in days:
now = now + datetime.timedelta(days=1)
while current_sessions != required_sessions:
#Iterate over every day in your desired days
for day in days:
#Just a precautionary measure so the for loops breaks as soon as you have the max number of dates
#Or the while loop will run for ever
if current_sessions == required_sessions:
break
#If it's Saturday, you wanna hop onto the next week
if now.weekday() == 6:
#Check if Sunday is in the days, add it
if 0 in days:
date = now + datetime.timedelta(days=1)
dates.append(date)
current_sessions += 1
now = date
else:
#Explains itself.
if now.weekday() == day:
dates.append(now)
now = now + datetime.timedelta(days=1)
current_sessions += 1
#If the weekday today is greater than the day you're iterating over, this means you've iterated over all the days in a NUMERIC ORDER
#NOTE: This only works if the days in your "days" list are in a correct numeric order meaning 0 - 6. If it's random, you'll have trouble
elif not now.weekday() > day:
difference = day - now.weekday()
date = now + datetime.timedelta(days=difference)
dates.append(date)
now = date
current_sessions += 1
#Reset the cycle after the for loop is done so you can hop on to the next week.
reset_cycle_days = 6 - now.weekday()
if reset_cycle_days == 0:
original_now = now + datetime.timedelta(days=1)
now = original_now
else:
original_now = now + datetime.timedelta(days=reset_cycle_days)
now = original_now
for date in dates:(
print(date.strftime("%d/%m/%y"), date.weekday()))
Btw, I know this answer is pointless compared to #Daniel Geffen 's answer. If I were you, I would definitely choose his answer as it is very simple. This was just my contribution to my own question in case anyone would want to jump into the "technicalities" of how it's done by just using datetime. For me, this works best as I'm having issues with _bz2 in Python3.7 .
Thank you all for your help.

Weekly Countdown Timer in Python

I am trying to write a script that runs continuously in the background to countdown to a repeated weekly event. For example, it should tell me how many days, hours, and minutes it will take to reach the specified time.
I know how to do it if I had a specific date and time.
import datetime
delta = datetime.datetime(2018, 5, 5, 8) - datetime.datetime.now()
But what if I don't have a specific date and time? Can datetime let me choose the day of the week?
EDIT:
i.e. Some pseudocode like this is what I need.
delta = datetime(Thursday 8 PM) - datetime.datetime.now()
#returns datetime or timedelta in days, hours, minutes
EDIT:
Thanks Ethan, i appreciate your constructive advice.
I wrote a small script which should do what you want:
import datetime
import time
wanted_day = 'thursday'
wanted_time = 8
list = [['monday', 0],['tuesday', 1],['wednesday', 2],['thursday', 3],['friday', 4],['saturday', 5],['sunday', 6]]
for i in list:
if wanted_day == i[0]:
number_wanted_day = i[1]
# today delivers the actual day
today = datetime.datetime.today().weekday()
# delta_days describes how many days are left until the wanted day
delta_days = number_wanted_day - today
# time delivers the actual time
time = time.localtime(time.time())
if wanted_time > time[3]:
delta_hours = wanted_time - time[3]
delta_mins = 59 - time[4]
delta_secs = 59 - time[5]
else:
delta_days = delta_days - 1
delta_hours = 23 - time[3] + wanted_time
delta_mins = 59 - time[4]
delta_secs = 59 - time[5]
print [delta_days, delta_hours, delta_mins, delta_secs]
The output looks like this then:
[2, 21, 3, 49]
2 is the number of days, 21 the number of hours, 3 the number of mins and 49 the number of secs (I used thursday 8 am as wanted time).
You just need to input the time in the format 0-23, with am and pm you would need to adapt it a bit

Convert two timestamp floats to a readable number of years, months, and days

I have two timestamps which are stored in float format:
tms1 = 1479081600.0
tms2 = 1482105600.0
Upon calculating the difference I get
tms2 - tms1
3024000.0
How do I go about displaying this time difference of 3024000 into a readable format in days, months or years? (The answer is 35 days between 14 Nov 2016 to 19 Dec 2016 using an online unix time difference calculator)
You can use (after importing datetime)
datetime.timedelta(seconds=3024000).days
which is
35
You should use timedelta as this is a time delta - a difference in time, not an absolute time. A full representation can also be obtained by coercing a timedelta to a string:
print(datetime.timedelta(seconds=3024000))
Gives the output:
35 days, 0:00:00
Note that you don't need an online calculator for anything - datetime comes with batteries included. You could do:
import datetime
date_format = "%d %b %Y"
start_date = datetime.datetime.strptime("14 Nov 2016", date_format)
end_date = datetime.datetime.strptime("19 Dec 2016", date_format)
print(start_date == datetime.datetime.fromtimestamp(1479081600))
print(start_date)
print(end_date.strftime("%d/%m/%Y"))
diff = end_date - start_date
print(diff)
print(diff.days)
which outputs:
True
2016-11-14 00:00:00
19/12/2016
35 days, 0:00:00
35
Note that diff here is identical to the original timedelta object, but is dynamically created from datetimes rather than statically constructed. I've also demonstrated the fact that you can build a datetime from a timestamp, should you wish, and I've also taken the liberty of demonstrating strftime and the like to illustrate the power of datetime. I highly recommend the datetime approach over an arithmetic approach as it's a lot more readable and extensible.
This answer is pretty lightweight, which isn't necessarily bad, as often you might not need any more functionality than it provides, but if the timedelta between two days is less than 24 hours, it will round down to 0 days, for example. It also can't handle timezones. If you need either of those, see the legendary Raymond's awesome answer
Just subtracting seconds doesn't help you know whether a day boundary has been crossed, so it is necessary to convert the timestamps to datetime objects before computing the days.
Add since the timezone can affect what the calendar day is for a UTC timestamp, you may need a tzinfo object as well.
Once the calendar dates are known, a little calendar math is needed to compute the difference in years, months, and days:
from datetime import timedelta, datetime
def time_diff(start_timestamp, end_timestamp, tz=None):
""" Return time difference in years, months, and days.
If *tz* is None, the timestamp is converted to the platform’s local date
and time. Otherwise, *tz* should be an instance of a *tzinfo* subclass.
"""
# Determine whether we're going forward or backward in time
ago = ''
if end_timestamp < start_timestamp:
ago = 'ago'
start_timestamp, end_timestamp = end_timestamp, start_timestamp
# Compute the calendar dates from the timestamps
d1 = datetime.fromtimestamp(start_timestamp, tz)
d2 = datetime.fromtimestamp(end_timestamp, tz)
# Advance d1 day-by-day until the day is at or above d2.day
days = 0
while d2.day < d1.day:
days += 1
d1 += timedelta(days=1)
# Now compute the day difference
days += d2.day - d1.day
# Compute the totals months difference and express in years and months
total_months = (d2.year * 12 + d2.month) - (d1.year * 12 + d1.month)
years, months = divmod(total_months, 12)
# format the output
plural = lambda n: '' if n == 1 else 's'
return '%d year%s, %d month%s, and %d day%s %s' % (
years, plural(years), months, plural(months), days, plural(days), ago)
Here is an example of how to use the function:
from datetime import tzinfo
class GMT1(tzinfo):
# Example tzinfo subclass taken from the Python docs
def utcoffset(self, dt):
return timedelta(hours=1)
def dst(self, dt):
return timedelta(0)
def tzname(self,dt):
return "Europe/Prague"
print(time_diff(1479081600.0, 1482105600.0, tz=GMT1()))
This outputs:
0 years, 1 month, and 5 days

Calculate Elapsed time in python in specific format

I am trying to write a program to determine the time and date corresponding to a elapsed number of seconds since 00: 00: 00 on 1 January 2016.
But i wanted my result in specific format.
i.e should be the corresponding hour (in military time), minute, second, day of the month, month name, year, and day of the week name (Sunday – Saturday).
for example,output should look like the following
23:59:32 2 January 2018 Tuesday
i tried to code this
import time
start = time.time()
#do something
end = time.time()
temp = end-start
print(temp)
hours = temp//3600
temp = temp - 3600*hours
minutes = temp//60
seconds = temp - 60*minutes
print('%d:%d:%d' %(hours,minutes,seconds))
But i could only get the hours, minutes and seconds part,
Is there any way i can get the month,year,and week name too ?
Also i'm trying to handle leap years too.since a leap year has 366 days with 29 days in February. Leap years are years that are evenly divisible by 4, with the exception of those evenly divisible by 100 but not 400.
To format a datetime you can use strftime().
import datetime
my_time = datetime.datetime(year=2018, month=1, day=2, hour=23, minute=59,second=32)
print (my_time.strftime("%X %-d %B %Y %A"))
If you want to change the format use this table for reference
You want to use the Datetime Module. Handling dates and times is trickier than it appears! That's why it's good that Python has datetime handling built-in. Datetime and timedelta objects account for leap years and leap seconds, and they handle operations like addition and subtraction 'intuitively'.
import datetime
def convert_seconds_since_jan_1(seconds):
JAN_1_2001 = datetime.datetime(year = 2001, month = 1, day = 1)
added_time = datetime.timedelta(seconds = seconds)
return (JAN_1_2001+added_time)
Getting the weekday is simply a matter of string formatting. Remember that weekdays are modulo 7!

Categories