Monday 5 January 2009

From procedural to Object Oriented programming

When working with Delphi a while ago, I was in need to format dates and times. As far as I know there is hardly any build-in support for that in Delphi. A colleague pointed me to a unit (Delphi is working with units, they are similar to header files of C/C++ or the source files of Java and C#) where he had created functions to convert different types of date/time combinations.
Although they worked very well, inside me everything was screaming. I was working with objects all over the project, and now I needed to use procedural functions in my domain. Today I want to try to convert those functions to a real object, which makes it easy to convert from any date/time format to a specific one that is used in the whole company. Why? to show the power of objects ;)

The procedural interface

The unit that is used to convert a specific date and/or time format to another one, for example: 23122008 to 20081223 or 2008-12-23 to a TDateTime object. The code is made to handle conversion errors, so in the time this unit was created (years ago) it would have been very useful. Now (as far as I know) the standard Delphi library has some functionality to perform the conversions. There are around the 100 functions to choose from and some of these functions looks very similar. Similar code can lead us to code duplication and that can lead us to bugs in multiple places. But even these 100 functions don't give you all the possibilities you can have. Every function is working on its own and every result should be stored in memory. To be able to do some calculations or comparisons or multiple conversions we are in need for some other "magic". This magic are objects.

The object oriented interface

Before I describe the interface of the new object, I will describe some requirements for this class.
  • It should be possible to parse a given date and or time to a TDateTime object.
  • It should be possible to convert a TDateTime object to a specific date and or time format.
  • It should be possible to add or remove time to the object.
Basically these are the things that are done in the original unit. Lets see how we can put this in an object: disclaimer, I don't have a Delphi compiler at hand, so I am not sure this is 100% correct.
type DateTime = class
begin
public:
procedure ParseDateTime(value, format : string);
function ToString(format : string) : string;
function Compare(dt : DateTime) : integer;
end;
The method ParseDateTime is used to convert a string to format that is used internally. The method ToString will convert the internal time value to a specific format. Compare will compare the two different values (by using the internal format).

OK, now we have a basic interface how do we implement this and what is our internal value? The internal value should be (in my opinion) in a format that the computer understands the best. In Delphi we can use the TDateTime type (if I remember correct) which is just a alias for a Long type which can be found in most programming languages.
The parsing of the Date and Time values can be done by using functions that are included in Delphi. We can make sure that errors are caught and we can throw an exception (for example IncorrectFormat exception, or something like that). The same we can do for the ToString function. (I won't show a real implementation, because I don't want to show an example with errors.)
To compare two DateTime objects we simply compare the internal values. If I recall correctly Delphi can access private fields of a class instance (an object) from another class instance of the same type, so this shouldn't be a problem. For convenience and to follow standards we return some kind of value depending on the comparison (0 means self equals dt, negative means self is smaller (earlier) than dt and positive means self is greater (later) than dt). There might be a function for this in Delphi as well, but I am not sure.

Time zones

OK, but what about different time zones you might say. How can a datetime from Europe (GMT +1 and GMT +2) be compared with America (GMT -7)? There are a few solutions for this I think:
  • We don't mind, so we don't implement
  • Convert date and time to UTC (GMT 0)
  • Use region information
This list is probably not complete, the first item is unacceptable when you have that above question. The second and third (converting to UTC and region info) are going hand in hand.

*Disclaimer* What I write here is pure theoretical, I couldn't test it, but by providing this information, the reader can try it out in his/her preferred programming language.
For converting a date and time format we need to know in which time zone the given time is.
There we could use for example the TTimeZoneInformation type form Delphi (_TIME_ZONE_INFORMATION structure in Win32 API). To get the UTC time first convert the datetime-value to a long (as before) and then add the TTimeZoneInformation.Bias value. I am not sure if it is possible to get the TTimeZoneInformation type for other time zones, but it is worth trying.

When you choose to include the timezone info into the DateTime class, you can use this information for comparison as well.

Default formats

Of course there are a couple of default formats you can choose from. The easiest way to do this is to create string constants for them, so they can be included easily.

Conclusion

Although converting programs from procedural to object oriented is very time consuming, it could be done quiet easy when doing it in very small steps. I hope this post gives you a good example of how to approach a typical conversion. When you have questions, please feel free to add a comment :)

No comments:

Post a Comment

Note: only a member of this blog may post a comment.