I am writing a scraper that scrapes offers off websites and these offers have end dates. One such website has offers that expire every Sunday. I have gone through the golang time documentation but still dont get how that can be done the equivalence I found in PHP and is pretty simple.
$endDate = strtotime('this Sunday, 23:59:59');
Is there a golang way to do this?
Write a function in Go using the Go standard library time package. For example,
package main
import (
"fmt"
"time"
)
func endDate(t time.Time, wd time.Weekday) time.Time {
next := int((wd - t.Weekday() + 7) % 7)
y, m, d := t.Date()
return time.Date(y, m, d+next+1, 0, 0, 0, -1, t.Location())
}
func main() {
now := time.Now().Round(0)
fmt.Println(now, now.Weekday())
end := endDate(now, time.Sunday)
fmt.Println(end, end.Weekday())
}
Playground: https://play.golang.org/p/T0oZGRO9NV8
Output:
2018-11-08 05:25:01.104445722 -0500 EST Thursday
2018-11-11 23:59:59.999999999 -0500 EST Sunday
Related
I have a list of date strings that appear in this format without any time zone or offset information:
[
"2019-04-30T12:34:00.000", // In 2019, DST started in March 10, 2019, so this should have the appropriate DST offset
"2017-11-20T13:45:00.000" // In 2017, DST ended on November 5, 2017 so this should have the appropriate standard time offset
]
I know the IANA region (eg, America/New_York) that these dates and times were created in, but I cannot figure out how to dynamically generate the appropriate offset given this information using go and the time package.
I have thought about the following:
Appending a hardcoded value to the end of the date string (ie, "2019-04-30T12:34:00.000" + "-04:00)
Write custom logic to determine if a date falls within the boundary of standard or daylight savings time
However, these solutions only work for some dates or the logic becomes exceedingly complicated.
I was able to figure it out
package main
import (
"log"
"time"
)
func main() {
Chicago, _ := time.LoadLocation("America/Chicago")
t := time.Date(2019, time.March, 1, 12, 30, 0, 0, Chicago)
log.Print(t) // 2019-03-01 12:30:00 -0600 CST
log.Print(t.UTC()) // 2019-03-01 18:30:00 +0000 UTC
t = time.Date(2019, time.November, 2, 12, 30, 0, 0, Chicago)
log.Print(t) // 2019-11-02 12:30:00 -0500 CDT
log.Print(t.UTC()) // 2019-11-02 17:30:00 +0000 UTC
}
go playground # https://play.golang.org/p/nP28y9jSDAk
An even cleaner solution by leveraging a custom layout and time.LoadLocation
package main
import (
"fmt"
"time"
)
func main() {
Chicago, _ := time.LoadLocation("America/Chicago")
cdt, _ := time.ParseInLocation("2006-01-02T15:04:05.999999", "2019-04-30T12:34:00.000", Chicago)
fmt.Println(cdt)
fmt.Println(cdt.UTC())
cst, _ := time.ParseInLocation("2006-01-02T15:04:05.999999", "2017-11-20T13:45:00.000", Chicago)
fmt.Println(cst)
fmt.Println(cst.UTC())
}
go playground # https://play.golang.org/p/3Ai4qVz0af5
In Ruby, for example, there's the Time#dst? function, which returns true in the case it is daylight saving time. Is there a Go standard library API call to do the same?
In August 2021 go 1.17 was released which now adds the time.Time method IsDST:
IsDST reports whether the time in the configured location is in
Daylight Savings Time.
The Location api doesn't export the DST value of the timezone. This was brought up in the golang-nuts forum several years ago. One suggestion is to compare the January 1 timezone offset to the July 1 timezone offset. A working solution of this was posted using this method. One caveat is that goplay has the wrong local time, so it doesn't correctly report the information if you run it there. You can run it locally to verify that it does work.
Another way would be to use reflection via the reflect package. A solution that I wrote to do this is available here. There are a lot of problems with this method.
Edit: Really it should probably use cacheZone but does a linear search of the zones to find one that matches. This can lead to errors because some timezones share name and offset. The correct way would be to look at cacheZone and use that if it is set. Otherwise, you'll need to either look at zoneTrans or at least look at how lookup(int64) is implemented.
You can infer the result. For example,
package main
import (
"fmt"
"time"
)
// isTimeDST returns true if time t occurs within daylight saving time
// for its time zone.
func isTimeDST(t time.Time) bool {
// If the most recent (within the last year) clock change
// was forward then assume the change was from std to dst.
hh, mm, _ := t.UTC().Clock()
tClock := hh*60 + mm
for m := -1; m > -12; m-- {
// assume dst lasts for least one month
hh, mm, _ := t.AddDate(0, m, 0).UTC().Clock()
clock := hh*60 + mm
if clock != tClock {
if clock > tClock {
// std to dst
return true
}
// dst to std
return false
}
}
// assume no dst
return false
}
func main() {
pstLoc, err := time.LoadLocation("America/Los_Angeles")
if err != nil {
fmt.Println(err)
return
}
utc := time.Date(2018, 10, 29, 14, 0, 0, 0, time.UTC)
fmt.Println(utc, utc.Location(), ": DST", isTimeDST(utc))
local := utc.In(time.Local)
fmt.Println(local, local.Location(), ": DST", isTimeDST(local))
pst := utc.In(pstLoc)
fmt.Println(pst, pst.Location(), ": DST", isTimeDST(pst))
utc = utc.AddDate(0, 3, 0)
fmt.Println(utc, utc.Location(), ": DST", isTimeDST(utc))
local = utc.In(time.Local)
fmt.Println(local, local.Location(), ": DST", isTimeDST(local))
pst = utc.In(pstLoc)
fmt.Println(pst, pst.Location(), ": DST", isTimeDST(pst))
}
Output:
2018-10-29 14:00:00 +0000 UTC UTC : DST false
2018-10-29 10:00:00 -0400 EDT Local : DST true
2018-10-29 07:00:00 -0700 PDT America/Los_Angeles : DST true
2019-01-29 14:00:00 +0000 UTC UTC : DST false
2019-01-29 09:00:00 -0500 EST Local : DST false
2019-01-29 06:00:00 -0800 PST America/Los_Angeles : DST false
I'd like to rename some files based on their modification date.
When I use the time.Format method to get the correct string, basically in this format YYYY-MM-DD_HH-MM-SS, the day has a trailing 0.
Am I doing something wrong here?
package main
import (
"time"
"fmt"
)
func main() {
loc, _ := time.LoadLocation("Europe/Berlin")
const layout = "2006-01-20_15-04-05"
t := time.Date(2013, 07, 23, 21, 32, 39, 0, loc)
fmt.Println(t)
fmt.Println(t.Format(layout))
}
Click to play
Output:
2013-07-23 21:32:39 +0200 CEST
2013-07-230_21-32-39
Your layout isn't using the reference date: change it to "2006-01-02_15-04-05"
When you use "2006-01-20_15-04-05", the formatter see the 2, and uses that for the day, then keeps the extra 0 since it doesn't match any part of the reference date.
I'm trying to parse a string as time with but unfortunately go gets the wrong month (January instead of June)
package main
import "fmt"
import "time"
func main() {
t := "2014-06-23T20:29:39.688+01:00"
tc, _ := time.Parse("2006-01-02T15:04:05.000+01:00", t)
fmt.Printf("t was %v and tc was %v", t, tc)
}
Play
The problem is that your timezone offset is ill-defined in the layout: the reference offset is -0700. You defined yours as +01:00, so the 01 is interpreted as the month and erase the previously defined one. And as your working offset is 01 as well, it is parsed as january.
The following example works for me playground
package main
import "fmt"
import "time"
func main() {
t := "2014-06-23T20:29:39.688+01:00"
tc, _ := time.Parse("2006-01-02T15:04:05.000-07:00", t)
fmt.Printf("t was %v and tc was %v", t, tc)
}
Your layout string is incorrect. The numbers in the layout string have special meanings, and you are using 1 twice: once in the month portion and once in the time zone portion. The time zone in the string you are parsing is 01:00, so you are storing 1 into the month. This explains why the returned month was January (the first month).
A corrected layout string is 2006-01-02T15:04:05.000-07:00. Or, if you're happy with using Z to represent UTC, the time.RFC3339 constant might be appropriate.
How do I parse non-standard date/time strings in Go. In example if I wanted to convert the string 10/15/1983 into a time.Time? The time.Parse() function supposedly allows you to specify a format.
http://play.golang.org/p/v5DbowXt1x
package main
import "fmt"
import "time"
func main() {
test, err := time.Parse("10/15/1983", "10/15/1983")
if err != nil {
panic(err)
}
fmt.Println(test)
}
This results in a panic.
panic: parsing time "10/15/1983" as "10/15/1983": cannot parse "" as "0/"
Logically that makes sense because how is it supposed to know which is the day and which is the month.
Other languages have a function similar to the following:
parse("mm/dd/yyyy", "10/15/1983")
I cannot find such a function in the Go docs, is my only choice to regex?
There are some key values that the time.Parse is looking for.
By changing:
test, err := time.Parse("10/15/1983", "10/15/1983")
to
test, err := time.Parse("01/02/2006", "10/15/1983")
the parser will recognize it.
Here's the modified code on the playground.
package main
import "fmt"
import "time"
func main() {
test, err := time.Parse("01/02/2006", "10/15/1983")
if err != nil {
panic(err)
}
fmt.Println(test)
}
You can utilize the constants list in the src/pkg/time/format.go file to create your own parse formats.
const (
stdLongMonth = "January"
stdMonth = "Jan"
stdNumMonth = "1"
stdZeroMonth = "01"
stdLongWeekDay = "Monday"
stdWeekDay = "Mon"
stdDay = "2"
stdUnderDay = "_2"
stdZeroDay = "02"
stdHour = "15"
stdHour12 = "3"
stdZeroHour12 = "03"
stdMinute = "4"
stdZeroMinute = "04"
stdSecond = "5"
stdZeroSecond = "05"
stdLongYear = "2006"
stdYear = "06"
stdPM = "PM"
stdpm = "pm"
stdTZ = "MST"
stdISO8601TZ = "Z0700" // prints Z for UTC
stdISO8601ColonTZ = "Z07:00" // prints Z for UTC
stdNumTZ = "-0700" // always numeric
stdNumShortTZ = "-07" // always numeric
stdNumColonTZ = "-07:00" // always numeric
)
So anytime your format specifies a year, it should be done with "06" or "2006", seconds are specified by "05" or "5" and time zones are specified at "MST", "Z0700", "Z07:00", "-0700", "-07" or "-07:00". If you reference the constants list you can likely put together any standard format you'd need to parse.
For example, if you want to parse the date/time in the Common Log Format, the format Apache uses for its log files, you would do so by passing the following string to time.Parse() as the layout argument.
"02/Jan/2006:15:04:05 -0700"
"02" denotes the day of the month field, "Jan" denotes the month name field, "2006" denotes the year field, "15" denotes the hour of day field in 24 hour format, "04" denotes the minutes field, "05" denotes the seconds field and "-0700" denotes the time zone field.
That format would parse the current PST time: 31/Dec/2012:15:32:25 -0800
So the time.Parse() call would look like this:
test, err := time.Parse("02/Jan/2006:15:04:05 -0700", "31/Dec/2012:15:32:25 -0800")
If you can't remember the Numbers in the specifying layout ("2006-01-02T15:04:05.000Z"), you may use my simple date formatting library github.com/metakeule/fmtdate that uses MS Excel conventions, like Y,M,D,h and internally translates them to the number format:
package main
import (
"github.com/metakeule/fmtdate"
"fmt"
)
func main() {
test, err := fmtdate.Parse("MM/DD/YYYY", "10/15/1983")
if err != nil {
panic(err)
}
fmt.Println(test)
}
If you are looking for a C-Style formatting function: After reviewing some of the options I have chosen https://github.com/cactus/gostrftime as it generally follows the strfmt(3) notation.
To quote the example:
import (
"fmt"
"time"
"github.com/cactus/gostrftime"
)
func main() {
now := time.Now()
fmt.Println(gostrftime.Format("%Y-%m-%d", now))
}
If a date format has to be used by both C and Go and the C implementation is not to be touched there's no choice but to adapt on the Go end. The above package fulfills that need.
If you don't want bother remembering the magic numbers, you can do this:
package main
import (
"fmt"
"time"
)
func main() {
var (
y, d int
m time.Month
)
fmt.Sscanf("10/15/1983", "%v/%v/%v", &m, &d, &y)
t := time.Date(y, m, d, 0, 0, 0, 0, time.UTC)
fmt.Println(t) // 1983-10-15 00:00:00 +0000 UTC
}
https://golang.org/pkg/fmt#Sscanf