如何对文本文件中的日期进行排序?

如何对文本文件中的日期进行排序?

现在是 2019 年 8 月 14 日,有一个包含日期列表 (dd.mm.yyyy) 的文本文件:

30.07.2018
14.08.2019
18.08.2019
20.08.2019
01.01.2020

列表中的日期是截止日期。

输出应如下所示:早于当前日期的日期和将在 10 天后到期的日期的列表(如果在 1 到 10 天后到期,则会列出,如果今天到期,也会列出。如果是11 天或更长时间后到期,则不列出)。

答案1

使用 GNU date

#! /bin/sh -
printf '%s days\n' -1 +10 |
  date -f - +%d.%m.%YT |
  sort -nt. -k3 -k2 -k1 -- - "$@" |
  sed '/T/,/T/!d;/T/d'

我们的想法是对列表进行排序,其中我们插入了昨天和 10 天后的日期,以 结尾T,例如如果在 2019-08-14 运行,我们会得到:

30.07.2018
13.08.2019T
14.08.2019
18.08.2019
20.08.2019
24.08.2019T
01.01.2020

我们提取这两行之间的部分T

$ faketime 2019-08-14 that-script your-file
14.08.2019
18.08.2019
20.08.2019

答案2

使用 GNU awk,或mawk

$ cat file
30.07.2018
13.08.2019
14.08.2019
18.08.2019
20.08.2019
21.08.2019
23.08.2019
24.08.2019
25.08.2019
01.01.2020
$ awk -f script.awk file   # running this on 2019-08-14
30.07.2018      expired 380 days ago
13.08.2019      expired 1 days ago
14.08.2019      expires in 0 days
18.08.2019      expires in 4 days
20.08.2019      expires in 6 days
21.08.2019      expires in 7 days
23.08.2019      expires in 9 days
24.08.2019      expires in 10 days

这将以与输入数据相同的顺序输出日期,每个日期都带有一个选项卡和一条短消息后缀。未来十一天或更长时间的日期将不会被输出。

剧本awk

BEGIN {
        FS = "."
        now = systime()/(24*60*60) - 1
}

{
        datespec = sprintf("%s %s %s 00 00 00", $3, $2, $1)
        timestamp = mktime(datespec)/(24*60*60)

        diff = timestamp - now

        if (diff < 0)
                message = sprintf("expired %d days ago", 1 - diff)
        else if (diff < 11)
                message = sprintf("expires in %d days", diff)
        else
                next

        printf("%s\t%s\n", $0, message)
}

mawkGNUawk已扩展了与日期/时间相关的函数,这里我们使用systime()UNIX 时间戳来获取当前时间(自 1970 年 1 月 1 日 00:00 以来的秒数)。我们通过除以一天中的秒数将其四舍五入为一整天。

对于每个输入行,我们使用三个点分隔的数字 来mktime()创建给定日期午夜(一天的开始)的 UNIX 时间戳,并将其四舍五入为完整天数,并将now其称为timestamp

然后就是根据它们的不同之处进行比较nowtimestamp找出要打印的内容。

请参阅 GNU 文档awkmawk了解它们如何记录systime()mktime()


通过计算相应的 UNIX 时间戳对日期进行排序,以此为日期添加前缀,按数字排序,并丢弃现在使用的时间戳(使用mawk或 GNU awk):

$ awk -F . -v OFS="\t" '{ print mktime(sprintf("%d %d %d 00 00 00", $3, $2, $1)), $0 }' file | sort -n | cut -f 2
30.07.2018
13.08.2019
14.08.2019
18.08.2019
20.08.2019
21.08.2019
23.08.2019
24.08.2019
25.08.2019
01.01.2020

相同的事情,但更简单,并且也适用于 BSD awk

$ awk -F . -v OFS="\t" '{ print sprintf("%s-%s-%s", $3, $2, $1), $0 }' file | sort | cut -f 2
30.07.2018
13.08.2019
14.08.2019
18.08.2019
20.08.2019
21.08.2019
23.08.2019
24.08.2019
25.08.2019
01.01.2020

只需将日期转换为 YYYY-MM-DD (ISO 8601 日历日期) 格式并按字典顺序排序,而不是 UNIX 时间戳。

从一开始就使用 YYYY-MM-DD 日期格式会使排序变得更加简单,但我们仍然需要进行与awk上面第一个脚本中相同的计算,以便能够(轻松地)说“这一天更不到 10 天的未来”。

答案3

如果我很好地理解你的话,我已经想到了这一点,并以@Kunasalanda 的答案为例:

这是使用 bash(或兼容的 shell)和 GNU date。它会在例如 macOS(非 GNU 日期)上失败。

today=$(date -d $(date +'%Y-%m-%d') +%s)

while read i; do
  [[ -z "$i" ]] && continue
  date_format=$(echo "$i" | awk -F'.' -vOFS='-' '{print $3,$2,$1}')
  convert_date=$(date -d "$date_format" +%s)
  if [[ "$convert_date" -ge "$today" ]]; then
    expires=$(( ("$convert_date"- "$today")/86400 ))
    [[ ! "$expires" -gt 10 ]] && echo $i expires in $expires days
  fi
done < file

输出:

14.08.2019 expires in 0 days
18.08.2019 expires in 4 days
20.08.2019 expires in 6 days

忽略空行

[[ -z "$i" ]] && continue

格式化dd.mm.yyyyyy-mm-dd能够执行操作

date_format=$(echo "$i" | awk -F'.' -vOFS='-' '{print $3,$2,$1}')

将日期转换为秒

convert_date=$(date -d "$date_format" +%s)

仅选择大于今天的日期

if [[ "$convert_date" -ge "$today" ]]; then

获取给定日期的到期天数

expires=$(( ("$convert_date"- "$today")/86400 ))

仅当到期时间不超过 10 天时输出

[[ ! "$expires" -gt 10 ]] && echo "$i" expires in "$expires" days

相关内容