Time durations using a component representation =============================================== This project implements a class representing a duration in hours, minutes, and seconds. It is a teaching example intended to illustrate how the choice of an internal representation affects the complexity of an implementation. Unlike a time of day, a duration may contain arbitrarily many hours (subject to the range of the stored hour field). For example, 125:17:42 is a valid duration. A duration may also be negative: -0:03:05 -27:15:30 The class is designed to provide the same public operations as the alternative Time_s implementation, which stores a signed count of seconds. Comparing the two implementations illustrates encapsulation: clients need not know how a duration is represented internally. The representation ================== This implementation stores four private member variables: std::uint32_t hours_; std::uint8_t minutes_; std::uint8_t seconds_; bool is_negative_; The first three fields store the magnitude of the duration. The Boolean field stores its sign. Minutes and seconds are always between 0 and 59, so eight bits are sufficient for each. The hours are stored using 32 bits, allowing more than four billion hours. The fields require seven bytes in total, but padding and alignment may affect the actual size of the object. The demonstration program prints sizeof(Time_hms) so that you can observe the size chosen by your compiler. Why is the sign stored separately? ================================== It might initially seem that the hours could simply be stored using a signed integer. This would not work for durations whose magnitude is less than one hour. For example, -0:03:05 has an hour component of zero. In C++, the integer values 0 and -0 are identical, so the sign cannot be stored in the hour component. The sign is therefore stored separately. Constructing a Time_hms ======================= The public constructor constructs only non-negative durations: Time_hms t{ 2, 15, 30 }; creates 2:15:30 The hour argument is unsigned, and the minute and second arguments must each be between 0 and 59. An invalid minute or second argument causes the constructor to throw std::invalid_argument. A negative duration is constructed by applying unary minus: Time_hms t{ 0, 3, 5 }; std::cout << -t; prints -0:03:05 This is necessary because Time_hms{ -0, 3, 5 } could not specify a negative duration: -0 and 0 are the same integer value. Zero ==== There is only one permitted representation of zero. The class maintains the invariant that a duration with zero hours, minutes, and seconds is never negative. The object Time_hms{ 0, 0, 0 } is non-negative. Applying unary minus to zero still produces 0:00:00 rather than -0:00:00 Accessors and comparisons ========================= The hours(), minutes(), and seconds() accessors return the stored magnitude components. The negative() accessor indicates whether the duration is negative. All six comparison operators are supported: t == u t != u t < u t <= u t > u t >= u Comparisons must take account of the signs. For durations with the same sign, the magnitudes are compared by examining hours, then minutes, then seconds. For two negative durations, the order of the magnitudes is reversed. Arithmetic ========== Time_hms objects may be added and subtracted: t + u t - u The result may be positive, zero, or negative. Compound assignment is also supported: t += u; t -= u; Unary plus and unary minus are supported: +t -t The class does not provide arithmetic operators that take an integer number of seconds. Arithmetic operands are Time_hms objects. The prefix and postfix increment and decrement operators change a duration by one second: ++t; t++; --t; t--; As usual in C++, prefix operators return the updated value, while postfix operators return the previous value. A complication of this representation ===================================== The component representation is convenient for construction, accessing the components, and displaying a duration. Arithmetic is less convenient. This implementation deliberately performs arithmetic directly on the stored hours, minutes, and seconds rather than converting the durations into signed totals of seconds. For example, adding 2:45:50 1:30:25 requires adding the seconds first. Since 50 + 25 = 75, the result has 15 seconds and carries one minute. The minutes then total 45 + 30 + 1 = 76, so the result has 16 minutes and carries one hour. The final result is 4:16:15 Subtraction can require borrowing one minute to obtain 60 more seconds, or borrowing one hour to obtain 60 more minutes. Signs introduce further complications. Adding durations with opposite signs is effectively a subtraction of magnitudes. The implementation must determine which magnitude is larger to choose the sign of the result. Subtracting durations may similarly become an addition of magnitudes. These operations are manageable, but they require considerably more logic than ordinary integer arithmetic. Increment and decrement also have to respect component boundaries and transitions through zero. This motivates the alternative Time_s design: instead of storing components and implementing carrying, borrowing, and sign handling, why not store the duration as a single signed number of seconds? The public interface can remain the same even though the internal representation and implementation are very different. Output ====== The to_string() function produces a human-readable duration in the form [-]HH:MM:SS with at least one digit for hours and exactly two digits each for minutes and seconds. The minus sign appears only for a negative duration. The stream insertion operator (<<) uses the same format. Files ===== Time_hms.hpp Declares the Time_hms class and its public interface. Time_hms.cpp Defines the constructors, accessors, comparisons, arithmetic operators, increment and decrement operators, and output operations. main.cpp Demonstrates construction, accessors, positive and negative durations, arithmetic, comparisons, compound assignment, increment and decrement, and the size of a Time_hms object. Compiling ========= For example: g++ -std=c++17 -Wall -Wextra -pedantic \ Time_hms.cpp main.cpp -o time Then run: ./time If main.cpp also exercises the Time_s implementation, include Time_s.cpp in the compilation command as well.