Skip to content

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 datetimedatetime and timetime modules.
  • 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

ConceptPurpose
datetimedatetimeGet 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.mixerMake 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 loopA while Truewhile True loop that wakes periodically and checks whether the alarm time has arrived.

Getting Started

Create the project

  1. Create a folder named basic-alarm-clockbasic-alarm-clock.
  2. Inside it, create basicalarmclock.pybasicalarmclock.py.
  3. (Optional) drop a custom alarm.wavalarm.wav file in the folder to use as the alert sound.

Write the code

Basic Alarm ClockSource
Basic Alarm Clock
# 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
# 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

command
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!
command
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

basicalarmclock.py
import datetime
import time
import winsound        # Windows only — see "Cross-platform sound" below
basicalarmclock.py
import datetime
import time
import winsound        # Windows only — see "Cross-platform sound" below
  • datetimedatetime gives you datetime.datetime.now()datetime.datetime.now(), which returns the current local date and time.
  • timetime gives you time.sleep(seconds)time.sleep(seconds) for pausing.
  • winsoundwinsound plays a .wav.wav file with one call. It only works on Windows. macOS and Linux need a different approach.

2. Ask for the alarm time

basicalarmclock.py
alarm_time = input("Enter the time of alarm (HH:MM:SS, 24-hour): ")
print(f"Alarm is set for {alarm_time}")
basicalarmclock.py
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

basicalarmclock.py
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)
basicalarmclock.py
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 as HH: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_ASYNC plays the sound in the background so the next printprint is 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:

install
pip install playsound==1.2.2
install
pip install playsound==1.2.2
cross_platform.py
from playsound import playsound
playsound("alarm.wav")
cross_platform.py
from playsound import playsound
playsound("alarm.wav")

Or with Pygame:

pygame_sound.py
from pygame import mixer
mixer.init()
mixer.music.load("alarm.wav")
mixer.music.play()
pygame_sound.py
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:

beep.py
print("\a")          # ASCII bell — terminal may flash or beep
beep.py
print("\a")          # ASCII bell — terminal may flash or beep

Robust 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:

validate.py
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.")
validate.py
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:

single_sleep.py
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!")
single_sleep.py
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 datetimedatetime object rather than two strings to compare.

Common Mistakes

ProblemCauseFix
Alarm never triggersMismatched format ("2:30:00""2:30:00" vs "02:30:00""02:30:00")Use strptimestrptime validation; require zero-padding
100 % CPU spikeForgot time.sleep(1)time.sleep(1)Always sleep inside polling loops
ModuleNotFoundError: No module named 'winsound'ModuleNotFoundError: No module named 'winsound' on Mac/Linuxwinsoundwinsound is Windows-onlyUse playsoundplaysound or pygame.mixerpygame.mixer
Sound plays once then silenceSND_ASYNCSND_ASYNC returns immediately — script then exitsLoop the sound or time.sleeptime.sleep after triggering
Alarm fires twice the same secondLoop did not breakbreak after matchAdd breakbreak after firing

Variations to Try

1. Snooze

After the alarm fires, ask the user:

snooze.py
choice = input("Snooze 5 minutes? (y/n): ")
if choice.lower() == "y":
    time.sleep(5 * 60)
    fire_alarm()
snooze.py
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:

multi.py
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.")
multi.py
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.

persist.py
import json
alarms = json.loads(Path("alarms.json").read_text())
persist.py
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:

recur.py
import schedule
schedule.every().day.at("07:00").do(fire_alarm)
while True:
    schedule.run_pending()
    time.sleep(30)
recur.py
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 timedatetimedatetime, 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 winsoundwinsound with 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 scheduleschedule library.
  • 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:

sketch A live analog clock p5.js
The hour, minute, and second hands track the current time, updating every second.

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 coffee

Was this page helpful?

Let us know how we did