如何从当前日期中减去格式为“xyz/[int][int]/[int][int][int][int]”的日期?

如何从当前日期中减去格式为“xyz/[int][int]/[int][int][int][int]”的日期?

设想

我的容器的上次部署日期格式如下:

月/日/年或 2024 年 2 月 11 日。

我无法更改他们输出上次部署日期的方式。

我想从当前日期中减去上次部署日期,然后如果上次部署日期超过 2 个月,则写入文件。

示例container1-last-deploy.txt:

last deployed: jan/01/2024

我有几个这样的文件。

挑战在于月份是用字母给出的。我可以使用正则表达式来获取日期。

我想到的一个解决方案是将月份放在字典中,例如:

dict=(
  ['jan']=1
  ['feb']=2
)

但一定有更好的方法。

是否可以将字母月份转换为整数?

这是针对 macOS 上的 bash,最好是 bash 3 解决方案。

答案1

MacOS date- 将日期字符串转换为自纪元以来的秒数

date命令可以通过libc函数理解字符串格式的月份strptime(3)。我没有 macos,所以我无法测试我的答案,但根据 macosdate 男人页:

date [-jRu] -f input_fmt new_date [+output_fmt]

[...]

   -f      Use input_fmt as the format string to parse the new_date provided rather than using the
           default [[[mm]dd]HH]MM[[cc]yy][.ss] format.  Parsing is done using strptime(3).

现在您只需要了解如何从手册页中设置输入日期的格式strptime(3):

       %b or %B or %h
              The month name according to the current locale,  in  abbreviated
              form or the full name.

       %d or %e
              The day of month (1-31).

       %Y     The year, including century (for example, 1991).

所以根据这个,输入格式应该是:"%b/%e/%Y"

接下来,您需要使用以下标志date

   -j      Do not try to set the date.  This allows you to use the -f flag in addition to the + option
           to convert one date format to another.  Note that any date or time components unspecified
           by the -f format string take their values from the current time.

最后,由于您希望能够从结果中减去当前时间,因此您需要确保您的 +输出_fmt将会:

       %s     The number of seconds since the Epoch, 1970-01-01 00:00:00
              +0000 (UTC).  Leap seconds are not counted unless leap
              second support is available.

因此,将日期格式转换为纪元的结果命令应该是:

$ DATE="jan/01/2024"
$ date -jf "%b/%e/%Y" $DATE +%s
1704060000

再说一次,因为我没有 Macos,所以我无法测试这一点来确认。我也不确定它是否会接受小写的月份,但如果您必须将月份大写,您可以替换$DATE${DATE^}

为了完整起见,我还将展示如何使用以下命令执行相同的操作:

GNU date- 将日期字符串转换为自纪元以来的秒数

这更容易一些,因为 GNU 版本date给予了更多自由

从其男人页:

       The --date=STRING is a mostly free format human readable date
       string such as "Sun, 29 Feb 2004 16:21:42 -0800" or "2004-02-29
       16:21:42" or even "next Thursday".  A date string may contain
       items indicating calendar date, time of day, time zone, day of
       week, relative time, relative date, and numbers.  An empty string
       indicates the beginning of the day.  The date string format is
       more complex than is easily documented here but is fully
       described in the info documentation.

因此,从我在机器上的测试来看,您应该从输入中删除斜杠,以便date命令理解它:

$ DATE="jan/01/2024"
$ echo ${DATE//\// }
jan 01 2024
$ date --date "${DATE//\// }"  +%s
1704060000

从当前日期中减去

现在您已获得原始日期的纪元时间。只需将其存储到变量中即可。例如,在 MacOS 中:

$ ORIG_DATE=$(date -jf "%b/%e/%Y" $DATE +%s)

要查看自纪元以来的当前日期(以秒为单位):

$ date +%s
1704965965

所以现在要计算以秒为单位的差异,这很简单:

$ echo $(( $(date +%s ) - $ORIG_DATE ))
905967

答案2

您应该尝试获得确切的要求(也许您有这些要求,但您没有在此处说明)。

  • 一是月份缩写所假定的区域设置。例如,在德语区域设置中,在解析纯英语应用程序打印的日期时,几个月的名称可能会导致问题(例如,德语中的mayismaioctis okt)。

    如果使用的月份名称与当前区域设置匹配,则date -d "$(echo jan/01/2024 | tr / ' ')"可以简单地解析部署日期。

  • 另一个问题是如何计算日期差异:

    • 如果您要计算天数(大约 60 天),您可以使用+%s格式参数将部署日期和当前日期转换为秒,减去这些数字,然后检查结果是否大于 60*86400。

    • 如果您想遵守一些更复杂的规则(例如,比较月份中的某一天),您仍然可以在 bash 中执行此操作,但会变得更加困难。

答案3

使用任何date和任何awk都会执行您所描述的操作:

$ awk -v now="$(date +%m/%Y)" -v dif=2 -F'[ /]' '
    BEGIN {
        split(now,n)
        now = n[1] + (n[2] * 12)
    }
    {
        mth = ( index("janfebmaraprmayjunjulaugsepoctnovdec",$3) + 2 ) / 3
        cur = mth + ($5 * 12)
    }
    (now - cur) > dif
' file
last deployed: oct/01/2023

上面的代码是在此输入文件上运行的:

$ cat file
last deployed: oct/01/2023
last deployed: nov/01/2023
last deployed: dec/01/2023
last deployed: jan/01/2024

如果输入中的月份名称不全是小写,例如JanJAN,则只需在脚本中更改$3为。tolower($3)

答案4

使用(以前称为 Perl_6)

首先将日期转换为有效的日期ISO 8601日期:

~$ raku -pe 'BEGIN my %months = [Z=>] <Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec>, 1..12;  \
             s{ ^ .+? <( ( <.alpha>**3 ) (\/)  (\d**1..2)  (\/) (\d**4) } = "$4-{sprintf q[%02d], %months{$0.tclc}}-$2".Date;'  file
last deployed: 2024-01-01

不用担心,我们随时可以重新格式化它:

~$ raku -pe 'BEGIN my %months = [Z=>] <Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec>, 1..12;  \
             s{ ^ .+? <( ( <.alpha>**3 ) (\/)  (\d**1..2)  (\/) (\d**4) } = "$4-{sprintf q[%02d], %months{$0.tclc}}-$2".Date.mm-dd-yyyy("/");'  file
last deployed: 01/01/2024

由于这个 Raku 元素现在是 Raku日期对象,你可以对其进行数学计算:

~$ raku -pe 'BEGIN my %months = [Z=>] <Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec>, 1..12;  \
             s{ ^ .+? <( ( <.alpha>**3 ) (\/)  (\d**1..2)  (\/) (\d**4) } = Date.new(now) - "$4-{sprintf q[%02d], %months{$0.tclc}}-$2".Date ~ " days ago";'  file
last deployed: 46 days ago  

有关更多代码示例,请参阅下面的链接。

https://docs.raku.org/language/temporal.html
https://github.com/Raku/examples/tree/master/categories/cookbook/03dates-and-times
https://raku.org

相关内容