11

I'm interested in outputting a representation of the current year-quarter, as well as the year-quarter for the previous month.

If today is 2012 January 1st, I'd like to get

2012q1

and

2011q4

as the respective outputs.

11 Answers 11

11

One (kinda ugly) solution, using BASH arithmetic evaluation and the GNU date command:

echo $(date +%Y)q$(( ($(date +%-m)-1)/3+1 ))
echo $(date -d "-1 month" +%Y)q$(( ($(date -d "-1 month" +%-m)-1)/3+1 ))

Note that the %-m prevents date from 0-padding, so this will still work for August and September.

4
  • 2
    Take $(...) instead of the deprecated backticks. They can easily be nested. Commented Feb 15, 2012 at 4:45
  • 1
    just be curious: why other answers use 4 as divisor. Commented Jan 29, 2013 at 10:32
  • 2
    @LiuYan刘研: Not sure. There are 3 months in a quarter, so I think 3 is the correct divisor. (Though that confused me at first, too.)
    – smokris
    Commented Jan 29, 2013 at 15:10
  • In September %m gives 09 which bash tries to interpret as octal due to the leading 0, so this throws an error that says 09: value too great for base (error token is "09"). This can be fixed by disabling 0-padding by changing %m to %-m.
    – Matthew
    Commented Jul 17, 2015 at 22:07
8

All solutions that divide by four fail, for instance November:

% echo $(( 11/4+1 ))
3

The correct math would be:

$(( (m-1)/3 +1 ))

And as such, the quarter of current and previous month would be:

echo curr ${y}q$(((m-1)/3+1))
if [ $m = 1 ]; then
  echo prev $((y-1))q4
else
  echo prev ${y}q$(((m-2)/3+1))
fi

It's only twelve values to check, really…

% for m in {1..12}; do echo $m Q$(((m-1)/3+1)); done
1 Q1
2 Q1
3 Q1
4 Q2
5 Q2
6 Q2
7 Q3
8 Q3
9 Q3
10 Q4
11 Q4
12 Q4
1
  • 1
    +1 For "All solutions that divide by for fail". You're right! A quarter last for 3 months so the answers should be dividing by 3. Commented May 28, 2015 at 0:31
7

Use my dateutils:

dconv 2012-01-01 -f '%Y%Q'
=>
  2012Q1

The %q and %Q flags are specific to dateutils, and return the quarter as number or in the form Q<NUMBER>.

1
  • dconv now -f%Y%Q | tr Q q if you really need that Q to be lower cased. (PS: we're packaging this in Fedora with date as the prefix instead of d, so "dateconv".)
    – mattdm
    Commented May 28, 2015 at 1:36
4

Probably, there is no direct solution.

You could use awk to avoid so many back-ticks.

date +"%Y %m" | awk '{q=int($2/4)+1; printf("%sq%s\n", $1, q);}'
date +"%Y %m" | awk '{q=int($2/4);y=$1;if (q==0){q=4;y=y-1;}; printf("%sq%s\n", y, q);}'

A perl solution would be cleaner but perl and DateTime are an heavy prerequisite.

#!/usr/bin/perl

use DateTime;

my $today = DateTime->now;
print "today: " . $today->year . "q" . $today->quarter . "\n";

my $ago = DateTime->now->subtract( months=> 4);
print "some time ago: " . $ago->year . "q" . $ago->quarter . "\n"
1
  • Cool. I like the first date/awk line, since it avoids calling (and passing parameters to) date twice.
    – smokris
    Commented Feb 15, 2012 at 1:49
4

Split the format with date, calculate with awk, format with printf:

date +"%Y %m" | awk '{printf ("%4dq%1d\n", $1, ($2/4)+1)}'

Just date and bash:

echo $(date +%Yq)$(($(date +%m)/4+1))
2
  • The first line outputs 2012q0 which is incorrect.
    – smokris
    Commented Feb 15, 2012 at 4:25
  • @smokris: You're right - I picked the wrong line of my tests. Commented Feb 15, 2012 at 4:41
4

An alternative, more as a curiosity. If GNU awk is involved, date is not needed:

awk 'BEGIN{print strftime("%Y")"q"int((strftime("%-m")-1)/3)+1}'
3

Call date to retrieve the current year and month, and do the rest with arithmetic in the shell.

set $(date '+%Y %m');
this_quarter=${1}q$(($2 / 4 + 1))
if [ $2 -eq 1 ]; then
  last_month_quarter=$(($1 - 1))q4
else
  last_month_quarter=${1}q$((($2 - 1) / 4 + 1))
fi
2

Year-Quarter for this month

date +"%Yq$(expr $(expr $(date +%m) - 1) / 3 + 1)"

Year-Quarter for yester-month

date +"%Yq$(expr $(expr $(date -d '-1 month' +%m) - 1) / 3 + 1)"
2

Basic math for this quarter and last month's quarter:

y1=$(date +%Y)
m1=$(date +%m)
q1=$(( (m1 - 1) / 3 + 1))
y2=$(( y1 - (m1 == 1) ))
m2=$(( (m1 + 10) % 12 + 1 ))
q2=$(( (m2 - 1) / 3 + 1 ))
echo This Quarter: $((y1))q$q1
echo Last Month Quarter: $((y2))q$q2

The script uses the following parts:

  1. $(unix-cmd) - evaluates a command in shell script
  2. $((expr)) - evaluates a mathematical expression
  3. remapping 1..12 -> 1..4 uses the following math (m-1)/3+1
  4. evaluating the previous month uses modulo math
2

There is now the %q format to show this information.

From the coreutils-8.26 release log from November 30, 2016:

New Features
...
date now accepts the %q format to output the quarter of the year.

And yes it works!

$ date "+%q"
4
$ date "+%Yq%q"
2016q4
0

If you want the fiscal 13-week quarter that's based on the ISO week calendar, then the new handy %q isn't going to work, sadly. Here's a version of @manatwork's dateless solution with awk/strftime.

awk 'BEGIN{$x=int((strftime("%V")-1)/13)+1;print strftime("%G")"q"($x>4?4:$x)}'

The last little ternary bit handles the ISO leap-week years where the last quarter has 14 weeks.

You must log in to answer this question.

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