batch-file - 午夜后的时间设置不正确
问题描述
我使用以下内容以更易读的格式获取当前日期/时间:
set day=%date:~4,2%
set mth=%date:~7,2%
set yr=%date:~10,4%
set hur=%time:~0,2%
set min=%time:~3,2%
set bdate=[%day%-%mth%-%yr%]-[%hur%-%min%]
这很好用并输出如下内容:[02-06-2020]-[22-59]
但是,在午夜之后,时间格式会从HH:MM:SS:MS更改为H:MM:SS:MS。这弄乱了格式,因为它:
在时间中包含冒号,因为字符被移了一个。这会导致在午夜之后创建的日志文件中出现无用的日期/时间信息。
有什么方法可以让时间始终采用两位数的格式?
解决方案
动态环境变量的使用DATE
和TIME
字符串替换,正如我对%date:~-4,4%%date:~-10,2%%date:~-7,2%问题的回答所解释的那样_%time:~0,2%%time:~3,2% 是什么意思?速度非常快,但缺点是日期和时间字符串的格式取决于为运行批处理文件时使用的帐户配置的地区(国家)。
日期可以在开头不带或带缩写的工作日,在缩写的工作日之后可以不带或带逗号。日期字符串的格式可以是年/月/日或月/日/年或日/月/年,使用/
or.
或-
or
作为分隔符。另请参阅有关国家/地区日期格式的 Wikipedia 文章。
时间可以是 12 或 24 小时格式。小时可以不带或带前导0
小于 10。
没有发布echo %DATE% %TIME%
在上午十点之前和下午三点之前运行时输出的内容,以了解当地的日期/时间格式。
看起来可以:
在时间字符串中使用以下代码作为分隔符:
if "%TIME:~1,1%" == ":" (
set "bdate=[%DATE:~-10,2%-%DATE:~-7,2%-%DATE:~-4%]-[0%TIME:~0,1%-%TIME:~2,2%]"
) else (
set "bdate=[%DATE:~-10,2%-%DATE:~-7,2%-%DATE:~-4%]-[%TIME:~0,2%-%TIME:~3,2%]"
)
如果时间字符串中的第二个字符是冒号,则条件为真,并且第一个表达式与0
左加一位数小时一起使用,否则ELSE块与两位数小时一起使用。
另一种在时间字符串中使用冒号或点或空格分隔符的解决方案是:
for /F "tokens=1,2 delims=:. " %%I in ("%TIME%") do set "Hour=0%%I" & set "Minute=%%J"
set "bdate=[%DATE:~-10,2%-%DATE:~-7,2%-%DATE:~-4%]-[%Hour:~-2%-%Minute%]"
剩下的小时:
或.
或
分配给Hour
带有前导零的环境变量。:
或.
或的分钟权
分配给变量Minute
。日期/时间字符串仅使用最后两位数字来构建,以Hour
使小时最终始终将日期/时间字符串中的两位数字分配给环境变量bdate
。
更好的是让当前日期和时间区域(国家)独立并使用字符串替换将它们重新格式化为想要的格式。
可以使用Windows Management Instrumentation 命令行工具WMIC获取与区域无关的日期/时间字符串。
命令行
wmic OS GET LocalDateTime /VALUE
输出 UTF-16 Little Endian 编码,例如:
LocalDateTime=20200208124514.468000+060
有两个空行,然后是当前本地日期/时间的行,格式为 yyyyMMddHHmmss.microsecond±UTC 偏移量(以分钟为单位)和另外两个空行。
数据可以与这样的批处理代码一起使用:
@echo off
for /F "tokens=2 delims==." %%I in ('%SystemRoot%\System32\wbem\wmic.exe OS GET LocalDateTime /VALUE') do set "DateTime=%%I"
set "bdate=[%DateTime:~6,2%-%DateTime:~4,2%-%DateTime:~0,4%]-[%DateTime:~8,2%-%DateTime:~10,2%]"
有趣的是这里只有等号和小数点之间的日期/时间字符串,这就是使用for /F
选项tokens=2 delims==.
来获取刚刚20200208124514
分配给循环变量I
的原因,该循环变量的值被分配在环境变量旁边DateTime
。
使用字符串替换重新格式化环境变量DateTime
以获取格式中的日期/时间字符串,[dd-MM-yyyy]-[HH-mm]
从而导致环境变量bdate
被定义为[08-02-2020]-[12-45]
.
当然可以在bdate
任何地方使用,而不是DateTime
只使用一个环境变量而不是两个变量。
使用WMIC获取本地日期/时间的优点是它独立于 Windows 区域设置,甚至在 Windows XP 上也可以工作。缺点是命令执行需要相当长的时间(一到两秒),而使用DATE和TIME环境变量需要几微秒才能访问。
命令FOR无法正确解析 UTF-16 LE 编码的 Unicode 输出。它将WMIC输出的字节序列0D 00 0A 00
(回车+换行)错误解释为,即两个回车和一个换行。这导致将WMIC输出末尾的最后两个空行解释为两行,并将单个回车符作为字符串。0D 0D 0A
这通常是一个问题,因为结果set "EnvironmentVariable=%%I"
是%%I
扩展为仅回车,删除了之前已经定义的具有正确值的环境变量。
有多种解决方案可以解决命令FOR的 Unicode 解析错误。一旦将值分配给环境变量,就可以追加& goto Label
退出循环并跳转到FOR:Label
循环下方,以避免遇到此问题。
另一种解决方案是在FOR命令行上方使用if not defined DateTime set "DateTime=%%I"
with以确保命令SET仅执行一次。set "DateTime="
另一种解决方案是此代码中使用的解决方案。由于使用了WMIC选项,因此属性的名称及其值在同一行上输出/VALUE
。命令FOR运行命令SET因为tokens=2
只有当它可以使用等号和点作为分隔符将当前行拆分为至少两个子字符串(标记)时,因为delims==.
. 但是WMIC输出的错误解析空行仅用于FOR仅包含回车的行,因此没有第二个标记。由于这个原因,错误解析的空行在这里也被FOR命令忽略。
请参阅如何在解析输出时纠正变量覆盖错误行为?并且cmd 以某种方式编写中文文本作为输出,以获取有关在 UTF-16 LE 编码输出上解析FOR问题的详细信息。
使当前日期和时间区域独立的另一种解决方案是使用ROBOCOPY,它在 Windows 系统目录中的 Windows Vista 和 Windows Server 2003 中可用,但默认情况下在 Windows XP 上不可用。robocopy.exe
可以将 Windows Server 2003 复制到%SystemRoot%\System32
Windows XP 以在 Windows XP 上使用此可执行文件,但默认情况下它在 Windows XP 上不可用。
ROBOCOPY使用无效的源目录字符串"C:\|"
和有效的目标目录字符串.
(也可以是其他有效的)和参数/NJH
来执行,以抑制头信息的输出。
robocopy "C:\|" . /NJH
此执行产生错误消息:
2020/02/08 12:45:14 ERROR 123 (0x0000007B) Accessing Source Directory C:\|\
The filename, directory name, or volume label syntax is incorrect.
空行后第二行开头的当前日期/时间格式与区域无关。
可以使用以下方式处理此输出:
set "bdate="
for /F "tokens=1-5 delims=/: " %%I in ('%SystemRoot%\System32\robocopy.exe "%SystemDrive%\|" . /NJH') do if not defined bdate set "bdate=[%%K-%%J-%%I]-[%%L-%%M]"
在这种情况下, FOR在后台启动另一个命令进程,%ComSpec% /c
两个命令之间的命令行'
作为附加参数附加,这导致 Windows 被安装到C:\Windows
执行以下操作:
C:\Windows\System32\cmd.exe /c C:\Windows\System32\robocopy.exe "C:\|" . /NJH
用于处理由FOR捕获的后台命令进程的STDOUT的错误消息输出的第一个非空行的前五个斜杠或冒号或空格分隔的字符串分配给:
I
... 年J
... 月K
... 天L
... 小时M
... 分钟
五个数据字符串以所需格式连接到日期/时间字符串。
但是FOR会为第二个错误消息行再次运行命令SET 。因此,在运行FORbdate
之前,环境变量是明确未定义的。环境变量首先使用日期/时间字符串定义,然后在FOR处理第二个非空行时不再修改,因为附加IF条件以避免用不需要的字符串覆盖感兴趣的日期/时间字符串。bdate
与WMIC解决方案相比, ROBOCOPY解决方案的优势在于其执行时间要快得多。
要了解使用的命令及其工作原理,请打开命令提示符窗口,在其中执行以下命令,并仔细阅读每个命令显示的所有帮助页面。
echo /?
for /?
if /?
robocopy /?
set /?
wmic /?
wmic os /?
wmic os get /?
wmic os get localdatetime /?
PS:还有许多其他解决方案可以以特定格式获取当前日期/时间,独立于为所用帐户配置的国家/地区。所有这些替代解决方案都可以在以下答案中找到:
推荐阅读
- angular - 如何动态生成并仅显示primeng表中存在并在数组中有数据的列
- azure - 尝试通过 Azure REST API 在 AAD 中提升访问权限时出现访问错误
- python - 重构:“提取方法”错误和意外结果
- javascript - 将变量从节点传递到本地 javascript 文件的最佳方式
- node.js - 如何将 Angular 前端和 Node 后端合并到一个 git 存储库中?
- plsql - 创建循环以在 oracle SQL 中传递变量数组
- html - 在引导卡中居中绘图图形
- azure-data-explorer - 通过 kql 语法将其他人设置为集群管理员
- python - 无法在python中导入我是编码新手请帮助:)
- kubernetes - 如何创建服务帐户以从 Kubernetes 集群中获取 pod 列表?