A PowerShell string cast can shift your UTC report window
AI-assisted technical note from the SeedanceCheap team, with Codex.
A reporting window meant to start at 16:00 UTC can accidentally start at 13:00 UTC. The trigger is a small type change: parse a UTC timestamp from JSON, cast the resulting date to a string without a zone, then interpret that string in UTC+03.
We encountered this class of bug in a reporting script. The example below uses invented input and an in-memory time zone. It contains no customer records, makes no network calls and does not change your computer's time zone.
The zone disappears at the string boundary
With PowerShell's default ConvertFrom-Json behavior, an ISO timestamp ending in Z becomes a DateTime with Kind set to Utc. That typed value still identifies the intended instant. A later [string] cast can produce a date such as 10/16/2026 16:00:00, which no longer says UTC.
A consumer must now guess the missing zone. If it treats 16:00 as local UTC+03, the corresponding UTC instant is 13:00. A report can include three extra hours while its heading still says it starts at 16:00 UTC. This is a boundary error, not a reason to subtract three hours from every timestamp.
Run the synthetic reproduction
Save this as utc_boundary.ps1 and run pwsh -NoProfile -File ./utc_boundary.ps1. It requires PowerShell 7.5 or later because the second fix uses -DateKind String. We ran the complete example on PowerShell 7.6.5 with an en-US culture. The custom UTC+03 zone makes the reinterpretation explicit rather than relying on the host's configured zone.
# Synthetic example: PowerShell 7.5 or later. No files or services are changed.
# Run with: pwsh -NoProfile -File ./utc_boundary.ps1
if ($PSVersionTable.PSVersion -lt [version]'7.5') {
throw 'This example uses -DateKind String, introduced in PowerShell 7.5.'
}
$invariant = [Globalization.CultureInfo]::InvariantCulture
$raw = '{"start_utc":"2026-10-16T16:00:00Z"}'
$parsed = $raw | ConvertFrom-Json
if ($parsed.start_utc.Kind -ne [DateTimeKind]::Utc) {
throw 'Expected the default JSON conversion to retain a UTC DateTime.'
}
# The cast drops the zone. Simulate a UTC+03 consumer without changing the OS.
$lost = [string]$parsed.start_utc
$unspecified = [DateTime]::SpecifyKind(
[DateTime]::Parse($lost, $invariant), [DateTimeKind]::Unspecified)
$demoZone = [TimeZoneInfo]::CreateCustomTimeZone(
'DemoUTCplus03', [TimeSpan]::FromHours(3), 'Demo UTC+03', 'Demo UTC+03')
$wrong = [TimeZoneInfo]::ConvertTimeToUtc($unspecified, $demoZone)
# Fix 1: retain the typed value before serializing it with an explicit zone.
$typed = ([DateTimeOffset]$parsed.start_utc).ToUniversalTime()
# Fix 2: keep the original explicit-zone string at the JSON boundary.
$strings = $raw | ConvertFrom-Json -DateKind String
$preserved = [DateTimeOffset]::Parse($strings.start_utc, $invariant).ToUniversalTime()
$format = "yyyy-MM-ddTHH:mm:ss'Z'"
$wrongText = $wrong.ToString($format, $invariant)
$typedText = $typed.ToString($format, $invariant)
$preservedText = $preserved.ToString($format, $invariant)
if ($wrongText -ne '2026-10-16T13:00:00Z' -or
$typedText -ne '2026-10-16T16:00:00Z' -or
$preservedText -ne '2026-10-16T16:00:00Z') {
throw 'Unexpected timestamp; inspect the runtime before using this example.'
}
"Parsed kind: $($parsed.start_utc.Kind)"
"Zone-free string: $lost"
"UTC+03 reinterpretation: $wrongText"
"Typed fix: $typedText"
"Preserved-string fix: $preservedText"
Our exact run printed:
Parsed kind: Utc
Zone-free string: 10/16/2026 16:00:00
UTC+03 reinterpretation: 2026-10-16T13:00:00Z
Typed fix: 2026-10-16T16:00:00Z
Preserved-string fix: 2026-10-16T16:00:00Z
Keep the instant, then format it
The first fix retains the UTC-typed value and converts it to DateTimeOffset before formatting. The second preserves the original JSON string, including its explicit Z, then parses it as DateTimeOffset. Both preserve the intended 16:00 UTC boundary in this example. The assertion checks the wrong and corrected results before printing them.
ToUniversalTime() cannot recover a zone that has already been discarded. Once an ambiguous string has crossed the boundary, obtain its intended zone from the original source. Do not blindly append Z: that would incorrectly relabel a timestamp that actually represented local time.
For external inputs, require an explicit Z or numeric offset, or a typed date whose zone is known. Reject timezone-free strings and DateTimeKind.Unspecified values instead of silently treating them as local. The small script above demonstrates a known UTC input; it is not a general timestamp validator.
Apply it to video-job and usage reports
Keep report-window parameters typed until their final serialization. Preserve explicit offsets across JSON, CSV and command-line boundaries. Record the normalized boundary alongside the report so that a job-polling or usage window can be checked against what the query actually received. Display formats belong at the presentation layer.
Microsoft documents the default date handling and the PowerShell 7.5 introduction of -DateKind in the ConvertFrom-Json reference. The DateTimeOffset.Parse reference explains why input without an offset needs special care.
For a separate delivery check, see our attached-cover and video-dimension example.
Plan a scene with our free prompt planner. Planning is free; video generation is separate. Check current Studio availability, model and format options, trial eligibility, wait estimates and funding terms before submitting or buying. This guide makes no generation-quality or business-results claim.
Comments
Post a Comment