Failing to do something fairly simple and can't find an answer
nor from uncle Google nor from here
I'm trying to get one full month in seconds (an int)
Something more elegant then this:
s := 3600 * 24 * 30
also tried :
m := time.Hour * 24 * 30
but that returns type 'time.Duration' which I also can't convert to an int
NOTE: Don't really care for the pressie days of each month (28-31)
but if it's possible to use specific month as input it will be super.
I'm not a Goxpert so Go (see what I did there?) easy on me..
Thanks in advance.
Use time.Duration Seconds() :
package main
import (
"fmt"
"time"
)
func main() {
m := time.Hour * 24 * 30
fmt.Println("In float: ", m.Seconds())
fmt.Println("In int: ", int(m.Seconds()))
}
Example on playground
If you do care about the different days in months, you can use time.Sub to get the duration:
package main
import (
"fmt"
"time"
)
func secondsInMonth(y int, m time.Month) int {
start := time.Date(y, m, 0, 0, 0, 0, 0, time.UTC)
end := time.Date(y, m+1, 0, 0, 0, 0, 0, time.UTC)
return int(end.Sub(start).Seconds())
}
func main() {
month := time.February
year := 2016
fmt.Printf("Days in %s %d: %d\n", month, year, secondsInMonth(year, month))
}
If you dont care just do 30*24*60*60. Thats as elegant as it gets.
Agree with the previous answers, but if you really want to remove the 24 * 30 it is possible.
Just goofing around; time.Time has some nice comparison features so this is another option:
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
// the current month in seconds - can vary
duration := now.AddDate(0, 1, 0).Sub(now)
fmt.Println("In int: ", int(duration.Seconds()))
// similar, but exactly 30 days
duration := now.AddDate(0, 0, 30).Sub(now)
fmt.Println("In int: ", int(duration.Seconds()))
}
Example on playground
The number of days in a month depends on the month and the year (leap or non-leap year). The number of seconds in a day varies when there is a transition to or from daylight savings time. Therefore, the number of seconds in a month will vary by month, year, and time zone.
For example, Los Angeles is in the Pacific Time Zone. January and March both have 31 days. Because of the daylight savings time transition in March, there is one hour less (3600 = 60*60 seconds) in March than January.
For example,
package main
import (
"fmt"
"time"
)
func secondsInMonth(month time.Month, year int, loc *time.Location) int {
u := time.Date(year, month, 1, 0, 0, 0, 0, loc)
v := time.Date(year, month+1, 1, 0, 0, 0, 0, loc)
return int(v.Sub(u).Seconds())
}
func main() {
loc, err := time.LoadLocation("America/Los_Angeles")
if err != nil {
fmt.Println(err)
return
}
year := 2016
jan := secondsInMonth(time.January, year, loc)
feb := secondsInMonth(time.February, year, loc)
mar := secondsInMonth(time.March, year, loc)
apr := secondsInMonth(time.April, year, loc)
fmt.Println(jan, feb, mar, apr)
fmt.Println(jan - mar)
}
Output:
2678400 2505600 2674800 2592000
3600
Since you are only having 30 day months, we have a constant here and there is absolutely no need for any calculation. Using basic maths, we simplify
30*24*60*60
to
const secsInMonth = 2592000
Related
I want to create a time.Time for an exact point in time the following day (tomorrow). For now I would like to set the hour and minute.
This is the code I use at the moment:
now := time.Now()
tomorrow := time.Date(now.Year(), now.Month(), now.Day(), 15, 0, 0, 0, time.UTC).AddDate(0,0,1)
This will create a Date for today with the exact time (hour and minute) I am looking for and then adds one day to that Date. This works fine.
Example:
Imagine time.Now() is 2009-11-10 23:00:00 +0000 UTC.
The result of the following code would be: 2009-11-10 15:00:00 +0000 UTC
tomorrow := time.Date(now.Year(), now.Month(), now.Day(), 15, 0, 0, 0, time.UTC)
To this date I add one day using AddDate(0, 0, 1). The result is then the desired time the next day: 2009-11-11 15:00:00 +0000 UTC.
See: https://play.golang.org/p/OKR9V1HN50x
Question:
Is there a shorter way to write this code?
Package time
import "time"
The month, day, hour, min, sec, and nsec values may be outside their
usual ranges and will be normalized during the conversion. For
example, October 32 converts to November 1.
This is more efficient. It minimizes calls to package time functions and methods.
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
fmt.Println(now.Round(0))
yyyy, mm, dd := now.Date()
tomorrow := time.Date(yyyy, mm, dd+1, 15, 0, 0, 0, now.Location())
fmt.Println(tomorrow)
}
Output:
2019-06-21 16:23:06.525478162 -0400 EDT
2019-06-22 15:00:00 -0400 EDT
Some benchmarks:
BenchmarkNow-8 31197811 36.6 ns/op
BenchmarkTomorrowPeterSO-8 29852671 38.4 ns/op
BenchmarkTomorrowJens-8 9523422 124 ns/op
bench_test.go:
package main
import (
"testing"
"time"
)
func BenchmarkNow(b *testing.B) {
for N := 0; N < b.N; N++ {
now := time.Now()
_ = now
}
}
var now = time.Now()
func BenchmarkTomorrowPeterSO(b *testing.B) {
for N := 0; N < b.N; N++ {
// now := time.Now()
yyyy, mm, dd := now.Date()
tomorrow := time.Date(yyyy, mm, dd+1, 15, 0, 0, 0, now.Location())
_ = tomorrow
}
}
func BenchmarkTomorrowJens(b *testing.B) {
for N := 0; N < b.N; N++ {
// now := time.Now()
tomorrow := time.Date(now.Year(), now.Month(), now.Day(), 15, 0, 0, 0, now.Location()).AddDate(0, 0, 1)
_ = tomorrow
}
}
Here below is my code to get the last complete quarter:
package main
import (
"fmt"
"time"
)
func main() {
layout := "2006-01-02T15:04:05.000Z"
str := "2017-11-30T12:00:00.000Z"
now, _ := time.Parse(layout, str)
endDate := now.AddDate(0, 0, 0-now.Day())
startDate := endDate.AddDate(0, -3, 0) // startDate is wrong: 2017-07-31
// the following statement is needed to fix startDate
if endDate.Month()-startDate.Month() == 3 {
startDate = startDate.AddDate(0, 0, 1) // now startDate is correct: 2017-08-01
}
fmt.Printf("Start date: %v\n", startDate.Format("2006-01-02"))
fmt.Printf("End date: %v\n", endDate.Format("2006-01-02"))
}
playground
Is there a better way to get the correct start date?
For instance, the last startDate = startDate.AddDate(0, 0, 1) statement has to be omitted if I want to get the last semester:
endDate := now.AddDate(0, 0, 0-now.Day())
startDate := endDate.AddDate(0, -6, 0) // startDate is correct: 2017-05-01
Why is there this difference?
Package time
import "time"
func Date
func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time
Date returns the Time corresponding to
yyyy-mm-dd hh:mm:ss + nsec nanoseconds
in the appropriate zone for that time in the given location.
The month, day, hour, min, sec, and nsec values may be outside their
usual ranges and will be normalized during the conversion. For
example, October 32 converts to November 1.
For example, using normalization to get the last complete period (for example, quarter or semester):
package main
import (
"fmt"
"time"
)
func lastPeriod(t time.Time, period time.Month) (start, end time.Time) {
y, m, _ := t.Date()
loc := t.Location()
start = time.Date(y, m-period, 1, 0, 0, 0, 0, loc)
end = time.Date(y, m, 1, 0, 0, 0, -1, loc)
return start, end
}
func main() {
layout := "2006-01-02T15:04:05.000Z"
str := "2017-11-30T12:00:00.000Z"
now, err := time.Parse(layout, str)
if err != nil {
fmt.Println(err)
return
}
const (
quarter = 3
semester = 6
)
fmt.Println("Quarter:")
start, end := lastPeriod(now, quarter)
fmt.Printf("Base date: %v\n", now.Format("2006-01-02"))
fmt.Printf("Start date: %v\n", start.Format("2006-01-02"))
fmt.Printf("End date: %v\n", end.Format("2006-01-02"))
fmt.Println("Semester:")
start, end = lastPeriod(now, semester)
fmt.Printf("Base date: %v\n", now.Format("2006-01-02"))
fmt.Printf("Start date: %v\n", start.Format("2006-01-02"))
fmt.Printf("End date: %v\n", end.Format("2006-01-02"))
}
Playground: https://play.golang.org/p/0t4exjVgr-
Output:
Quarter:
Base date: 2017-11-30
Start date: 2017-08-01
End date: 2017-10-31
Semester:
Base date: 2017-11-30
Start date: 2017-05-01
End date: 2017-10-31
When I have a time.Time:
// January, 29th
t, _ := time.Parse("2006-01-02", "2016-01-29")
How can I get a time.Time which represents January 31st? This example is trivial, but when there's a date in February, the last day might be 28th or 29th.
Package time
func Date
func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time
Date returns the Time corresponding to
yyyy-mm-dd hh:mm:ss + nsec nanoseconds
in the appropriate zone for that time in the given location.
The month, day, hour, min, sec, and nsec values may be outside their
usual ranges and will be normalized during the conversion. For
example, October 32 converts to November 1.
For example, normalizing a date,
package main
import (
"fmt"
"time"
)
func main() {
// January, 29th
t, _ := time.Parse("2006-01-02", "2016-01-29")
fmt.Println(t.Date())
// January, 31st
y,m,_ := t.Date()
lastday:= time.Date(y,m+1,0,0,0,0,0,time.UTC)
fmt.Println(lastday.Date())
}
Output:
2016 January 29
2016 January 31
You could write a function yourself, maybe something like this:
func daysInMonth(month, year int) int {
switch time.Month(month) {
case time.April, time.June, time.September, time.November:
return 30
case time.February:
if year%4 == 0 && (year%100 != 0 || year%400 == 0) { // leap year
return 29
}
return 28
default:
return 31
}
}
EDIT: since I really like measuring things:
$ go test -bench .
testing: warning: no tests to run
PASS
BenchmarkDim2-8 200000000 7.26 ns/op
BenchmarkDim-8 1000000000 2.80 ns/op // LIES!
BenchmarkTime-8 10000000 169 ns/op
BenchmarkTime2-8 10000000 234 ns/op
ok github.com/drathier/scratchpad/go 9.741s
BenchMarkDim2: not tested, but very fast.
func daysInMonthTime(month, year int) time.Time {
return time.Time{}.Add(time.Hour * 10 + time.Hour*24*30*time.Duration(month-1) + time.Second * time.Duration(daysInMonth(month, year)) * 24 * 60 + 1337)
}
BenchmarkDim: // LIES
func daysInMonth(month, year int) int {
switch time.Month(month) {
case time.April, time.June, time.September, time.November:
return 30
case time.February:
if year%4 == 0 && (year%100 != 0 || year%400 == 0) {
// leap year
return 29
}
return 28
default:
return 31
}
}
BenchmarkTime:
func timeDaysInMonth() time.Time {
// January, 29th
t, _ := time.Parse("2006-01-02", "2016-01-29")
y, m, _ := t.Date()
lastday := time.Date(y, m+1, 0, 0, 0, 0, 0, time.UTC)
return lastday
}
BenchmarkTime2
func time2daysinmonth() time.Time {
t, _ := time.Parse("2006-01-02", "2016-01-01")
t = t.AddDate(0, 1, 0).AddDate(0, 0, -1)
return t
}
I've used something similar to this in my own code:
func lastDayOfTheMonth(year, month int) time.Time {
if month++; month > 12 {
month = 1
}
t := time.Date(year, time.Month(month), 0, 0, 0, 0, 0, time.UTC)
return t
}
playground
It's not Go specific but usually I do following in any language:
package main
import "fmt"
import "time"
func main() {
fmt.Println("Hello, playground")
t, _ := time.Parse("2006-01-02", "2016-01-01")
t = t.AddDate(0, 1, 0).AddDate(0,0,-1)
fmt.Printf("Last day: %v\n", t)
}
http://play.golang.org/p/JhpOZvEhBw
I'm trying to get the first Monday of a given month.
Best way I can come up with is to loop through first seven days and return when .Weekday() == "Monday". Is there a better way to do this?
By looking at the .Weekday() of the time, you can compute the first Monday.
package main
import (
"fmt"
"time"
)
// FirstMonday returns the day of the first Monday in the given month.
func FirstMonday(year int, month time.Month) int {
t := time.Date(year, month, 1, 0, 0, 0, 0, time.UTC)
return (8-int(t.Weekday()))%7 + 1
}
func main() {
for m := 1; m <= 12; m++ {
fmt.Println(m, FirstMonday(2013, time.Month(m)))
}
}
I want to get a datetime, counting weeks from a date, days from a week and seconds from 00:00 time.
With Python I can use this:
BASE_TIME = datetime.datetime(1980,1,6,0,0)
tdelta = datetime.timedelta(weeks = 1722,
days = 1,
seconds = 66355)
mydate = BASE_DATE + tdelta
I'm trying to get it with Go, but I have some problems to reach it:
package main
import (
"fmt"
"time"
)
var base = time.Date(1980, 1, 6, 0, 0, 0, 0, time.UTC)
func main() {
weeks := 1722
days := 1
seconds := 66355
weeksToSecs := 7 * 24 * 60 * 60
daysToSecs := 24 * 60 * 60
totalSecs := (weeks * weeksToSecs) + (days * daysToSecs) + seconds
nanosecs := int64(totalSecs) * 1000000000
//delta := time.Date(0, 0, 0, 0, 0, totalSecs, 0, time.UTC)
date := base.Add(nanosecs)
fmt.Printf("Result: %s", date)
}
prog.go:21: cannot use nanosecs (type int64) as type time.Duration in function argument
http://play.golang.org/p/XWSK_QaXrQ
What I'm missing?Thanks
package main
import (
"fmt"
"time"
)
func main() {
baseTime := time.Date(1980, 1, 6, 0, 0, 0, 0, time.UTC)
date := baseTime.Add(1722*7*24*time.Hour + 24*time.Hour + 66355*time.Second)
fmt.Println(date)
}
Playground
Output
2013-01-07 18:25:55 +0000 UTC
jnml's answer works and is more idiomatic go. But to illustrate why your original code didn't work, all you have to do is change one line.
date := base.Add(time.Duration(nanosecs)) will cast the nanosecs to a time.Duration which is the type that Add expects as opposed to int64. Go will not automatically cast a type for you so it complained about the type being int64.
timeutil supports timedelta and strftime.
package main
import (
"fmt"
"time"
"github.com/leekchan/timeutil"
)
func main() {
base := time.Date(2015, 2, 3, 0, 0, 0, 0, time.UTC)
td := timeutil.Timedelta{Days: 10, Minutes: 17, Seconds: 56}
result := base.Add(td.Duration())
fmt.Println(result) // "2015-02-13 00:17:56 +0000 UTC"
}