Basic Alarm Clock
Abstract
An alarm clock teaches a deceptively rich set of concepts in a small amount of code: parsing user input that represents a time, comparing the current time against a target, looping until that target is reached, and triggering an external effect (sound) once it is. In this project you will build a command-line alarm clock that lets the user enter a 24-hour-format time and waits until exactly that moment to play an alert.
Along the way you will learn:
- How Python represents time with the
datetimedatetimeandtimetimemodules. - The polling pattern: “do something until a condition is true.”
- How to play sounds across platforms.
- How to validate human-friendly input.
- How a one-shot alarm grows into a scheduler with snooze, multiple alarms, and persistence.
Prerequisites
- Python 3.6 or above.
- A text editor or IDE.
- A working speaker. (Headphones if a 3 AM alarm test would disturb anyone.)
- Familiarity with running scripts from the terminal.
Concepts You Will Use
| Concept | Purpose |
|---|---|
datetimedatetime | Get the current date and time, format it as text, parse text back into a time object. |
time.sleep(seconds)time.sleep(seconds) | Pause execution. Used so we do not check the clock millions of times per second. |
winsoundwinsound / playsoundplaysound / pygame.mixerpygame.mixer | Make noise when the alarm fires. Choice depends on your OS. |
String formatting (strftimestrftime) | Turn a datetimedatetime object into a string like "14:30:00""14:30:00". |
| Polling loop | A while Truewhile True loop that wakes periodically and checks whether the alarm time has arrived. |
Getting Started
Create the project
- Create a folder named
basic-alarm-clockbasic-alarm-clock. - Inside it, create
basicalarmclock.pybasicalarmclock.py. - (Optional) drop a custom
alarm.wavalarm.wavfile in the folder to use as the alert sound.
Write the code
Basic Alarm Clock
Source# Basic Alarm Clock
# Importing the libraries
import datetime
import time
import winsound
# Defining the alarm function
def alarm(Timing):
altime = str(datetime.datetime.now().strptime(Timing,"%I:%M %p"))
altime = altime[11:-3]
print(altime)
Horeal = altime[:2]
Horeal = int(Horeal)
Mireal = altime[3:5]
Mireal = int(Mireal)
print(f"Done, alarm is set for {Timing}")
while True:
if Horeal == datetime.datetime.now().hour:
if Mireal == datetime.datetime.now().minute:
print("Alarm is running")
winsound.PlaySound('abc',winsound.SND_LOOP)
elif Mireal<datetime.datetime.now().minute:
break
# Taking user input
print("Set a time to alarm(HH:MM AM/PM)")
Alarm_Time = input("Enter the time of alarm to be set: ")
alarm(Alarm_Time)
# Basic Alarm Clock
# Importing the libraries
import datetime
import time
import winsound
# Defining the alarm function
def alarm(Timing):
altime = str(datetime.datetime.now().strptime(Timing,"%I:%M %p"))
altime = altime[11:-3]
print(altime)
Horeal = altime[:2]
Horeal = int(Horeal)
Mireal = altime[3:5]
Mireal = int(Mireal)
print(f"Done, alarm is set for {Timing}")
while True:
if Horeal == datetime.datetime.now().hour:
if Mireal == datetime.datetime.now().minute:
print("Alarm is running")
winsound.PlaySound('abc',winsound.SND_LOOP)
elif Mireal<datetime.datetime.now().minute:
break
# Taking user input
print("Set a time to alarm(HH:MM AM/PM)")
Alarm_Time = input("Enter the time of alarm to be set: ")
alarm(Alarm_Time)
Run it
C:\Users\username\Documents\basic-alarm-clock> python basicalarmclock.py
Enter the time of alarm to be set (HH:MM:SS, 24-hour): 14:30:00
Alarm is set for 14:30:00
Waiting for alarm time...
*Alarm sound plays*
Wake Up! Wake Up!C:\Users\username\Documents\basic-alarm-clock> python basicalarmclock.py
Enter the time of alarm to be set (HH:MM:SS, 24-hour): 14:30:00
Alarm is set for 14:30:00
Waiting for alarm time...
*Alarm sound plays*
Wake Up! Wake Up!💡 To test without waiting hours, set the alarm one minute into the future.
Step-by-Step Explanation
1. Import what you need
import datetime
import time
import winsound # Windows only — see "Cross-platform sound" belowimport datetime
import time
import winsound # Windows only — see "Cross-platform sound" belowdatetimedatetimegives youdatetime.datetime.now()datetime.datetime.now(), which returns the current local date and time.timetimegives youtime.sleep(seconds)time.sleep(seconds)for pausing.winsoundwinsoundplays a.wav.wavfile with one call. It only works on Windows. macOS and Linux need a different approach.
2. Ask for the alarm time
alarm_time = input("Enter the time of alarm (HH:MM:SS, 24-hour): ")
print(f"Alarm is set for {alarm_time}")alarm_time = input("Enter the time of alarm (HH:MM:SS, 24-hour): ")
print(f"Alarm is set for {alarm_time}")Using 24-hour format (14:30:0014:30:00 instead of 2:30:00 PM2:30:00 PM) is the simplest choice: it avoids ambiguity, and strftime("%H:%M:%S")strftime("%H:%M:%S") produces the same format directly.
3. Poll the clock
while True:
now = datetime.datetime.now().strftime("%H:%M:%S")
if now == alarm_time:
print("Wake Up! Wake Up!")
winsound.PlaySound("alarm.wav", winsound.SND_ASYNC)
break
time.sleep(1)while True:
now = datetime.datetime.now().strftime("%H:%M:%S")
if now == alarm_time:
print("Wake Up! Wake Up!")
winsound.PlaySound("alarm.wav", winsound.SND_ASYNC)
break
time.sleep(1)datetime.datetime.now()datetime.datetime.now()returns a full date+time object..strftime("%H:%M:%S").strftime("%H:%M:%S")formats it asHH:MM:SSHH:MM:SS(zero-padded).- The comparison is a string equality check — both sides are strings in the same format.
winsound.SND_ASYNCwinsound.SND_ASYNCplays the sound in the background so the nextprintprintis not delayed.time.sleep(1)time.sleep(1)is the key to making this loop efficient. Without it, Python would check the clock millions of times per second and pin a CPU core.
4. Why we sleep for one second
If the alarm is set for 14:30:0014:30:00, we have exactly one second during which the time string equals "14:30:00""14:30:00". Sleeping for more than a second could cause us to skip past it entirely. One second is the right balance.
Cross-Platform Sound
winsoundwinsound only exists on Windows. For a program that works everywhere, use playsoundplaysound (any OS) or pygame.mixerpygame.mixer:
pip install playsound==1.2.2pip install playsound==1.2.2from playsound import playsound
playsound("alarm.wav")from playsound import playsound
playsound("alarm.wav")Or with Pygame:
from pygame import mixer
mixer.init()
mixer.music.load("alarm.wav")
mixer.music.play()from pygame import mixer
mixer.init()
mixer.music.load("alarm.wav")
mixer.music.play()If you have no .wav.wav file handy, beep the terminal:
print("\a") # ASCII bell — terminal may flash or beepprint("\a") # ASCII bell — terminal may flash or beepRobust Input
The basic version accepts any string. A malformed value like "14:30""14:30" (missing seconds) silently fails — the loop just never matches. Validate up front:
from datetime import datetime
while True:
raw = input("Enter alarm time (HH:MM:SS): ")
try:
alarm_dt = datetime.strptime(raw, "%H:%M:%S")
break
except ValueError:
print("Bad format. Use HH:MM:SS, for example 07:30:00.")from datetime import datetime
while True:
raw = input("Enter alarm time (HH:MM:SS): ")
try:
alarm_dt = datetime.strptime(raw, "%H:%M:%S")
break
except ValueError:
print("Bad format. Use HH:MM:SS, for example 07:30:00.")strptimestrptime parses the string into a real datetimedatetime object and raises ValueErrorValueError if the format is wrong. Once you have a datetimedatetime, you can compare numerically instead of by string.
Better: Compute the Delay and Sleep Once
The polling pattern is fine for short waits. For long waits, it is cleaner to compute exactly how many seconds away the alarm is and sleep that long:
from datetime import datetime, timedelta
target = datetime.strptime(input("Time HH:MM:SS: "), "%H:%M:%S").time()
now = datetime.now()
alarm_dt = now.replace(hour=target.hour, minute=target.minute,
second=target.second, microsecond=0)
if alarm_dt <= now:
alarm_dt += timedelta(days=1) # next day if the time already passed
seconds = (alarm_dt - now).total_seconds()
print(f"Sleeping for {seconds:.0f} seconds...")
time.sleep(seconds)
print("Wake Up!")from datetime import datetime, timedelta
target = datetime.strptime(input("Time HH:MM:SS: "), "%H:%M:%S").time()
now = datetime.now()
alarm_dt = now.replace(hour=target.hour, minute=target.minute,
second=target.second, microsecond=0)
if alarm_dt <= now:
alarm_dt += timedelta(days=1) # next day if the time already passed
seconds = (alarm_dt - now).total_seconds()
print(f"Sleeping for {seconds:.0f} seconds...")
time.sleep(seconds)
print("Wake Up!")This:
- Handles next-day alarms automatically (e.g., set 06:00 at midnight).
- Sleeps a single, precise interval instead of waking every second.
- Uses one clear
datetimedatetimeobject rather than two strings to compare.
Common Mistakes
| Problem | Cause | Fix |
|---|---|---|
| Alarm never triggers | Mismatched format ("2:30:00""2:30:00" vs "02:30:00""02:30:00") | Use strptimestrptime validation; require zero-padding |
| 100 % CPU spike | Forgot time.sleep(1)time.sleep(1) | Always sleep inside polling loops |
ModuleNotFoundError: No module named 'winsound'ModuleNotFoundError: No module named 'winsound' on Mac/Linux | winsoundwinsound is Windows-only | Use playsoundplaysound or pygame.mixerpygame.mixer |
| Sound plays once then silence | SND_ASYNCSND_ASYNC returns immediately — script then exits | Loop the sound or time.sleeptime.sleep after triggering |
| Alarm fires twice the same second | Loop did not breakbreak after match | Add breakbreak after firing |
Variations to Try
1. Snooze
After the alarm fires, ask the user:
choice = input("Snooze 5 minutes? (y/n): ")
if choice.lower() == "y":
time.sleep(5 * 60)
fire_alarm()choice = input("Snooze 5 minutes? (y/n): ")
if choice.lower() == "y":
time.sleep(5 * 60)
fire_alarm()2. Multiple alarms
Keep a list of alarm times. Sort, then sleep until the soonest:
alarms = sorted({"06:30:00", "07:00:00", "07:15:00"})
for a in alarms:
wait_until(a)
fire_alarm()
print(f"Alarm at {a} fired.")alarms = sorted({"06:30:00", "07:00:00", "07:15:00"})
for a in alarms:
wait_until(a)
fire_alarm()
print(f"Alarm at {a} fired.")3. Custom alarm message
Let the user attach a label: “Take medicine — 14:30” — show it on trigger.
4. Persistent alarms
Save alarms to a JSON file so they survive restarts.
import json
alarms = json.loads(Path("alarms.json").read_text())import json
alarms = json.loads(Path("alarms.json").read_text())5. GUI version
Build a Tkinter window with time-entry fields, a list of pending alarms, and a snooze button. See Simple Reminder App for a similar pattern.
6. Recurring alarms
Use the scheduleschedule library:
import schedule
schedule.every().day.at("07:00").do(fire_alarm)
while True:
schedule.run_pending()
time.sleep(30)import schedule
schedule.every().day.at("07:00").do(fire_alarm)
while True:
schedule.run_pending()
time.sleep(30)7. System notification
On Windows use plyerplyer; on macOS use osascriptosascript. Combine with sound for a full desktop alert.
Real-World Applications
- Personal wake-up alarm or pomodoro timer.
- Reminder for medication, meetings, calls.
- Trigger automation scripts at a specific time (lights, backups, downloads).
- Build the timing core of a meditation or workout app.
- Server-side cron alternative for tasks that need a specific local time.
Features Implemented
- 24-hour time format input.
- Real-time monitoring with a polling loop.
- Audio alert via
winsoundwinsound. - Automatic termination once the alarm fires.
- Simple, distraction-free CLI.
Educational Value
This project teaches:
- Working with time —
datetimedatetime,strftimestrftime,strptimestrptime,timedeltatimedelta. - Loop control — polling with
time.sleeptime.sleep. - OS-specific code — recognizing when a module only works on one platform.
- Input validation — converting and validating user input early.
- Trade-offs — polling versus single-sleep, simple versus precise.
Next Steps
- Replace
winsoundwinsoundwith a cross-platform library so it runs on Mac/Linux. - Add a Tkinter GUI with multiple slots, on/off switches, and snooze.
- Schedule daily recurring alarms with the
scheduleschedulelibrary. - Combine with a TTS library (
pyttsx3pyttsx3) to have the alarm read out a message. - Persist alarms across restarts in a JSON or SQLite file.
- Wrap as a Windows Service or macOS LaunchAgent so it runs at boot.
Try it here
Here’s a live analog clock — the hands track the current time, the same value your code reads
from datetime.now()datetime.now() each tick:
Conclusion
You built a working alarm clock and saw two ways to wait for a target time — polling and single-sleep — each with its own trade-offs. The same patterns (polling loop, time formatting, input validation) appear in scheduled-task tools, monitoring scripts, and game timers. Source on GitHub. Explore more beginner projects on Python Central Hub.
If this helped you, consider buying me a coffee ☕
Buy me a coffeeWas this page helpful?
Let us know how we did
