datetime to string with time zone - c#

I have a DateTime stored in universal time (UTC) of value 2010-01-01 01:01:01.
I would like to display it in EST in this format 2010-01-01 04:01:01GMT-04:00, however the 'K' formatter for timezone doesn't work in ToString

Use the "zzz" format specifier to get the UTC offset. For example:
var dt = new DateTime(2010, 1, 1, 1, 1, 1, DateTimeKind.Utc);
string s = dt.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss \"GMT\"zzz");
Console.WriteLine(s);
Output:
2009-12-31 19:01:01 GMT-06:00
I'm in the CDT timezone. Make sure the DateTime is unambiguously DateTimeKind.Utc.

If like myself you happen to need a format like 2018-03-31T01:23:45.678-0300 (no colon in the timezone part), you can use this:
datetime.ToString("yyyy-MM-ddTHH:mm:ss.fffzzz").Remove(26,1)

This method will return the specified time in Eastern Standard Time (as the question requested), even if EST is not the local time zone:
public string GetTimeInEasternStandardTime(DateTime time)
{
TimeZoneInfo easternStandardTime = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
DateTimeOffset timeInEST = TimeZoneInfo.ConvertTime(time, easternStandardTime);
return timeInEST.ToString("yyyy-MM-dd hh:mm:ss tt\" GMT\"zzz");
}
Note: I haven't tested this in a non-English OS. See the MSDN documentation on TimeZoneInfo.FindSystemTimeZoneById.

Something like this works. You could probably clean it up a bit more:
string newDate = string.Format("{0:yyyy-MM-dd HH:mm:ss} GMT {1}", dt.ToLocalTime(), dt.ToLocalTime().ToString("%K"));

I think you are looking for the TimeZoneInfo class (see http://msdn.microsoft.com/en-us/library/system.timezoneinfo_members.aspx). It has many static methods to convert dates between time zones.

Related

C# DateTime - converting a DateTimeOffset to another TimeZone

When converting a DateTimeOffset to another TimeZone, the OffSet is incorrect.
I've read many articles and experimented for too many hours, but can't see what I'm missing here:
// It's June in the UK and we're in British Summer Time, which is 1 hour ahead of UTC (GMT)
var UKoffsetUtc = new TimeSpan(1, 0, 0);
// It's 4pm - declare local time as a DateTimeOffset
var UKdateTimeOffset = new DateTimeOffset(2020, 6, 17, 16, 0, 0, UKoffsetUtc);
// Convert to UTC as a date
var utc = DateTime.SpecifyKind(UKdateTimeOffset.UtcDateTime, DateTimeKind.Utc);
// Get Aus TimeZoneInfo
var AUSTimeZone = TimeZoneInfo.FindSystemTimeZoneById("AUS Eastern Standard Time");
// Check the Aus offset from UTC
var AUSOffset = AUSTimeZone.GetUtcOffset(utc);
Console.WriteLine(AUSOffset); // Output is 10 as expected
// Declare Aus Time as DateTimeOffset
var AUSDateTimeOffset = TimeZoneInfo.ConvertTimeFromUtc(utc, AUSTimeZone);
// The Aus Offset from UTC is not correct
Console.WriteLine(AUSDateTimeOffset.ToString("dd MM yyyy HH:mm zzz"));
The output is 18 06 2020 01:00 +01:00
Aus are 10 hours ahead of UTC (9 hours ahead of GMT) so the date and time are correct, but not the offset.
How can I get the correct offset in AUSDateTimeOffset?
You can create new offset and use it -
// Create new offset for UTC
var AUSOffset = new DateTimeOffset(utc, TimeSpan.Zero);
// Declare Aus Time as DateTimeOffset
var AUSDateTimeOffset = UKdateTimeOffset.ToOffset(AUSTimeZone.GetUtcOffset(AUSOffset));
Console.WriteLine(AUSDateTimeOffset.ToString("dd MM yyyy HH:mm zzz"));
Or:
Use ConvertTimeBySystemTimeZoneId as suggested by Jimi in the comment!
var finalDate = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(UKdateTimeOffset, "AUS Eastern Standard Time");
Console.WriteLine(finalDate.ToString("dd MM yyyy HH:mm zzz"));
The error is in this part:
var AUSDateTimeOffset = TimeZoneInfo.ConvertTimeFromUtc(utc, AUSTimeZone);
In that code, utc is a DateTime, and thus the resulting AUSDateTimeOffset is actually a DateTime. Its Kind will be DateTimeKind.Unspecified.
The conversion will have been done correctly, and thus you see the correct date and time in the result. However, the offset is wrong because it is not part of a DateTime. The documentation about the zzz specifier says:
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 doesn't reflect the value of an instance's DateTime.Kind property. For this reason, the "zzz" format specifier is not recommended for use with DateTime values.
Thus, the +01:00 is coming from your local time zone, not from the target time zone.
There are a few ways you could fix this:
You could make AUSDateTimeOffset a DateTimeOffset with the correct offset:
DateTime AUSDateTime = TimeZoneInfo.ConvertTimeFromUtc(utc, AUSTimeZone);
TimeSpan AUSOffset = AUSTimeZone.GetUtcOffset(utc);
DateTimeOffset AUSDateTimeOffset = new DateTimeOffset(AUSDateTime, AUSOffset);
You could use a UTC-based DateTimeOffset instead of a UTC-based DateTime:
DateTimeOffset utc = UKdateTimeOffset.ToUniversalTime();
DateTimeOffset AUSDateTimeOffset = TimeZoneInfo.ConvertTime(utc, AUSTimeZone);
You could just convert the orignal DateTimeOffset, as there's no need to convert to UTC first:
DateTimeOffset AUSDateTimeOffset = TimeZoneInfo.ConvertTime(UKdateTimeOffset, AUSTimeZone);
As Jimi pointed out in comments, you can even convert without constructing a TimeZoneInfo object at all:
DateTimeOffset AUSDateTimeOffset = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(UKdateTimeOffset, "AUS Eastern Standard Time");
Any of the above will give the same, correct response.
It's easy to get around:
var fromTime = DateTimeOffset.Parse("2020-06-17T16:00:00+01:00");
var timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById("AUS Eastern Standard Time");//TZConvert.GetTimeZoneInfo("Australia/Sydney");//for Mac
var toTime = fromTime.ToOffset(timeZoneInfo.GetUtcOffset(fromTime.UtcDateTime));
Console.Write(toTime.ToString("yyyy-MM-ddTHH:mm:sszzz")); //2020-06-18T01:00:00+10:00

Date format parser error [duplicate]

Why can't it parse this:
DateTime.Parse("Tue, 1 Jan 2008 00:00:00 UTC")
It can't parse that string because "UTC" is not a valid time zone designator.
UTC time is denoted by adding a 'Z' to the end of the time string, so your parsing code should look like this:
DateTime.Parse("Tue, 1 Jan 2008 00:00:00Z");
From the Wikipedia article on ISO 8601
If the time is in UTC, add a 'Z'
directly after the time without a
space. 'Z' is the zone designator for
the zero UTC offset. "09:30 UTC" is
therefore represented as "09:30Z" or
"0930Z". "14:45:15 UTC" would be
"14:45:15Z" or "144515Z".
UTC time is also known as 'Zulu' time,
since 'Zulu' is the NATO phonetic
alphabet word for 'Z'.
Assuming you use the format "o" for your datetime so you have "2016-07-24T18:47:36Z", there is a very simple way to handle this.
Call DateTime.Parse("2016-07-24T18:47:36Z").ToUniversalTime().
What happens when you call DateTime.Parse("2016-07-24T18:47:36Z") is you get a DateTime set to the local timezone. So it converts it to the local time.
The ToUniversalTime() changes it to a UTC DateTime and converts it back to UTC time.
Just use that:
var myDateUtc = DateTime.SpecifyKind(DateTime.Parse("Tue, 1 Jan 2008 00:00:00"), DateTimeKind.Utc);
if (myDateUtc.Kind == DateTimeKind.Utc)
{
Console.WriteLine("Yes. I am UTC!");
}
You can test this code using the online c# compiler:
http://rextester.com/
I hope it helps.
or use the AdjustToUniversal DateTimeStyle in a call to
DateTime.ParseExact(String, String[], IFormatProvider, DateTimeStyles)
You need to specify the format:
DateTime date = DateTime.ParseExact(
"Tue, 1 Jan 2008 00:00:00 UTC",
"ddd, d MMM yyyy HH:mm:ss UTC",
CultureInfo.InvariantCulture);
To correctly parse the string given in the question without changing it, use the following:
using System.Globalization;
string dateString = "Tue, 1 Jan 2008 00:00:00 UTC";
DateTime parsedDate = DateTime.ParseExact(dateString, "ddd, d MMM yyyy hh:mm:ss UTC", CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal);
This implementation uses a string to specify the exact format of the date string that is being parsed. The DateTimeStyles parameter is used to specify that the given string is a coordinated universal time string.
It's not a valid format, however "Tue, 1 Jan 2008 00:00:00 GMT" is.
The documentation says like this:
A string that includes time zone information and conforms to ISO 8601. For example, the first of the following two strings designates the Coordinated Universal Time (UTC); the second designates the time in a time zone seven hours earlier than UTC:
2008-11-01T19:35:00.0000000Z
A string that includes the GMT designator and conforms to the RFC 1123 time format. For example:
Sat, 01 Nov 2008 19:35:00 GMT
A string that includes the date and time along with time zone offset information. For example:
03/01/2009 05:42:00 -5:00
I've put together a utility method which employs all tips shown here plus some more:
static private readonly string[] MostCommonDateStringFormatsFromWeb = {
"yyyy'-'MM'-'dd'T'hh:mm:ssZ", // momentjs aka universal sortable with 'T' 2008-04-10T06:30:00Z this is default format employed by moment().utc().format()
"yyyy'-'MM'-'dd'T'hh:mm:ss.fffZ", // syncfusion 2008-04-10T06:30:00.000Z retarded string format for dates that syncfusion libs churn out when invoked by ejgrid for odata filtering and so on
"O", // iso8601 2008-04-10T06:30:00.0000000
"s", // sortable 2008-04-10T06:30:00
"u" // universal sortable 2008-04-10 06:30:00Z
};
static public bool TryParseWebDateStringExactToUTC(
out DateTime date,
string input,
string[] formats = null,
DateTimeStyles? styles = null,
IFormatProvider formatProvider = null
)
{
formats = formats ?? MostCommonDateStringFormatsFromWeb;
return TryParseDateStringExactToUTC(out date, input, formats, styles, formatProvider);
}
static public bool TryParseDateStringExactToUTC(
out DateTime date,
string input,
string[] formats = null,
DateTimeStyles? styles = null,
IFormatProvider formatProvider = null
)
{
styles = styles ?? DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal; //0 utc
formatProvider = formatProvider ?? CultureInfo.InvariantCulture;
var verdict = DateTime.TryParseExact(input, result: out date, style: styles.Value, formats: formats, provider: formatProvider);
if (verdict && date.Kind == DateTimeKind.Local) //1
{
date = date.ToUniversalTime();
}
return verdict;
//0 employing adjusttouniversal is vital in order for the resulting date to be in utc when the 'Z' flag is employed at the end of the input string
// like for instance in 2008-04-10T06:30.000Z
//1 local should never happen with the default settings but it can happen when settings get overriden we want to forcibly return utc though
}
Notice the use of '-' and 'T' (single-quoted). This is done as a matter of best practice since regional settings interfere with the interpretation of chars such as '-' causing it to be interpreted as '/' or '.' or whatever your regional settings denote as date-components-separator. I have also included a second utility method which show-cases how to parse most commonly seen date-string formats fed to rest-api backends from web clients. Enjoy.
Just replace "UTC" with "GMT" -- simple and doesn't break correctly formatted dates:
DateTime.Parse("Tue, 1 Jan 2008 00:00:00 UTC".Replace("UTC", "GMT"))
Not sure why, but you can wrap DateTime.ToUniversalTime in a try / catch and achieve the same result in more code.
Good luck.

Handling Time over TimeZones

I am attempting to convert this string time value 2017-01-10T13:19:00-07:00 to local time (Eastern). Now from my research 07:00 is Mountain Time which is 2 hours beind Eastern Time (my local). However, when I run this syntax to convert the returned output is 01/17/2017 10:19:00 AM which is 3 hours difference, not 2.
This is my syntax I am using, is this set-up incorrectly? What should I change in order to have the accurate local time returned from the UTC time?
static void Main(string[] args)
{
string green = "2017-01-10T13:19:00-07:00";
DateTime iKnowThisIsUtc = Convert.ToDateTime(green);
DateTime runtimeKnowsThisIsUtc = DateTime.SpecifyKind(
iKnowThisIsUtc,
DateTimeKind.Utc);
DateTime localVersion = runtimeKnowsThisIsUtc.ToLocalTime();
Console.WriteLine(localVersion);
Console.ReadKey();
}
EDIT
I have verified my computer is set to the correct time zone by using the following syntax which produces Eastern for both (which is correct)
TimeZone zone = TimeZone.CurrentTimeZone;
string standard = zone.StandardName;
string daylight = zone.DaylightName;
Console.WriteLine(standard);
Console.WriteLine(daylight);
Convert the string to a DateTime object:
var datetime = DateTime.Parse("2017-01-10T13:19:00-07:00");
Get the timezone for EST:
var easternZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
Convert to EST (note the conversion .ToUniversalTime()):
var easternTime = TimeZoneInfo.ConvertTimeFromUtc(datetime.ToUniversalTime(), easternZone);
Output of easternTime.ToString();:
10/01/2017 15:19:00
(I'm in the UK hence dd/MM/yyyy, yours may show differently)
// your input string
string green = "2017-01-10T13:19:00-07:00";
// parse to a DateTimeOffset
DateTimeOffset dto = DateTimeOffset.Parse(green);
// find the time zone that you are interested in.
// note that this one is US Eastern time - inclusive of both EST and EDT, despite the name.
TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
// convert the DateTimeOffset to the time zone
DateTimeOffset eastern = TimeZoneInfo.ConvertTime(dto, tzi);
// If you need it, you can get just the DateTime portion. (Its .Kind will be Unspecified)
DateTime dtEastern = eastern.DateTime;
When your application needs to be unambiguously aware of timezones, you may want to consider a DateTimeOffset instead of DateTime.
Choosing Between DateTime, DateTimeOffset, TimeSpan, and TimeZoneInfo
And this question looks like someone has collected lots of best practices on this topoic - Daylight saving time and time zone best practices

Converting UTC DateTime to EST DateTime with correct offset

My app gets a DateTime object in UTC and needs to output as a string it in EST format. I've tried the following code, but when I get the output the offset still shows as +00:00 instead of -05:00
static void Main(string[] args)
{
var currentDate = DateTime.Now.ToUniversalTime();
var convertedTime = ConvertUtcToEasternStandard(currentDate);
Console.WriteLine(currentDate.ToString("yyyy-MM-ddTHH:mm:sszzz"));
Console.WriteLine(convertedTime.ToString("yyyy-MM-ddTHH:mm:sszzz"));
}
private static DateTime ConvertUtcToEasternStandard(DateTime dateTime)
{
var easternZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
return TimeZoneInfo.ConvertTimeFromUtc(dateTime, easternZone);
}
This outputs:
2016-11-18T06:56:14+00:00
2016-11-18T01:56:14+00:00
So the time is being shifted correctly, but the offset stays at +00:00 when I want it to be -05:00. Any idea how I can get a DateTime object with the correct offset when output with the above format string?
I read this in the API not long ago, basically, with DateTime values, it's very much impossible to get the zzz format offset to be something useful.
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 DateTime.Kind property. For this reason, the "zzz" format specifier is not recommended for use with DateTime values.
With DateTimeOffset values, this format specifier represents the DateTimeOffset value's offset from UTC in hours and minutes.
The offset is always displayed with a leading sign. A plus sign (+) indicates hours ahead of UTC, and a minus sign (-) indicates hours behind UTC. A single-digit offset is formatted with a leading zero.
For example, I'm in Eastern Standard Time, and this is my result:
2016-11-18T07:9:38-05:00
2016-11-18T02:9:38-05:00
Obviously UTC time shouldn't have an offset of -05:00.
Modify your code just a bit and we have a solution:
void Main()
{
var currentDate = DateTimeOffset.Now.ToUniversalTime();
var convertedTime = ConvertUtcToEasternStandard(currentDate);
var format = "yyyy-MM-ddTHH:m:sszzz";
Console.WriteLine(currentDate.ToString(format));
Console.WriteLine(convertedTime.ToString(format));
}
// Define other methods and classes here
private static DateTimeOffset ConvertUtcToEasternStandard(DateTimeOffset dateTime)
{
var easternZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
return TimeZoneInfo.ConvertTime(dateTime, easternZone);
}
Result:
2016-11-18T07:17:46+00:00
2016-11-18T02:17:46-05:00
That's more like it.
Note: replaced previous code, ignorance got the best of me and I failed to realize that it wasn't working right. TimeZoneInfo.ConvertTime takes a DateTimeOffset which does what we want.
And if we add another case for Pacific Standard Time:
private static DateTimeOffset ConvertUtcToPacificStandard(DateTimeOffset dateTime)
{
var pacificZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
return TimeZoneInfo.ConvertTime(dateTime, pacificZone);
}
We get the correct result:
2016-11-17T23:21:21-08:00

Why can't DateTime.Parse parse UTC date

Why can't it parse this:
DateTime.Parse("Tue, 1 Jan 2008 00:00:00 UTC")
It can't parse that string because "UTC" is not a valid time zone designator.
UTC time is denoted by adding a 'Z' to the end of the time string, so your parsing code should look like this:
DateTime.Parse("Tue, 1 Jan 2008 00:00:00Z");
From the Wikipedia article on ISO 8601
If the time is in UTC, add a 'Z'
directly after the time without a
space. 'Z' is the zone designator for
the zero UTC offset. "09:30 UTC" is
therefore represented as "09:30Z" or
"0930Z". "14:45:15 UTC" would be
"14:45:15Z" or "144515Z".
UTC time is also known as 'Zulu' time,
since 'Zulu' is the NATO phonetic
alphabet word for 'Z'.
Assuming you use the format "o" for your datetime so you have "2016-07-24T18:47:36Z", there is a very simple way to handle this.
Call DateTime.Parse("2016-07-24T18:47:36Z").ToUniversalTime().
What happens when you call DateTime.Parse("2016-07-24T18:47:36Z") is you get a DateTime set to the local timezone. So it converts it to the local time.
The ToUniversalTime() changes it to a UTC DateTime and converts it back to UTC time.
Just use that:
var myDateUtc = DateTime.SpecifyKind(DateTime.Parse("Tue, 1 Jan 2008 00:00:00"), DateTimeKind.Utc);
if (myDateUtc.Kind == DateTimeKind.Utc)
{
Console.WriteLine("Yes. I am UTC!");
}
You can test this code using the online c# compiler:
http://rextester.com/
I hope it helps.
or use the AdjustToUniversal DateTimeStyle in a call to
DateTime.ParseExact(String, String[], IFormatProvider, DateTimeStyles)
You need to specify the format:
DateTime date = DateTime.ParseExact(
"Tue, 1 Jan 2008 00:00:00 UTC",
"ddd, d MMM yyyy HH:mm:ss UTC",
CultureInfo.InvariantCulture);
To correctly parse the string given in the question without changing it, use the following:
using System.Globalization;
string dateString = "Tue, 1 Jan 2008 00:00:00 UTC";
DateTime parsedDate = DateTime.ParseExact(dateString, "ddd, d MMM yyyy hh:mm:ss UTC", CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal);
This implementation uses a string to specify the exact format of the date string that is being parsed. The DateTimeStyles parameter is used to specify that the given string is a coordinated universal time string.
It's not a valid format, however "Tue, 1 Jan 2008 00:00:00 GMT" is.
The documentation says like this:
A string that includes time zone information and conforms to ISO 8601. For example, the first of the following two strings designates the Coordinated Universal Time (UTC); the second designates the time in a time zone seven hours earlier than UTC:
2008-11-01T19:35:00.0000000Z
A string that includes the GMT designator and conforms to the RFC 1123 time format. For example:
Sat, 01 Nov 2008 19:35:00 GMT
A string that includes the date and time along with time zone offset information. For example:
03/01/2009 05:42:00 -5:00
I've put together a utility method which employs all tips shown here plus some more:
static private readonly string[] MostCommonDateStringFormatsFromWeb = {
"yyyy'-'MM'-'dd'T'hh:mm:ssZ", // momentjs aka universal sortable with 'T' 2008-04-10T06:30:00Z this is default format employed by moment().utc().format()
"yyyy'-'MM'-'dd'T'hh:mm:ss.fffZ", // syncfusion 2008-04-10T06:30:00.000Z retarded string format for dates that syncfusion libs churn out when invoked by ejgrid for odata filtering and so on
"O", // iso8601 2008-04-10T06:30:00.0000000
"s", // sortable 2008-04-10T06:30:00
"u" // universal sortable 2008-04-10 06:30:00Z
};
static public bool TryParseWebDateStringExactToUTC(
out DateTime date,
string input,
string[] formats = null,
DateTimeStyles? styles = null,
IFormatProvider formatProvider = null
)
{
formats = formats ?? MostCommonDateStringFormatsFromWeb;
return TryParseDateStringExactToUTC(out date, input, formats, styles, formatProvider);
}
static public bool TryParseDateStringExactToUTC(
out DateTime date,
string input,
string[] formats = null,
DateTimeStyles? styles = null,
IFormatProvider formatProvider = null
)
{
styles = styles ?? DateTimeStyles.AllowWhiteSpaces | DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal; //0 utc
formatProvider = formatProvider ?? CultureInfo.InvariantCulture;
var verdict = DateTime.TryParseExact(input, result: out date, style: styles.Value, formats: formats, provider: formatProvider);
if (verdict && date.Kind == DateTimeKind.Local) //1
{
date = date.ToUniversalTime();
}
return verdict;
//0 employing adjusttouniversal is vital in order for the resulting date to be in utc when the 'Z' flag is employed at the end of the input string
// like for instance in 2008-04-10T06:30.000Z
//1 local should never happen with the default settings but it can happen when settings get overriden we want to forcibly return utc though
}
Notice the use of '-' and 'T' (single-quoted). This is done as a matter of best practice since regional settings interfere with the interpretation of chars such as '-' causing it to be interpreted as '/' or '.' or whatever your regional settings denote as date-components-separator. I have also included a second utility method which show-cases how to parse most commonly seen date-string formats fed to rest-api backends from web clients. Enjoy.
Just replace "UTC" with "GMT" -- simple and doesn't break correctly formatted dates:
DateTime.Parse("Tue, 1 Jan 2008 00:00:00 UTC".Replace("UTC", "GMT"))
Not sure why, but you can wrap DateTime.ToUniversalTime in a try / catch and achieve the same result in more code.
Good luck.

Categories

Resources