I'd need to allow accepting UTC datetimes: http://www.w3.org/TR/NOTE-datetime
Such as:
- Year:
YYYY (eg 1997) Year and month:
YYYY-MM (eg 1997-07) Complete date:
YYYY-MM-DD (eg 1997-07-16) Complete date plus hours and minutes:
YYYY-MM-DDThh:mmTZD (eg 1997-07-16T19:20+01:00) Complete date plus hours, minutes and seconds:
YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00) Complete date plus hours, minutes, seconds and a decimal fraction of
a second
YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00) where:
YYYY = four-digit year
MM = two-digit month (01=January, etc.)
DD = two-digit day of month (01 through 31)
hh = two digits of hour (00 through 23) (am/pm NOT allowed)
mm = two digits of minute (00 through 59)
ss = two digits of second (00 through 59)
s = one or more digits representing a decimal fraction of a second
TZD = time zone designator (Z or +hh:mm or -hh:mm)
How to Convert a string to a DateTime with a specific format?
I wrote the below code:
private const string Format = "YYYY-MM-DDThh:mm:ssZ";
public DateTime? Parse(string modifiedSince)
{
// how to know whether it's a valid DateTime in UTC format?
}
But it always returns null which means that it fails to parse to DateTime.
It should successfully validate below UTC values but it doesn't:
2013-05-10
2013-05-10T05:04:10
2013-05-10T05:04:10.40
013-05-10T05:04:10.4Z
(Z is optional usually)
How to use DateTime.Parse or .Parsexact so that it successfully returns a date for the above format?
All other date formats should fail e.g. 20130510
I could write a Regex instead?
You are missing something important. UTC is not a format.
UTC refers to "Coordinated Universal Time" (yes, the abbreviation is out of sequence intentionally). This is a fixed clock at the prime meridian, equivalent to GMT for all practical purposes. It does not change for "daylight savings time" or "summer time", and it is where we place the zero when talking about offsets of other time zones.
The format you are describing is known as ISO8601. Technically, the ISO8601 standard defines several formats, but the one most commonly used is also defined in RFC3339 and covers only the last two of the formats you listed. In other words, ISO8601 allows multiple representations of time at different precisions, but RFC3339 requires values up to the second.
Now when it comes to .Net, you are missing another couple of important concepts. Specifically, you need to be aware of DateTime.Kind
and DateTimeOffset
.
When you have a string value that includes a Z
or +00:00
at the end, you know that it represents time at UTC, and can be stored in a DateTime
that has DateTime.Kind == DateTimeKind.Utc
.
If you have some other offset, such as +01:00
or -04:00
, then you should be storing these in a DateTimeOffset
object instead. If you try to store it in a DateTime
, then you have to choose whether you are going to ignore the offset, or apply it and store the time as UTC.
If you don't have anything at the end, then you have ambiguity. You can store this in a DateTime
where Datetime.Kind == DateTimeKind.Unspecified
but you must be very careful with what you do with it. It doesn't have any information about what time zone or offset that value originated from - so any math operations might be incorrect. For example, you should never obtain a duration by subtracting two DateTime
valules with unspecified (or local) kinds. For additional details, see this blog post.
One last thing, understand that .Net has no built-in class that represents a date without a time. For that, we commonly use a DateTime
at midnight - but you would not be able to distinguish this from a value where you were actually given the time at midnight. In other worse, after parsing 2013-05-10
and 2013-05-10T00:00:00
- they are equivalent.
Here is some code to get you started.
public DateTime? Parse(string s)
{
// you may want to add a few more formats here
var formats = new[] { "yyyy-MM-dd",
"yyyy-MM-ddThh:mm:ss",
"yyyy-MM-ddThh:mm:ssZ" };
DateTime dt;
if (DateTime.TryParseExact(s, formats,
CultureInfo.InvariantCulture, // ISO is invariant
DateTimeStyles.RoundtripKind, // this is important
out dt))
return dt;
return null;
}
You should also look into DateTimeOffset.TryParseExact
if you plan to go down that route.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With