Linux下的Bash - 解析自定义格式的日期

13

我有一个日期,格式为%c(也可能是其他格式),需要在date命令中使用。 %c不是美国的格式,因为这是德国服务器,所以它是德国格式。这在美国服务器上也无法正常工作(语言环境设置为德语或英语)。

以下代码无法运行(包括错误信息):

user@server:~$ NOW=$(date +%c); echo $NOW
Do 19 Dez 2013 22:33:28 CET
user@server:~$ date --date="$NOW" +%d/%m/%Y
date: ungültiges Datum „Do 19 Dez 2013 22:33:28 CET“

(日期: ungültiges Datum "Do 19 Dez 2013 22:33:28 CET" = date: invalid date "Do 19 Dez 2013 22:33:28 CET")

问题在于我不知道用户将使用哪种语言环境或日期格式,因为他们可以设置自己的格式。因此,一个简单特定的解析方案并不能真正解决问题!

但是我该怎么办呢?

为了概括这个问题:

如果我有一个日期格式format1 (它可以是任何可逆的日期格式之一),我可以使用日期来获得一个格式化的日期。但是,如果我想将其格式化为另一个日期(format2),我该怎么做呢?
任何使用除核心工具以外的其他解决方案都是无意义的,因为我正在尝试为尽可能多的Unix机器开发一个Bash脚本。

DATE=$(date "+$format1")

date --date="$DATE" "+$format2" # Error in most cases!

我需要这样做是因为用户可以给出日期格式的指令,该日期字符串将被显示。但在稍后的步骤中,我需要将此日期字符串转换为另一个固定格式。我可以操纵指令将得到哪种格式,也可以操纵输出(或用户将看到的内容)
我无法运行两次指令,因为它非常耗时。


更新:

我已经找到了一种解决方案:

# Modify $user_format so it can be parsed later
user_format="$user_format %s"

# Time consuming command which will print a date in the given format
output=$(time_consuming_command params "$user_format" more params)

# This will only display what $user_format used to be
echo ${output% *}

# A simple unix timestamp parsing ("${output##* }" will only return the timestamp)
new_formated_date=$(date -d "1970-01-01 ${output##* } sec UTC" "+$new_format")

这个方案已经实践有效,可能对其他人也有帮助。因此我会与大家分享。


为什么不起作用?你遇到了什么错误? - Donovan
我们能看一下你的 date +%c 的输出吗? - Donovan
获取当前日期使用format1,然后再使用它以format2打印的意义是什么?难道你不能直接获取fomrat2中的当前日期吗?如果您正在尝试在非当前日期上执行此操作,例如从文件或表中提取的日期,则这将更有意义。 - alvits
你能更好地解释一下具体的用例吗?我认为你不能使用这种通用方法,特别是只使用bash和coreutils。(虽然有一些库尝试猜测日期时间格式,但它们并不完美,也不在任何标准库中..) - redShadow
@alvits 我读取了一个耗时很长的命令,它不会返回当前日期。在我的问题中,我只是使用了一个简单的可复现的伪问题。 - BrainStone
显示剩余3条评论
4个回答

10

截至GNU coreutils 8.22版本,使用--date不可行。根据日期手册:

‘-d datestr’

‘--date=datestr’

Display the date and time specified in datestr instead of the current date and time. datestr can be in almost any common format. It can contain month names, time zones, ‘am’ and ‘pm’, ‘yesterday’, etc. For example, --date="2004-02-27 14:19:13.489392193 +0530" specifies the instant of time that is 489,392,193 nanoseconds after February 27, 2004 at 2:19:13 PM in a time zone that is 5 hours and 30 minutes east of UTC.

Note: input currently must be in locale independent format. E.g., the LC_TIME=C below is needed to print back the correct date in many locales:

date -d "$(LC_TIME=C date)"

http://www.gnu.org/software/coreutils/manual/html_node/Options-for-date.html#Options-for-date

请注意,它指出输入格式不能是特定于语言环境的格式。

可能有其他库或程序可以识别更多的日期格式,但对于给定的日期格式,编写一个短小的程序将其转换为date可识别的格式(例如,使用Perl或awk)并不难。


谢谢您提供的信息,但这个解决方案对我没有帮助。我不确定日期格式是什么,所以无法使用特定的解析方法。 - BrainStone
谢谢!我的语言环境(“en_GB.UTF-8”)一直有问题。前缀“LC_TIME=C”解决了它!奇怪的是,我的系统手册中的文本要短得多(Manjaro,coreutils 8.30-1)。 - joeytwiddle

4

为什么不将时间存储为unixtime(即自1970年1月1日以来的毫秒数),例如1388198714

试图尝试使用一个无法合理依赖其他程序的一次性bash脚本解析全球各地所有日期格式是有些荒谬的要求。


1
我同意。正如楼主所说:“我不知道以后将使用哪个语言环境或日期格式,因为用户可以设置自己的格式。” 想象一下数字月份会发生什么(美国:12/9/2013,德国:9.12.2013)。保证会出现误解!切忌使用用户配置的输出作为附加脚本的输入。 - Ruud Helderman
1
我在我的问题中建议了类似的东西。我认为这样的解决方案是最好的。 - BrainStone

2

您可以使用libdatetime-format-flexible-perl

#!/usr/bin/perl
use DateTime::Format::Flexible;
my $date_str = "So 22 Dez 2013 07:29:35 CET";
$parser = DateTime::Format::Flexible->new;
my $date = $parser->parse_datetime($date_str);
print $date

默认输出将为2013-12-22T07:29:35,但由于$date不是常规字符串而是对象,因此您可以执行以下操作:

printf '%02d.%02d.%d', $date->day, $date->month, $date->year;

另外,date行为可能应该被视为一个错误。我认为是这样的,因为相同格式但是用俄语表示的日期可以被正确解析。

$ export LC_TIME=ru_RU.UTF-8
$ NOW="$(date "+%c")"
$ date --date="$NOW" '+%d.%m.%Y'
22.12.2013


我有两个问题:1. 这个是否总是安装好的?2. 这是否适用于所有日期格式? - BrainStone
@BrainStone 如果您无法安装其他软件包,那么很难找到任何解决方案。但是,假设 Perl 已经安装(这很可能是正确的),则可以将 Perl 模块与您的脚本一起安装到用户空间中。 - Dmitry Alexandrov
@DimitryAlexandrov 我真的很想避免安装任何东西。 - BrainStone
@BrainStone 但是你不能避免安装自己的脚本,对吧? - Dmitry Alexandrov
@DimitryAlexandrov 这只是一个小的bash脚本,没有更多的东西。上传完毕。用户知道他们是否要安装它。但是,如果我的脚本安装了某些东西,它可能会被视为病毒。这对我来说并不是一个合适的解决方案。 - BrainStone
显示剩余4条评论

0

如果您的意思是格式不正确,我认为您想要的是:

NOW=$(date +%c)
date --date="$NOW" +%d/%m/%Y

请注意小写的 %d%m
在本地,我得到的结果是:
root@server2:~# NOW=$(date +%c)
root@server2:~# date --date="$NOW" +%d/%m/%Y
19/12/2013

4
无法解析。我不在意输出的日期格式,输入的日期格式才是重要的! - BrainStone

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接