适用于 PowerShell 5.1 及以上版本
日期时间处理在运维脚本中无处不在——日志时间戳解析、定时任务调度、报表周期计算、服务运行时间监控、跨时区协作。PowerShell 基于 .NET 的 DateTime 和 TimeSpan 类型,提供了丰富的时间操作能力。但时区转换、UTC 处理、文化格式化等场景容易出错,需要掌握一些关键技巧。
本文将讲解 PowerShell 中日期时间的高级操作和常见陷阱。
格式化与解析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| $now = Get-Date Write-Host "默认:$now" Write-Host "短日期:$($now.ToString('d'))" Write-Host "长日期:$($now.ToString('D'))" Write-Host "ISO 8601:$($now.ToString('yyyy-MM-ddTHH:mm:ss.fff'))" Write-Host "文件名安全:$($now.ToString('yyyyMMdd_HHmmss'))" Write-Host "日志格式:$($now.ToString('yyyy-MM-dd HH:mm:ss.fff'))"
$formats = @{ "年月日" = "yyyy年M月d日" "时分秒" = "HH时mm分ss秒" "季度" = "yyyy年 第q季度" "周几" = "yyyy-MM-dd dddd" "相对简写" = "M/d HH:mm" }
foreach ($item in $formats.GetEnumerator()) { $formatted = $now.ToString($item.Value) Write-Host "$($item.Key):$formatted" }
$dateStrings = @( "2025-07-07", "2025/07/07 08:30:00", "07-Jul-2025", "2025年7月7日" )
foreach ($ds in $dateStrings) { try { $parsed = [datetime]::Parse($ds) Write-Host "解析 '$ds' => $($parsed.ToString('yyyy-MM-dd HH:mm:ss'))" -ForegroundColor Green } catch { Write-Host "解析失败 '$ds'" -ForegroundColor Red } }
$logTime = "07/07/2025:08:30:15 +0800" $parsed = [datetime]::ParseExact($logTime.Split(' ')[0], "MM/dd/yyyy:HH:mm:ss", $null) Write-Host "日志时间解析:$($parsed.ToString('yyyy-MM-dd HH:mm:ss'))"
|
执行结果示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 默认:2025/7/7 8:00:00 短日期:2025/7/7 长日期:2025年7月7日 ISO 8601:2025-07-07T08:00:00.000 文件名安全:20250707_080000 日志格式:2025-07-07 08:00:00.000 年月日:2025年7月7日 时分秒:08时00分00秒 季度:2025年 第3季度 周几:2025-07-07 星期一 相对简写:7/7 08:00 解析 '2025-07-07' => 2025-07-07 00:00:00 解析 '2025/07/07 08:30:00' => 2025-07-07 08:30:00 解析 '07-Jul-2025' => 2025-07-07 00:00:00 解析 '2025年7月7日' => 2025-07-07 00:00:00 日志时间解析:2025-07-07 08:30:15
|
时间差计算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| $bootTime = (Get-CimInstance Win32_OperatingSystem).LastBootUpTime $uptime = (Get-Date) - $bootTime
Write-Host "系统运行时间:$($uptime.Days) 天 $($uptime.Hours) 小时 $($uptime.Minutes) 分钟"
function Get-WorkingDays { param( [Parameter(Mandatory)][datetime]$Start, [Parameter(Mandatory)][datetime]$End )
$days = 0 $current = $Start.Date
while ($current -le $End.Date) { if ($current.DayOfWeek -notin @([DayOfWeek]::Saturday, [DayOfWeek]::Sunday)) { $days++ } $current = $current.AddDays(1) }
return $days }
$workingDays = Get-WorkingDays -Start (Get-Date "2025-07-01") -End (Get-Date "2025-07-31") Write-Host "7 月工作日:$workingDays 天"
function Get-NextWorkday { param([datetime]$Date = (Get-Date))
$next = $Date.AddDays(1) while ($next.DayOfWeek -in @([DayOfWeek]::Saturday, [DayOfWeek]::Sunday)) { $next = $next.AddDays(1) } return $next }
Write-Host "下一个工作日:$(Get-NextWorkday | Get-Date -Format 'yyyy-MM-dd dddd')"
function Get-LastDayOfMonth { param([datetime]$Date = (Get-Date)) return (New-Object System.DateTime $Date.Year, $Date.Month, 1).AddMonths(1).AddDays(-1) }
Write-Host "本月最后一天:$(Get-LastDayOfMonth | Get-Date -Format 'yyyy-MM-dd dddd')"
|
执行结果示例:
1 2 3 4
| 系统运行时间:45 天 12 小时 30 分钟 7 月工作日:23 天 下一个工作日:2025-07-08 星期二 本月最后一天:2025-07-31 星期四
|
时区处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| $tz = [System.TimeZoneInfo]::Local Write-Host "本地时区:$($tz.DisplayName)" Write-Host "UTC 偏移:$($tz.BaseUtcOffset)"
$utcNow = [System.DateTime]::UtcNow Write-Host "UTC 时间:$($utcNow.ToString('yyyy-MM-dd HH:mm:ss'))" Write-Host "本地时间:$([System.TimeZoneInfo]::ConvertTimeFromUtc($utcNow, $tz).ToString('yyyy-MM-dd HH:mm:ss'))"
function ConvertTo-TimeZone { param( [Parameter(Mandatory)] [datetime]$DateTime,
[Parameter(Mandatory)] [string]$TimeZoneId )
$targetTz = [System.TimeZoneInfo]::FindSystemTimeZoneById($TimeZoneId) return [System.TimeZoneInfo]::ConvertTime($DateTime, $targetTz) }
$timeZones = @{ "上海" = "China Standard Time" "东京" = "Tokyo Standard Time" "纽约" = "Eastern Standard Time" "伦敦" = "GMT Standard Time" "悉尼" = "AUS Eastern Standard Time" }
$now = Get-Date Write-Host "`n全球时间对照($($now.ToString('yyyy-MM-dd HH:mm:ss')) 本地时间):" -ForegroundColor Cyan
foreach ($city in $timeZones.GetEnumerator()) { try { $converted = ConvertTo-TimeZone -DateTime $now -TimeZoneId $city.Value Write-Host " $($city.Key):$($converted.ToString('yyyy-MM-dd HH:mm:ss dddd'))" -ForegroundColor Green } catch { Write-Host " $($city.Key):转换失败" -ForegroundColor Red } }
[System.TimeZoneInfo]::GetSystemTimeZones() | Where-Object { $_.DisplayName -match 'China|Tokyo|Eastern|GMT Standard' } | Select-Object Id, DisplayName | Format-Table -AutoSize
|
执行结果示例:
1 2 3 4 5 6 7 8 9 10 11
| 本地时区:(UTC+08:00) 北京,重庆,香港特别行政区,乌鲁木齐 UTC 偏移:08:00:00 UTC 时间:2025-07-07 00:00:00 本地时间:2025-07-07 08:00:00
全球时间对照(2025-07-07 08:00:00 本地时间): 上海:2025-07-07 08:00:00 星期一 东京:2025-07-07 09:00:00 星期一 纽约:2025-07-06 20:00:00 星期日 伦敦:2025-07-07 01:00:00 星期一 悉尼:2025-07-07 10:00:00 星期一
|
定时任务辅助
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| function Test-MaintenanceWindow { param( [int]$StartHour = 22, [int]$EndHour = 6, [datetime]$CheckTime = (Get-Date) )
$hour = $CheckTime.Hour
if ($StartHour -gt $EndHour) { return ($hour -ge $StartHour -or $hour -lt $EndHour) } else { return ($hour -ge $StartHour -and $hour -lt $EndHour) } }
if (Test-MaintenanceWindow -StartHour 22 -EndHour 6) { Write-Host "当前在维护窗口内(22:00-06:00)" -ForegroundColor Yellow } else { Write-Host "当前不在维护窗口内" -ForegroundColor Green }
function Wait-Until { param( [Parameter(Mandatory)] [datetime]$TargetTime )
$now = Get-Date if ($TargetTime -le $now) { Write-Host "目标时间已过" -ForegroundColor Yellow return }
$waitSpan = $TargetTime - $now Write-Host "等待 $($waitSpan.TotalMinutes -as [int]) 分钟到 $($TargetTime.ToString('HH:mm:ss'))..." -ForegroundColor Cyan
Start-Sleep -Seconds $waitSpan.TotalSeconds Write-Host "到达目标时间" -ForegroundColor Green }
function New-TimeRange { param( [datetime]$Start = (Get-Date "2025-07-07"), [datetime]$End = (Get-Date "2025-07-07 23:59:59"), [int]$IntervalMinutes = 30 )
$ranges = @() $current = $Start
while ($current -lt $End) { $next = $current.AddMinutes($IntervalMinutes) if ($next -gt $End) { $next = $End }
$ranges += [PSCustomObject]@{ Start = $current.ToString('HH:mm') End = $next.ToString('HH:mm') }
$current = $next }
return $ranges }
New-TimeRange -IntervalMinutes 60 | Format-Table -AutoSize
|
执行结果示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| 当前不在维护窗口内 Start End ----- --- 00:00 01:00 01:00 02:00 02:00 03:00 03:00 04:00 04:00 05:00 05:00 06:00 06:00 07:00 07:00 08:00 08:00 09:00 09:00 10:00 10:00 11:00 11:00 12:00 12:00 13:00 13:00 14:00 14:00 15:00 15:00 16:00 16:00 17:00 17:00 18:00 18:00 19:00 19:00 20:00 20:00 21:00 21:00 22:00 22:00 23:00 23:00 23:59
|
注意事项
- DateTime vs DateTimeOffset:
DateTime 不包含时区信息,跨时区场景使用 DateTimeOffset
- Kind 属性:
DateTime.Kind 可以是 Local、Utc 或 Unspecified,注意区分
- 闰年和月末:使用
.AddMonths(1).AddDays(-1) 计算月末,自动处理不同月份天数
- 夏令时:时区转换时注意夏令时影响,
TimeZoneInfo 会自动处理
- 字符串解析:
[datetime]::Parse() 使用当前文化设置,服务器脚本中推荐 ParseExact() 明确格式
- 精度:
Get-Date 精度为毫秒级,Stopwatch 可以达到纳秒级精度