在csv中编辑日期时间以调整时区

时间:2016-09-14 21:07:27

标签: bash csv datetime awk

我们经常在某些列中包含始终为GMT的日期时间值的csv文件 我们正在寻找一种方法将日期时间值从GMT更改为我们选择的时区 - 所需的目标时区可能因csv而异。调整也需要考虑夏令时。

来自csv的日期时间格式:

YYYY/MM/DD HH:MM:SS

示例数据:

col1,col2,col3,col4
aaa,bbb,2016/01/15 22:01:16,ccc
ddd,eee,,fff
hhh,iii,2014/09/19 00:53:37,jjj
kkk,lll,2015/11/15 22:01:16,mmm
nnn,ooo,2015/10/12 19:54:59,ppp

例如,如果我们想要将上面的样本数据从GMT调整到太平洋时间(GMT -8标准),我们期望的结果将是csv,其值如下:

col1,col2,col3,col4
aaa,bbb,2016/01/15 14:01:16,ccc
ddd,eee,,fff
hhh,iii,2014/09/18 17:53:37,jjj
kkk,lll,2015/11/15 14:01:16,mmm
nnn,ooo,2015/10/12 12:54:59,ppp

注意重新DST:对于上面的5行样本数据,DST仅对第3行和第5行的日期有效。调整可能在线与线之间有所不同,但在目标时区等效值(太平洋)方面是一致的。

日期时间格式本身可以保持原样 - 我们只需要调整时区的日期时间值,并最终将等效日期时间存储在东部,太平洋(或任何其他时区)而不是GMT。

如果可能的话,我们希望利用Ubuntu中本身可用的编辑工具,例如awk,因为我们已经有了使用它的清理例程。如果awk或类似解决方案不可能,将考虑其他解决方案。

其他说明:

  • 有些csv在多列中都有日期时间,只有列的 某些 需要从GMT调整到另一个时区。理想的解决方案是在我们指定的一个或多个列中调整日期时间,但跳过不需要调整的列。
  • 具有日期时间的csv列也可能在某些行上包含空白值。
  • 在每个csv的基础上,我们希望将日期时间从GMT更改为单个目标时区,对所有行进行相同的tz调整。

感谢任何见解 - 谢谢!

编辑:

在研究这个问题时,我发现了这样的陈述:

echo "1/15/2016  10:01:16 GMT" | awk -v q='"' '{cmd="TZ=America/Los_Angeles date -d"q$0 q" +"q"%F %H:%M:%S %Z"q; cmd|getline x; close(cmd);print x}'

...有点是概念的证明,但是1)我不得不添加" GMT"我自己的字符串,2)输出日期格式略有不同 我希望找到一个可以应用于可能有数千行的csv的解决方案。

2 个答案:

答案 0 :(得分:1)

使用GNU awk进行时间函数:

$ cat tst.awk
function dt2utcSecs(dateTime,   cmd,line,ret) {
    cmd = "TZ=UTC gawk -v dt='" dateTime "' 'BEGIN{print mktime(dt)}'"
    ret = ( (cmd | getline line) > 0 ? line : -1 )
    close(cmd)
    return ret
}
BEGIN{
    FS=OFS=","
    split(cols,f)
}
{
    for (i in f) {
        dateTime = gensub(/[\/:]/," ","g",$(f[i]))
        utcSecs = dt2utcSecs(dateTime)
        if (utcSecs >= 0) {
            $(f[i]) = strftime("%Y/%m/%d %T",utcSecs)
        }
    }
    print
}

$ TZ='US/Pacific' gawk -v cols=3 -f tst.awk file
col1,col2,col3,col4
aaa,bbb,2016/01/15 14:01:16,ccc
ddd,eee,,fff
hhh,iii,2014/09/18 17:53:37,jjj
kkk,lll,2015/11/15 14:01:16,mmm
nnn,ooo,2015/10/12 12:54:59,ppp

只需设置cols=3,5,9即可对这些字段进行转换。有关有效时区的列表,请参阅/ usr / share / zoneinfo。

请注意@webb's answer应该比上面的效率更高,因为上面调用shell为每个输入dateTime字段调用gawk一次,而@webbs只调用gawk两次。

答案 1 :(得分:1)

这是一个有趣的方式:

首先,将日期字符串转换为数字,utc时间戳,然后将数字时间戳转换为本地日期字符串:

TZ=UTC awk -F, '
  BEGIN{OFS=","}
  { if(NR>1&&$3){
      gsub("[/:]"," ",$3);
      $3=mktime($3" GMT")};
    print $0
  }' infile.csv | awk  -F, '
  BEGIN{OFS=","}
  { if(NR>1&&$3){
      $3=strftime("%Y/%m/%d %H:%M:%S %Z", $3, 0)};
    print $0
  }' > outfile.csv

输出:

col1,col2,col3,col4
aaa,bbb,2016/01/15 14:01:16 PST,ccc
ddd,eee,,fff
hhh,iii,2014/09/18 17:53:37 PDT,jjj
kkk,lll,2015/11/15 14:01:16 PST,mmm
nnn,ooo,2015/10/12 12:54:59 PDT,ppp

注意1:您可以通过删除第二个%Z中的awk来删除输出中的时区,但如果这样做,则只会伤害您未来的自我。

注意2:根据您的awk版本,这可能有效,也可能无效。如果您的系统有gawk,请尝试代替awk。如果没有,则应该很容易安装gawk