Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UNIX date: How to convert week number (date +%W) to a date range (Mon-Sun)?

I have list of week numbers extracted from huge log file, they were extracted using syntax:

$ date --date="Wed Mar 20 10:19:56 2012" +%W;
12

I want to create a simple bash function which can convert these week numbers to a date range. I suppose function should accept 2 arguments: $number and $year, example:

$ week() { ......... }
$ number=12; year=2012
$ week $number $year
"Mon Mar 19 2012" - "Sun Mar 25 2012"
like image 882
hellish Avatar asked Mar 25 '13 02:03

hellish


People also ask

How can I change the date format in Unix?

To format date in YYYY-MM-DD format, use the command date +%F or printf "%(%F)T\n" $EPOCHSECONDS . The %F option is an alias for %Y-%m-%d . This format is the ISO 8601 format.

How do I get the week number from a date in Unix?

the OP's week num is from date +%W . you can try: date --date="2020-05-17 01:00:00" +%W outputs 19 and date --date="2020-05-18 01:00:00" +%W outputs 20 . GNU date's manual: %W - week number of year, with Monday as first day of week (00.. 53) .

How do I print a date in dd mm yyyy format in Unix?

Linux date Command Format Options%D – Display date as mm/dd/yy. %Y – Year (e.g., 2020) %m – Month (01-12)


2 Answers

With GNU date:

$ cat weekof.sh
function weekof()
{
    local week=$1 year=$2
    local week_num_of_Jan_1 week_day_of_Jan_1
    local first_Mon
    local date_fmt="+%a %b %d %Y"
    local mon sun

    week_num_of_Jan_1=$(date -d $year-01-01 +%W)
    week_day_of_Jan_1=$(date -d $year-01-01 +%u)

    if ((week_num_of_Jan_1)); then
        first_Mon=$year-01-01
    else
        first_Mon=$year-01-$((01 + (7 - week_day_of_Jan_1 + 1) ))
    fi

    mon=$(date -d "$first_Mon +$((week - 1)) week" "$date_fmt")
    sun=$(date -d "$first_Mon +$((week - 1)) week + 6 day" "$date_fmt")
    echo "\"$mon\" - \"$sun\""
}

weekof $1 $2
$ bash weekof.sh 12 2012
"Mon Mar 19 2012" - "Sun Mar 25 2012"
$ bash weekof.sh 1 2018
"Mon Jan 01 2018" - "Sun Jan 07 2018"
$

NOTE:

As the OP mentions, the week number is got by date +%W. According to GNU date's manual:

%W: week number of year, with Monday as first day of week (00..53)

So:

  1. Each week starts from Mon.
  2. If Jan 1 is Mon, then the first week will be week #1.
  3. If Jan 1 is not Mon, then the first few days will be week #0 and the week #1 starts from the first Mon.
like image 141
pynexj Avatar answered Oct 12 '22 15:10

pynexj


Everything depends on the definition of week numbers you are used too.

European (ISO 8601)

This ISO 8601 standard is widely used in the world: EU and most of other European countries, most of Asia, and Oceania

The ISO 8601 standard states the following:

  • There are 7 days in a week
  • The first day of the week is a Monday
  • The first week is the first week of the year which contains a Thursday. This means it is the first week with 4 days or more in January.

With this definition, it is possible to have a week number 53. These occur with the first of January is on a Friday (E.g. 2016-01-01, 2010-01-01). Or, if the year before was a leap year, also a Saturday. (E.g. 2005-01-01)

   December 2015               January 2016        
 Mo Tu We Th Fr Sa Su CW    Mo Tu We Th Fr Sa Su CW
     1  2  3  4  5  6 49                 1  2  3 53
  7  8  9 10 11 12 13 50     4  5  6  7  8  9 10 01
 14 15 16 17 18 19 20 51    11 12 13 14 15 16 17 02
 21 22 23 24 25 26 27 52    18 19 20 21 22 23 24 03
 28 29 30 31          53    25 26 27 28 29 30 31 04

function week_range() {
    local _u _F _V
    # dow Jan 01 (Mon 01 ... Sun 07)
    _u="$(date -d "$1-01-01" "+%u")"
    # First Monday
    _F="$(date -d "$1-01-01 + $(( (8 - _u) % 7)) days" "+%F")"
    # Week number of first Monday
    _V="$(date -d "$_F" "+%V")"
    printf -- "%s-%s\n" "$(date -d "$_F + $(( 7*($2 - _V) )) days" "+%F")"       \
                        "$(date -d "$_F + $(( 7*($2 - _V) + 6 )) days" "+%F")"
}

$ week_range 2016 1; done
2016-01-04 - 2016-01-10
$ week_range 2020 1; done
2019-12-30 - 2020-01-05     << week one starts in the previous year
$ week_range 2020 20
2020-05-11 - 2020-05-17

American or Islamic (Not ISO 8601)

Not all countries use the ISO 8601 system. They use a more absolute approach. The American system is used in Canada, United States, New Zealand, India, Japan,... The Islamic system is generally used in the middle east. Both systems are very similar.

American:

  • There are 7 days in a week
  • The first day of the week is a Sunday
  • The first week starts on the 1st of January

With this definition, it is possible to have partial weeks at the beginning and the end of a year. Hence the first and last week of the year could not contain all weekdays.

    December 2015                January 2016       
 Su Mo Tu We Th Fr Sa CW     Su Mo Tu We Th Fr Sa CW
        1  2  3  4  5 49                     1  2 01
  6  7  8  9 10 11 12 50      3  4  5  6  7  8  9 02
 13 14 15 16 17 18 19 51     10 11 12 13 14 15 16 03
 20 21 22 23 24 25 26 52     17 18 19 20 21 22 23 04
 27 28 29 30 31       53     24 25 26 27 28 29 30 05
                             31                   06

function week_range() {
    local _w _F _V _d1 _d2
    # dow Jan 01 (Sun 01 ... Sat 07)
    _w="$(date -d "$1-01-01" "+%w")"
    (( _w = _w + 1 ))
    # First Saturday
    _F="$(date -d "$1-01-01 + $(( (8 - _w) % 7)) days" "+%F")"
    # Week number of first Sunday
    [[ "$_F" == "$1-01-01" ]] && _V=1 || _V=2
    # Start and end
    _d1="$(date -d "$_F + $(( 7*($2 - _V) )) days" "+%F")"
    _d2="$(date -d "$_F + $(( 7*($2 - _V) + 6 )) days" "+%F")"
    [[ "$_d1" < "$1-01-01" ]] && _d1="$1-01-01"
    [[ "$_d2" > "$1-12-31" ]] && _d2="$1-12-31"
    [[ "$_d1" > "$1-12-31" ]] && echo "invalid week number" > /dev/stderr && return
    printf -- "%s - %s\n"               \
        "$(date -d "$_d1" "+%m/%d/%Y")" \
        "$(date -d "$_d2" "+%m/%d/%Y")"
}
$ week_range 2015 53
12/27/2015 - 12/31/2015
$ week_range 2016 1
01/01/2016 - 01/02/2016
$ week_range 2020 20
05/10/2020 - 05/16/2020

Islamic:

  • There are 7 days in a week
  • The first day of the week is a Saturday
  • The first week starts on the 1st of January

With this definition, it is possible to have partial weeks at the beginning and the end of a year. Hence the first and last week of the year could not contain all weekdays.

   December 2015                 January 2016       
 Sa Su Mo Tu We Th Fr CW     Sa Su Mo Tu We Th Fr CW
           1  2  3  4 49                        1 01
  5  6  7  8  9 10 11 50      2  3  4  5  6  7  8 02
 12 13 14 15 16 17 18 51      9 10 11 12 13 14 15 03
 19 20 21 22 23 24 25 52     16 17 18 19 20 21 22 04
 26 27 28 29 30 31    53     23 24 25 26 27 28 29 05
                             30 31                06

function week_range() {
    local _w _F _V _d1 _d2
    # dow Jan 01 (Sat 01 ... Fri 07)
    _w="$(date -d "$1-01-01" "+%w")"
    (( _w = (_w + 8) % 7 + 1 ))
    # First Saturday
    _F="$(date -d "$1-01-01 + $(( (8 - _w) % 7)) days" "+%F")"
    # Week number of first Saturday
    [[ "$_F" == "$1-01-01" ]] && _V=1 || _V=2
    # Start and end
    _d1="$(date -d "$_F + $(( 7*($2 - _V) )) days" "+%F")"
    _d2="$(date -d "$_F + $(( 7*($2 - _V) + 6 )) days" "+%F")"
    [[ "$_d1" < "$1-01-01" ]] && _d1="$1-01-01"
    [[ "$_d2" > "$1-12-31" ]] && _d2="$1-12-31"
    [[ "$_d1" > "$1-12-31" ]] && echo "invalid week number" > /dev/stderr && return
    printf -- "%s - %s\n" "${_d1//-//}" "${_d2//-//}"
}

$ week_range 2015 53
2015/12/26 - 2015/12/31
$ week_range 2016 1
2016/01/01 - 2016/01/01
$ week_range 2020 20
2020/05/09 - 2020/05/15

Note: There are other methods of defining a week number. Nonetheless, the approach stays the same.

like image 44
kvantour Avatar answered Oct 12 '22 14:10

kvantour