powershell - 如何在没有科学记数法的情况下将长整数转换为字符串
问题描述
我有一个 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
如何避免这种转换?
解决方案
Update:
- In Windows PowerShell 5.1 and PowerShell (Core) 7+ (.NET Framework 4.6+) a much simpler solution is now available, using
System.DateTimeOffset.ToUnixTimeSeconds
:
[datetimeoffset]::UtcNow.AddMinutes(-5).ToUnixTimeSeconds()
tl;dr:
To work around the problem with culture-specific number formatting, avoid a [double]
cast and use [double]::Parse()
instead, which by default parses in a culture-sensitive manner:
[Math]::Round(
[double]::Parse(
(Get-Date -Uformat %s ((Get-Date).ToUniversalTime().AddMinutes(-5)))
) * 1000,
0,
'AwayFromZero'
)
The AwayFromZero
midpoint-rounding strategy ensures that .5
second values are always rounded up, whereas the default ToEven
strategy would situationally round down, namely whenever the integer part of the number happens to be even.
Indeed, your problem stems from the fact that:
Get-Date -UFormat %
outputs a string representation of the Unix timestamp- and uses culture-sensitive formatting for the underlying floating-point number[1], which means that in some cultures you'll get a string such as
'1538651788,87456'
(,
as the decimal mark) rather than'1538651788.87456'
as the output.
By contrast, PowerShell's casts always use the invariant culture, which only recognizes .
as the decimal mark - and ignores ,
, which is considered a thousands-grouping character.
PS> [double] '1538651788,87456'
153865178887456 # !! , was IGNORED
Because the decimal mark was ignored and there are 5 decimal places, the resulting number is too large by a factor of 10,000 in this case (though note that the number of decimal places can vary, because trailing zeros aren't displayed).
If you then multiply that result with 1000, you get a number so large that PowerShell defaults its string representation to the scientific format that you've experienced:
PS> [double] '1538651788,87456' * 1000
1.53865178887456E+17 # !! scientific notation.
[1] Optional reading: Get-Date -UFormat %s
problems in Windows PowerShell vs. PowerShell Core (v6+):
Unix timestamps are integers, so
Get-Date -UFormat %s
shouldn't return a floating-point number to begin with. This problem has been corrected in PowerShell Core.Unix timestamps are expressed in UTC, but Windows PowerShell only returns the correct value if you explicitly pass a UTC
[datetime]
instance. This problem has been corrected in PowerShell Core (v6+).- E.g., to get the current time's Unix timestamp in Windows PowerShell, using
Get-Date -UFormat %s
is not enough; useGet-Date -UFormat %s ([datetime]::UtcNow)
instead.
- E.g., to get the current time's Unix timestamp in Windows PowerShell, using
In short: This question's problem wouldn't have arisen in PowerShell Core, because string representations of integers aren't culture-sensitive; additionally, the need for rounding goes away, as does the need to convert the input date to UTC, so that a PowerShell Core solution simplifies to:
# PowerShell *Core* only
1000 * (Get-Date -UFormat %s ((Get-Date).AddMinutes(-5)))
Note: This technically returns a [double]
instance, but one without decimal places; use [long] (...)
if an integer type is expressly needed.
推荐阅读
- c++ - 致命错误 C1083:无法打开包含文件:'GL/glew.h':没有这样的文件或目录
- c++ - C++17无匹配函数调用以减少错误
- mysql - 如何根据返回的行选择多个列?
- vue.js - vue.js v-for 从 2 个不同的列表中更改
- java - Jetty 9 + Spring Boot - 由于缺少 ServletWebServerFactory bean,无法启动 ServletWebServerApplicationContext
- css - 如何更改 Semantic UI React Dropdown 的边框颜色?
- java - 如何使用命令提示符在两个 jar 文件之间创建管道?
- c++ - 如何使用 boost/operators.hpp 自动生成 == 运算符?
- android - 我在加载插页式广告时不断收到此错误
- javascript - 如何声明对现有命名空间的引用,该命名空间可在运行时从 JavaScript 包中获得