如何在Linux中通过时区更改分隔文件中字段的时间戳



假设我有一个如下文件:

1,aaa,2016-12-01 01:02:03 EST,bbb
2,ccc,2016-12-02 04:05:06 CST,ddd
3,eee,2016-12-03 07:08:09 EST,fff

我想添加第5个字段,它是字段3中的时间戳,但已转换为UTC。

这可以调用对date或Linux中的任何东西的调用。我并不太担心性能,因为它只在少量文件中被调用一次。

我似乎就是想不出最好的办法。awk的strftime没有接收到时区字段,所以我不知道使用它的最佳方式

使用Bash循环:

while IFS=, read -r -a linearr; do
printf '%s,' "${linearr[@]}"
date +'%F %T %Z' -u -d "${linearr[2]}"
done < infile

这导致

1,aaa,2016-12-01 01:02:03 EST,bbb,2016-12-01 06:02:03 UTC
2,ccc,2016-12-02 04:05:06 CST,ddd,2016-12-02 10:05:06 UTC
3,eee,2016-12-03 07:08:09 EST,fff,2016-12-03 12:08:09 UTC

这会将每一行读取到数组linearr中,打印末尾添加逗号的行,然后附加新的日期字符串。

或者,使用pastecut:

paste -d, infile <(while read line; do date +'%F %T %Z' -u -d "$line"; done < <(cut -d , -f 3 infile))

或者,可读性更强:

paste -d , 
infile 
<(while read line; do
date +'%F %T %Z' -u -d "$line"
done < <(cut -d, -f3 infile)
)

请注意,date-d选项是GNUdate特有的。POSIXdate没有使用当前系统日期以外的日期的选项,而FreeBSD中的date使用了另一个选项-r,它期望"自Epoch以来的秒数"作为其参数。

使用GNU awk,可以通过操作TZ环境变量来实现这一点。当您只处理时间偏移时,格式有点糟糕,但如果您有指定时区的std字符串,那么它就很简单了。

在awk中,环境存储在数组ENVIRON中。ENVIRON的修改是实现定义的:

ENVIRON:表示环境值的数组,如POSIX.1-2017的系统接口卷中定义的exec函数中所述。数组的索引应为包含环境变量名称的字符串,每个数组元素的值应为包含该变量值的字符串。如果合适,环境变量应被视为数字字符串(参见awk中的表达式);数组元素也应具有其数值。

在awk的行为受到环境变量影响的所有情况下(包括awk通过系统函数或通过print语句、printf语句或getline函数的管道重定向执行的任何命令的环境),所使用的环境应为awk开始执行时的环境实现定义了ENVIRON的任何修改是否会影响此环境

来源:POSIX.1-2017

GNU awk,另一方面,声明如下:

然而,从4.2版本开始,如果不处于POSIX兼容模式,gawk会在ENVIRON更改时更新自己的环境,从而更改它创建的程序所看到的环境。

所以现在可以通过做类似这样的事情来利用它:类似的事情

ENVIRON["TZ"] = std offset

以下是几个例子:

ENVIRON["TZ"] = "UTC"
ENVIRON["TZ"] = "UTC+03:00"
ENVIRON["TZ"] = "CET"

关于OP,我们可以这样做:

awk 'BEGIN{FS=OFS=","}
{time=$2; gsub(/[^0-9]/," ",time); tz=$2; gsub(/^.* /,"",tz)}
{ENVIRON["TZ"]=tz; print $0,strftime("%F %T",mktime(time),1)}
' file

注意:对于CSV文件,您应该使用What';使用awk高效解析CSV的最稳健方法是什么?

注:遗憾的是,并不是所有已知形式的TZ定义都得到认可。

最新更新