Skip to main content

Date and Time FAQ

Frequently asked questions about date- and time-related classes, including System.DateTime, System.TimeSpan, and System.TimeZone.

What are the most frequent misunderstandings about the DateTime class?
Is behavior of DateTime based on the CurrentCulture?
How do I get DateTime methods to take the current culture into account?
How do I get DateTime methods to stop taking the current culture into account?
Is a DateTime in Local or Universal time?
Does the .NET Framework support time-zone conversions to any given time zone?
Why is DateTime not always UTC internally even for cases when it is used a local time?
What is the Recommended Way to Store a DateTime in Binary?
What is the Recommended Way to Store a DateTime in Text?
Can I Round-Trip a DateTime format through ToString and Parse for all Cultures?
Can I Round-Trip a DateTime format through ToString and Parse for all Cultures with the u Format?
Why Does a Time-Only DateTime Not Round-Trip Through COM Interop?
Why do Values Between 0 and -1 not Round-Trip through FromOADate and ToOADate?
 

We love to hear about any ideas, issues, or requests you have. You could be wanting to know why something was designed a particular way, how best to solve a problem, or why your code isn't working the way you expect. In addition, we encourage you to send us (the BCL team) any suggestions for future releases of the Framework, such as additional classes to include (it always helps to outline a scenario you need to support), or new APIs for existing classes.

To submit your question, simply send an e-mail toThe BCL Team (bclpub@microsoft.com).


What are the most frequent misunderstandings about the DateTime class?
There are some very common misunderstandings with the DateTime class.

The first misunderstanding involves which DateTime behavior can be affected by the current culture, because most methods on DateTime used a fixed Gregorian calendar, but some are based on the calendar data in the current culture on the thread.

The second is about the time zone that a DateTime instance represents. The DateTime class does not represent a specific time zone at all, but rather, specific methods will assume input is in local time or UTC, and will return output in one format or another.

See:


Is behavior of DateTime based on the CurrentCulture?
Most of the behavior of the DateTime class is not based on the current culture, and is based around a standard Gregorian calendar with 1AD as the base year. However, a small set of the methods, most notably DateTime.Parse and DateTime.ToString, do vary behavior based on the culture.

In version 1.0 and 1.1 of the Framework, the following methods have behavior that changes with the current culture:

  • GetDateTimeFormats
  • Parse
  • ParseExact
  • ToLongDateString
  • ToLongTimeString
  • ToShortDateString
  • ToShortTimeString
  • ToString

All other members, including the Year, Month, and Day properties, and methods like AddMonths, do not having varying behavior and are based on a Gregorian calendar with 1AD as the base year.

The constructors that take a Calendar instance are a special case. These constructors cause the other parameters to be passed in to be interpreted according to the Calendar passed in, but this is still independent of the current culture on the thread.


How do I get DateTime methods to take the current culture into account?
If you need members like Year, Month, Day, or AddMonths to behave based on the current culture, you need to access the Calendar instance on the DateTimeFormatInfo instance for the current culture. For example, to add months:

[C#]
using System;
using System.IO;
Calendar cal = CultureInfo.CurrentCulture.DateTimeFormat.Calendar;
date = cal.AddMonths(date, 1);


How do I get DateTIme methods to stop taking the current culture into account?
To make operations such as DateTime.Parse and DateTime.ToString not change behavior based on the current culture, pass in the InvariantCulture instance as follows:

Console.WiteLine(date.ToString(CultureInfo.InvariantCulture));


Is a DateTime in Local or Universal time?
A common misconception about DateTime is that it represents a local time, or can represent a universal time by switching to a distinctly different mode. In fact, the DateTime is neither local nor universal. It can represent either of those, or it can be used in a more abstract sense, but there is no information in the DateTime instance that has anything to do with the Time Zone. A good analogy is the Decimal class. You can use it to represent a currency, but which currency it is depends on the context of the application.

What does get stored in the DateTime class is just a big number that can represent a time, such as 5 Sep 2003, 4:17:00 pm. However, that same time in local or universal or Sydney time has exactly the same representation.

There are various operations on DateTime that really require a time zone to work correctly. A good example of this is DateTime.ToUniversalTime, which needs to make an assumption about what the time zone of the input is. In this case, it assumes it is local.

Similarly, there are operations that return DateTime instances that are clearly associated with either local or universal times.

The majority of methods on DateTime are completely independent of the concept of a time zone. This includes all the constructors, properties such as Year, Month, and Day, and methods such as AddMonths and AddHours.

The following methods on the DateTime and TimeZone classes assume that their input is in local time:

  • DateTime.ToUniversalTime
  • DateTime.ToFileTime
  • TimeZone.GetUtcOffset
  • TimeZone.IsDaylightSavingTime
  • TimeZone.ToUniversalTime

The following methods return a local time: :

  • DateTime.Today
  • DateTime.Now
  • DateTime.FromFileTime

The following methods assume that their input is in UTC:

  • DateTime.ToLocalTime
  • DateTime.ToFileTimeUtc (V1.1 only)
  • TimeZone.ToLocalTime

The following methods return a UTC time:

  • DateTime.UtcNow
  • DateTime.FromFileTimeUtc (V1.1 only)

Throughout the rest of the Framework, almost all DateTime instances are either local times (e.g., System.IO.FileInfo.LastAccessTime), or the time zone does not matter (such as the dates in the System.Web.UI.Controls.Calendar control). A small number take or return UTC, but these will be post-fixed with Utc (e.g., System.IO.FileInfo.LastAccessTimeUtc in V1.1 only).

ToString, Parse, and ParseExact have a more complex relationship with the time zone. Most of the time their operations are independent of the time zone, but certain date and time formats do interact with it.

Beware: because the DateTime is time-zone agnostic, these methods cannot detect if you pass in a Local instance to a method that assumes UTC, or vice versa.


Does the .NET Framework support time zone conversions to any given time zone?
Not in V1.0, V1.1 or the Visual Studio 2.005 (code-named Whidbey) pre-release.

The .NET Framework does support conversion to and from UTC and the systems current local time. It can also support parsing a DateTime from an arbitrary time zone offset, such as 2003-10-26T13:11:07+10:00, but it must always convert this either to Local or UTC.

This is a very common feature request and is likely to be in a future version.

People are often surprised why this feature cannot be supplied by Microsoft at low cost. In particular, data to do conversions exists in the Windows registry and is used by the time zone selection dialog. However, there is a big distinction between having UI and registry data and having an API. This is a more expensive feature for Microsoft to undertake than most people would imagine because (a) an API must provide consistent behavior from one machine to another so we cant just re-expose the registry data, and, (b) there is cost for Microsoft in exposing an official time zone conversion because we face ongoing geopolitical costs for any country/region based data we gather and maintain. For example, a country may threaten to boycott our product if it is not listed in the data. This has happened to us with our CultureInfo data on many occasions, and we often need to tweak data in service packs, which is expensive and risky. However, there is agreement that this is a very important feature, and it is under serious consideration for the next release.


Why is DateTime not always UTC internally even for cases when it is used a local time?

People often ask why DateTime is not always UTC internally, or why we dont change it to be so now. This would address a number of problems with the DateTime, but there are various reasons why we cant do it. The short answer is that we probably could have designed DateTime this way from the beginning, but it would not possible to now transmute the DateTime into working this way without creating unacceptable compatibility problems.

In V1.0 the decision was made to have a purely numeric DateTime, and the context of the time zone was to be external. The driving reasons behind this were compatibility, simplicity and performance. Compatibility is relevant because the key legacy systems that .NET needed to interoperate with, COM, Win32 and databases, also had numeric-only instances. Marshaling from one to the other would have required a guess about the time zone context that would be wrong in many cases. There was no easy guess either because the convention for Win32 times was universal and the convention for COM times was local.

Simplicity and performance were relevant because it is much simpler and faster to be dealing with a purely numeric value, rather than having many methods converting back and forth between a local view and a universal representation.

After shipping V1.0 it quickly became obvious that the combination of this simple DateTime representation, combined with the fact that most public APIs were using local instances created a big problem. Local instances have reliability problem during daylight savings boundaries and whenever time zones change.

Serious investigation was done as to whether DateTime could change to be UTC internally and represent an absolute point in time. However this invariably resulted in behavior changes from V1.0 that would cause reasonably functioning code to break. It also created a performance compatibility problem in that it would not be possible to retain the performance characteristics of the class to any reasonable degree with all that extra logic going on.

Instead we chose a solution in Whidbey that solved the most difficult problems in the class and presented minimal compatibility risk. Local instances can now be converted back to universal time without data loss, and you can annotate whether the time is local universal or unspecified. Unfortunately compatibility has also meant that using this updated information has to be opt-in in most cases, so you need to pass in new flags and values to consume this information in technologies like XML Serialization.

Admitted, this is a very unfortunate situation. You still need to know about a lot of the details and pitfalls of DateTime in order to use it correctly. If we could have seen the full implications of the earliest design decisions, we may well have begun with a UTC based DateTime instead. As it is, it will be possible to get reliable DateTime usage in Whidbey, but you have to know what you are doing. We are planning some FxCop rules to help out with this.

All this being said, it may be possible to create an entirely new class that does represent an absolute point in time and does have time zone context. This may be considered for a post-Whidbey release.
 


What is the Recommended Way to Store a DateTime in Binary?

The way most people serialize a DateTime in Binary form is to use ticks. This the simplest and fastest way to store a DateTime, although this is not the recommended practice for local times, which are discussed below.

[C#]
public Int64 StoreDateTime(DateTime value) {
    return value.Ticks;
}

public DateTime ReadDateTime(Int64 value) {
    return new DateTime(value);
}

 

This will not work well for local times, because they exist in the context of the machine's local time zone. If the time zone changes or they are read from a different machine, they could be invalid. It is recommended that you convert local times to UTC by calling ToUniversalTime before serializing and ToLocalTime after de-serializing.


In Whidbey, the DateTime has additional information about whether it is Local, Universal or Unspecified. To preserve this information there are new ToBinary and FromBinary APIs. These APIs will automatically adjust local times as well, so they are safe to serialize with these methods:
 

[C#]
public Int64 StoreDateTime(DateTime value) {
    return value.ToBinary;
}

public DateTime ReadDateTime(Int64 value) {
    return DateTime.FromBinary(value);
}
 


What is the Recommended Way to Store a DateTime in Text?

Most of the standard formats for the DateTIme do not preserve all the information in the DateTime. The also vary from one culture to another. Thus, the recommended format for storing a typical DateTime is like so:

[C#]
public string StoreDateTime(DateTime value) {
    // e.g 2003-10-26T14:33:41.1234567
    return DateTime.ToString("yyyy-MM-ddTHH:mm:ss.fffffff",
                             CultureInfo.InvariantCulture);
}

public DateTime ReadDateTime(String value) {
    return DateTime.Parse(value, CultureInfo.InvariantCulture);
}


This format is useful, both because it is a standardized format, used in XML among other standards, and because with the 7 decimal places it retains all the precision of the DateTime.

There is a critical piece of information that is lost here, which is the context of the time zone of the DateTime, if it is available. If you are communicating with a 3rd party system, or if your application has writers and readers on different time zones, it is recommended to use slightly different formats.

If you handle your Dates in Universal time, use this format:

[C#]
public string StoreDateTimeUtc(DateTime value) {
    // e.g 2003-10-26T14:33:41.1234567Z
    return DateTime.ToString("yyyy-MM-ddTHH:mm:ss.fffffffZ",
                             CultureInfo.InvariantCulture);
}
public DateTime ReadDateTimeUtc(String value) {
    return DateTime.Parse(value, CultureInfo.InvariantCulture,
                          DateTimeStyles.AjdustToUniversal);
}
 

Note that it is important to pass AdjustToUniversal to this routine, because the output format identifies the time as UTC, and converted to local time by default by Parse if they have any sort of time zone marker.

If you handle your Dates in local time, use this format:

[C#]
public string StoreDateTimeLocal(DateTime value) {
    // e.g 2003-10-26T14:33:41.1234567-07:00
    return DateTime.ToString("yyyy-MM-ddTHH:mm:ss.fffffffzzz",
                             CultureInfo.InvariantCulture);
}
public DateTime ReadDateTimeLocal(String value) {
    return DateTime.Parse(value, CultureInfo.InvariantCulture);
}

Note that in this case that if the reader and writer are in different time zones, Parse will convert the result to the local time zone by default.

Beware: do NOT use a UTC format to store a local time or a Local format to store a UTC time. It will actually seem to work at first, but it will not correctly adjust when time zones change, and 3rd party systems reading the information will get the wrong time.

In Whidbey from Beta 1 onwards, there is a simpler way to do all this:

[C#]
public string StoreDateTime(DateTime value) {
    return DateTime.ToString(o, CultureInfo.InvariantCulture);
}
public DateTime ReadDateTime(String value) {
    return DateTime.Parse(value, CultureInfo.InvariantCulture,
                          DateTimeStyles.RoundTripKind);
}
 

The o format is a new shortcut. In Whidbey you can store whether the DateTime is Local, UTC or unspecified in the DateTime instance. The o format will pick a different format depending on what the Kind property of the DateTime is. The RoundTripKind style will effectively preserve what the kind was when persisted based on the format read in. If you don't know that your DateTime.


Can I Round-Trip a DateTime format through ToString and Parse for all Cultures?

By round-trip, the following test is implied:
 

[C#]
bool DoesRoundTrip(DateTime date, String format, CultureInfo culture) {
    string dateString = date.ToString(format, culture);
    return (date == DateTime.Parse(dateString, culture));
}
 

The use of being able to round-trip a date for any culture is usually so that you can create UI that cleans up user input by parsing it and emitting it in consistent format. If your UI application is intended to support the users culture, you want to be

The short answer is that there are some special formats like d and f that will round-trip for all cultures, but most expanded formats were not designed for this and will not work.

For most fixed formats, there is significant enough difference in the behavior of Parse and ToString that you cant pick a format and expect the above condition to be true for all cultures. Because of this difference, it is recommended that the InvariantCulture be used to store and read back any DateTime instances.

An obvious additional factor is that not all DateTime formats store all parts of the DateTime. For example, the d format stores the date only and not the time, and none of the standard formats store fractions of seconds. For a format that does store everything seeWhat is the Recommended Way to Store a DateTime in Text?.

The d and D formats can be used to round-trip a date for all cultures. Any time information will be discarded here. The specific format that these expand to will vary, but it will be a format that is unambiguous for all cultures.

The g and f will round trip with minute precision. The G and F formats will round trip with second precision for most cultures. (In V1.0 and V1.1, the Maldives culture (div-MV) round-tripped to minute precision. This was corrected in Whidbey,)

The t and T formats will round trip a time only. The t format will round trip minute precision. The T format will round trip second precision. (In V1.0 and V1.1, the Maldives culture (div-MV) round-tripped to minute precision. This was corrected in Whidbey,)

The other short formats, such as s and u will not round trip for all cultures, because their format is fixed. They were not designed to be used for round-tripping in UI.

Similarly, it is very difficult to design a fixed format that will round-trip in all cultures. As an example of why this is hard, consider a time format like hh:mm:ss tt, which might come out as 12:30:00 AM. This format does not work because many cultures dont have AM and PM separators and need to use 24 hour time. However, a 24-hour time format is ambiguous in a culture like Afrikaans which has a default assumption of AM, and would incorrectly interpret 12:30:00 as AM instead of PM.

Another factor is that formats like u and r are invariant formats, in that they effective apply CultureInfo.InvariantCulture when you call ToString with them. However, when calling Parse this is not recognized, so a different culture ends up being used to read it in, which leads to inconsistent results.


Can I Round-Trip a DateTime format through ToString and Parse for all Cultures with the u Format?

No. The u format was not designed to be used in this way, although it will round trip for many cultures. It is meant to be used for persistence, not UI that is aware of the current culture.

If you use the u format directly, e.g. DateTime.ToString(u) it is an invariant format, meaning that it formats the same way regardless of the culture you pass in, or the ambient culture on the thread. However, Parse does not have this corresponding invariant behavior, so it effectively parses it back in with a different culture. Obvious examples of this are cultures like Thai (th-TH) that have a different calendar and interpret the year 2000 as the equivalent of the year 1457.

If you use the expansion of the format, which is yyyy'-'MM'-'dd HH':'mm':'ss'Z', this will bypass problems associated with different cultures because the expanded format will always use the culture passed in. However, this will still not round trip successfully for all cultures.

The Afrikaans culture (af-ZA), for example, will not work because 24-hour date formats are ambiguous with the 12 hour date format because the AM designator is a blank string by the PM designator is not, which implies that AM is the default. This means that is must interpret 12:30 as the 12 hour format 12:30 AM, instead of the 24 hour time, which would be 12:30pm.

The Georgian culture (af-ZA) will not round trip because a date like 2001-10-12 is interpreted as Year-Day-Month rather than Year-Month-Day.

These are just specific examples, but the general principal is that fixed formats like this do not mean the same thing in all cultures, so they are not suitable for UI.


Why Does a Time-Only DateTime Not Round-Trip Through COM Interop?

If you use DateTime with a COM component, you may notice some strange behavior if you are dealing with a time-only DateTime. Specifically, they will round-trip back as the date "1899/12/30" instead of 0001/1/1 as would be expected. This is an unfortunate consequence of the differences in the way the two systems encode DateTime data, and there is no way to fix it without breaking other valid scenarios.

Suppose you have a COM object with a simple Date property, such as this one in VB6:
 

[VB]
Public Property Get DateProperty() As Date
    DateProperty = m_dateValue
End Property

Public Property Let DateProperty(ByVal value As Date)
    m_dateValue = value
End Property
 

In general you will find that if you set this property from .NET code, if you get it back again you will usually get back the date you set, i.e. the value will round trip. However, this does not work for time-only DateTime instances. If you set a value, such as the result of a call like this:
 

[VB]
Vb6Component.DateProperty = DateTime.Parse(3:00 pm,
                                     DateTimeStyles.NoCurrentDateDefault);
Console.WriteLine(Vb6Component.DateProperty);

This will actually output 1899/12/30 3:00 PM. What would be expected here would be the base yet of the .NET DateTime, which would be 0001/1/1 3:00 PM, which is how .NET represents a time-only DateTime.

The reason is that the two systems have a different base year, which they use to represent a time without a date, 0001/1/1 for .NET and 1899/12/30 for Ole Automation. When a time-only date is passed from .NET to COM, there is a special case to convert the time-only date to the format used by COM. However, when converting from COM to .NET, the reverse of this is not applied, because it could break a legitimate conversion from the real date 1899/12/30. Because COM can use dates below the base year, such as 1898 or 1066, applying this mapping on the way out as well would create a discontinuity in dates prior to 1900, which is arguably much harder to work around than the problem of time-only dates not round-tripping.

The work around for this behavior is that you must separately retain the context in your application when your DateTime is time-only, and strip off or otherwise ignore this extra year information. Here is an example of how to create a wrapper property for something that you know is a time of day.
 

[C#]
public DateTime TimeOfDayWrapperProperty {
    get {
        return MyComComponent.DateProperty.TimeOfDay;
    }
    set {
        MyComComponent.DateProperty = value;
    }
}

Interestingly, if the round-tripping is happening from the COM side, the time-only date status is preserved, because the date stays as 1899/12/30, which COM treats specially.


Why do Values Between 0 and -1 not Round-Trip through FromOADate and ToOADate?

This is a side effect of the rules for encoding Ole Automation dates, and also happens in tools like VB6. The values between 0.0 and -1.0 have exactly the same semantic meaning as the values between 0.0 and 1.0, and so they dont round-trip to their original value.

Consider this code:
 

[C#]
DateTime dt = DateTime.FromOADate(-0.9);
double ret = dt.ToOADate();
System.Console.WriteLine(ret);

This will print 0.9 rather than -0.9 as might be expected. The reason is because of how the Ole Automation date is encoded. The absolute value of the fractional part of the Double is considered the time of day, so that 0.5 is considered to be 12:00 PM, and 0.75 is considered to be 6:00 PM. The non-fraction part is the number of days difference from the base date, 12 December 1899, and can be positive of negative.

Because the sign is significant for the whole number part and not the fractional part, the region between 0.0 and -1.0 have exactly the same semantic meaning as the values between 0.0 and 1.0, and thus the negative values do not round-trip. This is a curiosity that is unlikely to create difficulty unless you are wrapping and re-testing these API across a broad domain.