如何在没有科学记数法的情况下将 long 转换为字符串

How to convert a long to a string without having the scientific notation

我有一个 Powershell 脚本可以获取当前时间并将其转换为 Unix 时间戳。我想使用这个值作为字符串参数来发出 HTTP 请求:

$currentTime = [math]::Round([double](get-date -uformat %s ((get-date).AddMinutes(-5).ToUniversalTime()))*1000)

Invoke-RestMethod -Method Delete -Uri "http://something?unmodifiedSince=$currentTime"

在一些 Windows 机器上它工作正常但在其他一些机器上(不同的区域设置?),我得到了用科学记数法转换的当前时间。例如

http://something?unmodifiedSince=1.53835531189786E+17

如何避免这种转换?

更新:

[datetimeoffset]::UtcNow.AddMinutes(-5).ToUnixTimeSeconds()

tl;dr:

解决文化特定数字格式的问题,避免[double]转换 使用 [double]::Parse(),默认情况下以 culture-sensitive 方式解析:

[Math]::Round(
  [double]::Parse(
     (Get-Date -Uformat %s ((Get-Date).ToUniversalTime().AddMinutes(-5)))
  ) * 1000,
  0,
  'AwayFromZero'
)

AwayFromZero 中点舍入策略确保 .5 第二个值 总是 舍入,而默认 ToEven 策略会在某些情况下舍入 ,即每当数字的整数部分恰好是偶数时。


确实,您的问题源于以下事实:

  • Get-Date -UFormat % 输出 Unix 时间戳
  • 字符串 表示
  • 并对基础浮点数使用文化敏感格式[1],这意味着在某些文化中你将得到一个字符串,例如 '1538651788,87456', 作为小数点)而不是 '1538651788.87456' 作为输出。

相比之下,PowerShell 的转换始终使用 不变 文化,. 识别为小数点 - 并且忽略 ,,它被认为是千位分组字符。

PS> [double] '1538651788,87456'
153865178887456  # !! , was IGNORED

由于忽略了小数点并且有 5 位小数,因此在本例中结果数字太大了 10,000 倍(但请注意,小数位数可能会有所不同,因为尾随零不是显示)。

如果您随后将该结果乘以 1000,您会得到一个非常大的数字,以至于 PowerShell 将其字符串表示默认为您所体验过的科学格式:

PS> [double] '1538651788,87456' * 1000
1.53865178887456E+17 # !! scientific notation.

[1] 可选阅读:Get-Date -UFormat %s Windows PowerShellPowerShell Core[= 中的问题106=] (v6+):

  • Unix 时间戳是 整数 ,因此 Get-Date -UFormat %s 不应该 return 以浮点数开头。此问题已在 PowerShell Core.

    中得到纠正
  • Unix 时间戳以 UTC 表示,但 Windows PowerShell 只有 return 如果您显式传递 UTC [datetime] 才是正确的值实例。此问题已在 PowerShell Core (v6+) 中得到纠正。

    • 例如,要在 Windows PowerShell 中获取当前时间的 Unix 时间戳,使用
      Get-Date -UFormat %s 不够;请改用 Get-Date -UFormat %s ([datetime]::UtcNow)

简而言之:这个问题的 问题不会出现在 PowerShell Core 中,因为 整数的字符串表示 对文化不敏感;此外,不需要四舍五入,也不需要将输入日期转换为 UTC,因此 PowerShell Core 解决方案可以简化为:

# PowerShell *Core* only 
1000 * (Get-Date -UFormat %s ((Get-Date).AddMinutes(-5)))

注意:从技术上讲,这 return 是一个 [double] 实例,但没有小数位;如果明确需要整数类型,请使用 [long] (...)

您可以将 $currentTime 变量数据类型显式指定为 [Decimal] 而不是自动分配 [Double]。像这样:

[Decimal]$currentTime = [math]::Round([double](get-date -uformat %s ((get-date).AddMinutes(-5).ToUniversalTime()))*1000)

我使用这个小函数来获取当前日期和时间作为 Unix 时间戳。 它 returns 是一个 int64,所以你在 url:

中添加它应该没有任何问题
function Get-CurrentUnixTimeStamp {
    [DateTime]$epoch = New-Object System.DateTime 1970, 1, 1, 0, 0, 0, 0, Utc
    [TimeSpan]$diff  = (Get-Date).ToUniversalTime() - $epoch
    return [int64][Math]::Floor($diff.TotalSeconds)
}

$currentTime = Get-CurrentUnixTimeStamp
Invoke-RestMethod  -Method Delete -Uri "http://something?unmodifiedSince=$currentTime"