Azure Cloud Shell is a handy tool, but some of the non-configurable/non-persistent defaults are annoying. en-US locale is one such setting - those date formats are truly awful.
Here's my approach to fix this for Powershell sessions by modifying the session profile. The prompt is modified to ensure the locale is set for each interactive prompt, as setting the CurrentCulture by itself doesn't persist for the session - just for the current pipeline :-(
mkdir -p ~/.config/PowerShell
@'
# ==============================================================================
# USER CONFIGURATION
# ==============================================================================
$Global:SessionCulture = "en-AU"
$Global:DesiredTimeZone = "Sydney" # Can be "Sydney", "AUS Eastern Standard Time", "Australia/Sydney", etc.
# ==============================================================================
# Force localized culture for the entire session
$newc = [System.Globalization.CultureInfo]::new($Global:SessionCulture)
[System.Threading.Thread]::CurrentThread.CurrentCulture = $newc
[System.Threading.Thread]::CurrentThread.CurrentUICulture = $newc
[System.Globalization.CultureInfo]::CurrentCulture = $newc
# Override the prompt to re-apply culture before every command
function prompt {
$newc = [System.Globalization.CultureInfo]::new($Global:SessionCulture)
[System.Threading.Thread]::CurrentThread.CurrentCulture = $newc
"PS [$Global:SessionCulture] $($ExecutionContext.SessionState.Path.CurrentLocation)> "
}
# az-local helper to convert UTC to Local Time Zone
# examples:
# az vm list -o json | az-local | Format-Table Name, timeCreated
function az-local {
param(
[Parameter(ValueFromPipeline)]
[string]$InputObject
)
begin {
$jsonLines = [System.Collections.Generic.List[string]]::new()
}
process {
if ($InputObject) {
$jsonLines.Add($InputObject)
}
}
end {
$fullJson = $jsonLines -join "`n"
if (-not [string]::IsNullOrWhiteSpace($fullJson)) {
$tz = $null
# AUTOMATIC FALLBACK ENGINE
try {
$tz = [System.TimeZoneInfo]::FindSystemTimeZoneById($Global:DesiredTimeZone)
} catch {
$allTz = [System.TimeZoneInfo]::GetSystemTimeZones()
$matchedTz = $allTz | Where-Object { $_.Id -match $Global:DesiredTimeZone -or $_.DisplayName -match $Global:DesiredTimeZone }
if ($matchedTz) {
$tz = $matchedTz
} else {
Write-Warning "Could not resolve time zone '$Global:DesiredTimeZone'. Falling back to local system time zone."
$tz = [System.TimeZoneInfo]::Local
}
}
# BULLETPROOF ACRONYM GENERATOR (.NET Framework Char Filter)
function Get-CleanAcronym {
param([string]$tzName)
$trimmed = $tzName.Trim()
# If it's already a short 3-4 letter acronym (e.g. AEST/AEDT on some Linux systems), return it directly
if ($trimmed -match '^[A-Za-z]{3,4}$') {
return $trimmed.ToUpper()
}
# Otherwise, extract ONLY the uppercase characters directly from the string
# This turns 'AustralianEasternStandardTime' or 'AUS Eastern Standard Time' into 'AEST' instantly.
$chars = [char[]]$trimmed | Where-Object { [char]::IsUpper($_) }
return -join $chars
}
$standardAcronym = Get-CleanAcronym $tz.StandardName
$daylightAcronym = Get-CleanAcronym $tz.DaylightName
$fullJson | ConvertFrom-Json | ForEach-Object {
$obj = $_
$obj.PSObject.Properties | Where-Object { $_.Name -match "time|date" } | ForEach-Object {
if ($_.Value) {
$stringValue = $_.Value.ToString()
$dto = [datetimeoffset]::MinValue
if ([datetimeoffset]::TryParse($stringValue, [ref]$dto)) {
# Convert the UTC time to the resolved time zone
$localTime = [System.TimeZoneInfo]::ConvertTime($dto, $tz)
# Select the acronym based on whether Daylight Saving Time is active for this date
$tzLabel = if ($tz.IsDaylightSavingTime($localTime)) { $daylightAcronym } else { $standardAcronym }
# Overwrite the property value with the explicit timezone label appended
$_.Value = "$($localTime.ToString('dd/MM/yyyy HH:mm:ss')) $tzLabel"
}
}
}
$obj
}
}
}
}
'@ > $PROFILE
. $PROFILE
Modify 'en-AU' and 'Sydney' to match your regional settings.
The language tags to use can be found here - Available Language Packs for Windows | Microsoft Learn.
The timezones you can use can be found by running 'tzutil /l' on a Windows device and using the second line of each entry in the call to FindSystemTimeZoneById.
The az-local function helps you convert the UTC times that the 'az' commands generate. See the examples in the comment section in the code above on how to use this.
If your cloud sessions are ephemeral (no storage account mounted), then you'll need to copy-paste this each time you launch a cloud session.
