From df05cdc578d20df7bdb0eb90869b72c9ed911664 Mon Sep 17 00:00:00 2001
From: Vadim Zeitlin <vadim@wxwidgets.org>
Date: Tue, 13 Mar 2001 13:17:46 +0000
Subject: [PATCH] fixed wxTimeSpan::Format() to behave more reasonably

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@9509 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
---
 docs/changes.txt            |   1 +
 docs/latex/wx/datetime.tex  |   6 +-
 docs/latex/wx/timespan.tex  |  73 +++++++++++++++++++++-
 samples/console/console.cpp |  31 +++++++++-
 src/common/datetime.cpp     | 118 ++++++++++++++++++++++++++++++------
 5 files changed, 204 insertions(+), 25 deletions(-)

diff --git a/docs/changes.txt b/docs/changes.txt
index 914e6c9692..c8ded70912 100644
--- a/docs/changes.txt
+++ b/docs/changes.txt
@@ -15,6 +15,7 @@ wxBase:
 - wxCopyFile() respects the file permissions (Roland Scholz)
 - wxFTP::GetFileSize() added (Søren Erland Vestø)
 - wxDateTime::IsSameDate() bug fixed
+- wxTimeSpan::Format() now behaves more as expected, see docs
 
 All (GUI):
 
diff --git a/docs/latex/wx/datetime.tex b/docs/latex/wx/datetime.tex
index ed7d98cc68..c2d02f6808 100644
--- a/docs/latex/wx/datetime.tex
+++ b/docs/latex/wx/datetime.tex
@@ -145,7 +145,7 @@ values as parameter:
     };
 \end{verbatim}
 
-Differnet parst of the world use different conventions for the week start.
+Different parst of the world use different conventions for the week start.
 In some countries, the week starts on Sunday, while in others - on Monday.
 The ISO standard doesn't address this issue, so we support both conventions in
 the functions whose result depends on it (\helpref{GetWeekOfYear}{wxdatetimegetweekofyear} and 
@@ -174,8 +174,8 @@ No base class
 \wxheading{See also}
 
 \helpref{Date classes overview}{wxdatetimeoverview},\rtfsp
-wxTimeSpan,\rtfsp
-wxDateSpan,\rtfsp
+\helpref{wxTimeSpan}{wxtimespan},\rtfsp
+\helpref{wxDateSpan}{wxdatespan},\rtfsp
 \helpref{wxCalendarCtrl}{wxcalendarctrl}
 
 \latexignore{\rtfignore{\wxheading{Function groups}}}
diff --git a/docs/latex/wx/timespan.tex b/docs/latex/wx/timespan.tex
index b5909c76b6..1a884f1bd4 100644
--- a/docs/latex/wx/timespan.tex
+++ b/docs/latex/wx/timespan.tex
@@ -11,5 +11,76 @@
 
 \section{\class{wxTimeSpan}}\label{wxtimespan}
 
-TODO
+wxTimeSpan class represents a time interval.
+
+\wxheading{Derived from}
+
+No base class
+
+\wxheading{Include files}
+
+<wx/datetime.h>
+
+\wxheading{See also}
+
+\helpref{Date classes overview}{wxdatetimeoverview},\rtfsp
+\helpref{wxDateTime}{wxdatetime}
+
+\latexignore{\rtfignore{\wxheading{Function groups}}}
+
+\membersection{Static functions}
+
+\membersection{Constructors}
+
+\helpref{wxTimeSpan()}{wxtimespandef}
+\helpref{wxTimeSpan(hours, min, sec, msec)}{wxtimespan}
+
+\membersection{Accessors}
+
+\membersection{Operations}
+
+\membersection{Tests}
+
+\membersection{Formatting time spans}
+
+\helpref{Format}{wxtimespanformat}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Start of member function part                                               %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\helponly{\insertatlevel{2}{
+    \wxheading{Members}
+}}
+
+\membersection{wxTimeSpan::Format}\label{wxtimespanformat}
+
+\func{wxString}{Format}{\param{const wxChar * }{format = "\%H:\%M:\%S"}}
+
+Returns the string containing the formatted representation of the time span.
+The following format specifiers are allowed after \%:
+
+\twocolwidtha{5cm}%
+\begin{twocollist}\itemsep=0pt
+\twocolitem{H}{number of {\bf H}ours}
+\twocolitem{M}{number of {\bf M}inutes}
+\twocolitem{S}{number of {\bf S}econds}
+\twocolitem{l}{number of mi{\bf l}liseconds}
+\twocolitem{D}{number of {\bf D}ays}
+\twocolitem{E}{number of w{\bf E}eks}
+\twocolitem{\%}{the percent character}
+\end{twocollist}
+
+Note that, for example, the number of hours in the description above is not
+well defined: it can be either the total number of hours (for example, for a
+time span of $50$ hours this would be $50$) or just the hour part of the time
+span, which would be $2$ in this case as $50$ hours is equal to $2$ days and
+$2$ hours.
+
+wxTimeSpan resolves this ambiguity in the following way: if there had been,
+indeed, the {\tt \%D} format specified preceding the {\tt \%H}, then it is
+interpreted as $2$. Otherwise, it is $50$.
+
+The same applies to all other format specifiers: if they follow a specifier of
+larger unit, only the rest part is taken, otherwise the full value is used.
 
diff --git a/samples/console/console.cpp b/samples/console/console.cpp
index 4caca091af..e25ccf588d 100644
--- a/samples/console/console.cpp
+++ b/samples/console/console.cpp
@@ -37,7 +37,7 @@
 
 //#define TEST_ARRAYS
 //#define TEST_CMDLINE
-//#define TEST_DATETIME
+#define TEST_DATETIME
 //#define TEST_DIR
 //#define TEST_DLLLOADER
 //#define TEST_ENVIRON
@@ -3383,6 +3383,32 @@ static void TestTimeZoneBug()
     puts("");
 }
 
+static void TestTimeSpanFormat()
+{
+    puts("\n*** wxTimeSpan tests ***");
+
+    static const char *formats[] =
+    {
+        _T("(default) %H:%M:%S"),
+        _T("%E weeks and %D days"),
+        _T("%l milliseconds"),
+        _T("(with ms) %H:%M:%S:%l"),
+        _T("100%% of minutes is %M"),       // test "%%"
+        _T("%D days and %H hours"),
+    };
+
+    wxTimeSpan ts1(1, 2, 3, 4),
+                ts2(111, 222, 333);
+    for ( size_t n = 0; n < WXSIZEOF(formats); n++ )
+    {
+        printf("ts1 = %s\tts2 = %s\n",
+               ts1.Format(formats[n]).c_str(),
+               ts2.Format(formats[n]).c_str());
+    }
+
+    puts("");
+}
+
 #if 0
 
 // test compatibility with the old wxDate/wxTime classes
@@ -4402,7 +4428,7 @@ int main(int argc, char **argv)
 #endif // TEST_TIMER
 
 #ifdef TEST_DATETIME
-    if ( 1 )
+    if ( 0 )
     {
         TestTimeSet();
         TestTimeStatic();
@@ -4421,6 +4447,7 @@ int main(int argc, char **argv)
 
         TestTimeZoneBug();
     }
+    TestTimeSpanFormat();
     if ( 0 )
         TestDateTimeInteractive();
 #endif // TEST_DATETIME
diff --git a/src/common/datetime.cpp b/src/common/datetime.cpp
index 6dab762700..5198a3773d 100644
--- a/src/common/datetime.cpp
+++ b/src/common/datetime.cpp
@@ -152,10 +152,16 @@ IMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule, wxModule)
 // some trivial ones
 static const int MONTHS_IN_YEAR = 12;
 
-static const int SECONDS_IN_MINUTE = 60;
+static const int SEC_PER_MIN = 60;
+
+static const int MIN_PER_HOUR = 60;
+
+static const int HOURS_PER_DAY = 24;
 
 static const long SECONDS_PER_DAY = 86400l;
 
+static const int DAYS_PER_WEEK = 7;
+
 static const long MILLISECONDS_PER_DAY = 86400000l;
 
 // this is the integral part of JDN of the midnight of Jan 1, 1970
@@ -3454,13 +3460,41 @@ wxString wxTimeSpan::Format(const wxChar *format) const
     wxString str;
     str.Alloc(wxStrlen(format));
 
+    // Suppose we have wxTimeSpan ts(1 /* hour */, 2 /* min */, 3 /* sec */)
+    //
+    // Then, of course, ts.Format("%H:%M:%S") must return "01:02:03", but the
+    // question is what should ts.Format("%S") do? The code here returns "3273"
+    // in this case (i.e. the total number of seconds, not just seconds % 60)
+    // because, for me, this call means "give me entire time interval in
+    // seconds" and not "give me the seconds part of the time interval"
+    //
+    // If we agree that it should behave like this, it is clear that the
+    // interpretation of each format specifier depends on the presence of the
+    // other format specs in the string: if there was "%H" before "%M", we
+    // should use GetMinutes() % 60, otherwise just GetMinutes() &c
+
+    // we remember the most important unit found so far
+    enum TimeSpanPart
+    {
+        Part_Week,
+        Part_Day,
+        Part_Hour,
+        Part_Min,
+        Part_Sec,
+        Part_MSec
+    } partBiggest = Part_MSec;
+
     for ( const wxChar *pch = format; *pch; pch++ )
     {
         wxChar ch = *pch;
 
         if ( ch == _T('%') )
         {
-            wxString tmp;
+            // the start of the format specification of the printf() below
+            wxString fmtPrefix = _T('%');
+
+            // the number
+            long n;
 
             ch = *++pch;    // get the format spec char
             switch ( ch )
@@ -3470,44 +3504,90 @@ wxString wxTimeSpan::Format(const wxChar *format) const
                     // fall through
 
                 case _T('%'):
-                    // will get to str << ch below
-                    break;
+                    str += ch;
+
+                    // skip the part below switch
+                    continue;
 
                 case _T('D'):
-                    tmp.Printf(_T("%d"), GetDays());
+                    n = GetDays();
+                    if ( partBiggest < Part_Day )
+                    {
+                        n %= DAYS_PER_WEEK;
+                    }
+                    else
+                    {
+                        partBiggest = Part_Day;
+                    }
                     break;
 
                 case _T('E'):
-                    tmp.Printf(_T("%d"), GetWeeks());
+                    partBiggest = Part_Week;
+                    n = GetWeeks();
                     break;
 
                 case _T('H'):
-                    tmp.Printf(_T("%02d"), GetHours());
+                    n = GetHours();
+                    if ( partBiggest < Part_Hour )
+                    {
+                        n %= HOURS_PER_DAY;
+                    }
+                    else
+                    {
+                        partBiggest = Part_Hour;
+                    }
+
+                    fmtPrefix += _T("02");
                     break;
 
                 case _T('l'):
-                    tmp.Printf(_T("%03ld"), GetMilliseconds().ToLong());
+                    n = GetMilliseconds().ToLong();
+                    if ( partBiggest < Part_MSec )
+                    {
+                        n %= 1000;
+                    }
+                    //else: no need to reset partBiggest to Part_MSec, it is
+                    //      the least significant one anyhow
+
+                    fmtPrefix += _T("03");
                     break;
 
                 case _T('M'):
-                    tmp.Printf(_T("%02d"), GetMinutes());
+                    n = GetMinutes();
+                    if ( partBiggest < Part_Min )
+                    {
+                        n %= MIN_PER_HOUR;
+                    }
+                    else
+                    {
+                        partBiggest = Part_Min;
+                    }
+
+                    fmtPrefix += _T("02");
                     break;
 
                 case _T('S'):
-                    tmp.Printf(_T("%02ld"), GetSeconds().ToLong());
+                    n = GetSeconds().ToLong();
+                    if ( partBiggest < Part_Sec )
+                    {
+                        n %= SEC_PER_MIN;
+                    }
+                    else
+                    {
+                        partBiggest = Part_Sec;
+                    }
+
+                    fmtPrefix += _T("02");
                     break;
             }
 
-            if ( !!tmp )
-            {
-                str += tmp;
-
-                // skip str += ch below
-                continue;
-            }
+            str += wxString::Format(fmtPrefix + _T("ld"), n);
+        }
+        else
+        {
+            // normal character, just copy
+            str += ch;
         }
-
-        str += ch;
     }
 
     return str;
-- 
2.45.2

