πŸ•’ Timestamp & Cron Converter

Last updated: September 29, 2025

Timestamp & Cron Converter

Convert Unix timestamps across timezones Β· Preview next cron job fires

Unix Timestamp Converter
Quick:
Timestamp Details
Date → Unix Timestamp
Result
Cron Schedule Checker
Examples:
Next Scheduled Fires

The Developer's Checklist for Working with Timestamps and Cron Schedules Without Losing Your Mind

Timestamps look deceptively simple. A number like 1700000000 sits in a database column or an API response and seems perfectly unambiguous β€” until you realise your monitoring dashboard is showing a job that "fired at 3am" when the server logs say 8am, and your on-call rotation was set for the wrong continent's timezone. Cron expressions have the same energy. 0 9 * * 1 looks clean, but did you mean 9am UTC, 9am EST, or 9am on the server that just moved to a new datacenter in a different region?

This checklist walks through exactly when and how to verify each layer of that problem, so you stop getting surprised by scheduled jobs firing at the wrong time.

Step 1: Always Verify What "Now" Means on Your Server

Before you debug any timestamp mismatch, run date and timedatectl (or date -u on macOS) on the server in question. A surprising number of production incidents trace back to a VM whose system clock drifted, an NTP daemon that was never configured, or a container image built with a baked-in timezone that doesn't match the host.

  • Check timedatectl status β€” confirm NTP is active and "System clock synchronized: yes"
  • Compare date against date -u to confirm the UTC offset matches your expectation
  • On containers, confirm TZ environment variable is explicitly set β€” don't rely on the host's timezone leaking in

Step 2: Treat All Stored Timestamps as UTC β€” No Exceptions

If you're storing timestamps in a database or passing them between services, they must be UTC. This sounds obvious, but it breaks in subtle ways: MySQL's DATETIME type stores no timezone information, so if a server inserts a row in America/Chicago local time and a different server reads it assuming UTC, every timestamp will be off by five or six hours. PostgreSQL's TIMESTAMPTZ type stores UTC and converts on read β€” use it.

  • In application code: store Date.now() (milliseconds) or time.time() (seconds) β€” both are UTC epoch
  • In APIs: always emit ISO 8601 with explicit UTC offset (2024-01-15T09:50:00Z), never a "local" datetime string
  • In logs: configure your logging library to emit UTC timestamps β€” mixing local-time log lines from multiple servers is a debugging nightmare

Step 3: Understand the Two Classes of Unix Timestamp

Unix timestamps come in seconds or milliseconds, and the two can look similar enough to cause silent bugs. A seconds-epoch value of 1700000000 corresponds to November 2023. The same digits interpreted as milliseconds gives you a date in the year 1970 β€” only 19 days past the epoch. JavaScript's Date.now() returns milliseconds; Python's time.time() returns seconds. When an API mixes the two, you get timestamps that are 1000x wrong.

  • Rule of thumb: a 10-digit number is seconds, a 13-digit number is milliseconds
  • In JavaScript, always divide by 1000 before constructing a Date from a seconds-epoch value: new Date(ts * 1000)
  • In Python, use datetime.fromtimestamp(ts, tz=timezone.utc) for seconds; add /1000 for milliseconds
  • When in doubt, sanity-check the year: if the result is 1970, you almost certainly passed milliseconds to a seconds function

Step 4: Know the 5-Field Cron Format Cold

The standard vixie-cron format used by Linux crontab, GitHub Actions schedules, and most job schedulers has exactly 5 fields in this order: minute, hour, day-of-month, month, day-of-week. Some platforms add a 6th field for seconds (Quartz scheduler, Spring's @Scheduled annotation) or even a 7th for year. Pasting a 6-field expression into a 5-field scheduler produces unexpected behaviour β€” it won't error, it will just silently misparse.

  • Confirm which format your platform expects before writing the expression
  • GitHub Actions cron uses UTC and the standard 5-field format
  • Quartz (Java) uses a 6-field format with seconds in position 1 β€” 0 30 9 * * MON-FRI is not the same as 30 9 * * 1-5
  • AWS EventBridge uses a 6-field format where the last field is year, not day-of-week β€” it has its own separate day-of-week field

Step 5: Test the Day-of-Week vs Day-of-Month Interaction

This is the most common source of wrong-fire cron bugs. In standard vixie-cron, if both day-of-month and day-of-week are restricted (neither is *), the job fires when either condition is true β€” OR logic, not AND. So 0 9 15 * 1 fires on the 15th of every month and every Monday at 9am, not only on Mondays that fall on the 15th. If you want Mondays that are also the 15th, you need to compute those dates externally and set up individual scheduled entries, or use a platform that supports AND semantics.

  • When day-of-month is *, only day-of-week matters β€” and vice versa
  • When both are specified, assume OR unless your platform documentation explicitly says otherwise
  • Always preview the next several fire dates to confirm the schedule matches your intention

Step 6: Confirm Cron Timezone β€” Three Times

Cron has no timezone field. The timezone is determined by the environment the cron daemon runs in. This means three different places can each specify a different timezone: the system's /etc/timezone, the user's shell profile, and some cron implementations allow a per-crontab CRON_TZ or TZ variable at the top of the crontab file. On systemd-based systems, a service's OnCalendar timer uses the system timezone by default but can be overridden with LocalTimeType.

  • In a crontab, add TZ=UTC at the top to make the timezone explicit and portable
  • For GitHub Actions, all cron triggers fire in UTC β€” document this in your workflow file with a comment
  • For cloud schedulers (AWS, GCP, Azure), check the console β€” many default to UTC but some regions default to local
  • After deploying a new cron job, convert its next expected fire time to UTC and verify it in the server logs

Step 7: Validate Step Expressions Carefully

The */N step syntax is convenient but has a non-obvious property: it always starts from the minimum value of the field. */15 in the minute field means minutes 0, 15, 30, 45 β€” not "every 15 minutes from when the job was added." If you start a service at 09:07 and want it to run every 15 minutes, */15 will fire at 09:15, 09:30 etc., not at 09:22, 09:37. For offset steps, use a specific list: 7,22,37,52.

  • Test all step expressions by computing the first 5 fire times, not just the first one
  • */6 in the hours field fires at 0, 6, 12, 18 β€” not at whatever hour you created the job
  • Range steps like 10-50/10 in minutes give you 10, 20, 30, 40, 50 β€” useful for avoiding top-of-hour pile-ons

Step 8: Cross-Check Scheduled Jobs Against Timestamp Evidence

When a bug report says "the job didn't run" or "it ran twice," the fastest path to truth is: get the Unix timestamp from the log line, convert it to the timezone the cron is configured in, and check whether that time matches any predicted fire time from the expression. If the log shows fires at 09:00:01, 09:00:03 and then nothing for two hours, you likely have two cron instances running (two servers, or crontab duplicated). If the fire is 30 seconds early, you have clock drift. If it's exactly one hour off, you have a DST boundary issue.

  • Log the Unix timestamp alongside the human-readable time in every scheduled job
  • On DST transitions, a 0 2 * * * job either runs twice (clocks fall back) or not at all (clocks spring forward) β€” move critical daily jobs to 3am or later to avoid this window
  • Use a tool that shows you the next N fire times so you can compare against actual execution logs directly

The underlying discipline is always the same: make implicit assumptions explicit. Cron and timestamps both quietly carry timezone and format assumptions that only surface as bugs at 2am on a Sunday when the quarterly report job fires in the wrong country's business hours. A 30-second check with a converter before you deploy saves a much longer incident review later.

FAQ

What is a Unix timestamp and why is it in seconds?
A Unix timestamp counts the number of seconds elapsed since January 1, 1970 at 00:00:00 UTC, known as the Unix epoch. It is in seconds because that precision was sufficient for the systems that originated it, and whole-second granularity matched the hardware timers of the era. JavaScript's Date.now() breaks from this convention and returns milliseconds, which is why you will often see 13-digit values in browser environments versus 10-digit values from server-side languages like Python or Go.
Why does my cron job run at the wrong time after a server migration?
The most common cause is a timezone mismatch between the old and new server. Cron reads the system timezone from the environment, and a new server in a different region or configured with a different /etc/timezone will shift all your job times accordingly. The fix is to always set TZ=UTC explicitly at the top of your crontab file, so the schedule is timezone-independent and portable across any server.
What does */15 in a cron minute field actually mean?
The expression */15 means 'every value that is divisible by 15 within the valid range of 0 to 59', which resolves to minutes 0, 15, 30, and 45. It does not mean 'every 15 minutes starting from when the job was created.' The job will always fire on those four specific minute marks within each hour, regardless of when it was added to the crontab.
When both day-of-month and day-of-week are specified in a cron expression, which takes priority?
Neither takes strict priority β€” standard vixie-cron uses OR logic when both fields are restricted. For example, the expression 0 9 15 * 1 fires at 9am on every 15th of the month AND at 9am on every Monday. If you intended the job to fire only on Mondays that fall on the 15th, cron cannot express that directly; you would need to add an internal date check inside the script itself.
How do I handle cron jobs that should fire daily but need to survive DST transitions?
Daylight saving time transitions create a window around 2am where clocks either skip an hour (spring forward) or repeat an hour (fall back). A daily cron job set to 0 2 * * * will either be skipped entirely or run twice during those transitions, depending on direction. The safest approach is to schedule daily jobs that must run exactly once at 3am or later, which is safely past the transition window in most timezones, or to configure your cron daemon to operate in UTC and accept that the human-readable fire time will shift by one hour seasonally.
What is the difference between 5-field and 6-field cron formats?
Standard Unix cron uses 5 fields: minute, hour, day-of-month, month, and day-of-week. The Quartz scheduler used in Java environments adds a seconds field as the first field, making it 6 fields total. AWS EventBridge uses a different 6-field format where year is added as the last field, and day-of-week uses named abbreviations. Always confirm which format your specific platform expects, because a 6-field expression pasted into a 5-field parser will silently misinterpret the schedule without throwing an error.