Click here to Skip to main content
15,881,882 members
Articles / Programming Languages / C++
Article

Simple(x) Date & Time Math Parser

Rate me:
Please Sign up or sign in to vote.
4.54/5 (8 votes)
22 Apr 2004CPOL3 min read 54.1K   1.3K   19   4
Date and time class from the SimplexParser freeware.

Introduction

In fact, it is not difficult to find a C/C++ date & time class for our main purposes. One of the best is surely P J Naughter's DTime. To bring the functionality for a Date & Time Mathematical Parser, we need additional methods to this or a special class for those purposes. SimplexParser has such a class, so I am decided to set it up to the CodeProject site.

Image 1

Sample Operations

The parser interprets editor's code like:

Result = Startdatetime + 40 hours + 12 min + 1 year

in the form:

Result = C<[21.04.2004 | 16:19:18] + h[40] + m[12] + Y[1]
Result = 23.04.2005 08:31:18

or as span:

Result = 366.925 days or +366d 22h 12m 0s

or as:

Julian Date: Result = 2453483.855069

and so on...

whereby C< stands for Start Clock, D< for Start Date and T< for Start Time. Or:

Result = Startdate - Enddate
Result = C<[21.04.2004 | 16:19:18] - C>[20.03.2003 | 06:19:15]
Result = +398d 10h 0m 3s

whereby C> stands for End Clock, D> for End Date and T> for End Time. The syntax was introduced in the early days of development and simplifies the parser's error input check. The output is in the normal form like +366d 22h 12m 0s (day, hour, minute and sec.) or 21.04.2004 16:19:18 or 04/21/2004.

Julian Day Algorithm

The parser's date & time class is based on the Julian Day system. The Julian Day is the number of days since noon on January 1, -4712, i.e., January 1, 4713 BC. It was proposed by J. J. Scaliger in 1583, so the name for this system derived from Julius Scaliger (not Julius Caesar). Scaliger defined Day One as a day when three calendrical cycles converged. The first cycle was the 28 year period over which the Julian calendar repeats days of the week (the so-called solar number). After 28 years, all the dates fall on the same days of the week, so one need only buy 28 calendars. (Note that since the Gregorian calendar was adopted, the calendar now takes 400 years to repeat.) The second was the 19 year golden number cycle over which phases of the moon almost land on the same dates of the year. The third cycle was the 15 year ancient Roman tax cycle of Emperor Constantine (the so-called indiction). Scaliger picked January 1, 4713 BC on the Julian calendar as Day One. The three cycles coincide every 7980 years. For more information, please see: this or this.

CJulianDay class

The following code shows the main methods from the CJulianDay class. Some of the method names are well known from other codes:

//...

class CJulianDay 
{
public:
  CJulianDay();
  virtual ~CJulianDay();

// Member Functions
public:
  // Gives Julian Date back
  bool Enter(CString& strBaseDate, CString& 
       strBaseTime, CString strInput, double& dJulian);
  double CalcJulianDayTime(CString& strBaseDate, 
       CString& strBaseTime, CString strInput, bool bSignPlus = true);
  double GetJulianDayTime(CString strBaseDate, CString strBaseTime);

  CString WeekNumber(int Jul);
  CString DayNumber(int Jul);
  CString WeekDay(int Jul);

  //...

  bool IsLeapYear(int nYear);
  int DaysInMonth(int nMonth, BOOL bLeapYear);

  double HmsToDec(CString strTime);
  CString DecToHms(double dDec, bool bF);

protected:
  double DatToJul(CString strDate);
  CString JulToDat(double Jul);

  bool ValidDate(int nYear, int nMonth, int nDay);
  bool IsValidDate(CString strDate, int& nDay, 
                   int& nMonth, int& nYear);

  double EvaluateNumberOfDays(CString strInput);
  int EvaluateNumberOfYears(CString strDate, CString strInput);
  int EvaluateNumberOfMonth(CString strDate, CString strInput);
  int EvaluateNumberOfWeeks(CString strInput);
  void AddYear(CString& strDate, int nAddYear);
  void AddMonth(CString& strDate, int nAddMonths);
};

//...

Using the code

You can use this code as shown next:

//...

case IDM_VERNALEQUINOX:
{
  double m = ((double) date.GetYear() - 2000.0) / 1000.0;
  double ve = 2451623.80984 + 365242.37404 * m + 
              0.05169 * m * m - 0.00411 * m * m * m - 
              0.00057 * m * m * m * m ;

  CJulianDay Julian;
  CString strDate, strTime;
  Julian.Enter(strDate, strTime, _T(""), ve);
  strResult = strDate + _T(" ") + strTime;

break;
}

//...

or

//...

CJulianDay Julian;
result.dJulianDay = Julian.CalcJulianDayTime(m_strBaseDate, 
                         m_strBaseTime, result.Command, true);

//...

I hope that the code is reasonably obvious how to use, so that we can go to the actual parser class CDateParser.

CDateParser class

//...

typedef struct _tagResultStruct
{
  CLTimeSpan tmSpan;
  LONG value;
  CString SubString;
  CString Command;
  double dJulianDay;
} stDateParser;


class CDateParser 
{
protected:
  //Implementation
  CString m_strFormula;
  CString m_strFunction;
  CString m_strErrortext;
  double m_dFunctionConstant[ANZFUNKTKONST];
  CStringArray m_strStandardFunction;

  stDateParser Expression(int& nPosition, CString& strCharacter);
  stDateParser Factor(int& nPosition, CString& strCharacter);
  double Char_n(int& nPosition, CString& strCharacter);
  CString GetNextToken(CString& strSrc, const CString strDelim);

private:
  CJulianDay m_Julian;
  CString m_strBaseDate;
  CString m_strBaseTime;
  CString m_strAssociateDate;
  CString m_strAssociateTime;

public:
  int StripFormula(CString& strFormula);
  CString GetFormula();
  void SetFormula(CString Formula);
  void SetFunctConst(int index, double val);

  CDateParser();
  virtual ~CDateParser();

//Interface
  stDateParser Calculation(CString strFormula, 
     int& ErrorPosition, CString& Errortext, 
     BOOL strip = true);
};

//...

Use this class in the following manner:

//...

CDateParser DateParser;
stDateParser retValue = DateParser.Calculation(m_strFormulainput, 
                                    nErrorPosition, strErrortext);

try
{
  if (nErrorPosition == 0)
  {
    if (strErrortext.IsEmpty())
    {
      if (m_nSpanSet == 1) // Span
      {
        int days = retValue.tmSpan.GetTotalDays();
        int hours = retValue.tmSpan.GetHours();
        int minutes = retValue.tmSpan.GetMinutes();
        int seconds = retValue.tmSpan.GetSeconds();

        //...

//...

whereby you can use the other public member functions for clean input, and for example, P J Naughter's DTime class CLTimeSpan for getting back days, hours, etc. The demo project shows you that.

Image 2

Certainly, I know that we can make for the demo project a better dialog for all the possibilities. But, maybe later...

History

  • 1.0 (18 April 2004) -Initial release.

Acknowledgement

This would not have been possible without all the authors making their code freely available! So, thanks for that, SimplexParser will be available as freeware for all the time.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Germany Germany
Ralf is the programmer of the well-known programs SimplexNumerica and SimplexParser. He's been programming since 1986 since he was at the Fraunhofer Institute for Laser Technology (ILT) in Aachen/Germany. His preferred programming language is Visual C++ with MFC/ATL and GFA Basic. Ralf was born near Gerolstein/Germany. He went to Aachen University where he obtained a Diploma of technical physics. He works as a senior design engineer for a fairly large, international company.

Comments and Discussions

 
GeneralScreenshot is wrong Pin
ronnyek24-Sep-04 8:56
ronnyek24-Sep-04 8:56 
QuestionTimezone? Pin
Stephane Rodriguez.24-Apr-04 3:41
Stephane Rodriguez.24-Apr-04 3:41 
AnswerRe: Timezone? Pin
Ralf Wirtz25-Apr-04 20:09
Ralf Wirtz25-Apr-04 20:09 
GeneralRe: Timezone? Pin
Stephane Rodriguez.26-Apr-04 8:21
Stephane Rodriguez.26-Apr-04 8:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.