Don't Let Server Time Decide Your Business Logic
Date.now() and getHours() are fine for the current moment — until machine local time quietly becomes business logic on a server, worker, or browser in another timezone.
It is easy to write code like this:
const now = Date.now();or:
const date = new Date();
date.getHours();
date.getDate();
date.getMonth();Most of the time, there is nothing wrong with using Date like this.
The problem starts when the result is used for business logic without considering the timezone of the machine running the code.
Date.now() Is Not Local Server Time
Date.now() returns the current moment as a number of milliseconds since the Unix epoch.
It does not return:
10:30 AM Indiaor:
10:30 AM New YorkIt represents the same instant regardless of where the server is running.
For example:
Date.now();is safe when the requirement is simply:
"What is the current moment?"
The problem appears when that moment is converted into a local date or time using the machine's timezone.
const date = new Date();
date.getHours();
date.getDate();
date.getMonth();These methods use the local timezone of the environment running the code.
So the same instant can produce different local values on different machines.
Server A → Asia/Kolkata
Server B → America/New_YorkThe instant is the same, but:
date.getHours();can return different values.
When Machine Time Becomes a Problem
This becomes important when local time is used to make a business decision.
For example:
if (new Date().getHours() === 10) {
// run something
}This code doesn't really mean:
"Run this at 10 AM for the business."
It means:
"Run this when the machine's local clock says 10 AM."
If the same application moves to another region, the behavior can change without any business configuration changing.
The same problem can appear with:
new Date().getDate();
new Date().getMonth();
new Date().getDay();If the code is deciding things like:
- which business day it is
- whether something is due today
- whether a schedule should run
- when a daily process should start
- which date should be shown
then relying on the machine's timezone can create unexpected behavior.
Servers and Workers Can Have Different Environments
This becomes easier to miss when the application has multiple processes.
For example:
API Server
↓
Queue
↓
WorkerThe API and worker may run on different machines, containers, or regions.
If business logic depends on:
new Date().getHours();the result can depend on where that particular process is running.
The problem isn't that the server has a timezone.
The problem is that the application silently uses that timezone as part of its business logic.
Use an Explicit Timezone When Local Time Matters
If the requirement is based on a particular timezone, make that timezone explicit.
For example:
const timeZone = "America/New_York";
const localTime = new Intl.DateTimeFormat("en-US", {
timeZone,
dateStyle: "medium",
timeStyle: "short",
}).format(new Date());Now the result doesn't depend on the timezone configured on the server.
The same idea applies on the frontend.
If the requirement is to show the user's local time, the browser's timezone can be used.
If the requirement is to show a business's local time, use the timezone configured for that business.
The important part is knowing which clock the operation belongs to.
Scheduling
Scheduling is where this distinction becomes more important.
Suppose a user selects:
Date: 2026-03-10
Time: 10:00 AM
Timezone: America/New_YorkSend those values to the backend:
createSchedule({
date: "2026-03-10",
time: "10:00",
timeZone: "America/New_York",
});The backend can interpret that local date and time using the supplied timezone and convert it into an exact moment.
Date + Time + Timezone
↓
Backend
↓
UTC timestamp
↓
Queue
↓
WorkerThe worker does not need to recalculate New York time.
It only needs to execute the job at the scheduled moment.
Delayed Jobs
For a delayed job, the delay should be based on the target moment.
Conceptually:
Target moment - Current moment
↓
Delay
↓
Queue
↓
WorkerThe worker's local timezone should not determine whether the job is due.
This is why code that derives business decisions from:
new Date().getHours();inside a worker can be risky.
Recurring Schedules Are Different
There is an important difference between:
Every day at 10:00 AMand:
Every 24 hoursThe first is based on a local clock.
The second is a duration.
For a recurring schedule such as:
Every day at 10:00 AM
America/New_Yorkthe next occurrence needs to be calculated using that timezone.
After calculating the next occurrence, the system can turn it into an exact moment and pass that to the queue.
Recurring rule
↓
Calculate next occurrence
↓
Exact moment
↓
Queue
↓
WorkerThe timezone information also allows the backend to apply rules such as daylight saving time when calculating the time.
A Simple Rule
A useful way to think about it is:
Date.now()
→ current moment
UTC timestamp
→ exact moment shared between systems
Timezone
→ converts an exact moment to local date/time
or gives meaning to a local date/time
Machine timezone
→ should not silently define business behaviorThe goal isn't to avoid Date.now() or new Date().
They are normal and useful APIs.
The goal is to avoid letting the timezone of a browser, server, container, or worker accidentally become part of the business logic.
When local time matters, make the timezone explicit.
When only an exact moment matters, work with the timestamp.
That small distinction prevents a lot of environment-dependent behavior.