text: //----------------------------------------------------------------------------
  //
  // TSDuck - The MPEG Transport Stream Toolkit
  // Copyright (c) 2005-2020, Thierry Lelegard
  // All rights reserved.
  //
  // Redistribution and use in source and binary forms, with or without
  // modification, are permitted provided that the following conditions are met:
  //
  // 1. Redistributions of source code must retain the above copyright notice,
  //    this list of conditions and the following disclaimer.
  // 2. Redistributions in binary form must reproduce the above copyright
  //    notice, this list of conditions and the following disclaimer in the
  //    documentation and/or other materials provided with the distribution.
  //
  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  // THE POSSIBILITY OF SUCH DAMAGE.
  //
  //----------------------------------------------------------------------------
  //!
  //!  @file
  //!  Declare the ts::Time class.
  //!
  //----------------------------------------------------------------------------
  
  #pragma once
  #include "tsPlatform.h"
  #include "tsException.h"
  #include "tsUString.h"
  
  namespace ts {
      //!
      //! The @c Time class implements a basic representation of time.
      //! @ingroup system
      //!
      //! The implementation is based on the operating system representation
      //! of time. This means that the range of representable time values
      //! may vary from one system to another. You may use the constants
      //! @link Epoch @endlink and @link Apocalypse @endlink as first and
      //! last representable time values.
      //!
      //! An instance of @c Time may be indifferently interpreted as a local time
      //! or UTC time value. Methods are provided to convert between local and
      //! UTC values. The accuracy of these conversions depend on the configuration
      //! of the operating system.
      //!
      //! The implementation of the class is designed to be light and fast so that
      //! @c Time objects may be copied without overhead.
      //! The class is not polymorphic, there is no virtual methods and no vtable.
      //! In fact, the actual representation is only a 64-bit integer.
      //!
      class TSDUCKDLL Time
      {
      public:
          //!
          //! Fatal low-level time error.
          //!
          TS_DECLARE_EXCEPTION(TimeError);
  
          //!
          //! Default constructor.
          //! The initial value is the Epoch.
          //!
          Time() : _value(0) {}
  
          //!
          //! Constructor from broken-down date fields.
          //! @param [in] year Number of years.
          //! @param [in] month Number of months (1 to 12).
          //! @param [in] day Number of days (1 to 31).
          //! @param [in] hour Number of hours (0 to 23).
          //! @param [in] minute Number of minutes (0 to 59).
          //! @param [in] second Number of seconds (0 to 59).
          //! @param [in] millisecond Number of milliseconds (0 to 999).
          //! @throw ts::Time::TimeError If any field is out of range
          //! or if the resulting time is outside the representable range
          //! for the local operating system.
          //!
          Time(int year, int month, int day, int hour, int minute, int second = 0, int millisecond = 0);
  
          //!
          //! Operator Time + MilliSecond => Time.
          //! @param [in] duration A number of milliseconds.
          //! @return A @c Time object representing this object plus the
          //! specified number of milliseconds.
          //!
          Time operator+(const MilliSecond& duration) const
          {
              return Time(_value + duration * TICKS_PER_MS);
          }
  
          //!
          //! Operator Time - MilliSecond => Time.
          //! @param [in] duration A number of milliseconds.
          //! @return A @c Time object representing this object minus the
          //! specified number of milliseconds.
          //!
          Time operator-(const MilliSecond& duration) const
          {
              return Time(_value - duration * TICKS_PER_MS);
          }
  
          //!
          //! Operator Time += MilliSecond
          //! @param [in] duration A number of milliseconds to add to this object.
          //! @return A reference to this object.
          //!
          Time& operator+=(const MilliSecond& duration)
          {
              _value += duration * TICKS_PER_MS;
              return *this;
          }
  
          //!
          //! Operator Time -= MilliSecond
          //! @param [in] duration A number of milliseconds to substract from this object.
          //! @return A reference to this object.
          //!
          Time& operator-=(const MilliSecond& duration)
          {
              _value -= duration * TICKS_PER_MS;
              return *this;
          }
  
          //!
          //! Operator: Time - Time => MilliSecond.
          //! @param [in] other Another time to substract from this object.
          //! @return The duration, in milliseconds, between this object and the @a other object.
          //!
          MilliSecond operator-(const Time& other) const
          {
              return (_value - other._value) / TICKS_PER_MS;
          }
  
          //!
          //! Equality operator.
          //! @param [in] other Another time to compare with this object.
          //! @return @c True is this object is equal to the @a other object,
          //! @c false otherwise.
          //!
          bool operator==(const Time& other) const
          {
              return _value == other._value;
          }
  
          //!
          //! Unequality operator.
          //! @param [in] other Another time to compare with this object.
          //! @return @c True is this object is different from the @a other object,
          //! @c false otherwise.
          //!
          bool operator!=(const Time& other) const
          {
              return _value != other._value;
          }
  
          //!
          //! Lower operator.
          //! @param [in] other Another time to compare with this object.
          //! @return @c True is this time is before the @a other object time,
          //! @c false otherwise.
          //!
          bool operator<(const Time& other) const
          {
              return _value < other._value;
          }
  
          //!
          //! Lower or equal operator.
          //! @param [in] other Another time to compare with this object.
          //! @return @c True is this time is before or equal to the @a other object time,
          //! @c false otherwise.
          //!
          bool operator<=(const Time& other) const
          {
              return _value <= other._value;
          }
  
          //!
          //! Greater operator.
          //! @param [in] other Another time to compare with this object.
          //! @return @c True is this time is after the @a other object time,
          //! @c false otherwise.
          //!
          bool operator>(const Time& other) const
          {
              return _value > other._value;
          }
  
          //!
          //! Greater or equal operator.
          //! @param [in] other Another time to compare with this object.
          //! @return @c True is this time is after or equal to the @a other object time,
          //! @c false otherwise.
          //!
          bool operator>=(const Time& other) const
          {
              return _value >= other._value;
          }
  
          //!
          //! Broken-down fields of a time value.
          //! This inner class is a simple aggregation of date fields with public members.
          //!
          struct TSDUCKDLL Fields
          {
              int year;        //!< Number of years.
              int month;       //!< Number of months (1 to 12).
              int day;         //!< Number of days (1 to 31).
              int hour;        //!< Number of hours (0 to 23).
              int minute;      //!< Number of minutes (0 to 59).
              int second;      //!< Number of seconds (0 to 59).
              int millisecond; //!< Number of milliseconds (0 to 999).
  
              //!
              //! Constructor.
              //! There is no verification of the field values.
              //! @param [in] year_ Number of years.
              //! @param [in] month_ Number of months (1 to 12).
              //! @param [in] day_ Number of days (1 to 31).
              //! @param [in] hour_ Number of hours (0 to 23).
              //! @param [in] minute_ Number of minutes (0 to 59).
              //! @param [in] second_ Number of seconds (0 to 59).
              //! @param [in] millisecond_ Number of milliseconds (0 to 999).
              //!
              Fields(int year_ = 0, int month_ = 0, int day_ = 0, int hour_ = 0, int minute_ = 0, int second_ = 0, int millisecond_ = 0);
  
              //!
              //! Equality operator.
              //! @param [in] other Another @c Fields to compare with this object.
              //! @return @c True is this object is equal to the @a other object,
              //! @c false otherwise.
              //!
              bool operator==(const Fields& other) const;
  
              //!
              //! Unequality operator.
              //! @param [in] other Another @c Fields to compare with this object.
              //! @return @c True is this object is different from the @a other object,
              //! @c false otherwise.
              //!
              bool operator!=(const Fields& other) const;
  
              //!
              //! Validation of the fields.
              //! @return True if the fields are valid, false otherwise.
              //!
              bool isValid() const;
          };
  
          //!
          //! Constructor from broken-down date fields in one single object.
          //! Can be used as a conversion operator from @c Time::Fields to @c Time.
          //! @param [in] fields The date fields.
          //! @throw ts::Time::TimeError If any field is out of range
          //! or if the resulting time is outside the representable range
          //! for the local operating system.
          //!
          Time(const Fields& fields);
  
          //!
          //! Conversion operator from @c Time to @c Time::Fields.
          //! @return A @c Time::Fields object containing the broken-down time.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          operator Fields() const;
  
          //!
          //! Convert a local time to UTC time.
          //! @return A UTC time from this object time, interpreted as a local time.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          Time localToUTC() const;
  
          //!
          //! Convert a UTC time to local time.
          //! @return A local time from this object time, interpreted as a UTC time.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          Time UTCToLocal() const;
  
          //!
          //! Convert a JST (Japan Standard Time) to UTC time.
          //! @return A UTC time from this object time, interpreted as a JST time.
          //!
          Time JSTToUTC() const;
  
          //!
          //! Convert a UTC time to JST (Japan Standard Time).
          //! @return A JST time from this object time, interpreted as a UTC time.
          //!
          Time UTCToJST() const;
  
          //!
          //! Flags indicating the list of time fields to display.
          //!
          enum FieldMask {
              YEAR        = 0x01,  //!< Display the year.
              MONTH       = 0x02,  //!< Display the month.
              DAY         = 0x04,  //!< Display the day.
              DATE        = YEAR | MONTH | DAY,  //!< Display the year, month and day.
              HOUR        = 0x08,  //!< Display the hours.
              MINUTE      = 0x10,  //!< Display the minutes.
              SECOND      = 0x20,  //!< Display the seconds.
              TIME        = HOUR | MINUTE | SECOND,  //!< Display the hours, minutes and seconds.
              DATETIME    = DATE | TIME,  //!< Display the year, month, day, hours, minutes and seconds.
              MILLISECOND = 0x40,  //!< Display the milliseconds.
              ALL         = DATE | TIME | MILLISECOND,  //!< Display all fields.
          };
  
          //!
          //! Format a string representation of a time.
          //! @param [in] fields A combination of option flags indicating which fields
          //! should be included. This is typically the result of or'ed values from the enum
          //! type FieldMask.
          //! @return A string containing the formatted date.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          UString format(int fields = ALL) const;
  
          //!
          //! Decode a time from a string.
          //! The resulting decoded time is stored in this object.
          //! @param [in] str A string describing a date and time in a rudimentary format.
          //! The string shall contain integer values describing the various fields
          //! in descending order, from year to millisecond. The integer fields can
          //! be separated by any combination of non-digit characters. The list of
          //! expected fields is given by the parameter @a fields.
          //! @param [in] fields A combination of option flags indicating which fields
          //! should be included. This is typically the result of or'ed values from the enum
          //! type FieldMask.
          //! @return True on success, false if the string cannot be decoded.
          //!
          bool decode(const UString& str, int fields = DATE | TIME);
  
          //!
          //! Conversion operator from @c Time to @c UString.
          //! Equivalent to <code>format (ALL)</code>.
          //! @return A string containing the formatted date.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          operator UString() const
          {
              return format(ALL);
          }
  
          //!
          //! Static method returning the current UTC time.
          //! @return The current UTC time.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          static Time CurrentUTC();
  
          //!
          //! Static method returning the current local time.
          //! @return The current local time.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          static Time CurrentLocalTime() {return CurrentUTC().UTCToLocal();}
  
          //!
          //! Get the beginning of the current hour.
          //! @return The time for the beginning of the current hour from this object time.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          Time thisHour() const;
  
          //!
          //! Get the beginning of the next hour.
          //! @return The time for the beginning of the next hour from this object time.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          Time nextHour() const
          {
              return thisHour() + MilliSecPerHour;
          }
  
          //!
          //! Get the beginning of the current day.
          //! @return The time for the beginning of the current day from this object time.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          Time thisDay() const;
  
          //!
          //! Get the beginning of the next day.
          //! @return The time for the beginning of the next day from this object time.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          Time nextDay() const
          {
              return thisDay() + MilliSecPerDay;
          }
  
          //!
          //! Get the beginning of the current month.
          //! @return The time for the beginning of the current month from this object time.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          Time thisMonth() const;
  
          //!
          //! Get the beginning of the next month.
          //! @return The time for the beginning of the next month from this object time.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          Time nextMonth() const;
  
          //!
          //! Get the beginning of the current year.
          //! @return The time for the beginning of the current year from this object time.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          Time thisYear() const;
  
          //!
          //! Get the beginning of the next year.
          //! @return The time for the beginning of the next year from this object time.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          Time nextYear() const;
  
          //!
          //! Get the beginning of the current hour, UTC.
          //! @return The time for the beginning of the current hour, UTC.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          static inline Time ThisHourUTC()
          {
              return CurrentUTC().thisHour();
          }
  
          //!
          //! Get the beginning of the current hour, local time.
          //! @return The time for the beginning of the current hour, local time.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          static inline Time ThisHourLocalTime()
          {
              return CurrentLocalTime().thisHour();
          }
  
          //!
          //! Get the beginning of the next hour, UTC.
          //! @return The time for the beginning of the next hour, UTC.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          static inline Time NextHourUTC()
          {
              return CurrentUTC().nextHour();
          }
  
          //!
          //! Get the beginning of the next hour, local time.
          //! @return The time for the beginning of the next hour, local time.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          static inline Time NextHourLocalTime()
          {
              return CurrentLocalTime().nextHour();
          }
  
          //!
          //! Get the beginning of the current day, UTC.
          //! @return The time for the beginning of the current day, UTC.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          static inline Time TodayUTC()
          {
              return CurrentUTC().thisDay();
          }
  
          //!
          //! Get the beginning of the current day, local time.
          //! @return The time for the beginning of the current day, local time.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          static inline Time TodayLocalTime()
          {
              return CurrentLocalTime().thisDay();
          }
  
          //!
          //! Get the beginning of the next day, UTC.
          //! @return The time for the beginning of the next day, UTC.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          static inline Time TomorrowUTC()
          {
              return CurrentUTC().nextDay();
          }
  
          //!
          //! Get the beginning of the next day, local time.
          //! @return The time for the beginning of the next day, local time.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          static inline Time TomorrowLocalTime()
          {
              return CurrentLocalTime().nextDay();
          }
  
          //!
          //! Get the beginning of the current month, UTC.
          //! @return The time for the beginning of the current month, UTC.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          static inline Time ThisMonthUTC()
          {
              return CurrentUTC().thisMonth();
          }
  
          //!
          //! Get the beginning of the current month, local time.
          //! @return The time for the beginning of the current month, local time.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          static inline Time ThisMonthLocalTime()
          {
              return CurrentLocalTime().thisMonth();
          }
  
          //!
          //! Get the beginning of the next month, UTC.
          //! @return The time for the beginning of the next month, UTC.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          static inline Time NextMonthUTC()
          {
              return CurrentUTC().nextMonth();
          }
  
          //!
          //! Get the beginning of the next month, local time.
          //! @return The time for the beginning of the next month, local time.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          static inline Time NextMonthLocalTime()
          {
              return CurrentLocalTime().nextMonth();
          }
  
          //!
          //! Get the beginning of the current year, UTC.
          //! @return The time for the beginning of the current year, UTC.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          static inline Time ThisYearUTC()
          {
              return CurrentUTC().thisYear();
          }
  
          //!
          //! Get the beginning of the current year, local time.
          //! @return The time for the beginning of the current year, local time.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          static inline Time ThisYearLocalTime()
          {
              return CurrentLocalTime().thisYear();
          }
  
          //!
          //! Get the beginning of the next year, UTC.
          //! @return The time for the beginning of the next year, UTC.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          static inline Time NextYearUTC()
          {
              return CurrentUTC().nextYear();
          }
  
          //!
          //! Get the beginning of the next year, local time.
          //! @return The time for the beginning of the next year, local time.
          //! @throw ts::Time::TimeError In case of operating system time error.
          //!
          static inline Time NextYearLocalTime()
          {
              return CurrentLocalTime().nextYear();
          }
  
          //!
          //! Check if a year is a leap year (29 days in February).
          //! @param [in] year The year to check.
          //! @return True if @a year is a leap year.
          //!
          static bool IsLeapYear(int year);
  
          //!
          //! Constant representing the Epoch, ie the first representable time
          //! on this operating system.
          //!
          static const Time Epoch;
  
          //!
          //! Constant representing the Epoch on UNIX operating systems.
          //! The UNIX epoch is 1 Jan 1970 00:00:00.
          //! It is assumed to be representable in all operating systems.
          //!
          static const Time UnixEpoch;
  
          //!
          //! Constant representing the GPS Epoch, 1980-01-06.
          //! It is assumed to be representable in all operating systems.
          //!
          static const Time GPSEpoch;
  
          //!
          //! Constant representing the End Of Time (at least on this operating system).
          //!
          static const Time Apocalypse;
  
          //!
          //! Constant representing the offset of the Julian Epoch from the
          //! time Epoch of the operating system (Julian epoch - Time epoch)
          //! in milliseconds.
          //!
          //! The Julian epoch is 17 Nov 1858 00:00:00.
          //! If this constant is negative, the Julian epoch if before the time
          //! epoch and cannot be represented as a @c Time object.
          //!
          static const MilliSecond JulianEpochOffset;
  
          //!
          //! Number of seconds between 1970-01-01 and 1980-01-06.
          //! This is the difference between ATSC time and Unix time.
          //! The ATSC system time is the number of GPS seconds since 00:00:00 UTC, January 6th, 1980.
          //! This value can be displayed on a Linux system using the command: `date +%s --date 1980-01-06utc`
          //!
          static const Second UnixEpochToGPS = 315964800;
  
          //!
          //! This static routine converts a UNIX @c time_t to a UTC time.
          //!
          //! @param [in] unixTime A UNIX @c time_t value. Must be unsigned. Can be 32 or 64 bits.
          //! @return The corresponding UTC time.
          //!
          static Time UnixTimeToUTC(uint64_t unixTime);
  
          //!
          //! Convert this time in a UNIX @c time_t.
          //!
          //! @return The corresponding Unix time on 64 bits.
          //!
          uint64_t toUnixTime() const;
  
          //!
          //! This static routine converts a number of GPS seconds to a UTC time.
          //!
          //! @param [in] gps The number of seconds since the GPS Epoch (1980-01-06).
          //! @return The corresponding UTC time.
          //!
          static Time GPSSecondsToUTC(Second gps);
  
          //!
          //! Convert this time to a number of seconds since 1980-01-06, the GPS epoch.
          //!
          //! @return The corresponding number of seconds on 64 bits.
          //!
          Second toGPSSeconds() const;
  
  #if defined(TS_WINDOWS) || defined(DOXYGEN)
          //!
          //! This static routine converts a Win32 @c FILETIME to @c MilliSecond (Microsoft Windows only).
          //!
          //! This function is available on Microsoft Windows systems only
          //! and should not be used on portable software.
          //!
          //! @param [in] fileTime A Win32 @c FILETIME value.
          //! @return The corresponding number of milliseconds.
          //!
          static MilliSecond Win32FileTimeToMilliSecond(const ::FILETIME& fileTime);
  
          //!
          //! This static routine converts a Win32 @c FILETIME to a UTC time (Microsoft Windows only).
          //!
          //! This function is available on Microsoft Windows systems only
          //! and should not be used on portable software.
          //!
          //! @param [in] fileTime A Win32 @c FILETIME value.
          //! @return The corresponding UTC time.
          //!
          static Time Win32FileTimeToUTC(const ::FILETIME& fileTime);
  #endif
  
  #if defined(TS_UNIX) || defined(DOXYGEN)
          //!
          //! This static routine gets a system clock and adds a delay in milliseconds (UNIX systems only).
          //!
          //! This function ensures that no overflow is possible.
          //! This function is available on UNIX systems only and should not be used on portable software.
          //!
          //! @param [in] clock Clock id, usually @c CLOCK_REALTIME or @c CLOCK_MONOTONIC.
          //! @param [in] delay Number of milliseconds to add to the current clock.
          //! @return Absolute time in nanoseconds according to @a clock.
          //!
          static NanoSecond UnixClockNanoSeconds(clockid_t clock, const MilliSecond& delay = 0);
  
          //!
          //! This static routine gets a system clock and adds a delay in milliseconds (UNIX systems only).
          //!
          //! This function ensures that no overflow is possible.
          //! This function is available on UNIX systems only and should not be used on portable software.
          //!
          //! @param [out] result A returned UNIX @c timespec value.
          //! @param [in] clock Clock id, usually @c CLOCK_REALTIME or @c CLOCK_MONOTONIC.
          //! @param [in] delay Number of milliseconds to add to the current real time clock.
          //!
          static void GetUnixClock(::timespec& result, clockid_t clock, const MilliSecond& delay = 0);
  #endif
  
      private:
          // A time is a 64-bit value. The resolution depends on the operating system.
          int64_t _value;
  
          // Private constructor from a 64-bit value
          Time(const int64_t& value) : _value(value) {}
  
          // Static private routine: Build the 64-bit value from fields
          static int64_t ToInt64(int year, int month, int day, int hour, int minute, int second, int millisecond);
  
          // Number of clock ticks per millisecond:
          static const int64_t TICKS_PER_MS =
  #if defined(TS_WINDOWS)
              // On Win32, a FILETIME is a 64-bit value representing the number
              // of 100-nanosecond intervals since January 1, 1601.
              // Number of FILETIME ticks per millisecond:
              10000;
  #else
              // On UNIX, a struct timeval is a 64-bit value representing the number
              // of microseconds since January 1, 1970
              1000;
  #endif
  
          // On Win32, a the FILETIME structure is binary-compatible with a 64-bit integer.
  #if defined(TS_WINDOWS)
          union FileTime {
              ::FILETIME ft;
              int64_t i;
          };
  #endif
      };
  }
  
  //!
  //! Output operator for the class @link ts::Time @endlink on standard text streams.
  //! @param [in,out] strm An standard stream in output mode.
  //! @param [in] time A @link ts::Time @endlink object.
  //! @return A reference to the @a strm object.
  //! @throw ts::Time::TimeError In case of operating system time error.
  //!
  TSDUCKDLL inline std::ostream& operator<<(std::ostream& strm, const ts::Time& time)
  {
      return strm << ts::UString(time);
  }
  