3

It's 14.08.2019, there is a text file that contains a list of dates (dd.mm.yyyy):

30.07.2018
14.08.2019
18.08.2019
20.08.2019
01.01.2020

The dates in the list are deadlines.

What the output should look like: a list of dates that are older than the current date and dates that are going to expire in 10 days (if it expires in 1 to 10 days it's listed and if it expires today it's also listed. If it expires in 11 or more days then it's not listed).

5
  • 2
    The output format does not involve sorting in any form or shape as far as I can see at the moment, and whatever algorithm I can think of would not requirer sorting of the input data either. Why do you need sorting?
    – Kusalananda
    Commented Aug 14, 2019 at 18:10
  • Since you can sort dates in a file from oldest to newest and vise versa then there should be a way to "filter" things out. I'm actually wondering if that can be done in the command line.
    – Hank
    Commented Aug 14, 2019 at 20:21
  • Could you give an example of the output? Commented Aug 14, 2019 at 20:49
  • 1
    If you can install an additional package, dateutils will make your life easier: fresse.org/dateutils
    – Wildcard
    Commented Aug 14, 2019 at 21:37
  • You could do this as a one-liner by first sending the output through date to get the date as seconds from 1970. Pipe that to a comparison, and if more than 10 days in the future, just drop that line. You could put a sort in here if you want, and finally, use date to convert back.
    – Diagon
    Commented Mar 15, 2022 at 4:39

3 Answers 3

1

With 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'

The idea being to sort the list where we've inserted a date for yesterday and 10 days later ending in T, so for instance if run on 2019-08-14, we get:

30.07.2018
13.08.2019T
14.08.2019
18.08.2019
20.08.2019
24.08.2019T
01.01.2020

And we extract the part between those two T lines.

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

Using GNU awk, or 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

This will output the dates in the same order as the input data, with each date suffixed by a tab and a short message. Dates eleven days in the future or more will not be outputted.

The awk script:

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)
}

mawk and GNU awk has been extended with date/time-related functions, and here we are using systime() to get the current time as a UNIX timestamp (seconds since 1970-01-01 00:00). We round this to full days by dividing by the number of seconds in a day.

For each input line, we use the three dot-delimited numbers with mktime() to create a UNIX timestamp for midnight on the given date (the very start of the day) and round this to full days as with now and call it timestamp.

Then it's a matter of comparing now and timestamp and figuring out what to print depending on how they differ.

See the documentation for GNU awk or mawk for how they document systime() and mktime().


Sorting the dates by computing their corresponding UNIX timestamps, prefixing the dates by this, sorting numerically, and discarding the now used timestamps (using mawk or 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

The same thing, but simpler, and also works with 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

This works by simply converting the dates into the YYYY-MM-DD (ISO 8601 Calendar date) format and sorting lexicographically on that rather than the UNIX timestamps.

Using the YYYY-MM-DD date format from the start would have made sorting much simpler, but we would still have to do the same sort of calculations as in the first awk script above to be able to (easily) say "this day is more than 10 days into the future".

0

I have come to this, if I understood you well, and taking the answer of @Kunasalanda as example:

This is using bash (or compatible shell) and GNU date. It would fall over on e.g. macOS (non-GNU date).

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

Ouput:

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

Ignore blank lines

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

Format dd.mm.yy to yyyy-mm-dd to be able to perform operations

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

Convert date to seconds

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

Choose only dates greater than today

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

Get the days that the given date will expire

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

Output only if expiration is not greater than 10 days

[[ ! "$expires" -gt 10 ]] && echo "$i" expires in "$expires" days
1
  • 1
    You should mention that this is using bash (or compatible shell) and GNU date. It would fall over on e.g. macOS (non-GNU date).
    – Kusalananda
    Commented Aug 14, 2019 at 22:18

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .