Types exist for a reason, if they added no benefit then we would not have them and would not use them and we would just have "the type" and everything would be that. They are not only convenient they also add safety and efficiency. The following is a list of why you should always persist types in their native format and not as strings. I used DateTime
as an example most of the time but the same principles apply for any primitive type like integers, decimals, binary, etc.
Data Store
Constraints
Type Constraint
Almost all data stores allow to specify constraints on the data, this includes type constraints. One of the main benefits of specifying a DateTime
instance is that the stored data will be constrained to that type. It will never be possible to enter anything other than a date time regardless of how the data was inserted into the store. The latter is important for larger systems where there are multiple processes that interact directly with the store. This also includes trying to add faulty dates like February 30, (of any year) as February can only have 29 days on a leap year and 28 days for non leap years.
Validation Constraints
There are also validation constraints which can be implemented in the Data Store like ensuring that a inserted date does not exceed the current date or that a start date occurs before an end date.
Operations
Most data stores also have built in operations / functions like DateAdd
or DatePart
in MS Sql Server. This allows you to start filtering or selecting specific data while the data is still in the store (not retrieved to the application yet).
Universally Accepted Format
By using the native type other developers or systems that also interact with the store do not have to be informed on the minute details of how that primitive type is stored. This is not the case if that type was stored as a string, then you have to ensure that everyone understands the format of that DateTime
string representation. This system becomes fragile when dealing with data that spans locales, regions, and cultures in data origin, the physical location of an application, and the attributes of the end user/system that is interacting with that data. Example: the date format in one country might be MM/dd/yyyy (like in the US) but in another it could be dd/MM/yyyy, detecting that difference becomes almost impossible.
Speed
Speed of retrieval, speed of validation, speed of operations and storage efficiency are all also important factors. Example of the retrieval speed: data stores allow for indexes on columns and these indexes can generally be more efficiently used if the type is stored in its native format.
Application
Data Access
Executing queries against the store becomes simpler using the native type system as developers, once again, do not have to guess as to the storage format. Almost all data store application providers (example: ado.net) provide mechanisms for creating the proper parameterized queries based on the native types passed in. Here is an example of adding the Date part to a ado.net query against a Sql Server store, doing the same with strings would be very cumbersome and fragile/error prone.
command.Parameters.Add(new SqlParameter("@startDate", SqlDbType.Date) {Value = myDateInstance.Date});
Operations
The native types in code also provide for standard operations like the .net type System.Date
. Operations are usually mathematical in nature like adding dates, finding the difference between dates, etc. Again, this is not possible to do easily on string types.
Presentation layer
Locale
When a primitive type is finally converted to a string in the presentation layer (the correct location in the program stack to do so) the programmer now has various options to display it correctly according to the context in which it is presented. This context generally consists of the actual meaning of the data and the locale of the user.
Example 1
A datetime instance can be automatically formatted based on the locale of the user.
DateTime.Now.ToString("D", CultureInfo.GetCultureInfo(userContext.Culture))
Example 2
A decimal instance could be representing an amount (currency) and the locale of the user should then also display the amount according to their preference. A c# application might then display the value using
amount.ToString("C", CultureInfo.GetCultureInfo(userContext.Culture))
This could be critical as different cultures display numbers differently. In the US period (.) and comma (,) have the exact reverse meaning as in the Netherlands.
Location
This is very specific to DateTime
instances. A date and time represents an occurrence at a specific moment in time but this usually has to be conveyed/presented to the user depending on their own time zone. Example: a DateTime
instance 2016-09-21T23:38:21.399Z
could be displayed as 9/21/2016 5:21 PM
for a user in the Eastern Timezone in the USA. There are many ways to accomplish this but it becomes next to impossible if the date time instance is kept in memory as a string type or in the data store as a string type.
General Rule
The general 2 rules for an application follow when it comes to converting any primitive type to a string representation are as these
- When accepting input convert that input to the correct primitive type as early as possible in the program stack (usually in the presentation layer)
- When retrieving data to be displayed, convert that data to the string representation as late as possible in the program stack (again, usually in the presentation layer)