Every language
13 langages, copy-ready. One at a time with syntax highlighting, or all inline.
SQLSQLrunnable
-- Naive UTC timestamp → Paris wall clock (the conversion idiom):
SELECT TIMESTAMP '2026-07-21 12:00:00'
AT TIME ZONE 'UTC' -- label the instant: this is 12:00Z
AT TIME ZONE 'Europe/Paris' -- render its Paris wall clock
AS paris_wall; -- 2026-07-21 14:00:00
-- now() is already timestamptz — only the trailing cast belongs:
SELECT now() AT TIME ZONE 'Europe/Paris';The two casts are not symmetric: on a naive timestamp AT TIME ZONE labels the instant; on a timestamptz it renders the wall clock — same operator, opposite direction. now() AT TIME ZONE 'UTC' AT TIME ZONE 'Europe/Paris' is therefore the classic bug (it re-labels now's UTC digits as Paris); run both forms in the playground and compare.
JSJavaScript
// No first-class API yet — the stdlib trick is Intl with the zone built in:
function inZone(instant, timeZone) {
const parts = new Intl.DateTimeFormat('en-CA', {
timeZone,
year: 'numeric', month: '2-digit', day: '2-digit',
hour: '2-digit', minute: '2-digit', second: '2-digit',
hourCycle: 'h23',
}).formatToParts(instant);
const get = (type) => parts.find((p) => p.type === type).value;
return `${get('year')}-${get('month')}-${get('day')} ` +
`${get('hour')}:${get('minute')}:${get('second')}`;
}
inZone(new Date('2026-07-21T12:00:00Z'), 'Europe/Paris');
// '2026-07-21 14:00:00' — the offset in effect AT that instant (+02:00, DST)No first-class timezone conversion in the language yet — formatToParts with a timeZone is the acknowledged hack, and Temporal (Stage 3) will replace it. The formatter looks up the offset for that exact instant, DST transitions included; hourCycle: 'h23' because hour12: false can emit '24' at midnight.
TSTypeScript
export function inZone(instant: Date, timeZone: string): string {
const parts = new Intl.DateTimeFormat('en-CA', {
timeZone,
year: 'numeric', month: '2-digit', day: '2-digit',
hour: '2-digit', minute: '2-digit', second: '2-digit',
hourCycle: 'h23',
}).formatToParts(instant);
const get = (type: string) =>
parts.find((p) => p.type === type)!.value;
return `${get('year')}-${get('month')}-${get('day')} ` +
`${get('hour')}:${get('minute')}:${get('second')}`;
}The same stdlib trick, typed and wrapped — the non-null assertion is safe because formatToParts always emits every component you requested. Temporal will replace this with a first-class TimeZone type; until it ships, this is the honest signature: instant in, wall-clock string out.
GoGo
import "time"
func inZone(t time.Time, zone string) (time.Time, error) {
loc, err := time.LoadLocation(zone) // IANA name, e.g. "Europe/Paris"
if err != nil {
return time.Time{}, err
}
return t.In(loc), nil // same instant, that zone's wall clock
}
utc, _ := time.Parse(time.RFC3339, "2026-07-21T12:00:00Z")
paris, _ := inZone(utc, "Europe/Paris")
paris.Format("2006-01-02 15:04:05") // 2026-07-21 14:00:00t.In(loc) re-labels the instant; t.Add(2 * time.Hour) is the wrong tool — it moves the instant itself and is off by the offset twice a year. LoadLocation reads the system tz database: in a bare container, import _ "time/tzdata" to embed one.
RsRust
use chrono::{TimeZone, Utc};
use chrono_tz::{Europe::Paris, Tz};
let utc = Utc.with_ymd_and_hms(2026, 7, 21, 12, 0, 0).unwrap();
utc.with_timezone(&Paris); // 2026-07-21 14:00:00 CEST
// zone from a string (e.g. a user preference):
let tz: Tz = "Europe/Paris".parse().unwrap();
utc.with_timezone(&tz); // same instant, DST-correctchrono alone has offsets but NOT the IANA database — chrono-tz embeds it at compile time, so no system tz files are read at runtime. with_timezone keeps the instant and recomputes the wall clock; the result's offset reflects the rules in effect at that instant.
PHPPHP
$utc = new DateTime('2026-07-21T12:00:00', new DateTimeZone('UTC'));
$paris = (clone $utc)->setTimezone(new DateTimeZone('Europe/Paris'));
$paris->format('Y-m-d H:i:s T'); // '2026-07-21 14:00:00 CEST'setTimezone converts the instant; DateTime::modify('+2 hours') is NOT a conversion — it shifts the wall clock onto a different instant. DateTime is mutable, hence the clone; and construct with the source zone, or the server's date-default-timezone is silently assumed.
PyPython
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
utc = datetime(2026, 7, 21, 12, 0, tzinfo=timezone.utc)
paris = utc.astimezone(ZoneInfo('Europe/Paris'))
paris.isoformat() # '2026-07-21T14:00:00+02:00'zoneinfo is stdlib since Python 3.9 — no pytz needed (its localize() is a known footgun). astimezone() on a NAIVE datetime doesn't raise; it assumes your local time and quietly converts the wrong instant — attach tzinfo first.
C++C++
#include <chrono>
#include <format>
std::chrono::zoned_time paris{"Europe/Paris", std::chrono::system_clock::now()};
std::format("{:%Y-%m-%d %H:%M:%S %Z}", paris); // 2026-07-21 14:00:00 CESTzoned_time applies the offset in effect AT that instant, DST transitions included — never add hours by hand. It reads the OS tz database (IANA): locate_zone throws std::runtime_error for an unknown zone name, so guard user-supplied input. C++20 time zones need GCC 13+ or MSVC 19.29+.
C#C#
using System;
using System.Globalization;
DateTimeOffset utc = DateTimeOffset.Parse(
"2026-07-21T12:00:00Z", CultureInfo.InvariantCulture);
TimeZoneInfo zone = TimeZoneInfo.FindSystemTimeZoneById("Europe/Paris");
DateTimeOffset paris = TimeZoneInfo.ConvertTime(utc, zone);
paris.ToString("yyyy-MM-dd HH:mm:ss zzz"); // 2026-07-21 14:00:00 +02:00On .NET 8+ / Linux FindSystemTimeZoneById accepts IANA names — Windows IDs are converted internally via ICU, so either spelling works cross-platform. ConvertTime applies the offset in effect at that instant; AddHours(2) does not.
JvJava
import java.time.ZoneId;
import java.time.ZonedDateTime;
ZonedDateTime utc = ZonedDateTime.parse("2026-07-21T12:00:00Z");
ZonedDateTime paris = utc.withZoneSameInstant(ZoneId.of("Europe/Paris"));
paris.toString(); // 2026-07-21T14:00+02:00[Europe/Paris]withZoneSameInstant converts; withZoneSameLocal is the trap with the friendly name — it keeps the wall clock and silently changes the instant. ZoneId.of takes IANA names, and toString carries offset AND zone, which is the shape to log.
SwSwift
import Foundation
let utc = ISO8601DateFormatter().date(from: "2026-07-21T12:00:00Z")!
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
formatter.timeZone = TimeZone(identifier: "Europe/Paris") // the converter
formatter.string(from: utc) // "2026-07-21 14:00:00"Foundation has no convert-by-name one-liner: Date is instants (UTC) and the FORMATTER does the zone math — setting timeZone is where the conversion happens. DateFormatter is expensive to create; hoist one per zone in hot paths.
KtKotlin
import java.time.ZoneId
import java.time.ZonedDateTime
val utc = ZonedDateTime.parse("2026-07-21T12:00:00Z")
val paris = utc.withZoneSameInstant(ZoneId.of("Europe/Paris"))
println(paris) // 2026-07-21T14:00+02:00[Europe/Paris]Same JVM java.time APIs in a cleaner chain. ZoneId is the modern type — the legacy TimeZone's 3-letter IDs ('CET', 'EST') are ambiguous and resolved differently per platform; always use the IANA name.
RbRuby
require 'time'
utc = Time.utc(2026, 7, 21, 12, 0, 0)
paris = utc.getlocal('Europe/Paris')
paris.strftime('%Y-%m-%d %H:%M:%S %z') # '2026-07-21 14:00:00 +0200'require 'time' is what lets getlocal take an IANA zone name — without it only a numeric offset works. Rails adds utc.in_time_zone('Europe/Paris'); zone-less getlocal/getutc fall back to ENV['TZ'], so pass the zone explicitly.