Schedule AWS Lambda based on time zone with Serverless

Tie Liang
3 min readApr 9, 2023

--

Daylight Savings Time

Lambda functions are traditionally scheduled with a cron expression based on UTC time. But this can cause issues if you are based somewhere that has daylight savings time and your time offset changes twice a year. It is now possible to schedule your function based on local time with EventBridge Scheduler by specifying a time zone. It will automatically take daylight savings time changes into account. We will be using Serverless Framework to create the infrastructure.

Serverless Framework does not yet have a built-in way to schedule lambdas using EventBridge Scheduler. Using the current built-in scheduling will create an EventBridge rule, which only supports UTC time. So we will need to provision it using CloudFormation resources.

Here is a serverless.yml file for scheduling a lambda using the built-in scheduling:

service: sample-service

provider:
name: aws
runtime: nodejs18.x

functions:
function1:
handler: index.handler
events:
- eventBridge:
schedule: cron(0 8 * * ? *)

This will create an EventBridge rule to run function1 every day at 8:00 AM UTC.

Using the EventBridge scheduler is a bit more involved. We will need a role that can be assumed by the scheduler and have the rights to invoke the lambda function as well as the schedule itself. These will both be provisioned using CloudFormation resources.

Let’s start with the role:

SchedulerRole:
Type: AWS::IAM::Role
Properties:
RoleName: SchedulerRole
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- scheduler.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: invoke-lambda
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- lambda:InvokeFunction
Resource:
- !GetAtt Function1LambdaFunction.Arn
- !Sub "${Function1LambdaFunction.Arn}:*"

You will notice here that the CloudFormation resource name of the function referenced is Function1LambdaFunction even though the name is function1 under functions. This is because Serverless will normalize the name by capitalizing the first letter and also append LambdaFunction to the end when naming the CloudFormation resource.

And here is the schedule:

function1Schedule:
Type: AWS::Scheduler::Schedule
Properties:
Description: Invokes function1 at 3am ET
FlexibleTimeWindow:
Mode: "OFF"
ScheduleExpression: cron(0 3 * * ? *)
ScheduleExpressionTimezone: America/New_York
Target:
Arn: !GetAtt Function1LambdaFunction.Arn
RoleArn: !GetAtt SchedulerRole.Arn

Here we can specify the time zone we want it to run in. In this example we are using America/New_York for eastern time. The cron expression is also based on the time zone, so this expression means 3:00 AM eastern time every day.

Put them together and this should be how the serverless.yml file looks:

service: sample-service

provider:
name: aws
runtime: nodejs18.x

functions:
function1:
handler: index.handler

resources:
Resources:
SchedulerRole:
Type: AWS::IAM::Role
Properties:
RoleName: SchedulerRole
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- scheduler.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: invoke-lambda
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- lambda:InvokeFunction
Resource:
- !GetAtt Function1LambdaFunction.Arn
- !Sub "${Function1LambdaFunction.Arn}:*"
function1Schedule:
Type: AWS::Scheduler::Schedule
Properties:
Description: Invokes function 1 at 3am ET
FlexibleTimeWindow:
Mode: "OFF"
ScheduleExpression: cron(0 3 * * ? *)
ScheduleExpressionTimezone: America/New_York
Target:
Arn: !GetAtt Function1LambdaFunction.Arn
RoleArn: !GetAtt SchedulerRole.Arn

And there you have it! Hopefully the next version of Serverless Framework will give us an easier way to schedule a lambda in local time. As of this writing, the latest version is 3.30.1.

--

--