首页 > 解决方案 > 午夜后的时间设置不正确

问题描述

我使用以下内容以更易读的格式获取当前日期/时间:

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。这弄乱了格式,因为它:在时间中包含冒号,因为字符被移了一个。这会导致在午夜之后创建的日志文件中出现无用的日期/时间信息。

有什么方法可以让时间始终采用两位数的格式?

标签: batch-filetime

解决方案


动态环境变量的使用DATETIME字符串替换,正如我对%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 上也可以工作。缺点是命令执行需要相当长的时间(一到两秒),而使用DATETIME环境变量需要几微秒才能访问。

命令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%\System32Windows 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:还有许多其他解决方案可以以特定格式获取当前日期/时间,独立于为所用帐户配置的国家/地区。所有这些替代解决方案都可以在以下答案中找到:

如何以适合文件/文件夹名称的格式在 Windows 命令行上获取当前日期/时间?


推荐阅读