Useful information about dates and times. category 'KB', language C#, created 26-Dec-2009, version V1.2 (25-Aug-2010), by Luc Pattyn |
License: The author hereby grants you a worldwide, non-exclusive license to use and redistribute the files and the source code in the article in any way you see fit, provided you keep the copyright notice in place; when code modifications are applied, the notice must reflect that. The author retains copyright to the article, you may not republish or otherwise make available the article, in whole or in part, without the prior written consent of the author. Disclaimer: This work is provided “as is”, without any express or implied warranties or conditions or guarantees. You, the user, assume all risk in its use. In no event will the author be liable to you on any legal theory for any special, incidental, consequential, punitive or exemplary damages arising out of this license or the use of the work or otherwise. |
This article contains practical information about dates and times.
The .NET FrameWork has a DateTime type, which is a struct, hence a value type. It holds both date and time information, however one can keep the time at zero (=midnight) when only interested in dates, or the date constant (say at today) when only interested in time.
One of the popular constructors would be
public DateTime(int year, int month, int day, int hour, int minute, int second)
and the most
popular property is DateTime.Now
which returns the current date and time in the highest
available resolution, i.e. up to the tick (here a tick is a tenth of a microsecond).
For dates, the most useful constructor is new DateTime(int year, int month, int day)
which
constructs a date provided the numbers are OK: year in [1,9999], month in [1, 12], day in [1, number of days
in that month].
Some specials values are made available as static members of the DateTime class:
DateTime.Today;
// today's dateDateTime.MinValue;
// the smallest valid date and time, i.e. midnight, January 1, 0001DateTime.MaxValue;
// the largest valid date and time, i.e. the last tick of December 31, 9999.Other dates can be derived by adding a positive or negative number of days, months or years; the methods
are AddDays(int days)
, AddMonths(int months)
, AddYears(int years)
.
The latter two will keep the day number constant, unless the resulting day would be invalid, in which case
the last valid day of the resulting month is returned (e.g. one month after March 31 is April 30;
however one month before April 30 is March 30).
There are several methods for formatting a DateTime, i.e. converting it to a string:
DateTimeFormatInfo
object,
which can be modified through the "Regional Settings" control panel. These methods are fine when you want the user
to choose the format and probably have all dates to look the same across all applications. This is no good for
storing dates in files that will be exchanged across the world.ToString()
method has several overloads; it basically has three modes of operation:ToString("G")
which combines the short date format and the long time format.CultureInfo
, a DateTimeFormatInfo
, or a custom object that implements
IFormatProvider
. We refer to the MSDN documentation for the details.String.Format()
also accepts the standard and custom format strings, as in this example:
DateTime dt=new DateTime(2009, 12, 26, 01, 23, 45); string s=String.Format("Time = {0:s} = {0:HH:mm:ss}", dt); // generates "Time = 2009-12-21T01:23:45 = 01:23:45"
As for most data types, there are several ways to store a date, a time, a DateTime in a file or database:
Most often it is a bad idea to store a date or time as a string: it isn't the most compact way of storing the information, and it probably is culture-sensitive, so data created on one machine may not be readable by an app on another machine, assuming both have different settings.
When you keep dates or datetimes in a database (such as SQL Server, or MySQL), by all means use the appropriate database types, and don't use strings; the specialized types will avoid all culture sensitivity and the query language is bound to provide a set of functions to operate on datetimes, so that is the way to go. And whenever available (most databases, but not MS Access) use SQL parameters, not date literals, within your SQL queries, so you never need to format a date at all to get at your data. Here is an example:
DateTime dt = dateTimePicker1.Value.Date;
SqlCommand cmd = new SqlCommand("INSERT INTO myTable (DateColumn) VALUES (@DT)");
cmd.Parameters.AddWithValue("@DT", dt);
When using OLEDB (the "JET Engine") for MS Access, SqlParameter is not available, and literal dates
need to be inserted into the SQL query strings themselves. Here a pound sign is used as a delimiter,
and the format is pretty forgiving; when an ambiguous date is given, it is taken as mm/dd/yy
.
Since ambiguity may depend on the actual value (1/2/3 is ambiguous, 27/12/99 isn't), I strongly
recoomend to always use the format yyyy-MM-dd
which never is ambiguous.
Here are some examples:
string preferredQuery = "INSERT INTO myTable (DateColumn) VALUES (#2010-08-25#)";
string sufficientQuery = "INSERT INTO myTable (DateColumn) VALUES (#25/8/2010#)";
string confusingQuery = "INSERT INTO myTable (DateColumn) VALUES (#1/2/3#)";
When the only possible way to store date information is through a string (e.g. because it is a simple data file; or the database has no datetime type, see Ingres), at least try and use one of a few formats that are culture-insensitive:
DateTime.ToString("s")
: the sortable format (example: "2009-12-26T01:23:45"), which conforms to
ISO-8601 and which I strongly recommend;DateTime.ToString("r")
: the RFC1123 pattern (example: "Sat, 26 Dec 2009 01:23:45 GMT");DateTime.ToString("u")
: the "universal sortable" format (example: "2009-12-26 01:23:45Z");DateTime.ToString(CultureInfo InvariantCulture)
: the "invariant" format (example: "12/26/2009 01:23:45").Parsing is the inverse of formatting, so the DateTime parsing methods try and turn a date/time string into a DateTime instance. There are basically four parse methods:
DateTime.TryParse()
and DateTime.TryParseExact()
are alternatives that, in case of a parse problem,
return false, rather than throwing a FormatException.As an example, this is what you could do when the exact format is known:
string str="Wed Feb 24 04:56:30 2010";
log("str="+str);
DateTime dt1=DateTime.ParseExact(str, "ddd MMM dd HH:mm:ss yyyy", System.Globalization.CultureInfo.InvariantCulture);
log("dt1="+dt1.ToString());
These are the relevant properties:
DateTime.Now // yields a DateTime holding the current date and time, with 1-tick resolution
DateTime.Today // yields a DateTime holding the current date
myDateTime.Date // yields a DateTime holding the date part
myDateTime.TimeOfDay // yields a TimeSpan holding the time part (time elapsed since midnight)
Getting the first day of a month is straightforward; here are two ways to get it:
public static DateTime FirstDay(DateTime date) {
return new DateTime(date.Year, date.Month, 1);
}
public static DateTime FirstDay(DateTime date) {
return date.AddDays(1-date.Day);
}
Getting the last day of a month is slightly more complex as the number of days in a month varies; here are two ways to get it:
public static DateTime LastDay(DateTime date) {
date=date.AddMonths(1); // this gets us somewhere in the next month
date=new DateTime(date.Year, date.Month, 1); // first day of next month
return date.AddDays(-1); // last day of current month
}
public static DateTime LastDay(DateTime date) {
return new DateTime(date.Year, date.Month, DateTime.DaysInMonth(date.Year, date.Month));
}
ISO 8601 defines the first week of a year as the week that contains the first Thursday of that year. Here is a simple method to get that first Thursday:
DateTime FirstThursday(int year) {
DateTime dt=new DateTime(year, 1, 1);
return dt.AddDays((11-(int)dt.DayOfWeek)%7);
}
The other days of the same week obviously can be found by adding a small number of days (-3 for Monday, -2 for Tuesday, ..., +3 for Sunday).
How many Sundays are there in a specific month? or Mondays? or... I came up with a magic formula that can
calculate that for any given day without resorting to loops or conditional statements;
it uses the DayOfWeek enum which
ranges from 0=Sunday
to 6=Saturday
.
public static int CountDays(int year, int month, DayOfWeek dayOfWeek) {
return (DateTime.DaysInMonth(year, month) + (int)new DateTime(year, month, 7-(int)dayOfWeek).DayOfWeek) / 7;
}
Basically it divides the number of days by 7, which is fine if the previous month ended on a day of interest, which implies the seventh is also such date; when it is not, we have to tweak the month length. Keep in mind that integer division in most languages rounds down (this would be most relevant when trying the formula in VB.NET).
A very similar formula would yield the number of Sundays, Mondays, ... in a year:
public static int CountDays(int year, DayOfWeek dayOfWeek) {
return (365-28+DateTime.DaysInMonth(year, 2) + (int)new DateTime(year, 1, 7-(int)dayOfWeek).DayOfWeek) / 7;
}
Some mistakes seem to occur quite frequently when using the DateTime type; the most popular ones are listed here:
DateTime date=DateTime.Today;
date.AddDays(1); // this is wrong, the result is discarded
Console.WriteLine("tomorrow is "+date.ToShortDateString ()); // shows today, not tomorrow!
The right way to do that would be:
DateTime date=DateTime.Today;
date=date.AddDays(1);
Console.WriteLine("tomorrow is "+date.ToShortDateString ());
DateTime.Now
and DateTime.Today
are live properties,
if you call them repeatedly at the turn of a second, minute, hour, day, month or year, they will be re-evaluated and may
return inconsistent property values, so don't write:
int hour=DateTime.Now.Hour;
int minute=DateTime.Now.Minute;
// when it just turned 3 PM (from 14:59 to 15:00), this could result in hour=14, minute=0 (i.e. wrong by one hour)
instead just call DateTime.Now only once:
DateTime now=DateTime.Now;
int hour=now.Hour;
int minute=now.Minute;
// this yields values that belong together
DateTime dt=new DateTime(2009, 12, 21);
dt=dateTime.ToString("dd-MM-yyyy"); // will result in 21-Dec-2009
dt=dateTime.ToString("dd/MM/yyyy"); // result depends on regional settings, slash being a special character
dt=dateTime.ToString("dd'/'MM'/'yyyy"); // will result in 21/Dec/2009
The .NET FrameWork also holds a TimeSpan type (also a struct). It gets used to express the difference between two DateTime instances, so you can have:
timeSpan=dateTime1-dateTime2;
timeSpan=dateTime1.Subtract(dateTime2);
dateTime2=dateTime1+timeSpan;
dateTime2=dateTime1.Add(timeSpan);
Note: a TimeSpan instance has time properties (such as Hours
) similar to a DateTime instance;
its date properties are limited to Days
, there is no Months
or Years
property as it is impossible to turn days into months or years without having a reference date,
as months and years have a variable number of days in them.
Perceler |
Copyright © 2012, Luc Pattyn |
Last Modified 02-Sep-2013 |