# Input any time, output timezones that are currently that time

Posted on

Problem

I made a program that will take in a time and a weekday, and return which parts of the
world are at times closest to the one specified. Part of the program focuses on extracting a date and country out of a user input.

Here are some example inputs and outputs:

``````"In what part of the world is it 4:00 PM Friday right now?" (datetime.datetime(2021, 1, 1, 16, 0), 'World')
"Where in America is it 6:40 AM Thu?"                       (datetime.datetime(2020, 12, 31, 6, 40), 'America')
"It is 10:30 pm fri in which part of Asia?"                 (datetime.datetime(2021, 1, 1, 22, 30), 'Asia')
"Where would the time be 10:10 AM Sat?"                     (datetime.datetime(2021, 1, 2, 10, 10), 'World')
"Which state of brazil has the time 8:30 AM Saturday now?"  (datetime.datetime(2021, 1, 2, 8, 30), 'Brazil')
``````

The reason that the only part of the date that needs to be given is the weekday,
is because the maximum possible days in difference between a timezone and the timezone
the user is currently at is one day, either ahead or behind.

So the program checks if the weekday given is at the current day, one day before the current day, or one day after the current day, and can return the rest of the date.

Here is my code:

``````import pytz
from datetime import datetime, timedelta
import re

def return_words(string):
return re.findall(r'bS+b', string.lower())

def string_to_datetime(string):
weekdays = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"]
string = string.lower()
words = sorted(return_words(string), key=len)[::-1]
countries = [t.split("/") for t in pytz.all_timezones]

for word in words:
in_day = [weekday for weekday in weekdays if word in weekday]
if len(in_day) == 1: # Make sure that the word found is a weekday name, not just something like "day"
weekday = in_day
break
else:
return

for word in words:
if word.title() in countries:
country = word.title()
break
else:
country = "World" # If no country name found, use "World"

date = datetime.now()
one_day = timedelta(days=1)
if (datetime.now() + one_day).strftime("%A").lower() == weekday:
date += one_day
elif (datetime.now() - one_day).strftime("%A").lower() == weekday:
date -= one_day
elif datetime.now().strftime("%A").lower() != weekday:
return

year, month, day = date.year, date.month, date.day
hours_minutes = re.findall("d+:d+", string)

half = re.findall(f"(?<={hours_minutes}).{{0,3}}[ap]m", string) # Play it safe, and only extract the am or pm that is at most 3 steps away from the hours:minutes
if half: # If found an am or pm
half = half.strip()
else: # If not found when playing it safe, resort to finding any am or pm in the string
half = re.findall("[ap]m", string).strip()
string = f"{year} {month} {day} {hours_minutes} {half}"
try:
return datetime.strptime(string, "%Y %m %d %I:%M %p"), country
except ValueError:
return

while True:
``````

Can you show me how to improve the efficiency of my code? Also, any tips on tidying up would be of big help.

Solution

``````    in_day = [weekday for weekday in weekdays if word in weekday]
if len(in_day) == 1:
``````

seems odd. You make the entire list, put it in memory, take its length and then throw it away. Consider instead

``````n_matching = sum(1 for weekday in weekdays if word in weekday)
if n_matching == 1:
``````

This loop:

``````for word in words:
in_day = [weekday for weekday in weekdays if word in weekday]
if len(in_day) == 1: # Make sure that the word found is a weekday name, not just something like "day"
weekday = in_day
break
``````

can be simplified if you use set operations; `lower` everything in `words` before this:

``````countries = {t.split("/", 1).lower() for t in pytz.all_timezones}
try:
country = next(iter(countries & set(words))).title()
except StopIteration:
country = "World" # If no country name found, use "World"
``````