Skip to content
← All articles
Schedule social media across timezones and DST

Schedule social media across timezones and DST

To schedule social media across timezones, resolve the requested local date and time in an IANA zone, reject missing or ambiguous daylight saving times, and send the resulting UTC instant to the publishing API. Store the local intention too. UTC alone cannot explain whether a customer meant the same instant worldwide or 9 a.m. in each market.

Developer scheduling / Tested conversion, explicit DST policy

In short

  • Store a wall-clock date and time, an IANA zone, and the resolved UTC instant as separate fields.
  • Reject spring-forward gaps and ask which occurrence to use in fall-back overlaps.
  • Resolve every local recurrence independently; adding a fixed UTC duration is a different promise.

I am PostSider’s founder. This article describes application-side scheduling decisions, not a claim that PostSider implements the recurrence policy below. The API scheduling tutorial covers the request after you have selected an unambiguous instant.

Start by choosing between an instant and a local appointment

A simultaneous launch and a local morning campaign require different schedules. Decide which promise you are making before writing a conversion function.

For an illustrative simultaneous launch, every channel publishes at one UTC instant, even though the calendar displays different local hours. For an illustrative regional campaign, each audience gets a post at 9 a.m. in its own zone. Those are multiple instants, and the spacing can change around seasonal clock changes.

Write the promise in the approval record. “Monday at 9” is incomplete without a date, a zone, and the intended audience. Also distinguish the account’s schedule zone from the browser’s display zone. An operator traveling with a laptop should not accidentally change a client’s appointment.

Keep a review screen that shows both the local appointment and its UTC conversion. For important launches, show the numeric offset too. That makes an accidental choice of a similarly named city easier to catch before the job enters the queue.

An IANA zone contains rules that a fixed offset cannot

Use names such as America/New_York or Europe/Warsaw when the requirement follows local civil time. An offset such as -05:00 identifies one relationship to UTC, not a complete timezone rule set.

Avoid abbreviations such as CST. They can be ambiguous and do not encode a location’s future transitions. The IANA Time Zone Database records changes to local time rules, which can include political changes as well as daylight saving schedules.

A timestamp with an explicit offset is useful once a choice has been made. It cannot tell you whether that offset is correct for the customer’s chosen city on a later date. A database of customer offsets copied during onboarding will eventually produce wrong local appointments somewhere.

For recurring work, retain the zone identifier rather than replacing it with today’s offset. Keep timezone data maintained in the runtime that resolves future jobs. Record the data version when practical so a later rules update can be distinguished from an operator editing the schedule.

Reject gaps and overlaps instead of accepting library defaults

Some local timestamps map to no UTC instant, while others map to two. A safe default is to reject both and ask the operator to make an explicit choice.

During a spring-forward gap, a clock skips a range of local times. During a fall-back overlap, it repeats a range. Attaching a timezone object to a naive timestamp does not, by itself, establish that the local time is valid and unique.

The following Python example uses zoneinfo, available in Python 3.9 and later, plus an installed IANA timezone database. Python’s zoneinfo documentation describes the system database and the tzdata fallback for environments that lack it.

Save this as resolve_schedule.py. It tries both fold values, converts each candidate to UTC, then converts back. Only candidates that reproduce the original local wall time are retained; a set removes the duplicate candidates for normal times.

from datetime import datetime, timezone
from zoneinfo import ZoneInfo


def resolve_local(wall, zone_name):
    local = datetime.fromisoformat(wall)
    if local.tzinfo is not None:
        raise ValueError("Supply a local time without an offset")
    zone = ZoneInfo(zone_name)
    candidates = set()
    for fold in (0, 1):
        aware = local.replace(tzinfo=zone, fold=fold)
        utc = aware.astimezone(timezone.utc)
        back = utc.astimezone(zone).replace(tzinfo=None)
        if back == local:
            candidates.add(utc)
    if not candidates:
        raise ValueError("Nonexistent local time")
    if len(candidates) != 1:
        raise ValueError("Ambiguous local time")
    return candidates.pop().isoformat().replace("+00:00", "Z")


cases = [
    ("normal", "2026-11-16T09:00:00"),
    ("gap", "2026-03-08T02:30:00"),
    ("overlap", "2026-11-01T01:30:00"),
]
for label, wall in cases:
    try:
        result = resolve_local(wall, "America/New_York")
    except ValueError as error:
        result = str(error)
    print(f"{label}: {result}")

assert resolve_local(
    "2026-07-01T09:00:00", "America/New_York"
) == "2026-07-01T13:00:00Z"
assert resolve_local(
    "2026-11-16T09:00:00", "America/New_York"
) == "2026-11-16T14:00:00Z"

Executed locally on October 18, 2026, the script prints:

normal: 2026-11-16T14:00:00Z
gap: Nonexistent local time
overlap: Ambiguous local time

The transition dates are fixtures for New York, not rules to apply globally. The same test suite should include every business-critical customer zone, including a zone without seasonal transitions. Do not assume every clock change is one hour.

Asking which occurrence is a product decision

When a time repeats, show both possible instants and their offsets. Python’s fold attribute can represent the choice, but the user or an agreed policy must make it.

PEP 495 defines the fold mechanism. In a fall-back overlap, fold=0 selects the earlier occurrence and fold=1 the later one. That is an implementation tool, not permission to silently choose the first occurrence for every customer.

For example, New York’s 2026-11-01T01:30:00 corresponds to 05:30:00Z with offset -04:00 or 06:30:00Z with offset -05:00. Present the offset and UTC result together instead of two identical “1:30 a.m.” buttons.

For a gap, ask for a valid replacement time. If a business explicitly wants “move forward to the next valid time,” document that policy and show the changed appointment before approval. The sample intentionally implements neither automatic shifting nor overlap selection.

A recurring local schedule needs fresh resolution

A local recurrence is a calendar rule, not repeated addition of a fixed duration to a UTC timestamp. Resolve each occurrence from its local date, local time, and IANA zone.

For “every weekday at 9 a.m. New York time,” generate the next local calendar date first. Apply the same missing-time and repeated-time policy, then convert to UTC. Adding 24 hours to yesterday’s UTC instant preserves elapsed time but can shift the local hour across a transition.

Keep recurrence configuration separate from the concrete scheduled job. Store the rule, zone, policy, and each materialized occurrence’s UTC instant. If the customer edits a rule, decide explicitly whether already-approved posts should move or only newly generated occurrences should change.

Timezone database updates need the same care. Recalculate future candidates when necessary, compare changes, and require review for material shifts. Do not silently rewrite a persisted payload that an in-flight request may already have accepted.

Send a frozen UTC job, then inspect its readback

Once an appointment is resolved and approved, persist it before creating the API request. Retries must use the same selected instant, not run the timezone resolver again against a moving rule.

For PostSider scheduling, pass the resulting UTC ISO timestamp in date with type: schedule. The official posts reference documents the date and endpoint. Keep the original local fields in your own job record so you can explain the conversion without claiming the API stores your recurrence policy.

After creation, read the returned post ID and compare publishDate with the saved instant. Render it back into the customer’s selected zone for review. A browser displaying a different hour can be a presentation setting, but a stored instant that differs from the saved request needs investigation.

Use the safe retry guide to keep a replay from becoming a different scheduling operation. Use the API error diagnostic when acceptance or readback fails. Scheduling is still not proof of publication; provider processing can take additional time after the appointed instant.

Test the boundaries that customer support will have to explain

A useful timezone test suite checks normal dates, transition boundaries, and the review display. Testing only UTC-to-local formatting misses the harder local-to-UTC decision.

Include a summer and winter appointment in the same zone, rejected gap and overlap inputs, and a successful round trip for a zone without DST. Test an unknown zone and a missing timezone database as explicit failures, rather than falling back to the server’s local timezone.

Also test the workflow around conversion: editing the audience zone, importing an offset-free CSV date, retrying a saved request, and changing a recurrence after approval. An unambiguous timestamp is only useful if the operator can see which audience and calendar date it belongs to.

The publishing API test checklist covers the surrounding request lifecycle. Keep timezone assertions beside those integration checks, and keep a human review path for appointments the resolver cannot choose safely.

Sources and test scope

Sources checked October 18, 2026: IANA Time Zone Database, Python zoneinfo, PEP 495, and PostSider posts. The Python conversion examples were executed locally; no social media publication was required for these tests.

Lukasz Blania is the founder of PostSider, a social media scheduling product with a human dashboard and API access.

Frequently asked questions

Should I store UTC or the customer's local timezone?

Store both the resolved UTC instant and the original local wall time with its IANA zone. UTC identifies the scheduled execution; the local time and zone preserve what the customer intended.

What should happen when a scheduled local time does not exist?

Reject it and ask for a valid replacement, or apply a documented policy approved in advance. Do not silently let a library normalize a spring-forward gap into another time.

How do I handle a local time that occurs twice?

Ask the user to select the occurrence and show its numeric offset. Alternatively use an explicitly agreed earlier-or-later policy. A timezone name alone cannot choose between the two instants.

Can I repeat a local morning post by adding 24 hours in UTC?

Not if the promise is the same local hour across daylight saving changes. Resolve each calendar occurrence in the chosen IANA zone, then apply the gap and overlap policy before converting it to UTC.

Run your social media
on autopilot.

7 days free, no credit card. Publish it yourself, or let your AI agent take the wheel.

30+ networks · MCP, REST and SDK · No credit card