4

I understand the command date -d 'last-monday - 14 days' +%Y%m%d will print the Monday two weeks ago from "Today's date". I need a way to test this for different dates and see the result. Almost like I need to mention the date command to do calculations off a relative date.

I need something to test with different dateslike :

date -d 'last-monday - 14 days' %Y%m%d from 20190315

date -d 'last-monday - 14 days' %Y%m%d from 20180217

date -d 'last-monday - 14 days' %Y%m%d from 201700914

and see the respective outputs.

1
  • You'd want a way to get (respectively to your example) results such as: 20190225 and 20180129 and 20170828, am I correct ?
    – LL3
    Commented Mar 20, 2019 at 22:24

6 Answers 6

5
#!/bin/bash

# Our given dates
dates=(
    20190315
    20180217
    20170914
)

# Loop over the given dates
for thedate in "${dates[@]}"; do
    # Get day of the week as digit (1 is Monday, 7 is Sunday)
    day=$( date -d "$thedate" +%u )
    # The Monday the same week is $(( day - 1 )) days earlier than the given date.
    # The Monday two weeks earlier is 14 days earlier still.

    date -d "$thedate -$(( day - 1 + 14 )) days" +"$thedate --> %Y%m%d"
done

Output:

20190315 --> 20190225
20180217 --> 20180129
20170914 --> 20170828

The difficult bit about this is to figure out how to construct the correct --date or -d string for GNU date to compute the final date. I opted for computing the day of week of the given date, and then using that to compute a date string that offsets the given date by a number of days so that the resulting date is the Monday two weeks earlier.

The actual strings that ends up being used for the option argument to -d in the above script, using the dates given in the script, are

20190315 -18 days
20180217 -19 days
20170914 -17 days

Condensing the script into a single command that does the computation for a single date in $thedate:

date -d "$thedate -$(date -d "$thedate" +%u) days -13 days" +%Y%m%d

or

date -d "$thedate -$(date -d "$thedate" +"-%u days -13 days")" +%Y%m%d
0
5
$ date
Wed Mar 20 15:02:23 MST 2019
$ date -d "last-monday"
Mon Mar 18 00:00:00 MST 2019
$ date -d "last-monday - 1 week"
Mon Mar 11 00:00:00 MST 2019
2
  • 3
    The issue seems to be finding the date of the Monday two weeks before a particular date.
    – Kusalananda
    Commented Mar 20, 2019 at 22:16
  • 3
    Not an answer to the original question, but definitely what I was looking for. Useful to know the date-command can do maths like this. Commented Jan 20, 2021 at 9:01
5

On at least Debian there is a faketime package

faketime '2019-03-15' date
Fri 15 Mar 00:00:00 GMT 2019

faketime '2019-03-15' date --date 'last monday - 14 days'
Mon 25 Feb 00:00:00 GMT 2019
0
0

The overkill solution:

Create a bare-bones virtual machine.  Run it in single user mode, so no background services are running.  Just to be sure, double-check that NTP is not running.

Then set the date to whatever you want to set it to, and do your tests.

0

I tried to take a "reusable tool" approach.

My monday script:

#!/bin/bash
date=${1:-now}
shift
dow=`date +%u -d "$date"`
dow0=$[ $dow - 1 ]
date -d "$date -$dow0 days" "$@"

How it works:

  • get date -d argument from first command line argument, e.g. "2020-01-01 -2 weeks"
  • additional args are passed on to the final date command, e.g. to format with +%F
  • get the day of week with %u (1..7); 1 is Monday
  • subtract 1 as we want the range 0 to 6.
  • show the date for the given date, minus the "day of the week" offset

A similar sunday script, which turns out to be a little simpler:

#!/bin/bash
date=${1:-now}
shift
dow=`date +%w -d "$date"`
date -d "$date -$dow days" "$@"

Usage:

# monday 
Mon Aug 29 02:28:45 PM AEST 2022
# sunday now +%F
2022-08-28
# monday '2022-12-01' +%F
2022-11-28
-1

Use the date command's -s/--set option:

# date
Wed Mar 20 23:02:18 CET 2019
# date -s '20190315' > /dev/null; date -d 'last-monday - 14 days'
Mon Feb 25 00:00:00 CET 2019

Depending on your system, you might have to prefix with sudo or reset afterwards via ntpdate

6
  • 2
    Setting the system's clock seems like overkill just to get the correct output. It may also confuse certain services and scheduled tasks, and other users of the system if it's a multi-user system.
    – Kusalananda
    Commented Mar 20, 2019 at 22:09
  • @Kusalananda I agree, in principle, but most modern systems will automatically resync via ntp anyway. When I tested this, date returned the correct time immediately afterwards; hence the ; instead of using two separate commands to show what I'm doing. But if you really don't want to touch your system time, there does exist a faketime package.
    – Entropy0
    Commented Mar 20, 2019 at 22:19
  • The ntpd daemon won't resync if you exceed its the 300 second variance. Commented Mar 20, 2019 at 22:44
  • Then there is something else going on in my system (Mint 19), because 5d > 300s and as I said: running above command didn't affect anything beyond the commands in the current line. In fact: # date -s '19700102' ; date ; sleep 1 ; date yields Fri Jan 2 00:00:00 CET 1970 Fri Jan 2 00:00:00 CET 1970 Wed Mar 20 23:49:26 CET 2019
    – Entropy0
    Commented Mar 20, 2019 at 22:49
  • Maybe you're running ntpdate from cron (ugh), or perhaps chrony has a larger leeway. Or there again there's whatever the systemd borg has done to NTP. Commented Mar 20, 2019 at 22:56

You must log in to answer this question.

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