4

I use MacOS and I have variable date in format

3.Jan.2023,
12.Nov.2017,
9.Apr.2022,
...

I need to change in

03.01.2023,
12.11.2017,
09.04.2022,
...

5 Answers 5

10

MacOS date allows you specify input and output formats for conversions:

for inp in "3.Jan.2023" "12.Nov.2017" "9.Apr.2022"; do
    date -j -f "%d.%b.%Y" "$inp" "+%d.%m.%Y"
done

According to strptime():

  • %b The month, using the locale's month names; either the abbreviated or full name may be specified.
  • %d The day of the month [01,31]; leading zeros are permitted but not required.
  • %m The month number [01,12]; leading zeros are permitted but not required.
  • %Y The year, including the century (for example, 1988).

Output:

03.01.2023
12.11.2017
09.04.2022
1
  • 1
    Note that "locale's month names" means that modifying the locale prior to executing the command depending on whether the source of the dates used the same locale as the current one. Commented Feb 15, 2023 at 11:33
6

Using any awk:

$ awk '
    BEGIN { FS=OFS="," }
    {
        split($1,d,".")
        $1 = sprintf("%02d.%02d.%04d", d[1], index("  JanFebMarAprMayJunJulAugSepOctNovDec",d[2])/3, d[3])
        print
    }
' file
03.01.2023,
12.11.2017,
09.04.2022,

That's 2 blanks before Jan.

3

You can use awk to convert from one format to the other:

awk -F. '
    BEGIN { m["Jan"]=1; m["Feb"]=2; m["Mar"]=3; m["Dec"]=12 }
    { printf "%02d.%02d.%04d,\n", $1, m[$2], $3 }
' file

The BEGIN line needs to be completed to follow the pattern for all months.

1

Assuming that the input file could be interpreted as a header-less CSV data set, we may read it with Miller (mlr) and rewrite the first field of each record with the reformatted date.

We may use strptime() to parse the existing datestamp into a Unix timestamp representation. We may then pass that timestamp straight to strftime() to present it in the wanted format.

In other words,

mlr --csv -N put '$1 = strftime(strptime($1, "%e.%b.%Y"), "%d.%m.%Y")' file

Here, put is the Miller sub-command for applying custom operations on the record stream, and $1 is the first field of the current record. The format string argument to strptime(), %e.%b.%Y, describes how the datestamp in the input should be parsed. The %b format is locale-dependent and will affect how the abbreviated month names are interpreted. Likewise, the format string argument to strftime(), %d.%m.%Y, describes how we want to format the Unix timestamp that strptime() outputs.

See the manual for the strftime library function on your system for further information about these format strings (man stftime).

The output, given the input in the question, would be

03.01.2023,
12.11.2017,
09.04.2022,

A macOS user may install Miller using e.g. Homebrew:

brew install miller
0

Using Raku (formerly known as Perl_6)

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

#OR

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

OR:

~$ raku -e 'my %months = [Z=>] <Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec>, 1..12; \
            put S/ <( (\d**1..2) \. (<alpha>**3) )> \. \d**4 \, /{sprintf q[%02d.%02d], $0, %months{$1} }/ for lines();' file

#OR

~$ raku -e 'my %months = [Z=>] <Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec>, 1..12; \
            put S[ <( (\d**1..2) \. (<alpha>**3) )> \. \d**4 \, ] = "{sprintf q[%02d.%02d], $0, %months{$1} }" for lines();'  file

Briefly, you can use Raku to swap out string months and replace with numeric (Integer). Days can be formatted as well (with sprintf). Declare a hash %months which has 3-letter month names as key, assigning the numbers 1..12 as value. The [Z=>] reduction metaoperator pulls successive elements off the two lists given to it, and pairs them as key => value. The result is such that say %months.sort(*.value); returns the following:

(Jan => 1 Feb => 2 Mar => 3 Apr => 4 May => 5 Jun => 6 Jul => 7 Aug => 8 Sep => 9 Oct => 10 Nov => 11 Dec => 12)

Once you have the %months hash, it's a simple s/// substitution (or S/// substitution, as the case may be). In the first two code examples, the -pe autoprinting flags are used. The match in the left-half of the s/// recognizes all elements, but the <(...)> "Capture Markers" drop everything outside, so that only the day and string month are captured (day into $0; month into $1). In the right half of the s/// operator, the %months hash is fed the $1 variable key like so: %months{$1}, which tells the hash to return the key's value. In the output both day and month are formatted with sprintf.

The third and fourth code examples are similar to the first, except that the %months hash is only declared once. Instead of -pe or -ne command-line flags, the lines are looped through using for lines(). Finally, Raku takes care not to over-write immutable strings, so the S/// capital-S form is used to return the resultant string.

Sample Output (all code examples):

03.01.2023,
12.11.2017,
09.04.2022,

https://raku.org

You must log in to answer this question.

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