Time durations using a total-seconds representation =================================================== This project implements a class representing a duration in hours, minutes, and seconds. Unlike a time of day, a duration may contain more than 24 hours and may be negative. For example, 125:17:42 -0:03:05 -27:15:30 are all valid durations. This is a teaching example. It implements the same public operations as the component-based Time_hms class, but uses a different internal representation. The goal is to show how encapsulation allows the same abstraction to be implemented in different ways, and how the choice of representation affects the complexity of the implementation. The representation ================== The class stores the entire signed duration in one private member: std::int64_t seconds_; Positive values represent positive durations, negative values represent negative durations, and zero represents zero. There are no separate fields for hours, minutes, or seconds, and no separate Boolean sign. Why store seconds? ================== Humans normally describe durations using hours, minutes, and seconds, so storing those components separately may initially seem natural. However, a class's internal representation need not resemble the way its values are entered or displayed. When the duration is stored as a signed count of seconds, addition, subtraction, comparison, and negation correspond directly to ordinary integer operations. There is no need to carry seconds into minutes, borrow minutes from hours, or separately determine the sign of an arithmetic result. The tradeoff is that constructing a duration and accessing or displaying its components require conversions. Construction ============ The public constructor accepts non-negative hours, minutes, and seconds: Time_s t{ 2, 15, 30 }; This represents 2:15:30 The hour argument is unsigned. The minute and second arguments must each be between 0 and 59; otherwise the constructor throws std::invalid_argument. The constructor calculates the total number of seconds: 3600*hours + 60*minutes + seconds Negative durations are constructed using unary minus: -Time_s{ 0, 3, 5 } which represents -0:03:05 The public constructor cannot express a negative duration by writing -0 for the hours: the integers -0 and 0 are identical. Internally, a private constructor accepts a signed count of seconds. This makes it straightforward for arithmetic operations to construct their results. Accessing components and determining the sign ============================================= The hours(), minutes(), and seconds() accessors cannot simply return stored components. Instead, they recover the magnitude of the duration using integer division and remainder: hours = magnitude / 3600 minutes = (magnitude / 60) % 60 seconds = magnitude % 60 The magnitude is calculated before extracting components so that the accessors return non-negative values even for negative durations. The implementation uses an unsigned 64-bit magnitude to avoid a problem with the most negative signed 64-bit integer, whose positive magnitude cannot be represented as std::int64_t. The negative() accessor simply tests whether seconds_ is less than zero. There is only one zero value, so negative zero cannot arise. Arithmetic ========== Two Time_s objects can be added or subtracted: t + u t - u The calculations are essentially seconds_ + other.seconds_ seconds_ - other.seconds_ Compound assignment modifies the existing object: t += u; t -= u; Unary plus leaves the duration unchanged, while unary minus changes its sign. Comparisons likewise use the ordinary ordering of signed integers; for example, operator< compares the stored seconds directly. Prefix and postfix increment and decrement change a duration by one second: ++t; t++; --t; t--; As usual in C++, prefix operators return the updated object, while postfix operators return the value before the change. Unlike an earlier version of this example, the public interface does not provide arithmetic operators taking raw integer seconds, such as t + 90 t -= 90 The intended arithmetic operands are Time_s objects. To add a duration of 90 seconds, for example, construct another duration: t += Time_s{ 0, 1, 30 }; The examples focus on ordinary durations and do not attempt to handle all arithmetic at the extreme limits of a signed 64-bit integer. Output ====== The to_string() function produces a familiar representation: [-]HHH:MM:SS Hours have at least one digit, and minutes and seconds have exactly two digits. A minus sign appears only for negative durations. For example, -37:15:42 Because the components are not stored individually, formatting first recovers the hours, minutes, and seconds. The stream insertion operator uses the same representation as to_string(). Storage and range ================= The class contains only std::int64_t seconds_; so an object normally occupies eight bytes. The demonstration program can report sizeof(Time_s) to show the actual size on your system. A signed 64-bit count of seconds has a much larger internal numerical range than a representation using a 32-bit hour component. However, the public hours() accessor still returns std::uint32_t to match the Time_hms interface. The examples therefore assume durations whose components fit the common public interface. Comparing the two designs ========================= The component-based Time_hms design makes construction, component access, and formatting particularly simple. However, arithmetic must handle carries, borrows, magnitudes, and signs directly in the component representation. The total-seconds Time_s design makes arithmetic, comparisons, and sign handling simple, but construction, accessors, and formatting require conversions. Both implementations provide corresponding public operations, so the same kinds of client expressions can be used with either class. Their class names differ, and a demonstration program may test both classes side by side. This illustrates a central principle of encapsulation: client code should depend on a class's public interface rather than on its private data representation. The representation most convenient for humans need not be the representation most convenient for computation. This is an instructional exercise, not a production-quality duration library. Its purpose is to make the consequences of design choices visible, rather than to address every possible range or overflow case. Files ===== Time_s.hpp Declares the Time_s class and its public interface. Time_s.cpp Implements the class using a single signed 64-bit count of seconds. main.cpp Demonstrates construction, accessors, positive and negative durations, arithmetic, comparisons, increment and decrement, output, and object size. It may exercise both implementations. Compiling ========= If main.cpp uses only Time_s, for example: g++ -std=c++17 -Wall -Wextra -pedantic \ Time_s.cpp main.cpp -o time If main.cpp demonstrates Time_s and Time_hms together, include both implementation files: g++ -std=c++17 -Wall -Wextra -pedantic \ Time_s.cpp Time_hms.cpp main.cpp -o time Then run: ./time