-
Notifications
You must be signed in to change notification settings - Fork 18k
proposal: time: add civil time package #19700
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
And because the drinking age in California is 21. The ballot measure never happened. |
21 in NY too, it turns out. Edited comment. |
fun story... the drinking age is 21 in every state because of a law that withheld federal highway money unless states mandated 21 as the drinking age. https://en.wikipedia.org/wiki/National_Minimum_Drinking_Age_Act |
On hold for #17244, which in turn is essentially blocked on understanding the long-term plan for package management. Soon. |
I left a comment on the implementation in the CL. I'll mirror the parts that are not about implementation details here: In my opinion time.Duration already implements a civil time (see #20757 for a list of issues that may be interesting). Please clarify the difference between a civil date and just a normal date (dates don't have timezone issues to my knowledge, especially because they don't represent a moment in time). There are multiple tiny "date" packages that look more or less the same (I implemented one here https://godoc.org/github.com/infobaleen/date, but there are others which are practically identical). |
Since we need a civil DateTime type, and a civil Time type to support SQL, it seems reasonable to put the Date type in the same package with them. Since the |
Maybe I misunderstood the goal, as I don't see the range problem you are mentioning. Are you considering other calendars where a day doesn't have |
Other calendars are not a goal. The time package documents that:
|
Where does that type live? And what about the type that just represents a time of day (
I didn't understand your proposal. I thought you wanted to represent a DateTime as a duration from some single reference time, like Unix represents time as an offset from 1/1/1970. |
I don't have a strong opinion on that. I would suggest putting it in "exp" first and integrating it in "time" later
time.Duration is sufficient (look at it's metods). Maybe alias it. |
I've also needed a pure Date for SQL and other things, but I implemented it by wrapping |
i don't understand why is duplicate "time: add weekday helper functions #25469" (btw please see my last comment)... please don't take me wrong i only try to understand how do you think. |
Change https://golang.org/cl/38571 mentions this issue: |
If nothing is blocking this any longer, I'd like to proceed. I appreciate the suggestions for alternative implementations (wrapping |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
What's the status of this? I'm a bit unfamiliar with how these CLs work, but it looks like it hasn't seen any activity for about a month. Is it just waiting for some final confirmation on its inclusion? Right now we're importing the google package, which is a huge import, just to get access to the civil one (can't get subdirectory importing to work with go modules). |
I'd just like to put in a word for having both zoned and unzoned civil datetime types. For calendars, you want to be able to support implicit time zone, as well as explicit. |
Isn't a zoned civil datetime just a
|
To clarify, I'm saying that a date/time library needs to support at least all of the following:
|
@rsc (or more generally the Proposal committee) I would like to re-open this proposal for consideration in order to get closure on it. Why Now?The SQL Server Driver currently supports "https://godoc.org/cloud.google.com/go/civil" for date, time and date time types. However, "cloud.google.com/go" is a heavy module and I would prefer we do not require it, when I really just want the civil types from it. I have extracted it from that package as "github.com/golang-sql/civil" with a PR to switch to using it denisenkom/go-mssqldb#501 . However, if this proposal was accepted and https://golang.org/cl/38571 was accepted, I would want to use that. Why use Civil types at all?Business applications frequently use both Date, Timestamp and Timestampz (or equivalent) types. One type of bug I have encountered is where a DB Date type is used, but in certain instances time.Time will set the wrong date based on the time of day (due to timezone). This is fixable, but in newer code I've chosen to use civil.Date. This simplifies reading and understanding the code and prevents through fundamental design an entire class of bugs. When working with Dates in a date based scheduler, converting to civil.Date simplified the code over time.Time. Equality and inequality became much simpler cognitively and slightly simpler in code. When sending a parameter to a database instance, can be important to have proper types so they are represented correctly the the SQL:
What do I wantGo1.13 is almost out. I would like resolution whether x/time/civil will be a package or not. If not, I will use "github.com/golang-sql/civil". If it may, then I will wait until it gets merged. Once this is sorted out, I will encurage other database drivers to support these civil types as well. Date, DateTime, Time (of day), and Decimal types are the last types commonly supported by Databases. I'm addressing Decimal support with https://golang.org/issue/30870 . |
Seems like we still need to sort out #17244. But I'll take that one off hold. |
Any updates on this? :) Would love to see this added. See above mentioned issue. |
I have used However it is nice if there is a package that would do date and time without time zones and I think this should live in x/time/civil to indicate that it could be included in the standard library one day, most likely never will though as it’s not common enough. I understand by saying this I give an opinion that isn’t robust at all considering the ‘what goes in golang.org/x’ discussion. |
I personally find it quite necessary. The usecase is representing a regular schedule, for example the timetable of a train.
As seen in this timetable excerpt above, train R 11361 leaves Brașov every day at 06:52. When combined with a date and the Europe/Bucharest location, on the 17 of December 2023 the ISO8601 timestamp is 2023-12-17T04:58:00Z, and on the 1st of July 2024 the timestamp is 2024-07-01T03:58:00Z. Another example is an alarm clock, which should also not be affected by timezone or DST changes. |
For a Time as in "point in day", a time.Duration would suffice, such that a time.Now().Truncate(24time.Hour).Add(6time.Hour+58*time.Minute). |
Your message is badly formatted because of not using a code block, so it's a bit harder to read. Assuming I understood it right, however, that's actually incorrect, particularly on days when DST changes. 6 hours and 58 minutes after midnight of the 31th of March 2024 is 07:58, and 6 hours and 58 minutes after midnight of the 27th of October 2024 is 05:58. However, on both of those days, the train I mentioned departs from the 2nd station at 06:58. package main
import (
"fmt"
"time"
)
func main() {
duration := 6*time.Hour + 58*time.Minute
location, _ := time.LoadLocation("Europe/Bucharest")
fmt.Println(time.Date(2024, time.March, 31, 0, 0, 0, 0, location).Add(duration))
fmt.Println(time.Date(2024, time.July, 1, 0, 0, 0, 0, location).Add(duration))
fmt.Println(time.Date(2024, time.October, 27, 0, 0, 0, 0, location).Add(duration))
} Using the example above, you can see that all of the printed hours are different. As such, a |
in 2025.....Having recently started using Go far more than I had been previously, I stumbled upon this old proposal. Forgive my long-windedness, but I'd like to revisit it. I think it's a good proposal, and I think is worth some thought (even if I'm providing too much thought 😅) to really weigh if it should be included in the standard library. I think I've groked the existing arguments in this proposal for not adopting a separate "civil" or "non-absolute" time (dates without location, times without dates, etc.) It sounds like the predominate problem is: Go conscientiously reduced the representation of time to one ( First, a gentle rebuttalI understand that Go made a conscience decision to have only one time representation in the language, and introducing a "civil" representation would therefore seem like a step back. I also understand that the Go team is very careful about introducing new APIs and that they are not taken lightly. Introducing this type would be a significant change to the language and would require careful consideration of the implications. That being said, I'd point out this small but important point: there are numerous examples where the same "type" (subjectively) of data is represented multiple ways in Go with their own APIs (with subtle difference even between some). For instance, how many ways are there to represent a "number" in Go? There's I'd argue each of those has a specific purpose even if they are technically representing the same "type" of data. I don't think it necessarily a desired goal that "There Can Be Only One" representation for a type of data in Go. Which one should you use? It depends on the use-case. For example, if you need to represent a number that can be very large or very small, you might want to use So why is this an untenable problem when considering "time" data types? I do not believe that it is. Avoiding multiple representations of the type because there's a possibility of fragmentation is not a compelling enough reason alone in my opinion. I think, rather, that becomes an important point for how any "civil" date or time is introduced. Therefore, I think we should reframe that problem statement. Rather I think it is truly a debate around whether the introduction of these types is widely-applicable or distinct enough from existing types for Go to add into the standard library. So far, the last word on the proposal was that the Go team is not convinced that a "civil" date or time type is widely-applicable nor distinct enough for Go to introduce it. Second, a demonstration and proposalApplicabilityWhile perhaps less common than the use cases designed for with For instance, @dancojocaru2000 example of a train schedule is a good use-case many of us encounter and may even use daily as commuters. Another I'll mention is Periodic Jobs (e.g., like cron jobs) where one is typically expressing an interval or relative "moment" of occurrence when a action should be taken. I think one could claim that such "location-less" time or date problems are present in very known (and hard) problems like Interval Scheduling. Capacity planning (related) often leverages this same type of "civil" date/time representation and handling. My belief is that many of these problems deal with a simplified calculus with time data.
I'm also not sure how widely-applicable the last one ("clock spinning") is in isolation, but I believe (pure conjecture) the primary reason it's included in many "civil" implementation is for "actualizing" a "civil" date into an "absolute" time. Grab your flip calendar and your multi-handed clock, put them together in a location and you have an "absolute" time instance. DemonstrationTo evaluate whether a "civil time" API is distinct enough from We have an application designed to manage train schedules and bookings. Trains operate on clock times and a "recurrence" (e.g., daily) to destinations - the "Schedule". For simplicity, let's say they our train operates on a "daily" Schedule at a fixed time table, but not on Christmas in the Georgian calendar. In this case, we have a "civil" time (the clock time of day a train arrives), but we also have a "holiday" which is a "civil" date but without a "civil" time. We could represent this data in Go today with Alternatively we could use something like To use @dancojocaru2000's example schedule:
Our task is to provide the next 7 departures from a given date (exclusive) for the train's departures from Pavilion CFR Brașov Triaj. Below you'll find two implementations of the same task. One using https://go.dev/play/p/v6RX-be2JdY package main
import (
"fmt"
"time"
"cloud.google.com/go/civil"
)
// for simplicity we're going to make these hardcoded, but recall that holidays
// vary and may be specific month+days or might be moving year+month+days. They
// don't include times though.
// Christmas 2024 - convention is to add "UTC midnight" to the date to make it
// vaild time.Time
var timeChristmas = time.Date(2024, time.December, 25, 0, 0, 0, 0, time.UTC)
// Also Christmas 2024 - civil.Date only represents the date sans a location
var civilChristmas = civil.Date{Year: 2024, Month: time.December, Day: 25}
func main() {
location, _ := time.LoadLocation("Europe/Bucharest")
// Time schedule with easy to make mistake
fmt.Println("*** time.Time schedule with two easy mistakes to make when attempting to follow conventions ***")
// By convention the application provides time.Time in UTC. e.g., pulled from a database and converted to `time.Time`
fmt.Println("1. Christmas is accidentally included in the next 7 departures")
fmt.Println("2. A subtle time zone bug shifted the times we show to a user")
endOfMarchSchedule := incorrectNextSevenDepartures(time.Date(2024, time.March, 29, 0, 0, 0, 0, time.UTC))
fmt.Println(incorrectNextSevenDepartures(time.Date(2024, time.December, 22, 0, 0, 0, 0, time.UTC)))
for _, arrival := range endOfMarchSchedule {
fmt.Printf("Train departs at %s\n", arrival.In(location))
}
fmt.Println("****************************************")
// Civil date schedule
fmt.Println("*** Civil date schedule ***")
fmt.Println("1. Christmas is not included in the next 7 departures")
fmt.Println("2. Train schedule uses the correct time zones")
civilEndMarchSchedule := nextSevenDepartures(civil.Date{Year: 2024, Month: time.March, Day: 29})
fmt.Println(nextSevenDepartures(civil.Date{Year: 2024, Month: time.December, Day: 22}))
for _, arrival := range civilEndMarchSchedule {
fmt.Printf("Train departs at %s\n", arrival.In(location))
}
fmt.Println("****************************************")
}
func incorrectNextSevenDepartures(start time.Time) []time.Time {
schedule := make([]time.Time, 7)
// First mistake, we're using the wrong time zone, we need to remember to use the departure location
startingDay := time.Date(start.Year(), start.Month(), start.Day(), 6, 58, 0, 0, start.Location())
schedule[0] = startingDay.AddDate(0, 0, 1)
for i := 1; i < 7; i++ {
next := schedule[i-1].AddDate(0, 0, 1)
// Second mistake, the times won't match because of convention of adding a
// time of Midnight to a plain date. To correct we would need to remember
// to use Format to check just the Date element (or use Date to compare the
// components directly)
// e.g.,
// if next.Format(time.DateOnly) == timeChristmas.Format(time.DateOnly)
// or
// y, m, d := next.Date()
// if y == timeChristmas.Year() && m == timeChristmas.Month() && d == timeChristmas.Day()
if next == timeChristmas {
next = next.AddDate(0, 0, 1)
}
schedule[i] = next
}
return schedule
}
func nextSevenDepartures(start civil.Date) []civil.DateTime {
schedule := make([]civil.DateTime, 7)
// We're saved from our first mistake because there's no time zone to worry
// over at this point
trainTime := civil.Time{Hour: 6, Minute: 58}
schedule[0] = civil.DateTime{
Date: start.AddDays(1),
Time: trainTime,
}
for i := 1; i < 7; i++ {
nextDay := civil.DateTime{
Date: schedule[i-1].Date.AddDays(1),
Time: trainTime,
}
// We're saved from our second mistake because of type checking, to compare
// to the civil.Date Christmas holiday we need to use the civil.Date type
if nextDay.Date == civilChristmas {
nextDay.Date = nextDay.Date.AddDays(1)
}
schedule[i] = nextDay
}
return schedule
} While contrived, this example demonstrates that while conventions make it possible to use Proposed Full APIOn the subject of names, the old joke comes to mind:
I know @rsc recommended For the sake of clarity in this proposal of aligning on an interface, however, let's assume we use a separate For the sake of this proposal, I've put these as "interfaces" just to align on what the API would look like. I think they would be better implemented as // A civil.Date represents a single calendar day.
// Its range is January 1, 0001 to December 31, 9999.
// DateFrom returns the Date for the given year, month, and day-of-month.
//
// Example:
//
// day := civil.DateFrom(2024, time.January, 1)
func DateFrom(year int, month time.Month, day int) Date
// DateOf returns the Date for the given time.Time.
//
// Example:
//
// now := time.Now()
// day := civil.DateOf(now)
func DateOf(time.Time) Date
type Date interface {
Year() int
Month() time.Month
Day() int
MonthDay() int
YearDay() int
Weekday() time.Weekday
ISOWeek() (year, week int)
Add(years int, months int, days int) Date
Sub(e Date) (years int, months int, days int)
After(e Date) bool
Before(e Date) bool
Compare(e Date) int
Format(layout string) string
String() string
MarshalBinary() ([]byte, error)
MarshalText() ([]byte, error)
UnmarshalBinary(data []byte) error
UnmarshalText(data []byte) error
Scan(v any) error
Value() (driver.Value, error)
ParseDate(layout, value string) (Date, error)
}
// A ClockTime represents a 24-hour clock time during an unspecified day.
// Its range is 00:00:00 (midnight) to 23:59:59.999999999
// ClockTimeOf returns the ClockTime for the given hour, minute, second.
//
// Example:
//
// now := time.Now()
// clock := civil.ClockTimeOf(now)
func ClockTimeOf(time.Time) ClockTime
// ClockTimeFrom returns the ClockTime for the given hour, minute, second, and nanoseconds.
// Example:
//
// clock := civil.ClockTimeFrom(12, 30, 45, 9999)
func ClockTimeFrom(hour, min, sec, nano int)
type ClockTime interface {
Hour() int
Minute() int
Second() int
Nanosecond() int
Add(hours, mins, secs, nanos int) ClockTime
Sub(d ClockTime) (hours, mins, secs, nanos int)
After(d ClockTime) bool
Before(d ClockTime) bool
Compare(d ClockTime) int
Format(layout string) string
String() string
MarshalBinary() ([]byte, error)
MarshalText() ([]byte, error)
UnmarshalBinary(data []byte) error
UnmarshalText(data []byte) error
Scan(v any) error
Value() (driver.Value, error)
ParseClockTime(layout, value string) (ClockTime, error)
}
// A civil.Time represents a specific civil date and time.
// civil.Of returns the civil.Time for given time.Time.
//
// Example:
//
// now := time.Now()
// dateTime := civil.Of(now)
func Of(time.Time) Time
// civil.From returns the civil.Time for given civil.Date and civil.Time.
// Example:
//
// date := civil.DateFrom(2024, time.January, 1)
// clock := civil.ClockTimeFrom(12, 30, 45, 0)
// dateTime := civil.From(date, clock)
func From(date Date, clock ClockTime) Time
type Time interface {
Date() Date
ClockTime() ClockTime
After(e Time) bool
Before(e Time) bool
Compare(e Time) int
In(loc *time.Location) time.Time
Format(layout string) string
String() string
MarshalBinary() ([]byte, error)
MarshalText() ([]byte, error)
UnmarshalBinary(data []byte) error
UnmarshalText(data []byte) error
Scan(v any) error
Value() (driver.Value, error)
ParseTime(layout, value string) (Time, error)
} First, I think it's important to note what can and cannot be done with these interfaces. One can:
One cannot:
If we're particularly worried about fragmentation, we could drop the various ConsiderationsLooking at those interfaces, if I turn my head and squint, it looks very similar to To put that question another way, what's the real distinction between that proposed full API and
ComparisonFor this I mean, that FormattingFor this I mean, which "layout(s)" is considered a "correct" layout when parsing or formatting for a As an example what would you expect ClockTime "math"For this I mean, spinning the hands of a clock is fundamentally different than actual time math ( For instance, if you are at Jumping locationsFor this I mean, that An alternative name to Less "civil" API ProposalOf those distinction, if we reject the default formatting and parsing expectations (after all you can use If we were to remove the package time
type Time struct {
// contains filtered or unexported fields
}
//....
// Jump returns a new Time with the same date, and time components but in the given location.
func (t Time) Jump(loc *Location) Time
// AfterDate reports whether t is after the given time by date (regardless of time).
func (t Time) AfterDate(u Time) bool
// BeforeDate reports whether t is before the given time by date (regardless of time).
func (t Time) BeforeDate(u Time) bool
// CompareDate compares t and u by date (regardless of time).
func (t Time) CompareDate(u Time) int
// EqualDate reports whether t and u are equal by date (regardless of time).
func (t Time) EqualDate(u Time) bool This is a much smaller changeset to the language. A Our corrected example would then look something like: package main
import (
"fmt"
"time"
)
var timeChristmas = time.Date(2024, time.December, 25, 0, 0, 0, 0, time.UTC)
func main() {
endOfMarchSchedule := nextSevenDepartures(time.Date(2024, time.March, 29, 0, 0, 0, 0, time.UTC))
fmt.Println(nextSevenDepartures(time.Date(2024, time.December, 22, 0, 0, 0, 0, time.UTC)))
location, _ := time.LoadLocation("Europe/Bucharest")
for _, arrival := range endOfMarchSchedule {
fmt.Printf("Train departs at %s\n", arrival.Jump(location))
}
}
func nextSevenDepartures(start time.Time) []time.Time {
schedule := make([]time.Time, 7)
startingDay := time.Date(start.Year(), start.Month(), start.Day(), 6, 58, 0, 0, time.UTC)
schedule[0] = startingDay.AddDate(0, 0, 1)
for i := 1; i < 7; i++ {
next := schedule[i-1].AddDate(0, 0, 1)
if next.EqualDate(timeChristmas) {
next = next.AddDate(0, 0, 1)
}
schedule[i] = next
}
return schedule
} While one has to carry around time components on things that don't really have them, I don't feel like this is a huge burden. You do have to be consistent with your time zones if doing any time math, but as that's already the convention, I feel like these additions just make it easier to avoid subtle mistakes. ConclusionI think this proposal is worth revisiting. It's worth debating whether the outlined "civil" date and time types in a standlone Lastly, I want to thank the Go team for their hard work and dedication to the language. I hope this proposal is taken up again for consideration and that we can have a fruitful discussion about it. |
I think we have a general agreement on what the shape of an API would look like. @rsc's Day/Clock (#19700 (comment)), cloud.google.com/go/civil, and @wspurgin's proposal above are all fairly similar. I think the main unresolved question is whether this would be new types in the time package, or a new time/civil package. The question, as I see it, is whether we should add this at all. Does this need to be in std? Is a third-party package like cloud.google.com/go/civil sufficient? In general, we add new API to std when it is
For example, TB.Parallel is difficult to implement outside the testing package, TB.TempDir is trivial to implement but widely useful, and the recently-accepted TB.Attr enables interoperability between different systems that produce or consume test attributes. Civil time is not difficult to implement outside the standard library. I don't think we have much evidence for against how widely useful it is. pkg.go.dev lists 499 importers of cloud.google.com/go/civil, but I think many of those are forked versions of cloud.google.com packages and don't really constitute use of the package. Perhaps a survey of real usage of existing civil time implementations (including non-Go ones like ABSL's civil time) would constitute evidence for or against adopting something in std. There's a clear argument from interoperability, but it turns on usage: If programmers often pass civil times between modules, then this is evidence that a common type definition would be useful. If few programs need civil times, or if those times are not passed between modules, then this may not be a strong argument. Speaking as someone who is not on the proposal committee and has no deciding power here, I think the best way to move this proposal forward is to gather evidence that there's a need for it in std. (A train scheduler might need a civil time type, but does it need one defined in the standard library?) |
Thanks for the feedback @neild. I appreciate the time you took to respond. These are all good and valid points, however I want to challenge a few of your concerns (though I recognize that you aren't on the proposal committee).
I think that would be valuable, but in terms of practically obtainable evidence, what would constitute "proof" of "real usage" of other civil time implementations? For example, I could list a bunch of references in literature in which various problems sets interact with a "location-less" date/time (i.e., the scheduling class of problems). I could also point out its prevalence at very large software companies (e.g., google made CCTZ and cloud.google.com/go/civil to use it "for real" 😄). I could point at other standard library adoptions, Joda started out as its own "civil" time library (and more) that was so widely useful and popular that Java 8 adopted it into the standard. Java 8 and its later variants account for 89% of the Java ecosystem (as of 2023). Is that proof of real usage or the need for interoperability? I (subjectively) view all of these as proof for how widely useful civil time implementations are. On the subject of Barring a hat-in-hand straw survey asking Gopher's to respond with a "use it" or "don't use it", I don't know how we could get a sense of the "real usage" of civil time implementations (or attempts at it) in Go specifically. Which is beyond my abilities to gather, and in my opinion unnecessary to prove the utility of civil time in Go.
Perhaps, but to your early point A standard civil time implementation leveraging |
To me (also not on the proposal committee!), the primary question from @neild's analysis is the matter of whether multiple independently-maintained codebases are likely to benefit from having a shared agreement on one idiomatic way to represent each of the concepts under discussion. If we focus on that specific question then perhaps it can be answered by looking for currently-existing (not hypothetical) examples of situations such as:
Although of course there can be other reasons to justify including something in the standard library, the need for multiple codebases to agree on a representation or approach often seems to be a strong justification, and so if we can find examples of that then I'd hope that would be compelling enough to make the other avenues of argument less important. |
I think that a civil date is widely useful. (But I have never used a civil time.) At work we have our own implementation that's similar to One unfortunate thing that happens without a civil date representation is that there are many scenarios where a And in fact, if I'm writing code outside of my work monorepo, I would probably be inclined to make do without a date type if I'm only doing a few simple date calculations, just to avoid a dependency. So I'm strongly in favor of this proposal, or at least a limited version that only adds Finally, I will also mention that I don't love depending on the |
@cespare can you describe the tweaks you made? What is omitted from Russ's I agree that having a unified API is worth it here, and the problem with Personally I think that this package should live in |
I'm not the subject of your question @jba, but for me, Russ's omitted:
The last of which is very minor and if undesired I wouldn't be too broken up about. On the subject of packages, IMO (having no say or even as great of experience with Go as many here), I interpreted the If that's the case, a |
Ours is much closer to Comparing our internal
The other difference is that our
That said, we chose to diverge because we knew that we didn't want to bring in the |
I think this is all evidence that civil time implementations are common. Evidence that they're useful would be code that uses those existing implementations. If civil time is widely useful, there should be ample code out there which uses it. (In the case of implementations that include both absolute and civil times--to use the ABSL terminology--this would be code that specifically uses the civil time component.) Unrelated, on terminology: Joda time appears to call civil time "local time", with LocalDate, LocalTime, and LocalDateTime classes. |
Adding my $0.02 ... I've repeatedly come across problems that have required both civil time and civil date throughout my career, initially in the early 00s working in C++. I found that code useful enough that I ported it to C# and later to Go here. I called the types As a point of consideration, Having worked at multiple companies that were extremely security conscious, an external dependency of that size would raise all kinds of flags where this being part of Go's "standard library" would not. At each of those companies I would have been required to create an internal module rather than take on a dependency on
A big caveat here is that it disregards non-public code. In every case where I've needed this functionality, it wasn't part of an OSS project. Please don't limit the evaluation of utility to only publicly available code. This is not the first time that I've been part of Go discussions that, intentionally or not, assumed that non-OSS code doesn't exist. As a pedantic side note, if I were building this for myself today I would probably consider calling |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
To pull the conversation back to a civil spot
@neild - I understand your perspective, but I disagree. There is ample code using this (there are multiple folks in this thread to attest to their own anecdotal evidence of that) - it's just not easily publicly available. However, if you are only satisfied by publicly available numbers, then may I point out the --- edit --- to add more examples with public numbers for other implementations
--- end edit --- By comparison, a very popular and widely used framework like React has ~40M weekly downloads.
Yes, however since go Also "Local Time" in RFC3339 defines "local time" as the local system at a given local UTC offset. "Unqualified Local Time" is what it refers to as what ABSL calls civil time. 🤷 naming is hard. |
I don't think use in other languages is good evidence, different languages are used for different purposes, we want to see how this addition would fit in to the Go ecosystem. Note that downloads provide inflated numbers and should not be considered an accurate representation of popularity. It's why we generally prefer instances of use in code, and by proxy, package imports in Go. |
@seankhliao who is "we"? |
For what it's worth, I wasn't intending #19700 (comment) to be a trick question or for it to be a barrier to this proposal being accepted. Quite the contrary actually: I expected that it wouldn't be hard at all to find examples of library APIs that need to include values corresponding to the concepts we've been discussing in this issue, but have ended up employing weird workarounds to deal with the fact that there's no single idiomatic representation of these concepts in the ecosystem. Here's one example which I found by using GitHub Code Search to find references to some different representations of "civil date" and then use some human intuition to try to spot cases which seemed like they might be compensating in some way for a lack of a common vocabulary type to talk about dates in the Go ecosystem. The following example is from a codebase called "Sybil". I have no affiliation whatsoever with this codebase and don't mean any kind of endorsement of it; it's just an example I found in my research. The repository says it's under the MIT license and so I'm sharing the following snippets of it in good faith under the terms of that license. Inside this codebase there is an interface called DividendRequest(ctx context.Context, ticker string, start, end time.Time) ([]*ent.DividendCreate, []time.Time, error) This takes two There are currently two implementations of
Some general observations, then:
I expect that having a Since these three codebases were all seemingly developed by disjoint sets of authors, a third-party library offering a date type could only solve this problem if all three groups had independently chosen the same third-party. |
@seankhliao thank you for weighing in. Like others here, I would like to gently challenge your position: Downloads as a metric
I'm certainly not claiming that downloads are a perfect metric. They have inflation (CI/CD, bots, etc.) and deflation (i.e., caching - once a version is downloaded, package managers won't download it again unless forced). My comment #19700 (comment) includes a link if you're curious to learn more. Despite that, that's why I referenced a irrefutably popular package like React in that comment as a comparison. For whatever imperfection downloads has as a metric, it includes uses in applications and libraries alike both of open and closed source. The numbers themselves in isolation aren't as meaningful, but comparing the metric to something known to be popular and widely useful still has meaning. They share the imperfections (systemic error / bias), and the correlation, therefore, can still be proof of the trend of popularity regardless. Don't take my word for it, that's a known statistical fact about correlation. Package Imports in GoPackage imports in Go as a metric is also imperfect (and perhaps more).
It also is not a good barometer for whether something is widely applicable enough to live in a stdlib or not. The Ignoring other languages
This is a reduction. Go is a general purpose language - like many others. It has differences in design and philosophy, yes, but it is not so singular as to abhor applicability of a use-case though it be from a separate language. Moreover, this argument is in danger of an Appeal To Ignorance fallacy. We can still learn from other languages faults and successes as evidence. Rejecting this as evidence of the use-case simply because "it's not Go" is not compelling. For instance, I particularly picked the Javascript ecosystem because, like Go, there is only one representation of time in the standard library for Javascript - its (poorly named) The Javascript community there has attempted to fill the gap this proposal describes with those 3rd-party packages I listed in that earlier comment. Use the Dependants metric if you reject downloads - tens of thousands of other packages leverage those various different interpretations of the "civil" date/time. This produces a large interoperability challenge in the language. As an example, look at a package trying to provide a framework for giving users a Datepicker: https://www.npmjs.com/package/@mui/x-date-pickers - it has to support many of these popular libraries (Luxon, Day.js, date-fns, Moment.js) rather than a shared singular one the language could have provided. Numbers as requestedTo provide a summary of the numbers you prefer (bearing in mind the imperfections of this metric I described above):
I picked these three by searching A resolution as requestedFinally, 8 years is a long time for this proposal to sit in purgatory. As this repo's readme states, the goal is:
I think, whatever the outcome, the committee should take up this proposal to reach a long-deserved resolution on this. |
I propose a package with minimal implementations of the types Date, Time and DateTime, which represent times without a corresponding location.
A civil time or date does not represent a point or interval of time, but they are useful for representing events transpiring between humans. For example, your birthday begins at midnight on your birthdate regardless of where you are in the world. If you're turning 21, you can buy a drink in New York at midnight, but teleport instantaneously to San Francisco and you'll be denied, because it is 9 PM the day before.
In practice, the main motivation for these types is to represent values in database-like storage systems, like BigQuery and Spanner (and other, non-Google products).
The package currently exists at cloud.google.com/go/civil, and has been in use by the BigQuery and Spanner client libraries for a few months. For now, I'd like to move it to golang.org/x/time/civil. It is probably too esoteric to be worth including in the standard library, but if there were ever a "second-tier" set of packages that augmented the standard library, it could live there. (See #17244.)
A CL is in progress at https://go-review.googlesource.com/c/38571.
The text was updated successfully, but these errors were encountered: