At a high level, this is a fairly simple architecture.
You have something (we'll call it the Writer) which sends a message to something else (which we'll call the Scheduler). With the message is a time that the Writer wants the Scheduler to forward that message on to an action taker to do something with.
You haven't mentioned the Writer, so I assume you already have an idea of what this is, how it works and where it's hosted. You already know that the Action Taker component will be an AWS Lambda (presumably in conjunction with SES or some other SaaS). So it is only the Scheduler component you care about.
You only really have three ways about attacking this, and you have already considered two of them.
Build it yourself
Use a SaaS service
String some existing commodity services together
Let's look at some of the advantages/disadvantages of each.
Build It Yourself
Assuming you are going to do this within AWS, that means building something in EC2, ECS, Fargate, or some other long running environment. It will also need some form of separate data storage, probably an actual database to make sure data doesn't get lost in the event of your container/VM falling over.
On the plus side, you have full control over message structure and you could probably eliminate that Lambda in the Action Taker component and call the email service direct.
But, depending on how you host this new thing you're going to build, you are going to be responsible for writing and maintaining the code, patching OS/platform/etc, making it highly available (i.e. deploying in multiple availability zones) and securing it.
SaaS
In some ways, this would be the perfect solution if you could find a vendor you were happy with. You just send it messages and at the specified time it sends an event. No such standalone service exists in AWS however, so you would need to make that Lambda invokable, probably using AWS API Gateway.
String Some Existing Commodity Services Together
Staying the AWS context, this could be DynamoDB storing messages with a TTL, with a DynamoDB Stream listening for deletions which triggers Action Taker Lambda.
On the plus side, these are all AWS commodity services, so are by default running over multiple Availability Zones, and you are only responsible for managing the configuration, which you shouldn't need to change unless you change the Action Taker component.
On the negative side, this is not a use-case that TTL and DynamoDB streams was built for. Setting a TTL doesn't give to the second accuracy, and could by all account take 48 hours to actually delete something (although it could just take 10 minutes). Then if you have multiple deletes at the same time, the DynamoDB Stream handles those events sequentially.
Here is a link to a blog about where someone built something similar and how it performed.
https://theburningmonk.com/2019/03/dynamodb-ttl-as-an-ad-hoc-scheduling-mechanism/
My advice would be to prefer SaaS over everything else, but if you can't find a vendor you are happy with, then take the time to build something properly yourself.