How to cut the first Sunday to Saturday of each month in a year? - bash
We have green zone logic where the job has to run only between first Sunday to Saturday, i.e. 7 days starting from first Sunday of every month. I'm using the below awk command to get that, but somewhere it is breaking. I'm just trying for first 3 months i.e Jan to March
seq 75 | awk ' BEGIN {ti=" 0 0 0"}
function dtf(fmt,dy) { return strftime(fmt,mktime("2020 1 " dy ti)) }
{ day=dtf("%A %F",$0);mm=dtf("%m",$0);if(day~/Sunday/ || a[mm]) a[mm]++ ; if(a[mm]<8) print day } '
My output is below, which is incorrect:
Wednesday 2020-01-01
Thursday 2020-01-02
Friday 2020-01-03
Saturday 2020-01-04
Sunday 2020-01-05
Monday 2020-01-06
Tuesday 2020-01-07
Wednesday 2020-01-08
Thursday 2020-01-09
Friday 2020-01-10
Saturday 2020-01-11
Saturday 2020-02-01
Sunday 2020-02-02
Monday 2020-02-03
Tuesday 2020-02-04
Wednesday 2020-02-05
Thursday 2020-02-06
Friday 2020-02-07
Saturday 2020-02-08
Sunday 2020-03-01
Monday 2020-03-02
Tuesday 2020-03-03
Wednesday 2020-03-04
Thursday 2020-03-05
Friday 2020-03-06
Saturday 2020-03-07
Expected output:
Sunday 2020-01-05
Monday 2020-01-06
Tuesday 2020-01-07
Wednesday 2020-01-08
Thursday 2020-01-09
Friday 2020-01-10
Saturday 2020-01-11
Sunday 2020-02-02
Monday 2020-02-03
Tuesday 2020-02-04
Wednesday 2020-02-05
Thursday 2020-02-06
Friday 2020-02-07
Saturday 2020-02-08
Sunday 2020-03-01
Monday 2020-03-02
Tuesday 2020-03-03
Wednesday 2020-03-04
Thursday 2020-03-05
Friday 2020-03-06
Saturday 2020-03-07
How can I adjust the awk command to get the expected output?
Any other solutions using other bash tools are also welcome.
I suggest the following alternative to awk:
#! /usr/bin/env bash
for month in {01..03}; do
for day in {01..13}; do
date -d "2020-$month-$day" '+%A %F'
done |
grep -A6 -m1 -F Sunday
done
The script is not very efficient, but does the job. For each month, we simply print the dates of the 13 first days in that month. We know that the green zone has to be in that area, therefore we do not need the remaining days of the month.
The date format is Weekday YYYY-MM-DD. We use grep to find and print the first Sunday, print the 6 days behind that Sunday (-A6) and exit because we limited the search to one match (-m1).
The procedure described above is done for each of the months 1 to 3.
Here's a simple way to get GNU awk to create a list of dates and day names for any given year:
$ cat tst.awk
BEGIN {
year = (year == "" ? 2020 : year)
beg = mktime(year " 1 1 12 0 0")
for (i=0; i<=400; i++) {
dateday = strftime("%F %A", beg+24*60*60*i)
split(dateday,d,/[ -]/)
if ( d[1] != year ) {
break
}
print d[1], d[2], d[3], d[4]
}
}
.
$ awk -f tst.awk | head -20
2020 01 01 Wednesday
2020 01 02 Thursday
2020 01 03 Friday
2020 01 04 Saturday
2020 01 05 Sunday
2020 01 06 Monday
2020 01 07 Tuesday
2020 01 08 Wednesday
2020 01 09 Thursday
2020 01 10 Friday
2020 01 11 Saturday
2020 01 12 Sunday
2020 01 13 Monday
2020 01 14 Tuesday
2020 01 15 Wednesday
2020 01 16 Thursday
2020 01 17 Friday
2020 01 18 Saturday
2020 01 19 Sunday
2020 01 20 Monday
I'm starting at noon and looping from 0 to 400 days and breaking when the year changes just so I don't have to try to accommodate DST or leap years or leap seconds in the determination of days in the year in a more accurate calculation.
Just add some code to test for the current month being different from the previous and the current day name being a Sunday and print 7 days starting there, e.g.:
$ cat tst.awk
BEGIN {
year = (year == "" ? 2020 : year)
beg = mktime(year " 1 1 12 0 0")
for (i=0; i<=400; i++) {
dateday = strftime("%F %A", beg+24*60*60*i)
split(dateday,d,/[ -]/)
if ( d[1] != year ) {
break
}
dayName[d[2]+0][d[3]+0] = d[4]
}
for (monthNr=1; monthNr<=3; monthNr++) {
for (dayNr=1; dayNr in dayName[monthNr]; dayNr++) {
if (dayName[monthNr][dayNr] == "Sunday") {
for (i=0; i<7; i++) {
printf "%s %04d-%02d-%02d\n", dayName[monthNr][dayNr+i], year, monthNr, dayNr+i
}
break
}
}
}
}
.
$ awk -f tst.awk
Sunday 2020-01-05
Monday 2020-01-06
Tuesday 2020-01-07
Wednesday 2020-01-08
Thursday 2020-01-09
Friday 2020-01-10
Saturday 2020-01-11
Sunday 2020-02-02
Monday 2020-02-03
Tuesday 2020-02-04
Wednesday 2020-02-05
Thursday 2020-02-06
Friday 2020-02-07
Saturday 2020-02-08
Sunday 2020-03-01
Monday 2020-03-02
Tuesday 2020-03-03
Wednesday 2020-03-04
Thursday 2020-03-05
Friday 2020-03-06
Saturday 2020-03-07
There are slightly more efficient ways to do it but the above is clear and simple and will run in the blink of an eye.
A (rather wordy - I don't have time to make it shorter:-) ) Perl solution:
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
use Time::Piece;
use Time::Seconds;
my $year = shift || localtime->year;
first_week($year, $_) for 1 ..12;
sub first_week {
my ($yr, $mn) = #_;
$mn = sprintf '%02d', $mn;
# Use midday to avoid DST issues
my $start = Time::Piece->strptime(
"$year-$mn-01 12:00:00",
'%Y-%m-%d %H:%M:%S'
);
$start += ONE_DAY while $start->day ne 'Sun';
for (1 .. 7) {
say $start->strftime('%A %Y-%m-%d');
$start += ONE_DAY;
}
}
Try this
for i in $(seq 12); do cal ${i} 2020 | awk -v month=${i} 'NF==7 && !/^Su/{ for (j=0;j<=6;j++){print "2020-"month"-"$1+j;}exit}'
EDIT : Updated code for printing day
for i in $(seq 2); do cal ${i} 2020 | awk -v month=${i} 'NF==7 && !/^Su/{for (j=0;j<=6;j++){print strftime("%A %F", mktime("2020 " month " " $1+j " 0 0 0"))}exit}'; done;
Demo for Jan and Feb
$for i in $(seq 2); do cal ${i} 2020 | awk -v month=${i} 'NF==7 && !/^Su/{a[0]="Sunday";a[1]="Monday";a[2]="Tuesday";a[3]="Wednesday";a[4]="Thursday";a[5]="Friday";a[6]="Saturday";for (j=0;j<=6;j++){print a[j]" " "2020-"month"-"$1+j}exit}'; done;
Sunday 2020-1-5
Monday 2020-1-6
Tuesday 2020-1-7
Wednesday 2020-1-8
Thursday 2020-1-9
Friday 2020-1-10
Saturday 2020-1-11
Sunday 2020-2-2
Monday 2020-2-3
Tuesday 2020-2-4
Wednesday 2020-2-5
Thursday 2020-2-6
Friday 2020-2-7
Saturday 2020-2-8
$
With Perl, using DateTime
use warnings;
use strict;
use feature 'say';
use DateTime;
my $dt = DateTime->new(year => 2020, month => 1, day => 1);
my $first_sunday = 7 - $dt->day_of_week + 1; # day of month for first Sun
while (1) {
my $day = $dt->day;
if ($day >= $first_sunday and $day < $first_sunday + 7) {
say $dt->ymd, " (", $dt->day_abbr, ")";
}
}
continue {
$dt->add(days => 1);
if ($dt->day == 1) { # new month
last if $dt->month > 3;
$first_sunday = 7 - $dt->day_of_week + 1;
}
}
This keeps a state (on the first in a month in finds out what day the first Sunday is), what is quite suitable if the program is meant to generate and go through all dates from the span of interest.
On the other hand, the program may need to check for a given day; perhaps it runs daily and needs to check for that day. Then it is simpler to see whether the day is between the first and second Sunday in the month
my $dt = DateTime->today;
while ( $dt->add(days => 1)->month <= 3) {
if ($dt->day_of_week == 7) { # it's a Sunday
if ($dt->weekday_of_month == 1) { # first Sunday in the month
say $dt->ymd, " (", $dt->day_abbr, ")";
}
}
else {
my $sdt = $dt->clone; # preserve $dt
$sdt->subtract( $dt->day_of_week ); # drop to previous Sunday
if ($sdt->weekday_of_month == 1) { # was first Sunday in the month
say $dt->ymd, " (", $dt->day_abbr, ")";
}
}
}
The while loop around the code is there to facilitate a check.
For days other than Sunday we drop to the past Sunday, to check whether that was the first Sunday in the month. If so, then our day is within the required interval. If the day is a Sunday we only need to check whether it is the first one in the month.
The code can be made a bit more efficient and concise if that matters
if ( (my $dow = $dt->day_of_week) == 7) {
if ($dt->weekday_of_month == 1) {
say $dt->ymd, " (", $dt->day_abbr, ")";
}
}
elsif ( $dt->clone->subtract(days => $dow)->weekday_of_month == 1 ) {
say $dt->ymd, " (", $dt->day_abbr, ")";
}
... on the account of readability.
$ printf "%s\n" 2020-{01..03}-01 \
| xargs -I{} date -d "{}" "+{} %u" \
| join -j3 - <(seq 0 6) \
| xargs -n3 sh -c 'date -d "$1 + 7 days - $2 days + $3 days" "+%A %F"' --
There is some nasty stuff in here, but I'll try to explain. The idea is to compute the day of the week of the first day of the month (assume u). If you know that, you know directly which day is the first Sunday (7-u days later). So from that point forward you only need to compute the next 6 days.
Use brace expansion to generate the months you are interested in
Use xargs to compute the day of the week and output it as YYYY-MM-DD u
Per day, we want to create a list of 7 strings YYYY-MM-DD u d where d runs from 0 to 6. For this we use a nasty join hack. By telling join to join to files on a non-existing field, we create an outer product.
Use xargs in combination with sh to create a command that accepts 3 arguments and do the computation.
This method is now easily expanded to other months and years:
$ printf "%s\n" 20{20..30}-{01..12}-01 | xargs ...
The above looks a bit messy, and you might be more interested in the loop version:
for yyyymm in {2020..2030}-{01..03}; do
u=$(date -d "$yyyymm-01" "+%u");
for ((dd=7-u;dd<14-u;++dd)); do
date -d "$yyyymm-01 + $dd days" "+%A %F"
done
done
Previous solution:
This is for the first 3 months of 2020:
$ printf "%s\n" 2020-{01..03}-{01..13} \
| xargs -n1 -I{} date -d '{}' '+%A %F' \
| awk -F"[- ]" '/Sun/{a[$3]++} a[$3]==1'
This is for the first years 2020 till 2030
$ printf "%s\n" 20{20..30}-{01..12}-{01..13} \
| xargs -n1 -I{} date -d '{}' '+%A %F' \
| awk -F"[- ]" '/Sun/{a[$2,$3]++} a[$2,$3]==1'
This is understood in 3 steps:
Use brace-expansion to create a list of the first 13 days of months and years you are interested in. This works nicely because the bash starts expanding left to right. This means that the day is the fast-running index. We ask for the first 13 days, because we know that the first Sunday must be within the first 7 days.
Convert the days to the expected format using xargs and date
Use awk to do the filtering.
By adding one more condition, I'm able to make it work. a[mm]<8 && a[mm]>0
seq 75 | awk '
BEGIN { ti=" 0 0 0" }
function dtf(fmt,dy) {
return strftime(fmt,mktime("2020 1 " dy ti))
}
{ day=dtf("%A %F",$0);
mm=dtf("%m",$0);
if(day~/Sunday/ || a[mm]) a[mm]++ ;
if(a[mm]<8 && a[mm]>0 ) print day
}'
Output:
Sunday 2020-01-05
Monday 2020-01-06
Tuesday 2020-01-07
Wednesday 2020-01-08
Thursday 2020-01-09
Friday 2020-01-10
Saturday 2020-01-11
Sunday 2020-02-02
Monday 2020-02-03
Tuesday 2020-02-04
Wednesday 2020-02-05
Thursday 2020-02-06
Friday 2020-02-07
Saturday 2020-02-08
Sunday 2020-03-01
Monday 2020-03-02
Tuesday 2020-03-03
Wednesday 2020-03-04
Thursday 2020-03-05
Friday 2020-03-06
Saturday 2020-03-07
As a additional note, though I hardcoded 1 for the month, when the day parameter is >31 mktime() just moves to the next month. So in a way you can pass julian day to mktime with month set to 1.
echo -e "1\n31\n32\n60\n61\n366" | awk '
BEGIN { ti=" 0 0 0" }
function dtf(fmt,dy) {
return strftime(fmt,mktime("2020 1 " dy ti))
}
{
day=dtf("%A %F",$0);
j=dtf("%j",$0);
print j,day
}'
Output:
001 Wednesday 2020-01-01
031 Friday 2020-01-31
032 Saturday 2020-02-01
060 Saturday 2020-02-29
061 Sunday 2020-03-01
366 Thursday 2020-12-31
Related
List months ending on the same day of the week
This is an assignment. I have the following code: #! /bin/bash y=$1 if [ -z $1 ] # if year is not specified use the current year then y=(`date +%Y`) fi for m in {1..12}; do if [ $m -eq 12 ] # december exception then echo $(date -d $m/1/$y +%b) - $(date -d "$(($m%12+1))/1/$y" +%A) break fi echo $(date -d $m/1/$y +%b) - $(date -d "$(($m%12+1))/1/$y - 1 days" +%A) # print the last day of the week for the month done It lists the last day of the week for every month: Jan - Monday Feb - Monday Mar - Thursday Apr - Saturday May - Tuesday Jun - Thursday Jul - Sunday Aug - Wednesday Sep - Friday Oct - Monday Nov - Wednesday Dec - Saturday Now I need to reverse it, so that it lists months ending on every day of the week like so: Sunday - Jul Monday - Jan Feb Oct Tuesday - May Wednesday - Aug Nov Thursday - Mar Jun Friday - Sep Saturday - Apr Dec I'm thinking of a nested loop, for d in {1..7}; And storing months in an array?
#! /usr/bin/env bash # if year is not specified use the current year declare -r year="${1:-$(date +%Y)}" # associative array (aka hash table) declare -A months_per_day=() for m in {01..12}; do day_month=$(LANG=C date -d "${year}-${m}-01 +1 month -1 day" +"%A %b") months_per_day[${day_month% *}]+=" ${day_month#* }" done for day in Sunday Monday Tuesday Wednesday Thursday Friday Saturday; do echo "${day} -${months_per_day[${day}]:-}" done Output: Sunday - Jul Monday - Jan Feb Oct Tuesday - May Wednesday - Aug Nov Thursday - Mar Jun Friday - Sep Saturday - Apr Dec
Using GNU awk for time functions, with lots of intermediate and descriptively named variables to make it easy to understand: $ cat tst.sh #!/usr/bin/env bash awk -v year="$1" ' BEGIN { OFS = " - " year = (year == "" ? strftime("%Y") : year) secsInDay = 24*60*60 for ( mthNr=1; mthNr<=12; mthNr++ ) { lastDayEpochSecs = mktime(year " " (mthNr+1) " 1 12 0 0") - secsInDay mthAbbrDayName = strftime("%b %A", lastDayEpochSecs) split(mthAbbrDayName,m) mthAbbr = m[1] dayName = m[2] mthNr2mthAbbr[mthNr] = mthAbbr mthAbbr2dayName[mthAbbr] = dayName dayName2mthAbbrs[dayName] = \ (dayName in dayName2mthAbbrs ? dayName2mthAbbrs[dayName] " " : "" ) mthAbbr } for ( mthNr=1; mthNr<=12; mthNr++ ) { mthAbbr = mthNr2mthAbbr[mthNr] dayName = mthAbbr2dayName[mthAbbr] print mthAbbr, dayName } print "\n--------\n" for ( dayName in dayName2mthAbbrs ) { mthAbbrs = dayName2mthAbbrs[dayName] print dayName, mthAbbrs } } ' $ ./tst.sh Jan - Monday Feb - Monday Mar - Thursday Apr - Saturday May - Tuesday Jun - Thursday Jul - Sunday Aug - Wednesday Sep - Friday Oct - Monday Nov - Wednesday Dec - Saturday -------- Tuesday - May Friday - Sep Sunday - Jul Thursday - Mar Jun Saturday - Apr Dec Monday - Jan Feb Oct Wednesday - Aug Nov The above will be much faster than calling date multiple times in a shell loop and is trivial to modify to do anything else you need.
This answer refactors your implementation as a reusable getlastday function. Then, we go through the loop 7 times matching the getlastday to the matching months, and print it out: #!/bin/bash getlastday() { if [ $m -eq 12 ]; then echo $(date -d "$(($m%12+1))/1/$y" +%A) return fi echo $(date -d "$(($m%12+1))/1/$y - 1 days" +%A) # print the last day of the week for the month } y=$1 if [ -z $1 ] # if year is not specified use the current year then y=(`date +%Y`) fi for d in Monday Tuesday Wednesday Thursday Friday Saturday Sunday; do months=() for m in {1..12}; do lastday=$(getlastday) if [ $lastday != $d ]; then continue; fi months+=($(date -d $m/1/$y +%b)) done echo $d - ${months[#]} done [EDIT: version 2] As per #markp-fuso's comment below the above has an inefficiency in that there are 84 iterations to build the list. The following is an improvement where we construct an res result array with 7 entries in it. One for each day in the week. Then we use +%u to get the day number instead of the day string. This will give us the index to the res result append where to append the month to: #!/bin/bash y=$1 if [ -z $1 ] # if year is not specified use the current year then y=(`date +%Y`) fi res=("Sunday -" "Monday -" "Tuesday -" "Wednesday -" "Thursday -" "Friday -" "Saturday -") for m in {1..12}; do mstr=$(date -d $m/1/$y +%b) if [ $m -eq 12 ]; then d=$(date -d "$(($m%12+1))/1/$y" +%u) else d=$(date -d "$(($m%12+1))/1/$y - 1 days" +%u) fi res[$d]="${res[$d]} $mstr" done for d in {0..6}; do echo "${res[$d]}" done
Using jq your task can be solved: INPUT=' Jan - Monday Feb - Monday Mar - Thursday Apr - Saturday May - Tuesday Jun - Thursday Jul - Sunday Aug - Wednesday Sep - Friday Oct - Monday Nov - Wednesday Dec - Saturday ' jq -Rrs ' (split("\n") | map(split(" - "))) as $input | # split lines and split each line by "-" reduce ("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday") as $day # iterate over all weekdays ([]; . + [ ($input | map(select(.[1] == $day)) | # select entries for $day .[0][1] + " - " + (map(.[0]) | join(" "))) # generate output for $day ] ) | .[] ' <<< "$INPUT" Output Sunday - Jul Monday - Jan Feb Oct Tuesday - May Wednesday - Aug Nov Thursday - Mar Jun Friday - Sep Saturday - Apr Dec
Try this Shellcheck-clean code: #! /bin/bash -p year=${1-$(date +%Y)} months=( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ) day_months=( 'Sunday -' 'Monday -' 'Tuesday -' 'Wednesday -' 'Thursday -' 'Friday -' 'Saturday -' ) for m in {1..12}; do last_day=$(date -d "$year-$m-1 +1 month -1 day" +%w) day_months[last_day]+=" ${months[m-1]}" done printf '%s\n' "${day_months[#]}"
declare -A a for i in '1/1 + 1 year' {2..12}/1; do d=($(LC_ALL=C date '+%A %B' -d "$i - 1 second")) a["$d"]+=${a["$d"]:+ }${d[1]} done for i in {Sun,Mon,Tues,Wednes,Thurs,Fri,Satur}day; do echo "$i - ${a["$i"]}" done This prints the list for the current year: Sunday - July Monday - January February October Tuesday - May Wednesday - August November Thursday - March June Friday - September Saturday - December April
technically the only info you'll need is what Jan ends on, and either (a) whether year is leap or not, or (b) whether Feb ends on same day of week as Jan. The rest are easily deduced without running date 12 times
Using ksh93: #!/bin/ksh93 # if year is not specified use the current year year="${1:-$(date +%Y)}" typeset -A days for month in Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec do lastday=$(printf "%(%A)T" "final day $month $year") days[$lastday]+=" $month" done for day in Sunday Monday Tuesday Wednesday Thursday Friday Saturday do printf "%-9s -%s\n" "$day" "${days[$day]:-}" done For 2022 produces the following output: Sunday - Jul Monday - Jan Feb Oct Tuesday - May Wednesday - Aug Nov Thursday - Mar Jun Friday - Sep Saturday - Apr Dec For 2023 produces the following output: Sunday - Apr Dec Monday - Jul Tuesday - Jan Feb Oct Wednesday - May Thursday - Aug Nov Friday - Mar Jun Saturday - Sep
fetch date and covert into weekday
i have data below in csv Date 2022-06-09 22:30:20 2022-06-10 15:55:21 2022-06-11 00:34:05 2022-06-11 19:51:52 2022-06-13 11:34:10 2022-06-15 03:59:54 2022-06-18 16:13:20 2022-06-19 00:24:21 2022-06-19 00:25:36 2022-06-19 00:25:36 2022-06-19 00:25:49 i required output in 2 fields as weekday and shift time, if hh:mm is between 7:30AM to 7:30PM it should be print as morning, remaining will be print as Night. date | Weekday | Shift -------------------------------------------------------------- 09-06-2022 22:30 | Thursday | Night 10-06-2022 15:55 | Friday | Morning 11-06-2022 00:34 | Saturday | Night 11-06-2022 19:51 | Saturday | Night 13-06-2022 11:34 | Monday | Morning 15-06-2022 03:59 | Wednesday | Night 18-06-2022 16:13 | Saturday | Morning 19-06-2022 00:24 | Sunday | Night 19-06-2022 00:25 | Sunday | Night 19-06-2022 00:25 | Sunday | Night 19-06-2022 00:25 | Sunday | Night I tried with below command to get weekdays and facing difficulties in shift column please help date --date="$dates" +%A
Using GNU awk: gawk 'function dayofweek(time) { gsub(/[:-]/, " ", time) return strftime("%A", mktime(time)); } BEGIN { OFS="," } NR == 1 { print "Date", "Weekday", "Shift"; next } { print substr($0, 0, length($0) - 3), dayofweek($0), $2 >= "07:30:00" && $2 <= "19:30:00" ? "Morning" : "Night" }' input.csv produces Date,Weekday,Shift 2022-06-09 22:30,Thursday,Night 2022-06-10 15:55,Friday,Morning 2022-06-11 00:34,Saturday,Night 2022-06-11 19:51,Saturday,Night 2022-06-13 11:34,Monday,Morning 2022-06-15 03:59,Wednesday,Night 2022-06-18 16:13,Saturday,Morning 2022-06-19 00:24,Sunday,Night 2022-06-19 00:25,Sunday,Night 2022-06-19 00:25,Sunday,Night 2022-06-19 00:25,Sunday,Night from your input. It trims the seconds from the date, uses GNU awk specific functions mktime() and strftime() to get the weekday from the time, and finally just compares the hours portion to the desired range to see if it's morning or night.
fetch date and covert into weekday I would use GNU AWK for this task following way, let file.txt context be Date 2022-06-09 22:30:20 2022-06-10 15:55:21 2022-06-11 00:34:05 2022-06-11 19:51:52 2022-06-13 11:34:10 2022-06-15 03:59:54 2022-06-18 16:13:20 2022-06-19 00:24:21 2022-06-19 00:25:36 2022-06-19 00:25:36 2022-06-19 00:25:49 then awk 'BEGIN{FS="-| |:"}NR==1{print "Date","Weekday"}NR>1{t=mktime($1 " " $2 " " $3 " " $4 " " $5 " " $6);print $0,strftime("%A",t)}' file.txt gives output Date Weekday 2022-06-09 22:30:20 Thursday 2022-06-10 15:55:21 Friday 2022-06-11 00:34:05 Saturday 2022-06-11 19:51:52 Saturday 2022-06-13 11:34:10 Monday 2022-06-15 03:59:54 Wednesday 2022-06-18 16:13:20 Saturday 2022-06-19 00:24:21 Sunday 2022-06-19 00:25:36 Sunday 2022-06-19 00:25:36 Sunday 2022-06-19 00:25:49 Sunday Explanation: firstly I inform GNU AWK that field separator is - or (space) or :, then for 1st line I print header, for all lines after 1st I use Time Functions, mktime converts string like YYYY MM DD HH MM SS into timestamp (number of seconds since start of epoch), then I use strftime to convert said variable into string, %A denotes full weekday name. (tested in gawk 4.2.1)
With awk (tested with GNU awk): $ awk ' BEGIN { sep = sprintf("%41s", " ") gsub(/ /, "-", sep); printf("%-19s | %-9s | %-10s\n%s\n", "Day", "Weekday", "Shift", sep) mmin = 7 * 3600 + 30 * 60 mmax = 19 * 3600 + 30 * 60 } NR > 1 { dt = $0 gsub(/-|:/, " ", dt) s = mktime(dt) dt0 = $1 " 00 00 00" gsub(/-/, " ", dt0) s0 = mktime(dt0) d = s - s0 shift = (d > mmin && d < mmax) ? "Morning" : "Night" printf("%-19s | %-9s | %s\n", $0, strftime("%A", s), shift) }' file Day | Weekday | Shift ----------------------------------------- 2022-06-09 22:30:20 | Thursday | Night 2022-06-10 15:55:21 | Friday | Morning 2022-06-11 00:34:05 | Saturday | Night 2022-06-11 19:51:52 | Saturday | Night 2022-06-13 11:34:10 | Monday | Morning 2022-06-15 03:59:54 | Wednesday | Night 2022-06-18 16:13:20 | Saturday | Morning 2022-06-19 00:24:21 | Sunday | Night 2022-06-19 00:25:36 | Sunday | Night 2022-06-19 00:25:36 | Sunday | Night 2022-06-19 00:25:49 | Sunday | Night mmin is 7:30 AM in seconds. mmax is 7:30 PM in seconds. dt is the input date - time with all - and : replaced by a space (this is the input format of mktime). s is dt converted to seconds since Epoch using mktime. dt0 and s0 are the same as dtand s but at 00:00:00. d is the time in seconds since 00:00:00. The rest is straightforward.
awk ' NR==1{ printf "%-16s | %-9s | %s\n", "Date","Weekday","Shift"; next } { "date -d \"" $0 "\" \"+%d-%m-%Y %H:%M | %A\"" | getline d gsub(/:/, "", $2); t=int($2) printf "%-28s | %s\n", d ,(t > 73000 && t < 193000) ? "Morning" : "Night" }' file.csv Date | Weekday | Shift 09-06-2022 22:30 | Thursday | Night 10-06-2022 15:55 | Friday | Morning 11-06-2022 00:34 | Saturday | Night 11-06-2022 19:51 | Saturday | Night 13-06-2022 11:34 | Monday | Morning 15-06-2022 03:59 | Wednesday | Night 18-06-2022 16:13 | Saturday | Morning 19-06-2022 00:24 | Sunday | Night 19-06-2022 00:25 | Sunday | Night 19-06-2022 00:25 | Sunday | Night 19-06-2022 00:25 | Sunday | Night
With text and string and csv support, ruby is my go to for such projects: ruby -r csv -e ' options={ :headers=>true, :converters=>:date_time} def d_or_n(dt) t=dt.strftime( "%H%M%S%N" ) st=DateTime.new(2000,1,1,7,30).strftime( "%H%M%S%N" ) et=DateTime.new(2000,1,1,19,30).strftime( "%H%M%S%N" ) t >= st && t <= et ? "Day" : "Night" end cols=[18,12,7] fmt="%*s|%*s|%*s\n" printf(fmt,cols[0],"Date".center(cols[0]), cols[1],"Weekday".center(cols[1]), cols[2], "Shift".center(cols[2])) printf("-"*(cols.sum+2)+"\n") inp=CSV.parse($<.read, **options).to_a inp[1..].each{|r| printf(fmt, cols[0], r[0].strftime("%d-%m-%Y %R "), cols[1], r[0].strftime("%A "), cols[2], d_or_n(r[0])) } ' dates.csv Prints: Date | Weekday | Shift --------------------------------------- 09-06-2022 22:30 | Thursday | Night 10-06-2022 15:55 | Friday | Day 11-06-2022 00:34 | Saturday | Night 11-06-2022 19:51 | Saturday | Night 13-06-2022 11:34 | Monday | Day 15-06-2022 03:59 | Wednesday | Night 18-06-2022 16:13 | Saturday | Day 19-06-2022 00:24 | Sunday | Night 19-06-2022 00:25 | Sunday | Night 19-06-2022 00:25 | Sunday | Night 19-06-2022 00:25 | Sunday | Night
using system() is far more light-weight than a getline : {m,g}awk '($++NF=substr("SunMonTueWedThuFriSat",1+3 * \ system("exit \140 gdate -d\42"($_)" UTC-4\42 +%w \140"), 3)) \ ($++NF=substr("MorngNight",6^($2~/^(2|0[0-6]|07:[0-2]|19:[^0-2])/),5)) 2022-06-09 22:30:20 Thu Night 2022-06-10 15:55:21 Fri Morng 2022-06-11 00:34:05 Sat Night 2022-06-11 19:51:52 Sat Night 2022-06-13 11:34:10 Mon Morng 2022-06-15 03:59:54 Wed Night 2022-06-18 16:13:20 Sat Morng 2022-06-19 00:24:21 Sun Night 2022-06-19 00:25:36 Sun Night 2022-06-19 00:25:36 Sun Night 2022-06-19 00:25:49 Sun Night
Determining week number on a 4 week cycle in bash script
what I want is a cycle of 4 weeks in a bash script My question is: How do I know this week's number in the cycle. week x monday : echo one week x+1 monday : echo two week x+2 monday : echo three week x+3 monday : echo four and again week x+4 monday : echo one and so on what I have is the epoch (UTC), Thursday, 1 January 1970 consequently (UTC), monday, 5 January 1970 (I can set this to echo 1) Any suggestions? Converting dates is no problem. Just a general idea is ok.
I think you are expecting do something like this, with GNU date, start_date=$(date -d "1970-01-05" '+%s') # Corresponding to 1 end_date=$(date -d "2017-01-02" '+%s') # Current week Number of weeks between the dates numberOfWeeks=$(( ( end_date - start_date )/(60*60*24*7) )) printf "%s\n" "$numberOfWeeks" 2452 Now to determine which week this corresponds to, do printf "The current week %s belongs to week %d" "$(date)" "$(((numberOfWeeks%4) + 1))" The current week Mon, Jan 02, 2017 4:47:09 PM belongs to week 1 For further weeks down the line, say. 4th Monday of March 2017, using the above computation, i.e. with end_date=$(date -d "2017-03-27" '+%s') printf "The week %s belongs to week %d" "$(date -d "2017-03-27")" "$(((numberOfWeeks%4) + 1))" The week Mon, Mar 27, 2017 12:00:00 AM belongs to week 1 Another example for the 3rd Monday or March 2017, end_date=$(date -d "2017-03-20" '+%s') printf "The week %s belongs to week %d" "$(date -d "2017-03-20")" "$(((numberOfWeeks%4) + 1))" The week Mon, Mar 20, 2017 12:00:00 AM belongs to week 4
You can format the date output for showing the week number: function printweek { weeknr=$(date '+%V' -d "+$1 weeks") echo "$((weeknr%4))" } # Test for week in 0 1 2 3 4 5 6 30 31 32 33; do echo "Week offset from today ${week} => $(printweek ${week})" done This will work when you start over counting each year (first week 1 again). When you want to continue counting on 1 Januari, the script will be more difficult. You can look at the solution of #Inian. Another option might be looking at the output of the last run, and add one %4 to the weeknumber of the last run.
How to find date n days in the future from past date?
I want to find a date that is 57 –working– days after an arbitrary date using date or bash. For instance, today is august 21st, reference date is july 15th, what days will be 57 working days after july 15th?
This should work to just get all days date -d '7/15/14 +57 days' To get number of work days (M..F) you can do something lazy like this #!/bin/bash days=0 for ((i=1;i>0;i++)) do future=$(date -d "7/15/14 +$i days" '+%w') ((future!=0 && future!=6)) && ((days++)) # ignore sunday (0) and saturday (6) ((days==57)) && date -d "7/15/14 +$i days" && break done e.g. > ./abovescript > Thu Oct 2 00:00:00 CDT 2014
Weird solution: day=21 mon=8 year=2014 days=4 curl -s "http://www.timeanddate.com/scripts/dateserver.php?mode=addweekdays&d1=$day&m1=$mon&y1=$year&type=add&ad=$days&atyp=0&ach=3" | sed -n 's|.*<h2>Result: \(.*\)</h2>.*|\1|p' prints Wednesday, August 27, 2014 the date after 4 working days from 2014.08.21 for the day=15 mon=7 year=2014 days=57 prints Friday, October 3, 2014
How in Bash script receive Tuesday before last Tuesday?
I know that last Tuesday: date --date="2 Tuesday" But if I need to get Tuesday before Tuesday ? -2 doesn't work. Thanks.
date --date='Tuesday - 2 weeks'
If it's tuesday it is date --date='Today - 2 weeks'