Get the first and last day of a year from a timestamp - time

Given a time object, t := time.Now(), is there a way I could get a timestamp for the first and last days of that year?
I could do something like d := t.YearDay() to get the number of days through the year, and then t.AddDate(0, 0, -d) to get the start of the year, and t.AddDate(0, 0, 365-d) to get the end of the year, but that seems to brittle as it doesn't deal with leap years etc.

You can use your Time struct object and create new Time struct object using time.Date and time.Time.Year() functions. So, if current time is t := time.Now() then last day in this year in UTC will be lt := time.Date(t.Year(), time.December, 31, 0, 0, 0, 0, time.UTC)
Here's an example:
package main
import "fmt"
import "time"
func main() {
t := time.Now()
last := time.Date(t.Year(), time.December,31, 0, 0, 0, 0, time.UTC)
first := time.Date(t.Year(), time.January,1, 0, 0, 0, 0, time.UTC)
fmt.Println("Current: ", t)
fmt.Println("First: ", first)
fmt.Println("Last: ", last)
}
http://play.golang.org/p/jahjd_mi6K

Related

How to get specific time from next day

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
}
}

How to get the last quarter

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

time.Time Round to Day

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())
}

How to get one month in seconds

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

Something like python timedelta in golang

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"
}

Resources