Solved

COleDateTime

Posted on 2003-10-30
18
799 Views
Last Modified: 2013-11-20
hi

  i have a peculiar problem while sorting date using COleDateTime in quick sort.

i have 2 dates

CString strL,strR;
strL = "01/06/2000 00:43PM"
strR = "09/05/2000 10:47AM"
COleDateTime dtL,dtR;

dtL.ParseDateTime(strL);
dtR.ParseDateTime(strR);

when i find the

COleDateTimeSpan spanElapsed = dtL - dtR;

spanElapsed is supposed to be a positive value i.e., > 0

but i get it as a negative value . what could be the problem ? how do i solve it ?

bcos of this problem the sorting is erroneous .

urgent pls help.

thnx in advance
cira.

0
Comment
Question by:cira
  • 6
  • 4
  • 3
  • +1
18 Comments
 
LVL 23

Expert Comment

by:Roshan Davis
ID: 9655770
I think your local time setting is in dd/mm/yyyy format
0
 

Author Comment

by:cira
ID: 9655860
yes, it is in dd/mm/yyyy format

then will that give a problem ?

what should i do ?
0
 
LVL 23

Expert Comment

by:Roshan Davis
ID: 9655896
So you have to format the date in dd/mm/yyyy
becoz ParseDateTime uses LANG_USER_DEFAULT as default parameter.
0
Master Your Team's Linux and Cloud Stack!

The average business loses $13.5M per year to ineffective training (per 1,000 employees). Keep ahead of the competition and combine in-person quality with online cost and flexibility by training with Linux Academy.

 

Author Comment

by:cira
ID: 9656190
the string what i gave is also in  dd/mm/yyyy format

why should i convert ,

I am not able to understand pls elaborate

thnx
cira
0
 

Author Comment

by:cira
ID: 9656191
the string what i gave is also in  dd/mm/yyyy format

why should i convert ,

I am not able to understand pls elaborate

thnx
cira
0
 
LVL 23

Expert Comment

by:Roshan Davis
ID: 9656302
strL = "01/06/2000 00:43PM"  <------mm/dd/yyyy

I think thatz Y u are getting negative value.
0
 

Author Comment

by:cira
ID: 9656925
how will i compare or format for each instance ?

or is it possible to change the default parameter of parsedatetime ?
0
 
LVL 23

Expert Comment

by:Roshan Davis
ID: 9657088
use the format: Year/Month/Day

eg:
csDateStr = "1982/08/22";


Good Luck
0
 
LVL 4

Expert Comment

by:inox
ID: 9657745

specify the correct LCID in the ParseDateTime( here am. english) to get it work well:

      dtL.ParseDateTime(strL,0x0409);
      dtR.ParseDateTime(strR,0x0409);
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 9664461
hold the presses!!

You are getting a negative timespan because the first date (dtL) is older (longer ago) than the second date (dtR)!

It is working perfectly.

For instance,

1998 - 1997  (first date is newer than second date)
                   result is positive
1997 - 1998  (first date is OLDER than second date)
                   result is negative

You just need to realize this and then rewrite your comparison function accordingly!  SHow your comparison function and describe what you want the sorted list to look like.  I can help there.

-- Dan
0
 

Author Comment

by:cira
ID: 9668912
Thanx for the reply.

The code is as follows :

int CProbeListCtrl::CmpItems(CString cstL, CString cstR, int nCol)
{
   switch (nCol) {
      //Date time column(s)
      //***********************
      case 12:
    case 13:
    case 14:
      {
         COleDateTime odtL;
         COleDateTime odtR;        
            
             odtL.ParseDateTime(cstL);
             if (odtL.m_status == COleDateTime::invalid)
             {
                   cstL = "01/01/0100 00:00AM";
                   odtL.ParseDateTime(cstL);                   
             }

         odtR.ParseDateTime(cstR);
             if (odtR.m_status == COleDateTime::invalid)
             {
                   cstR = "01/01/0100 00:00AM";
                   odtR.ParseDateTime(cstR);
             }

         COleDateTimeSpan spanElapsed = odtL - odtR;

//             int totmin = (int)spanElapsed.GetTotalSeconds();
         return((int)spanElapsed.GetTotalSeconds());
    }

      //Numbers column(s)
      //***********************
   case 0:
   case 8:
   case 9:
         return(atoi(cstL) - atoi(cstR));

      // Strings column(s)
      //***********************
      default:
         return (cstL.Compare(cstR));
   }
}
 
BOOL CProbeListCtrl::SortTextItems( int nCol, BOOL bAscending,      int low /*= 0*/, int high /*= -1*/ )
{

      if( nCol >= ((CHeaderCtrl*)GetDlgItem(0))->GetItemCount() )
            return FALSE;

      if( high == -1 ) high = GetItemCount() - 1;

      int lo = low;
      int hi = high;
      CString midItem;

      if( hi <= lo ) return FALSE;
      
      midItem = GetItemText( (lo+hi)/2, nCol );

      // loop through the list until indices cross
      while( lo <= hi )
      {
            // rowText will hold all column text for one row
            CStringArray rowText;

            // find the first element that is greater than or equal to
            // the partition element starting from the left Index.
            if( bAscending )
            {
                  while( ( lo < high ) && ( CmpItems( GetItemText(lo, nCol) , midItem, nCol) < 0 ) )
                        ++lo;                  
            }
            else
            {            
                  while( ( lo < high ) && ( CmpItems( GetItemText(lo, nCol) , midItem, nCol) > 0 ) )
                        ++lo;                  
            }

            // find an element that is smaller than or equal to
            // the partition element starting from the right Index.
            if( bAscending )
            {
                  while( ( hi > low ) && ( CmpItems(GetItemText(hi, nCol) , midItem, nCol ) > 0 ) )
                        --hi;            
            }
            else
            {
                  while( ( hi > low ) && ( CmpItems(GetItemText(hi, nCol) , midItem, nCol ) < 0 ) )
                        --hi;            
            }
            // if the indexes have not crossed, swap
            // and if the items are not equal
            if( lo <= hi )
            {
                  // swap only if the items are not equal
            
                  if( CmpItems (GetItemText(lo, nCol) , GetItemText(hi, nCol), nCol  ) != 0)
                  {
                        // swap the rows
                        LV_ITEM lvitemlo, lvitemhi;
                        int nColCount =
                              ((CHeaderCtrl*)GetDlgItem(0))->GetItemCount();
                        rowText.SetSize( nColCount );
                        int i;
                        for( i=0; i<nColCount; i++)
                              rowText[i] = GetItemText(lo, i);
                        lvitemlo.mask = LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
                        lvitemlo.iItem = lo;
                        lvitemlo.iSubItem = 0;
                        lvitemlo.stateMask = LVIS_CUT | LVIS_DROPHILITED |
                                    LVIS_FOCUSED |  LVIS_SELECTED |
                                    LVIS_OVERLAYMASK | LVIS_STATEIMAGEMASK;

                        lvitemhi = lvitemlo;
                        lvitemhi.iItem = hi;

                        GetItem( &lvitemlo );
                        GetItem( &lvitemhi );

                        for( i=0; i<nColCount; i++)
                              SetItemText(lo, i, GetItemText(hi, i));

                        lvitemhi.iItem = lo;
                        SetItem( &lvitemhi );

                        for( i=0; i<nColCount; i++)
                              SetItemText(hi, i, rowText[i]);

                        lvitemlo.iItem = hi;
                        SetItem( &lvitemlo );
                  }
                        
                  ++lo;
                  --hi;
            }
      }

      // If the right index has not reached the left side of array
      // must now sort the left partition.
      if( low < hi )
            SortTextItems( nCol, bAscending , low, hi);

      // If the left index has not reached the right side of array
      // must now sort the right partition.
      if( lo < high )
            SortTextItems( nCol, bAscending , lo, high );

      return TRUE;
}
0
 
LVL 4

Expert Comment

by:inox
ID: 9670436

ParseDateTime uses
LCID lcid = LANG_USER_DEFAULT
If not specified. If this doesn't match it will not work.
If DateTime-format in the string is inmutable the corresponding LCID should be supplied
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 9674516
I can't analyze your sorting routine because of it's complexity.  I don't have a dataset to test it and I don't know what your goal is (it it clearly not a simple sort).  Furthermore, it calls itself recursively which opens a whole can of worms.  It's hard to imagine that you need such a complicated function.  If you describe your goal, as I requested, it would help.

That said...
I verified that Your comparison routine works correctly:

void CompareAndShowResult( CString cstL, CString cstR )
{
      int nRet= CmpItems( cstL, cstR, 12 );

      CString sCmp= "EQUAL TO";
      if (nRet < 0 ) sCmp= "LESS THAN (longer ago than)";
      if (nRet > 0 ) sCmp= "GREATER THAN (more recent than)";

      CString sMsg; sMsg.Format("date %s is %s %s",
            (LPCSTR)cstL,
            (LPCSTR)sCmp,
            (LPCSTR)cstR
      );
      AfxMessageBox( sMsg );
}

void CD18Dlg::OnButton1()
{
      CString sL, sR;

      sL="01/01/2000"; sR="01/01/2000"; CompareAndShowResult(sL,sR);
      sL="01/01/2000"; sR="02/01/2000"; CompareAndShowResult(sL,sR);
      sL="02/01/2000"; sR="01/01/2000"; CompareAndShowResult(sL,sR);
      sL="01/01/2000"; sR="01/02/2000"; CompareAndShowResult(sL,sR);
      sL="01/02/2000"; sR="01/01/2000"; CompareAndShowResult(sL,sR);

      sL="01/01/2000"; sR="01/01/2001"; CompareAndShowResult(sL,sR);
      sL="01/01/2001"; sR="01/01/2000"; CompareAndShowResult(sL,sR);

}
0
 

Author Comment

by:cira
ID: 9676309
thanx for the effort taken.

Actually my application has a list control with different columns each with string,number and date items. I have to sort the columns in ascending & descending on header click.

it works fine for string & number items but for date it gives a small problem as this .

Consider this data set for date :

26/08/2003 03:26PM
15/11/2000 11:20AM
21/08/1995 03:46PM
09/05/2000 10:47AM
01/06/2000 00:43PM
NULL
NULL

onAscending the output i get is :

21/08/1995 03:46PM
01/06/2000 00:43PM
09/05/2000 10:47AM
15/11/2000 11:20AM
26/08/2003 03:26PM
NULL
NULL

onDescending the output i get is :

NULL
NULL
26/08/2003 03:26PM
15/11/2000 11:20AM
09/05/2000 10:47AM
01/06/2000 00:43PM
21/08/1995 03:46PM

0
 
LVL 49

Accepted Solution

by:
DanRollins earned 100 total points
ID: 9677173

OK, it is now clear that your dates are in dd/mm/yyyy format.  That means you need to use the LCID parmeter in the ParseDate function:

      WORD nParsingLCID= MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_UK ); // format is dd/mm/yyyy
      odtL.ParseDateTime(sItem1, 0, nParsingLCID);

=-=-=-=-=-=-=-=-=-=-=-=-=-=
As to the sorting, you should use the SortItems() member function of the CListCtrl object.  It is just a little bit tricky, but once you understand how it works, it's pretty straightforward (even though the documentation is confusing).

You create a callback fn that gets called repeatedly during the sorting.  Each call passes in the 'ItemData' of two list items.   The *best* way to use this is to set the ItemData to be a pointer to some in-memory representation of your list data.  However, in the following code, I just set it to be a unique number -- the *original* index of the items in the list.

Then in the comparison callback, I can use CListCtrl::FindItem() to locate that item (recall that its position [index] in the list is likely to change after each sort) and that lets me look up the text for any column using CListCtrl::GetItemText().

The other trick is to pass in a structure full of info to the callback.  That way, you can know which column to sort and which direction.

Here is the complete test harness:

typedef struct {
     CListCtrl* m_pListCtl;
     int        m_nClmNum;
     BOOL       m_fAscending;
} MYSORTINFO;


static int CALLBACK
MyCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
      MYSORTINFO* pInfo= (MYSORTINFO*)lParamSort;
      int nClmNum= pInfo->m_nClmNum;
      BOOL fSortLoToHi = pInfo->m_fAscending;

      // we know the value of the item data from the lParam, but we need the index
        //  and FindItem can return that... assuming the ItemData is unique for each item
      LVFINDINFO rFI;
      rFI.flags=  LVFI_PARAM;
      rFI.lParam= lParam1; int nListIdx1= pInfo->m_pListCtl->FindItem( &rFI );
      rFI.lParam= lParam2; int nListIdx2= pInfo->m_pListCtl->FindItem( &rFI );

      // format is dd/mm/yyyy
      WORD nParsingLCID= MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_UK );

      CString sItem1= pInfo->m_pListCtl->GetItemText(nListIdx1, nClmNum);
      CString sItem2= pInfo->m_pListCtl->GetItemText(nListIdx2, nClmNum);

      switch ( nClmNum ) {
            case 1:                // comparing dates
            { COleDateTime odt1, odt2;        
                  odt1.ParseDateTime(sItem1, 0, nParsingLCID);
                  if (odt1.m_status == COleDateTime::invalid) {
                        odt1.ParseDateTime( "01/01/0100 00:00AM", 0, nParsingLCID);
                  }
                  odt2.ParseDateTime(sItem2, 0, nParsingLCID);
                  if (odt2.m_status == COleDateTime::invalid) {
                        odt2.ParseDateTime( "01/01/0100 00:00AM", 0, nParsingLCID);
                  }
                  // int nMonth1= odt1.GetMonth(); // eyeball check for debugging
                  if ( fSortLoToHi ) {
                        if (odt1 < odt2 ) return( -1 ); // the lower one goes on top
                        if (odt1 > odt2 ) return( 1 );
                        return( 0 ); // equal
                  }
                  else {  // sorting HighToLow
                        if (odt1 < odt2 ) return( 1 );
                        if (odt1 > odt2 ) return( -1 );
                        return( 0 );
                  }
            }
            case 0: // other columns that need sorting are string
            default:
                  if ( fSortLoToHi )       return strcmp( sItem1, sItem2);
                  else                     return -strcmp( sItem1, sItem2);
      }
      return 0; // should never get here
}

void CD18Dlg::OnButton1()
{
      MYSORTINFO rSI;
      rSI.m_fAscending= FALSE;
      rSI.m_nClmNum= 0;
      rSI.m_pListCtl= &m_ctlList;

      m_ctlList.SortItems( MyCompareProc, (LPARAM)&rSI );
}
//----------------------------- fill the list with some data for testing
void CD18Dlg::OnButton2()
{
      m_ctlList.InsertColumn( 0, "clm 1", LVCFMT_LEFT, 50 );
      m_ctlList.InsertColumn( 1, "clm 2", LVCFMT_LEFT, 130 );
      m_ctlList.InsertColumn( 1, "clm 3", LVCFMT_LEFT, 130 );

      m_ctlList.InsertItem(0,"aaa", 0 );
      m_ctlList.InsertItem(1,"bbb", 0 );
      m_ctlList.InsertItem(2,"ccc", 0 );
      m_ctlList.InsertItem(3,"ddd", 0 );
      m_ctlList.InsertItem(4,"zzz", 0 );
      m_ctlList.InsertItem(5,"fff", 0 );
      m_ctlList.InsertItem(6,"ggg", 0 );

      m_ctlList.SetItemText( 0, 1,"26/08/2003 03:26PM" );
      m_ctlList.SetItemText( 1, 1,"15/11/2000 11:20AM" );
      m_ctlList.SetItemText( 2, 1,"09/05/2000 10:47AM" );
      m_ctlList.SetItemText( 3, 1,"01/06/2000 00:43PM" );
      m_ctlList.SetItemText( 4, 1,"21/08/1995 03:46PM" );
      m_ctlList.SetItemText( 5, 1,"" );
      m_ctlList.SetItemText( 6, 1,"" );

      // need to SetItemData to a unique number, used in sorting
      for (int j=0; j<7; j++ ) {
            m_ctlList.SetItemData( j, j );
      }
}

=-=-=-=-=-=-=-=-=-=-=-
By using the built-in sorting capability of the CListCtrl, you can remove your SortTextItems() function altogether.  That simplifies your program and avoids a lot of possible errors.

That should fix you up!

-- Dan
0

Featured Post

3 Use Cases for Connected Systems

Our Dev teams are like yours. They’re continually cranking out code for new features/bugs fixes, testing, deploying, testing some more, responding to production monitoring events and more. It’s complex. So, we thought you’d like to see what’s working for us.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
bobThere java chalenge 45 92
Tab names are off by one 5 54
maxBlock challenge 30 130
post4 challenge 28 101
Here is how to use MFC's automatic Radio Button handling in your dialog boxes and forms.  Beginner programmers usually start with a OnClick handler for each radio button and that's just not the right way to go.  MFC has a very cool system for handli…
Exception Handling is in the core of any application that is able to dignify its name. In this article, I'll guide you through the process of writing a DRY (Don't Repeat Yourself) Exception Handling mechanism, using Aspect Oriented Programming.
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
This video shows how to use Hyena, from SystemTools Software, to bulk import 100 user accounts from an external text file. View in 1080p for best video quality.

803 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question