Ada-Rapporteur-Group / User-Community-Input

Ada User Community Input Working Group - Github Mirror Prototype
27 stars 1 forks source link

"Seconds" function (with a Time_Zone parameter) would be useful in Ada.Calendar.Formatting #18

Closed sttaft closed 11 months ago

sttaft commented 2 years ago

Ada.Calendar.Formatting should provide a function to get Day_Duration with a time_zone offset.

Currently, the simplest call is to use the Split function that returns ""Seconds"" as a Day_Duration, along with all the other parameters.... if all you want is the Day_Duration in seconds, it stands out that there's a function provided for everything else, but not for Day_Duration.


Ada.Calendar.Formatting should have a 'Seconds' function, similar to Ada.Calendar, but with a Time_Zone parameter defaulted to '0', just like the other time component functions in Ada.Calendar.Formatting.

Joshua Fletcher, fletcher.ua@gmail.com, 7/19/2022 15:16:19

ARG-Editor commented 1 year ago

I believe this omission was intentional, as having a function Second and a function Seconds in the same package would be rather confusing. The primary intent of the Formatting package was to be able to get Hours:Minutes:Seconds (as that capability was sadly lacking from Ada.Calendar), and all of the other stuff was added afterwards. It would be unfortunate to obscure the important capability for a secondary one.

sttaft commented 1 year ago

It is rather confusing in general how seconds are handled by this package, since in some cases (e.g. in function Second) they are a count of seconds since the most recent minute ended, and in other cases (e.g. in function Split) they represent the duration since the most recent day ended. It seems easy enough to add another function, but I would agree that "Seconds" would not be clear. Perhaps "Seconds_In_Day" with parameters Time and Time_Zone. It would make sense to declare it immediately after the "Day" function.

ARG-Editor commented 1 year ago

The Day_Duration type is rather vestigial, it was defined by the original Ada.Calendar but really does not have much use in the new packages. Indeed, I haven't been able to think of a single use case for Day_Duration that wouldn't be better handled by doing operations directly on a Time value. The problem being that doing operations just on a Day_Duration potentially causes issues when near midnight (that is, when the day might be changing), and the operations in Ada.Calendar and Ada.Calendar.Arithmetic give the correct answers in such cases. I had a lot of Ada 83 code that skimped on those calculations, and thus fail at month-end or year-end because of incomplete handling of those end conditions. There is no reason for new or heavily modified code (and any code using time zones must be one of those) to be making that mistake.

So I guess I would like to understand what the usage case for Day_Duration (separate from a Time) is before adding a function which is confusing and which one should generally avoid. (My recollection is that the Split giving a Day_Duration with a Time_Zone parameter was added by the ARG, I don't believe it was in the original design for the same reason of general uselessness.)

ARG-Editor commented 1 year ago

I've created AI22-0049-1 for this issue, based on Tucker's suggestion. We'll discuss it at an upcoming meeting.

joshua-c-fletcher commented 1 year ago

I think it’s rather arbitrary to declare the Day_Duration type as vestigial. It’s a subtype of the Duration type, which is as current as ever, and if there’s a context where the time duration into a day is needed, Day_Duration is appropriate.

Ada.Calendar.Formatting does have a Seconds_Of function that builds a Day_Duration value and it also has a Split function that splits a Day_Duration value, so saying it “does not have much use in the new packages” really depends on whether you use those newer subprograms - unless we’re not talking about Ada.Calendar.Formatting as a new package, since it’s been around for a while, now, too.

In fact… if all you want is time-of-day represented in hour, minute, second and sub-second values, the missing Seconds function becomes apparent again. Using Ada.Calendar.Formatting, there are two options: 1) call the separate “Hour”, “Minute”, “Second”, and Sub-Second functions individually - which basically means splitting the Time value 4 times or 2) call Split on the Time value once, and throw away the Year, Month, and Day values.

A 3rd option would be to call a “Seconds” function giving Time-of-day as a Day_Duration, and pass the result to the existing Split function to split the Day_Duration into Hours, Minutes, Seconds, etc.

It’s conspicuous that the Split function exists, but not the Seconds function to give you the input for it. Ostensibly, the input comes from calling Ada.Calendar.Seconds, except that doesn’t give you the time zone control that the rest of the Formatting package does. There is another Split function that gives a Day_Duration value from a Time value, but for the Time-Of-Day use case, you’d end up declaring dummy variables for the other parts (Year, Month, Day) again.

I ran into wanting a Seconds function in particular because of an interface in a program that was mixed language Ada and C++. On the C++ side was a Qt QTime class, which represents time-of-day. The Day_Duration is the best fit for a widget that shows Time-of-day without reference to the date. The language boundary could have passed hours/minutes/seconds, but it was chosen to pass the value as a single numerical value such as milliseconds. As a result, the choice was to get Seconds representing the seconds in the day, scale that to the right integral value, and pass that to the C/C++ side.

Although Ada.Calendar.Formating adds a Time_Zone modified version of most of the functions from its parent Ada.Calendar package, the Seconds function is missing, and I think conspicuously so. To get the seconds in the day with Time Zone control, I had to call Split on the date and throw-away the Year, Month, and Day number values.

Fundamentally, though, I think the issue is about consistency. There is an obvious parallel between the functions in Ada.Calendar.Formatting and its parent package, and nobody discovers Ada.Calendar.Formatting without first being introduced to Ada.Calendar.

While I recognize the argument that a “Seconds” function returning Day_Duration and a Second function returning Second_Number could, on the surface, be confusing, I’m not convinced we need a different name because the types are completely different. Day_Duration is a subtype of Duration - a fixed-point type, and a Seconds function returning the time in a day is well established. The Second function returns a number from 0 to 59, as a Natural.

If we were going to leave out Day_Duration altogether, the Seconds_Of that builds a Day_Duration, the Split function that Splits up a Day_Duration, and the Split function that splits up time and returns Day_Duration (in a parameter simply called “Seconds”) in Ada.Calendar.Formatting shouldn’t have been included. I think it is good to have all of those functions, however, but with them present, the Seconds function is notably absent.

Therefore, my vote would be to include a Seconds function (immediately after the Day function, like Tucker suggested), but to simply call it Seconds, for consistency with Ada.Calendar, and with the out parameter name in the existing Split function, and with the in parameter name in the existing Time_Of function, which all use Day_Duration, and call it 'Seconds'.

ARG-Editor commented 1 year ago

Thanks for the reply. I'd don't agree with most of it, but that's life.

There are lots of things in Ada language-defined package specifications that are perfectly good in a vacuum, but really have little practical use. Day_Duration is far from the worst offender, but it certainly is in that category.

If it had been solely up to me, only the Split (with individual parameters) and Time_Of function would have been provided, since the rest of them are seductively convenient but expensive (given that every implementation I've seen calls Split internally). I believe that the other stuff was added more out of a silly consistency rather than usefulness.

Almost all uses of Day_Duration are at risk of overflow (and that includes the ones in Calendar, it is very painful to keep it from raising an exception in boundary cases). Duration is not much better (since there is no guaranteed range), but at least you haven't self-imposed problems on oneself. Your example is interesting mainly in that it proves that someone will find a use for any nonsense. ;-) Best to keep the nonsense out of the language in the first place. (A widget that doesn't have an option to display the date is evil. :-)

Even so, I would not be spending time on this if the name wasn't a common typo different than an existing function. Function names in a single package should never differ by a single trailing character, particularly an 's'. That sort of thing ensures that one will get a much more useless message if one makes the common typo (the sort of thing I do with regularity), especially in realistic scenarios (where the function result is passed to another subprogram).

It seems that the most important thing here is to provide a way to get the (dubious) value, and not precisely how that is done. I can understand that it is annoying to call a procedure and discard all of the results to get a single value (even if hardly anyone should ever want that value). The name of the function should not matter as much. To say otherwise is to double down on foolish consistency.

joshua-c-fletcher commented 1 year ago

If we absolutely need to avoid the ambiguity of “Seconds” for the Seconds-in-a-day Day_Duration versus “Second” being the count of seconds since the last minute (corresponding to the singular “Hour” and “Minute” functions), Tucker’s suggestion of “Seconds_In_Day”, after the day function is OK.

I do think subprogram names do matter, though, and following existing patterns can help make name selection easier when established naming exists. Readability and Intuitiveness are also good guides for making language decisions, but consistency is part of that, too.

Ada’s type system would help to avoid issues due to typos, when using the wrong one of the similarly named functions. If you tried, for example, to call the “Split” function that takes a Day_Duration by passing it the value of the count-based Second function, the compiler would identify the type mismatch and give a meaningful error message, not a useless one. The ranges on the types make sense, they are not defined in any far-off place, and there is consistency in the naming for how they are used, currently, like it or not. There’s just the one function missing (Seconds, or Seconds_In_Day if need be).

I find the disdain for consistency rather shocking. I would think anyone interested in defining a language would place a high value on consistency. I think it is also good to ‘think outside the box’ and expand one’s horizons, but within established norms, one should strive to be as consistent as possible, and when adding new features, consistency is important there, too.

If one uses Day_Duration for getting Seconds-in-a-day, there shouldn’t be a risk of overflow - certainly no more than already exists with existing functions, but short of accounting for leap-seconds (which is already done another way) a snapshot time value should always be able to populate Day_Duration in-range without overflow in that call.

If one is using Day_Duration for setting Seconds-in-a-day using a known set of values (with a known, compatible date and time of day), there shouldn’t be a risk of overflow there, either, since any value of Day_Duration should be settable for any day.

If one is managing the day and the time-of-day independently and ends up with day rollover issues, that’s not a good use of the Time_Of function; that someone should re-think their approach. That bad usage doesn’t invalidate the whole concept of the established Day_Duration type. In fact, the Constraint_Errors you might get using Day_Duration (if you’re calculating them) help to show which approaches are no good (if not caught before then!).

The fact that there’s no guaranteed range for the Standard Duration base type itself is rather awful, though. I have used an Ada implementation where the standard Duration type’s range was less than a few days, and it was unpleasant. The Day_Duration in the language does, in fact, guarantee that the Duration type is at least a day long.

I much prefer implementations where Duration and Time actually have the same implementation under-the-hood, or at least where one can subtract Time values with any reasonable date values from history, or foreseeable future, and not risk a Constraint_Error when getting a delta-time in the form of a Duration. When the range is so short, one almost needs to implement their own Calendar package with an alternative Duration type… But that isn’t the topic of this thread.

Are widgets that show time without the date evil? I’d rather leave that up to the implementer; lots of clocks don’t show date. Sometimes third-party interfaces provide time-of-day values without a date reference, and the Ada.Calendar.Time type doesn’t really fit for that without inventing a date reference; Day_Duration, however, does fit.

Ada has a type for representing Date/Time values (Ada.Calendar.Time).
Ada has a type for representing Time-Of-Day (Day_Duration). Ada has a two types for representing delta times (Duration and Time_Span)… I think all of those are valid and useful within their context.

This proposal makes other parts of the existing package more useful, and make more sense - in particular the existing Day_Duration-related Split and Time_Of subprograms… which are already there, even if not everyone likes them.

Consistency doesn’t necessarily need to be the highest or most important value; deviations can sometimes be justified, but I do not at all think that striving for consistency is either silly or foolish; quite the contrary.

ARG-Editor commented 11 months ago

AI22-0049-1 has been approved by the ARG, so this topic is closed.