I just found AddDate() does not always works as expected.
ex:
mayEndDate := time.Date(2021, 5, 31, 12, 00, 00, 00, time.UTC)
finalDate := endOfMay.AddDate(0, -1, 0)
here
output:
myEndDate = 2021-05-31 12:00:00 +0000 UTC
finalDate = 2021-05-01 12:00:00 +0000 UTC
I was expecting finalDate to be in April.
After reading the documentation, I found out the reason.
AddDate normalizes its result in the same way that Date does, so, for example, adding one month to October 31 yields December 1, the normalized form for November 31.
My question: how to now correctly find out the last month's date from today's date?
Get the current month using Month(), then from there it’s pretty simple to get the previous one:
currentMonth := mayEndDate.Month()
previousMonth := currentMonth - 1
if currentMonth == time.January {
previousMonth = time.December
}
if date is 2021-05-31 then previous month is April 2021.
package main
import (
"fmt"
"time"
)
func prevMonth(t time.Time) (int, time.Month) {
y, m, _ := t.Date()
y, m, _ = time.Date(y, m-1, 1, 0, 0, 0, 0, time.UTC).Date()
return y, m
}
func main() {
endOfMay := time.Date(2021, 5, 31, 12, 00, 00, 00, time.UTC)
fmt.Println(endOfMay)
fmt.Println(prevMonth(endOfMay))
}
https://go.dev/play/p/rP25ramRrZ3
2021-05-31 12:00:00 +0000 UTC
2021 April
Related
I have a custom weekend time which is from Friday 10pm UTC to Sunday 10:05pm UTC. I have a current timestamp in UTC and just wanted to check if the time falls in weekend time. Any tricks will be much appreciated. I tried using weekdays() and time but was not able to achieve the desired results.
check if the time falls in weekend time which is from Friday 10pm UTC to Sunday 10:05pm UTC.
Use the Go time package.
For example,
package main
import (
"fmt"
"time"
)
// A weekend is Friday 10pm UTC to Sunday 10:05pm UTC
func isWeekend(t time.Time) bool {
t = t.UTC()
switch t.Weekday() {
case time.Friday:
h, _, _ := t.Clock()
if h >= 12+10 {
return true
}
case time.Saturday:
return true
case time.Sunday:
h, m, _ := t.Clock()
if h < 12+10 {
return true
}
if h == 12+10 && m <= 5 {
return true
}
}
return false
}
func main() {
t := time.Date(2019, 11, 22, 12+10, 5, 0, 0, time.UTC)
fmt.Println(t)
w := isWeekend(t)
fmt.Println(w)
}
Playground: https://play.golang.org/p/TZBoNcwH-qU
Output:
2019-11-22 22:05:00 +0000 UTC
true
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
I have a timestamp coming in, I wonder if there's a way to round it down to the start of a day in PST. For example, ts: 1305861602 corresponds to 2016-04-14, 21:10:27 -0700, but I want to round it to a timestamp that maps to 2016-04-14 00:00:00 -0700. I read through the time.Time doc but didn't find a way to do it.
The simple way to do this is to create new Time using the previous one and only assigning the year month and day. It would look like this;
func truncateToDay(t time.Time) time.Time {
return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
}
here's a play example; https://play.golang.org/p/jnFuZxruKm
You can simply use duration 24 * time.Hour to truncate time.
t := time.Date(2015, 4, 2, 0, 15, 30, 918273645, time.UTC)
d := 24 * time.Hour
t.Truncate(d)
https://play.golang.org/p/BTz7wjLTWX
I believe the simplest is to create a new date as shown in this answer.
However, if you wanna use time.Truncate, there is two distinct cases.
If you are working in UTC:
var testUtcTime = time.Date(2016, 4, 14, 21, 10, 27, 0, time.UTC)
// outputs 2016-04-14T00:00:00Z
fmt.Println(testUtcTime.Truncate(time.Hour * 24).Format(time.RFC3339))
If you are not, you need to convert back and forth to UTC
var testTime = time.Date(2016, 4, 14, 21, 10, 27, 0, time.FixedZone("my zone", -7*3600))
// this is wrong (outputs 2016-04-14T17:00:00-07:00)
fmt.Println(testTime.Truncate(time.Hour * 24).Format(time.RFC3339))
// this is correct (outputs 2016-04-14T00:00:00-07:00)
fmt.Println(testTime.Add(-7 * 3600 * time.Second).Truncate(time.Hour * 24).Add(7 * 3600 * time.Second).Format(time.RFC3339))
in addition to sticky's answer to get the local Truncate do like this
t := time.Date(2015, 4, 2, 0, 15, 30, 918273645, time.Local)
d := 24 * time.Hour
fmt.Println("in UTC", t.Truncate(d))
_, dif := t.Zone()
fmt.Println("in Local", t.Truncate(24 * time.Hour).Add(time.Second * time.Duration(-dif)))
func truncateToDay(t time.Time) {
nt, _ := time.Parse("2006-01-02", t.Format("2006-01-02"))
fmt.Println(nt)
}
This is not elegant but works.
I using theses functions on all my projects:
package time_utils
import "time"
func TruncateToStartOfDay(t time.Time) time.Time {
return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
}
func TruncateToEndOfDay(t time.Time) time.Time {
return time.Date(t.Year(), t.Month(), t.Day(), 23, 59, 59, 0, t.Location())
}
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