Convert DateTime to specific timezone for ToString() - c#

When writing DateTime values to a text file, I have to make sure the used timezone is always UTC +01:00. The format is then yyyy-MM-ddTHH:mm:sszzz, with the zzz part always equaling +01:00. This means that, in case the DateTime value is not in UTC +01:00, a conversion needs to happen before writing the output.
What would be the best way to go about this?

From the documentation:
With DateTime values, the "zzz" custom format specifier represents the signed offset of the local operating system's time zone from UTC, measured in hours and minutes. It does not reflect the value of an instance's System.DateTime.Kind property. For this reason, the "zzz" format specifier is not recommended for use with DateTime values.
Instead, either use DateTimeOffset values (in which "zzz" does what you think it should), or if you continue to use DateTime values then use the "K" specifier.
For example, on my computer (which is in the US Pacific time zone):
DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:sszzz") // "2017-06-21T14:57:17-07:00"
DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssK") // "2017-06-21T14:57:17Z"
DateTimeOffset.UtcNow.ToString("yyyy-MM-ddTHH:mm:sszzz") // "2017-06-21T14:57:17+00:00"
On line 1, even though the time is the UTC time, the offset is incorrectly showing local time.
On line 2, the K specifier picks up on the UTC kind and properly gives a Z in the result.
On line 3, the zero offset is properly conveyed by the zzz specifier.
Related: https://stackoverflow.com/a/31223893/634824

Using:
DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:sszzz")
Will result in an error.
Please try this instead:
DateTime.**Now**.ToString("yyyy-MM-ddTHH:mm:sszzz")

Related

c# Converting UTC time to Central including timezone in DateTime object

I'm converting the UTC time, taken from my local Server time to Central standard time. I have this running on a server in Germany.
Converting the time and date works, but when a library i have converts it to a string it has a wrong Timezone offset.
It comes out as 2019-05-11T14:44:09+02:00
when i need it to be 2019-05-11T14:44:09-06:00
TimeZoneInfo CRtimezone = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, CRtimezone);
The +02:00 is the UTCoffset for Germany, which i don't want, even the the time and date are correctly in Central Time.
Is there a way to pass or include the offset in the DateTime object?
Is there a way to pass or include the offset in the DateTime object?
No, DateTime structure does not have UTC Offset but DateTimeOffset has. If you really wanna keep your UTC Offset value in your code, I suggest you to work with DateTimeOffset instead of DateTime.
Since it doesn't keep UTC Offset value, when you get it's textual (aka string) representation, you still get the offset value of your server in Germany (includes K, z, zz and zzz specifiers by the way). TimeZoneInfo.ConvertTimeFromUtc method returns a DateTime instance, the offset value you might wanna represent depends on how you want to show it.
One option might be that you might wanna concatenate The Sortable ("s") Format Specifier representation of your DateTime and your TimeZoneInfo.BaseUtcOffset value.
TimeZoneInfo CRtimezone = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
$"{TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, CRtimezone).ToString("s")}{CRtimezone.BaseUtcOffset}".Dump();

How can I print DateTime in UTC format?

I'm simply trying to print a DateTime in its UTC equivalent time format. What am I doing wrong?
var utcEpoch = DateTime.Parse("1970-01-01", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); //This specifies the time I provided is in UTC
Console.WriteLine(utcEpoch.ToString("yyyy-MM-dd HH:mm:ss zzz")); //This properly shows my UTC offset of -6, so it's not wrong
Console.WriteLine(utcEpoch.ToString("u")); //This just flat out seems wrong because it doesn't specify a timezone or offset in its output
> 1969-12-31 18:00:00 -06:00
> 1969-12-31 18:00:00Z
I expected to see 1970-01-01 00:00:00Z for the last one.
From The Universal Sortable ("u") Format Specifier :
Although the result string should express a time as Coordinated
Universal Time (UTC), no conversion of the original DateTime value is
performed during the formatting operation. Therefore, you must convert
a DateTime value to UTC by calling the DateTime.ToUniversalTime method
before formatting it. In contrast, DateTimeOffset values perform this
conversion automatically; there is no need to call the
DateTimeOffset.ToUniversalTime method before the formatting operation.
Your utcEpoch.Kind is not UTC, it is Local. DateTime's are triciker than you might think. You are expecting that it will return UTC as Kind property but it is not. It returns Local.
This situation has been discussed on Phil Haack blog post as well and Matt Johnson has a quite nice comment about this;
AssumeLocal and AssumeUniversal are both related to how the input
string is interpreted. By themselves, neither will change the output
kind.
The default output kind is Local. To get it to be Utc, you can use the
AdjustToUniversal style.
The DateTimeStyles enum is flags-based, so you can combine these in
some ways that make sense. To achieve what you originally set out to
do (parse the input as UTC and output it as UTC), then you would use:
DateTime utcDate = DateTime.Parse("10/01/2006 19:30", culture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
As others pointed pointed out, a separate call to ToUniversalTime()
would also work, but this is technically more correct.
You can see it on referance source as well;
case 'u': // Universal time in sortable format.
if (offset != NullOffset)
{
// Convert to UTC invariants mean this will be in range
dateTime = dateTime - offset;
}
else if (dateTime.Kind == DateTimeKind.Local)
{
InvalidFormatForLocal(format, dateTime);
}
I think you are missunderstanding what the API is doing.
First thing to note is that both DateTimeStyles.AssumeUniversal and DateTimeStyles.AssumeLocalwill still return a DateTime where Kind = Local
> DateTime.Parse("1970-01-01 00:00:00", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).Kind
=> Local
> DateTime.Parse("1970-01-01 00:00:00", CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal).Kind
=> Local
So no matter what we will get a local date. That means that the API most likely is there to make it possible to get a local time from a UTC date. Let's try if that's correct.
I'm in Sweden so we are UTC + 1 during standard time. So if I use DateTimeStyles.AssumeUniversal and put in todays date I should get a local date being 01:00 today.
Running this in C# Interactive:
> DateTime.Parse("2018-03-03", .CultureInfo.InvariantCulture, .DateTimeStyles.AssumeUniversal)
=> [2018-03-03 01:00:00]
Meaning C# assumed that the string I inputed was in UTC and I wanted it in local so it "fixed" it for me.
Doing the same with AssumeLocal.
DateTime.Parse("2018-03-03", .CultureInfo.InvariantCulture, .DateTimeStyles.AssumeLocal)
=> [2018-03-03 00:00:00]
As expected we now treated the input as a local string and got the same value.
To get the date as UTC you can specify the kind
DateTime.SpecifyKind(DateTime.Parse("2018-03-03", CultureInfo.InvariantCulture), DateTimeKind.Utc).ToString("o")
=> "2018-03-03T00:00:00.0000000Z"

How to compare DateTime values, taking into account timezone?

I have two DateTime variables. Each has a timezone stored in the variable so that when I ToString with format including zzz I get a string including +01:00.
At design time I do not know what the timezones will be and I am expecting the variables to have different timezones from each other.
I want to compare the two DateTime values so that I know which is more recent.
For example, if variable A is 2015-07-04T02:00:00+03:00 and variable B is 2015-07-03T18:00:00-07:00 then B > A.
What do I write in C# to tell me this? (I would prefer not to use a third party library.)
(To the SO question-closing zealots: I have spent several hours investigating this using Google, MSDN and SO and am confused. I cannot find a very similar question to this on SO. I am confident that answers to this question will help others.)
You said:
I have two DateTime variables. Each has a timezone stored in the variable so that when I ToString with format including zzz I get a string including +01:00.
This is a common misunderstanding. DateTime doesn't have a time zone stored in the variable. It only has a Kind property, which is of type DateTimeKind, and can be either Utc, Local, or Unspecified.
When calling ToString, the zzz format specifier uses the Kind property to determine which offset to display.
When the Kind is DateTimeKind.Utc, the offset is always +00:00.
When the Kind is DateTimeKind.Local, the offset is determined from the local time zone on the computer where the code is executing. For example, my computer is set to US Pacific time, so the offset will be either -08:00 or -07:00 depending on whether daylight saving time is in effect or not.
When the Kind is DateTimeKind.Unspecified, the behavior is the same as if it were Local. Keep in mind that other methods treat Unspecified in different ways - this is just the particular behavior of the zzz specifier.
MSDN actually says:
For this reason, the "zzz" format specifier is not recommended for use with DateTime values.
Going back to your question:
At design time I do not know what the timezones will be and I am expecting the variables to have different timezones from each other.
Then you cannot use DateTime. You should instead use DateTimeOffset, as it retains a specific time zone offset instead of using a DateTimeKind.
For example, if variable A is 2015-07-04T02:00:00+03:00 and variable B is 2015-07-03T18:00:00-07:00 then B > A. What do I write in C# to tell me this?
DateTimeOffset a = DateTimeOffset.Parse("2015-07-04T02:00:00+03:00");
DateTimeOffset b = DateTimeOffset.Parse("2015-07-03T18:00:00-07:00");
bool result = b > a; // true
See also: DateTime vs DatetimeOffset
Furthermore
As Gustav pointed out, you can use just DateTime, as long as you convert back to universal time before comparing. This works due to DateTime's hidden fourth state (more here). The state is set properly during parsing, and is taken into account when ToUniversalTime is called. Then comparison has valid UTC times to operate from.
DateTime A = DateTime.Parse("2015-11-01T01:00:00-07:00");
DateTime B = DateTime.Parse("2015-11-01T01:00:00-08:00");
Console.WriteLine(A.ToUniversalTime().ToString("'A: 'yyyy'-'MM'-'dd hh:mm:ss"));
Console.WriteLine(B.ToUniversalTime().ToString("'B: 'yyyy'-'MM'-'dd hh:mm:ss"));
Console.WriteLine( B.ToUniversalTime() > A.ToUniversalTime() );
Console.WriteLine( B > A );
And the result:
A: 2015-11-01 08:00:00
B: 2015-11-01 09:00:00
True
False
If your local time zone is set to Pacific Time, you'll get the above results. However, if it's set to something else - it's possible you will get True for the last result, because the values may have been parsed to different local times in your time zone, even though they'd be the same local time in the Pacific time zone.
Using DateTimeOffset is still simpler, going through less conversions, and not being affected by the local time zone.
Did you try this?
var A = DateTime.Parse("2015-07-04T02:00:00+03:00");
var B = DateTime.Parse("2015-07-03T18:00:00-07:00");
Console.WriteLine( B > A );

DateTimeInvalidLocalFormat occurred

I am trying to get the local datetime with reference to UTC time gap and I am executing this code below.
var dtString =DateTime.UtcNow.ToString(#"yyyy-MM-ddTHH\:mm\:ss.fzzz");
but every time it return me an exception as
Why this exception is there, how to get this fixed?
The z format specifier is used to show the offset between local time and UTC time.
It does not make sense to use it with the UTC time (since it is always 0). That is why you get a warning (thanks to #HansPassant for this remark).
You can either:
Want to print the local time and the offset to the UTC (which is standard):
var dtString = DateTime.Now.ToString(#"yyyy-MM-ddTHH\:mm\:ss.f zzz");
Or want to print the UTC time and the local time zone (which is REALLY not common):
var dtString = DateTime.UtcNow.ToString(#"yyyy-MM-ddTHH\:mm\:ss.f") + " " + DateTime.Now.ToString(#"zzz");
Which is more or less equivalent to your code (as explained in #JeroenMostert link):
var dtString = DateTime.UtcNow.ToString(#"yyyy-MM-ddTHH\:mm\:ss.fzzz");
But the resulting string is not standard at all and lead to misinterpretation.
"2015-02-18T12:08:15.1 +01:00"
Is read as local time and local time zone, not UTC time and local time zone.
Also you can find more information about time zone and good examples here : TimeZone.CurrentTimeZone Property
The full message text will probably answer your question:
This can happen when calling DateTime.ToString using the 'z' format
specifier, which will include a local time zone offset in the output.
In that case, either use the 'Z' format specifier, which designates a
UTC time, or use the 'o' format string, which is the recommended way
to persist a DateTime in text.
Your format string contains the z specifier "yyyy-MM-ddTHH\:mm\:ss.fzzz". Try changing it to "yyyy-MM-ddTHH\:mm\:ss.fZZZ".

getting hour part and time-zone part from datetime

I had a string in config file, defining date time with time zone.
I am not able to get this value, while reading values from config file.
In config file:
Setting name="abcdefgh" value="2012-08-10T22:00:00-08:00"
In C#, I am reading this as follows:
DateTime StartDate;
StartDate = DateTime.ParseExact(RoleEnvironment.GetConfigurationSettingValue("abcdefgh"), "yyyy-MM-dd HH:mm:ss", null);
Configuration.Instance.abcdefgh= StartDate;
In start date, i am getting 11 Aug, 2012 11:30:00, with no time zone.
I want to read it as it is. also tell, if my format of writing datetime in config file is correct
MSDN link to DateTimeOffset.
Use DateTimeOffset whenever you are referring to an exact point in
time. For example, use it to calculate "now", transaction times, file
change times, logging event times, etc. If the time zone is not
known, use it with UTC. These uses are much more common than the
scenarios where DateTime is preferred, so this should be considered
the default.
var date = DateTimeOffset.Parse("2012-08-10T22:00:00-08:00");
date.Offset // -08:00:00, offset from Coordinated Universal Time (UTC)
date.DateTime // 10/08/2012 22:00:00,
DateTime doesn't keep information about timezone. To parse the string and keep information about timezone - you should use DateTimeOffset structure.
Use the DateTimeOffset structure (and DateTimeOffset.ParseExact) if you want to store timezone information.
Your ParseExact format also doesn't quite match the setting value: it should have a zz at the end for the timezone information. You can also use DateTimeOffset.Parse since your setting string is in a standard format.
It's a standard format, so the ParseExact isn't needed, try:
StartDate = DateTime.Parse(RoleEnvironment.GetConfigurationSettingValue("abcdefgh"));
I substituted the hard-coded value you provided and got the correct result for my timezone (GMT-4) as
8/11/2012 2:00 AM
Note: as others mentioned, the timezone is not retained, so you will get the correct localized time corresponding to whatever timezone information was in the string, but you won't be able to find out what timezone that was. The DateTime.Kind property will reflect that it's a local time.

Categories

Resources