Sitemap
My Activity Diaries

Simple notes and experiments about online tools, AI images, and everyday digital activities.

Mail App #1x02: Using Python and IMAP to Fetch Only the Time Slots I Care About

--

Splitting the day into blocks and letting a script pull just the emails I need

Press enter or click to view image in full size
Image generated with Midjourney.

For this episode, I shift from thinking about my inbox conceptually to actually writing code. In Mail App #1x01, I decided that instead of trying to “handle 50–80 emails a day”, I would think in terms of “which emails arrive in which time slots”. Now the goal is to make that idea concrete: use Python and IMAP to fetch only the emails that arrived during specific time ranges in a day.

Get Tetsuji Kondo’s stories in your inbox

Join Medium for free to get updates from this writer.

Yahoocom supports IMAP access, which means I can connect to the mail server from my local environment (Python running on Laragon) and read messages programmatically. The first step was to enable IMAP access in my account, generate an app password, and write a minimal script using Python’s imaplib to connect to imap.mail.yahoo.com, log in, and select the inbox.

import imaplib
import email
from email.header import decode_header
from datetime import datetime, timedelta, timezone

IMAP_HOST = "imap.mail.yahoo.com"
IMAP_USER = "your_account@yahoo.com"
IMAP_PASS = "your_app_password" # app password generated on Yahoocom

From the beginning, I wanted this Mail App to let me define dates and time ranges explicitly. In practice, that meant introducing variables like START_DATE, END_DATE, START_HOUR, and END_HOUR, so I could say things like “previous day 18:00–23:59”, “current day 00:00–08:00”, or “daytime 09:00–17:00” and treat each as a separate block. These values live in a small configuration section at the top of the script, so I can adjust them before each run.

# Japan Standard Time (JST)
JST = timezone(timedelta(hours=9))

# Example: from previous day 18:00 to current day 08:00 (JST)
START_DATE = "2026-03-04"
END_DATE = "2026-03-05"
START_HOUR = 18
END_HOUR = 8

def build_time_range():
"""Build start and end datetimes in JST for the current run."""
start_dt = datetime.fromisoformat(f"{START_DATE}T{START_HOUR:02d}:00:00").replace(tzinfo=JST)
end_dt = datetime.fromisoformat(f"{END_DATE}T{END_HOUR:02d}:00:00").replace(tzinfo=JST)
return start_dt, end_dt

Standard IMAP searches can only filter by date, not by exact time of day. To work around this, I decided to take a two-step approach: first fetch all emails for the dates that cover my target window, then apply a second filter in Python based on each message’s internal timestamp. In other words, I ask IMAP for “all emails since date A and before date B”, and then I narrow it down locally to match the exact time range I care about.

def connect_imap():
"""Connect to Yahoocom via IMAP and select the INBOX."""
m = imaplib.IMAP4_SSL(IMAP_HOST)
m.login(IMAP_USER, IMAP_PASS)
m.select("INBOX")
return m

def search_by_date_range(m, start_dt, end_dt):
"""
Search messages broadly by date.
IMAP can filter by date, but not by time of day, so we cover the whole date span here.
"""
since_str = start_dt.strftime("%d-%b-%Y")
before_str = (end_dt + timedelta(days=1)).strftime("%d-%b-%Y")
typ, data = m.search(None, f'(SINCE "{since_str}" BEFORE "{before_str}")')
if typ != "OK":
return []
return data[0].split()

Handling time zones turned out to be an important detail. Yahoocom’s internal timestamps and what I see as Japan Standard Time (JST) do not always line up in a straightforward way. In early versions of the script, I often found that the emails I fetched were slightly outside the range I thought I had specified. To fix this, I carefully converted the internal dates to JST before comparing them, and I ran several trials where I logged both the target time range and the actual timestamps of fetched messages until the behavior matched my expectations.

def get_jst_datetime_from_internaldate(raw_internaldate: bytes) -> datetime:
"""
Parse the INTERNALDATE value returned by IMAP and convert it to JST.
Example: b'"05-Mar-2026 01:23:45 +0000"'
"""
s = raw_internaldate.decode().strip().strip('"')
dt = datetime.strptime(s, "%d-%b-%Y %H:%M:%S %z")
return dt.astimezone(JST)

def fetch_and_filter_messages(m, msg_ids, start_dt, end_dt):
"""
Fetch messages and filter them by the JST time window.
For now, we only keep a few fields: datetime, from, and subject.
"""
records = []
for msg_id in msg_ids:
typ, data = m.fetch(msg_id, "(RFC822 INTERNALDATE)")
if typ != "OK":
continue

raw_msg = data[0][1]
# INTERNALDATE is in the second element of the response
internaldate = data[1].split(b"INTERNALDATE")[1].split(b")")[0].strip()
msg_dt = get_jst_datetime_from_internaldate(internaldate)

if not (start_dt <= msg_dt < end_dt):
continue

msg = email.message_from_bytes(raw_msg)
subject, _ = decode_header(msg.get("Subject", ""))[0]
if isinstance(subject, bytes):
subject = subject.decode(errors="ignore")
from_addr = msg.get("From", "")

records.append({
"datetime_jst": msg_dt.isoformat(),
"from": from_addr,
"subject": subject,
})

return records

By this point, the script naturally fell into three main sections. The first is the configuration section, where I define dates, time ranges, and (later) output directories. The second is the retrieval section, which connects to Yahoocom via IMAP and pulls the broad set of emails for the date span. The third is the shaping section, which filters those emails by the JST time window and prepares a list of simple records that the next episode will save as JSON.

def main():
start_dt, end_dt = build_time_range()
print(f"Target time range (JST): {start_dt} to {end_dt}")

m = connect_imap()
try:
msg_ids = search_by_date_range(m, start_dt, end_dt)
print(f"Messages matched by IMAP date search: {len(msg_ids)}")

records = fetch_and_filter_messages(m, msg_ids, start_dt, end_dt)
print(f"Messages after JST time filter: {len(records)}")

# In Mail App #1x03, these records will be saved as a JSON file.
# save_daily_summaries(records)
finally:
m.close()
m.logout()

if __name__ == "__main__":
main()

I did not write all of this by hand from a blank file. At some point, I started asking Comet for help with prompts like “Write Python code that fetches Yahoocom emails for a given time range” or “Show me a sample script that uses variables for dates and hours.” Comet generated code snippets that showed me how to structure the IMAP connection and basic error handling, and I then adapted those snippets to fit my own environment, naming conventions, and time-zone handling.

Even with AI’s help, there was still a fair amount of trial and error involved in matching the script to my actual workflow: folder paths on Laragon, how I define time blocks in my day, and what kind of log output I want to see at 5 a.m. In that sense, this Mail App feels like a collaboration between my daily routine and the code that an AI helped me write — each iteration making the tool a bit more aligned with how I really use my email.

In the next episode, I will take the records list produced here and turn each time slot into a JSON file, so that “evening to midnight” and “midnight to early morning” each become a snapshot I can open later and review at my own pace.

--

--

Tetsuji Kondo
Tetsuji Kondo

Written by Tetsuji Kondo

I document daily insights, conversations with AI, and my experiences developing apps with the help of AI.

My Activity Diaries
My Activity Diaries

Published in My Activity Diaries

Simple notes and experiments about online tools, AI images, and everyday digital activities.