Find if ISOWeek() is shared between two months [duplicate] - go

With this simple function, I can get the week number. Now, with the number of the week, how can I get the date range, started on Sunday?
import (
"fmt"
"time"
)
func main() {
Week(time.Now().UTC())
}
func Week(now time.Time) string {
_, thisWeek := now.ISOWeek()
return "S" + strconv.Itoa(thisWeek)
}

Foreword: Time.ISOWeek() returns you the week number that starts on Monday, so I will answer your question that also handles weeks starting on Monday. Alter it to your needs if you want it to work with weeks starting on Sunday.
I released this utility in github.com/icza/gox, see timex.WeekStart().
The standard library does not provide a function that would return you the date range of a given week (year+week number). So we have to construct one ourselves.
And it's not that hard. We can start off from the middle of the year, align to the first day of the week (Monday), get the week of this time value, and corrigate: add as many days as the week difference multiplied by 7.
This is how it could look like:
func WeekStart(year, week int) time.Time {
// Start from the middle of the year:
t := time.Date(year, 7, 1, 0, 0, 0, 0, time.UTC)
// Roll back to Monday:
if wd := t.Weekday(); wd == time.Sunday {
t = t.AddDate(0, 0, -6)
} else {
t = t.AddDate(0, 0, -int(wd)+1)
}
// Difference in weeks:
_, w := t.ISOWeek()
t = t.AddDate(0, 0, (week-w)*7)
return t
}
Testing it:
fmt.Println(WeekStart(2018, 1))
fmt.Println(WeekStart(2018, 2))
fmt.Println(WeekStart(2019, 1))
fmt.Println(WeekStart(2019, 2))
Output (try it on the Go Playground):
2018-01-01 00:00:00 +0000 UTC
2018-01-08 00:00:00 +0000 UTC
2018-12-31 00:00:00 +0000 UTC
2019-01-07 00:00:00 +0000 UTC
One nice property of this WeekStart() implementation is that it handles out-of-range weeks nicely. That is, if you pass 0 for the week, it will be interpreted as the last week of the previous year. If you pass -1 for the week, it will designate the second to last week of the previous year. Similarly, if you pass max week of the year plus 1, it will be interpreted as the first week of the next year etc.
The above WeekStart() function only returns the given week's first day (Monday), because the last day of the week is always its first day + 6 days.
If we also need the last day:
func WeekRange(year, week int) (start, end time.Time) {
start = WeekStart(year, week)
end = start.AddDate(0, 0, 6)
return
}
Testing it:
fmt.Println(WeekRange(2018, 1))
fmt.Println(WeekRange(2018, 2))
fmt.Println(WeekRange(2019, 1))
fmt.Println(WeekRange(2019, 2))
Output (try it on the Go Playground):
2018-01-01 00:00:00 +0000 UTC 2018-01-07 00:00:00 +0000 UTC
2018-01-08 00:00:00 +0000 UTC 2018-01-14 00:00:00 +0000 UTC
2018-12-31 00:00:00 +0000 UTC 2019-01-06 00:00:00 +0000 UTC
2019-01-07 00:00:00 +0000 UTC 2019-01-13 00:00:00 +0000 UTC

The following does the work of finding the first day of week for me, although not from week number but from time. If you add an extra parameter - for the hard-coded time.Monday - that can be any day of week, e.g. Sunday.
func weekStartDate(date time.Time) time.Time {
offset := (int(time.Monday) - int(date.Weekday()) - 7) % 7
result := date.Add(time.Duration(offset*24) * time.Hour)
return result
}
Test:
func TestWeekStartDate(t *testing.T) {
date := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
for i := 0; i < 2000; i++ {
weekStart := weekStartDate(date)
log.Printf("%s %s", date.Format("2006-01-02 Mon"), weekStart.Format("2006-01-02 Mon"))
assert.NotNil(t, weekStart)
assert.Equal(t, time.Monday, weekStart.Weekday())
date = date.Add(24 * time.Hour)
}
}
Output:
...
2021/01/17 08:50:03 2020-12-15 Tue 2020-12-14 Mon
2021/01/17 08:50:03 2020-12-16 Wed 2020-12-14 Mon
2021/01/17 08:50:03 2020-12-17 Thu 2020-12-14 Mon
2021/01/17 08:50:03 2020-12-18 Fri 2020-12-14 Mon
2021/01/17 08:50:03 2020-12-19 Sat 2020-12-14 Mon
2021/01/17 08:50:03 2020-12-20 Sun 2020-12-14 Mon
2021/01/17 08:50:03 2020-12-21 Mon 2020-12-21 Mon
2021/01/17 08:50:03 2020-12-22 Tue 2020-12-21 Mon
2021/01/17 08:50:03 2020-12-23 Wed 2020-12-21 Mon
2021/01/17 08:50:03 2020-12-24 Thu 2020-12-21 Mon
2021/01/17 08:50:03 2020-12-25 Fri 2020-12-21 Mon
2021/01/17 08:50:03 2020-12-26 Sat 2020-12-21 Mon
2021/01/17 08:50:03 2020-12-27 Sun 2020-12-21 Mon
2021/01/17 08:50:03 2020-12-28 Mon 2020-12-28 Mon
2021/01/17 08:50:03 2020-12-29 Tue 2020-12-28 Mon
2021/01/17 08:50:03 2020-12-30 Wed 2020-12-28 Mon
2021/01/17 08:50:03 2020-12-31 Thu 2020-12-28 Mon
2021/01/17 08:50:03 2021-01-01 Fri 2020-12-28 Mon
2021/01/17 08:50:03 2021-01-02 Sat 2020-12-28 Mon
2021/01/17 08:50:03 2021-01-03 Sun 2020-12-28 Mon
2021/01/17 08:50:03 2021-01-04 Mon 2021-01-04 Mon
2021/01/17 08:50:03 2021-01-05 Tue 2021-01-04 Mon
2021/01/17 08:50:03 2021-01-06 Wed 2021-01-04 Mon
2021/01/17 08:50:03 2021-01-07 Thu 2021-01-04 Mon
2021/01/17 08:50:03 2021-01-08 Fri 2021-01-04 Mon
2021/01/17 08:50:03 2021-01-09 Sat 2021-01-04 Mon
2021/01/17 08:50:03 2021-01-10 Sun 2021-01-04 Mon
2021/01/17 08:50:03 2021-01-11 Mon 2021-01-11 Mon
2021/01/17 08:50:03 2021-01-12 Tue 2021-01-11 Mon
2021/01/17 08:50:03 2021-01-13 Wed 2021-01-11 Mon
2021/01/17 08:50:03 2021-01-14 Thu 2021-01-11 Mon
2021/01/17 08:50:03 2021-01-15 Fri 2021-01-11 Mon
2021/01/17 08:50:03 2021-01-16 Sat 2021-01-11 Mon
2021/01/17 08:50:03 2021-01-17 Sun 2021-01-11 Mon
2021/01/17 08:50:03 2021-01-18 Mon 2021-01-18 Mon
2021/01/17 08:50:03 2021-01-19 Tue 2021-01-18 Mon
2021/01/17 08:50:03 2021-01-20 Wed 2021-01-18 Mon
2021/01/17 08:50:03 2021-01-21 Thu 2021-01-18 Mon
2021/01/17 08:50:03 2021-01-22 Fri 2021-01-18 Mon
2021/01/17 08:50:03 2021-01-23 Sat 2021-01-18 Mon
2021/01/17 08:50:03 2021-01-24 Sun 2021-01-18 Mon
2021/01/17 08:50:03 2021-01-25 Mon 2021-01-25 Mon
2021/01/17 08:50:03 2021-01-26 Tue 2021-01-25 Mon
2021/01/17 08:50:03 2021-01-27 Wed 2021-01-25 Mon
2021/01/17 08:50:03 2021-01-28 Thu 2021-01-25 Mon
2021/01/17 08:50:03 2021-01-29 Fri 2021-01-25 Mon
2021/01/17 08:50:03 2021-01-30 Sat 2021-01-25 Mon
2021/01/17 08:50:03 2021-01-31 Sun 2021-01-25 Mon
2021/01/17 08:50:03 2021-02-01 Mon 2021-02-01 Mon
2021/01/17 08:50:03 2021-02-02 Tue 2021-02-01 Mon
2021/01/17 08:50:03 2021-02-03 Wed 2021-02-01 Mon
2021/01/17 08:50:03 2021-02-04 Thu 2021-02-01 Mon
2021/01/17 08:50:03 2021-02-05 Fri 2021-02-01 Mon
...

Thanks to #prajwal Singh, I've found more generic to find out the start and last day of the week w.r.t month, week, and year
func DateRange(week, month, year int) (startDate, endDate time.Time) {
timeBenchmark := time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.UTC)
weekStartBenchmark := timeBenchmark.AddDate(0, 0, -(int(timeBenchmark.Weekday())+6)%7)
startDate = weekStartBenchmark.AddDate(0, 0, (week-1)*7)
endDate = startDate.AddDate(0, 0, 6)
return startDate, endDate
}

Thanks to #icza for the solution, found a way to simplify it even further in terms of logic:
func DateRange(week, year int) (startDate, endDate time.Time) {
timeBenchmark := time.Date(year, 7, 1, 0, 0, 0, 0, time.UTC)
weekStartBenchmark := timeBenchmark.AddDate(0, 0, -(int(timeBenchmark.Weekday())+6)%7)
_, weekBenchmark := weekStartBenchmark.ISOWeek()
startDate = weekStartBenchmark.AddDate(0, 0, (week-weekBenchmark)*7)
endDate = startDate.AddDate(0, 0, 6)
return startDate, endDate
}
Works fine as well.

Related

Spring boot cron expression, every 2 month

I wanted to construct a spring cron expression that executes At 23:00:00pm, on first Sunday, every 2 months starting in January, so far I have this
0 0 23 ? 1/2 SUN but this one executes At 23:00:00pm, every Sunday, every 2 months starting in January. how can I make it execute only the first Sunday every two months?
0 0 23 ? 1/2 SUN next excution dates are as follows
Sun Mar 07 23:00:00 UTC 2021
Sun Mar 14 23:00:00 UTC 2021
Sun Mar 21 23:00:00 UTC 2021
Sun Mar 28 23:00:00 UTC 2021
Sun May 02 23:00:00 UTC 2021
Sun May 09 23:00:00 UTC 2021
Sun May 16 23:00:00 UTC 2021
Sun May 23 23:00:00 UTC 2021
Sun May 30 23:00:00 UTC 2021
Sun Jul 04 23:00:00 UTC 2021
but what I wanted was
Sun Mar 07 23:00:00 UTC 2021
Sun May 02 23:00:00 UTC 2021
Sun Jul 04 23:00:00 UTC 2021
how can I improve my expression to get the above result?
0 0 23 1-7 1/2 SUN
The only part you are missing, is day number in the range 1-7.
Code to verify the schedule:
var sundays = CronExpression.parse("0 0 23 1-7 1/2 SUN");
var nextDate = LocalDateTime.now();
var dateFormatter = DateTimeFormatter.ofPattern("EEE, MMM d, yyyy 'at' hh:mm:ss a");
for (var i = 0; i < 10; i++) {
nextDate = sundays.next(nextDate);
System.out.println(nextDate.format(dateFormatter));
}
Will run at:
Sun, Mar 7, 2021 at 11:00:00 pm
Sun, May 2, 2021 at 11:00:00 pm
Sun, Jul 4, 2021 at 11:00:00 pm
Sun, Sep 5, 2021 at 11:00:00 pm
Sun, Nov 7, 2021 at 11:00:00 pm
...

Cron Job in Spring Boot

I want to automate a task quarterly in a year.
Task should execute
Jan 1st
April 1st
July 1st
October 1st
etc
As I tried #Scheduled(cron = "0 0 6 1 1/3 ?") in Spring Boot application but its not working currently and mail didn't trigger quarterly.
Try this
#scheduled(cron = "0 0 6 1 */3 *")
So read the first day of each 3 month at 6:00
Try this one -
#scheduled(cron ="0 0 0 1 JAN,APR,JUL,OCT ? *")
Next execution:
Wed Jul 01 00:00:00 UTC 2020
Thu Oct 01 00:00:00 UTC 2020
Fri Jan 01 00:00:00 UTC 2021
Thu Apr 01 00:00:00 UTC 2021
Thu Jul 01 00:00:00 UTC 2021
Fri Oct 01 00:00:00 UTC 2021
Sat Jan 01 00:00:00 UTC 2022
Fri Apr 01 00:00:00 UTC 2022
Fri Jul 01 00:00:00 UTC 2022
Sat Oct 01 00:00:00 UTC 2022

How to fetch 5th and 6th day of every month in Unix?

Is it possible to fetch 5th and/or 6th day of every month in Unix?
I've tried this but it does not give me the desired output:
echo $((($(date +%-d)-1)/5))
Use this:
for month in {1..12}; do date -d "$month/5/2017" +"%c - is a %A"; done
Output:
Thu 05 Jan 2017 12:00:00 AM CET - is a Thursday
Sun 05 Feb 2017 12:00:00 AM CET - is a Sunday
Sun 05 Mar 2017 12:00:00 AM CET - is a Sunday
Wed 05 Apr 2017 12:00:00 AM CEST - is a Wednesday
Fri 05 May 2017 12:00:00 AM CEST - is a Friday
Mon 05 Jun 2017 12:00:00 AM CEST - is a Monday
Wed 05 Jul 2017 12:00:00 AM CEST - is a Wednesday
Sat 05 Aug 2017 12:00:00 AM CEST - is a Saturday
Tue 05 Sep 2017 12:00:00 AM CEST - is a Tuesday
Thu 05 Oct 2017 12:00:00 AM CEST - is a Thursday
Sun 05 Nov 2017 12:00:00 AM CET - is a Sunday
Tue 05 Dec 2017 12:00:00 AM CET - is a Tuesday
For the 6th of the month, it's done in a similar way.
I am not sure if this is what you are looking for: i am assuming you want to get the 5th(or 6th) day of a month sitting on any day. If so, you can make use of the current date and get the 5th day of current month like below:
dd=`date '+%d'`
if(( $dd > 5 )); then
(( diff = dd - 5 ))
myDate=`date -d "-$diff days"`
else
(( diff = 5 - dd ))
myDate=`date -d "+$diff days"`
fi
echo $myDate
For 6th day you can do similarly. Above code should work on Linux distro.

Excluding root from prt_jobs

I'm making a program that will run through a printer server and cancel jobs that are hung. As of right now it outputs everything, and what I want to do is exclude root:
Output:
credjet-898837 cdd 5312512 Wed 14 Oct 2015 03:42:32 PM CDT
credjet-898839 cdd 1998848 Wed 14 Oct 2015 03:45:32 PM CDT
credjet-940485 cdd 1206272 Mon 04 Jan 2016 01:10:30 PM CST
credjet-940499 cdd 342016 Mon 04 Jan 2016 01:21:42 PM CST
credjet-940505 cdd 342016 Mon 04 Jan 2016 01:29:26 PM CST
credjet-940509 cdd 342016 Mon 04 Jan 2016 01:38:24 PM CST
credjet-940514 cdd 342016 Mon 04 Jan 2016 02:00:02 PM CST
credjet-940515 cdd 2387968 Mon 04 Jan 2016 02:00:17 PM CST
credjet-940525 cdd 2387968 Mon 04 Jan 2016 02:10:46 PM CST
credjet-940526 cdd 2387968 Mon 04 Jan 2016 02:11:01 PM CST
credjet-940528 cdd 2387968 Mon 04 Jan 2016 02:12:44 PM CST
credjet-940602 cdd 2382848 Mon 04 Jan 2016 02:26:09 PM CST
devljet-931153 siv 1798144 Fri 18 Dec 2015 02:38:30 PM CST
devljet-931157 siv 3278848 Fri 18 Dec 2015 02:47:18 PM CST
devljet-931158 siv 1538048 Fri 18 Dec 2015 02:47:18 PM CST
laser11-917719 root 78848 Wed 18 Nov 2015 09:56:47 PM CST
laser11-918257 root 78848 Thu 19 Nov 2015 09:45:23 PM CST
laser11-918262 root 79872 Thu 19 Nov 2015 09:49:30 PM CST
laser11-918263 root 78848 Thu 19 Nov 2015 09:53:45 PM CST
Expected Output:
credjet-898837 cdd 5312512 Wed 14 Oct 2015 03:42:32 PM CDT
credjet-898839 cdd 1998848 Wed 14 Oct 2015 03:45:32 PM CDT
credjet-940485 cdd 1206272 Mon 04 Jan 2016 01:10:30 PM CST
credjet-940499 cdd 342016 Mon 04 Jan 2016 01:21:42 PM CST
credjet-940505 cdd 342016 Mon 04 Jan 2016 01:29:26 PM CST
credjet-940509 cdd 342016 Mon 04 Jan 2016 01:38:24 PM CST
credjet-940514 cdd 342016 Mon 04 Jan 2016 02:00:02 PM CST
credjet-940515 cdd 2387968 Mon 04 Jan 2016 02:00:17 PM CST
credjet-940525 cdd 2387968 Mon 04 Jan 2016 02:10:46 PM CST
credjet-940526 cdd 2387968 Mon 04 Jan 2016 02:11:01 PM CST
credjet-940528 cdd 2387968 Mon 04 Jan 2016 02:12:44 PM CST
credjet-940602 cdd 2382848 Mon 04 Jan 2016 02:26:09 PM CST
devljet-931153 siv 1798144 Fri 18 Dec 2015 02:38:30 PM CST
devljet-931157 siv 3278848 Fri 18 Dec 2015 02:47:18 PM CST
devljet-931158 siv 1538048 Fri 18 Dec 2015 02:47:18 PM CST
#<= No more root
Is there a way that I can output the same information, but exclude the root jobs?
Source:
#!/local/usr/bin/ruby
require 'rubygems'
require 'net/ssh'
require 'etc'
class PrintJobs
HOST = '<server here>' #<= Left blank for security
USERNAME = Etc.getlogin
PASSWORD = nil
def scan_for_jobs
check_jobs = Net::SSH.start(HOST, USERNAME, :password => PASSWORD) do |ssh|
cmd = "prt_jobs"
res = ssh.exec!(cmd)
puts res
end
end
end
test = PrintJobs.new
test.scan_for_jobs
You can get rid of unwanted lines by using below code:
res = ssh.exec!(cmd)
res = res.split("\n").reject {|line| line.match(/\s+root\s+/)}.join("\n")
puts res
The result of exec! is one string containing the output of command issued over ssh. Hence, we need to split it by newline and iterate over the array to reject the lines containing root and re-join the array using new line.

How do you get DateTime.parse to return a time in your time zone?

I need this
require 'date'
DateTime.parse "Mon, Dec 27 6:30pm"
to return a DateTime for 6:30pm in the EDT timezone, but it returns one in UTC. How can I get a EST DateTime or convert the UTC one into an EDT DateTime with a 6:30pm value?
OK I'm going to offer an answer to my own question
require 'time'
ENV["TZ"] = "US/Eastern"
Time.parse("Mon, Dec 27 6:30pm").to_datetime
=> #<DateTime: 2011-12-27T18:30:00-05:00 (117884327/48,-5/24,2299161)>
In Rails, this worked nicely for me
DateTime.parse "Mon, Dec 27 6:30pm #{Time.zone}"
It won't work in vanilla Ruby though.
Final answer ;-)
require 'date'
estHoursOffset = -5
estOffset = Rational(estHoursOffset, 24)
date = (DateTime.parse("Mon, Dec 27 6:30pm") - (estHoursOffset/24.0)).new_offset(estOffset)
(or -4 for EDT)
DateTime#change()
You can try using change() after parsing it to alter the timezone offset:
DateTime.parse( "Mon, Dec 27 6:30pm" ).change( offset: '-0400' )
# => Wed, 27 Dec 2017 18:30:00 -0400
You can also just use the hours:
DateTime.parse( "Mon, Dec 27 6:30pm" ).change( offset: '-4' )
# => Wed, 27 Dec 2017 18:30:00 -0400
But, be careful, you cannot use an integer:
DateTime.parse( "Mon, Dec 27 6:30pm" ).change( offset: -4 )
# => Wed, 27 Dec 2017 18:30:00 +0000
If you need to determine the correct offset to use based on a time zone you can do something like this:
offset = ( Time.zone_offset('EDT') / 1.hour ).to_s
# => "-4"
DateTime.parse( "Mon, Dec 27 6:30pm" ).change( offset: offset )
# => Wed, 27 Dec 2017 18:30:00 -0400
You can also use change() to manually set other parts of the DateTime as well, like setting the hour to noon:
DateTime.parse( "Mon, Dec 27 6:30pm" ).change( offset: '-4', hour: 12 )
# => Wed, 27 Dec 2017 12:00:00 -0400
Be careful with that one because you can see that it's cleared the minutes as well.
Here's the docs for the change() method: http://api.rubyonrails.org/v5.1/classes/DateTime.html#method-i-change
If you're using Rails' ActiveSupport:
"Mon, Dec 27 6:30pm".in_time_zone(-4.hours).to_datetime
# => Mon, 27 Dec 2021 18:30:00 -0400
Time.find_zone(-4.hours).parse("Mon, Dec 27 6:30pm").to_datetime
# => Mon, 27 Dec 2021 18:30:00 -0400
If you want to use the local daylight saving time (DST) rules, you could use:
"Mon, Dec 27 6:30pm".in_time_zone("Eastern Time (US & Canada)")
# => Mon, 27 Dec 2021 18:30:00 EST -05:00
Time.find_zone("Eastern Time (US & Canada)").parse("Mon, Dec 27 6:30pm")
# => Mon, 27 Dec 2021 18:30:00 EST -05:00
Time.find_zone("Eastern Time (US & Canada)").parse("Mon, Dec 27 6:30pm").to_datetime
# => Mon, 27 Dec 2021 18:30:00 -0500
Time.find_zone("Eastern Time (US & Canada)").parse("Mon, Jun 27 6:30pm")
# => Sun, 27 Jun 2021 18:30:00 EDT -04:00
Time.find_zone("Eastern Time (US & Canada)").parse("Mon, Jun 27 6:30pm").to_datetime
# => Sun, 27 Jun 2021 18:30:00 -0400
Time.find_zone("EST5EDT").parse("Mon, Jun 27 6:30pm").to_datetime
# => Sun, 27 Jun 2021 18:30:00 -0400
Notice the date in June, above, is automatically set to EDT (-0400) because this date is in DST, contrary to the December date.
To force EST regardless if date is within DST or not:
Time.find_zone("EST").parse("Mon, Jun 27 6:30pm")
# => Sun, 27 Jun 2021 18:30:00 EST -05:00
Time.find_zone("EST").parse("Mon, Jun 27 6:30pm").to_datetime
# => Sun, 27 Jun 2021 18:30:00 -0500

Resources