C# DateTime - converting a DateTimeOffset to another TimeZone - c#

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

Related

Convert date from UTC to local timezone during daylight saving [duplicate]

I'm trying to use DateTimeOffset to convey a specific moment in time across any time zone. I can't figure out how to use TimeZoneInfo to deal with daylight saving time.
var dt = DateTime.UtcNow;
Console.WriteLine(dt.ToLocalTime());
var tz = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
var utcOffset = new DateTimeOffset(dt, TimeSpan.Zero);
Console.WriteLine(utcOffset.ToOffset(tz.BaseUtcOffset));
This prints out:
6/2/2010 4:37:19 PM
6/2/2010 3:37:19 PM -06:00
I am in the central time zone, and and we are currently in daylight saving time.
I am trying to get the second line to read:
6/2/2010 4:37:19 PM -05:00
BaseUtcOffset apparently doesn't change based on DST.
How can I get the the right time with the proper offset value?
You can also use TimeZoneInfo.ConvertTimeFromUtc, which will allow for daylight saving time:
DateTime utc = DateTime.UtcNow;
TimeZoneInfo zone = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
DateTime localDateTime = TimeZoneInfo.ConvertTimeFromUtc(utc, zone);
You need to get the UtcOffset from the TimeZoneInfo, then pass that to the ToOffset() method:
var dt = DateTime.UtcNow;
Console.WriteLine(dt.ToLocalTime());
var tz = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
var utcOffset = new DateTimeOffset(dt, TimeSpan.Zero);
Console.WriteLine(utcOffset.ToOffset(tz.GetUtcOffset(utcOffset)));
Or better, if you don't want to hard code the time zone identifier:
TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById(TimeZoneInfo.Local.Id);
DateTime localDateTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tzi);
I'm a beginner both at .NET and stackoverflow, so I could be wrong, but here goes:
Using TimeZoneInfo.ConvertTimeFromUtc will allow for daylight saving time, and convert to the correct time according to the time zone + a possible DST offset. However - the offset itself in the resulting object will show the offset for standard time, and not take daylight saving time into account. So if you want to do a ToString on the object, you will end up with the correct time (in hours and minutes), but the wrong offset during daylight saving time, which may lead to the wrong moment in time later in the code.
If you instead use the GetUtcOffset to get the offset for a specific time, and then do a ToOffset on the DateTimeOffset object, both the hours/minutes and the offset itself will be correctly converted, and you can safely do a ToString.
string ExpectedDateTimePattern = "yyyy'-'MM'-'dd'T'HH':'mm':'ss''zzz";
string timeZoneId = "FLE Standard Time";
string dateTimestr = "2017-10-09T09:00:00+02:00";
DateTimeOffset dto = DateTimeOffset.Parse(dateTimeStr);
TimeZoneInfo zone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
TimeSpan offset = zone.GetUtcOffset(dto);
dto = dto.ToOffset(offset);
string localTime = dto.ToString(ExpectedDateTimePattern);
localTime will return "2017-10-09T10:00:00+03:00".
datetimeoffset timezoneinfo getutcoffset
This will Adjust automatically... and Return time as per your timezone.
public static string SetLastModified (
TimeZoneInfo csttzi = TimeZoneInfo.FindSystemTimeZoneById(TimeZone.CurrentTimeZone.StandardName);
DateTime cstTime = TimeZoneInfo.ConvertTime(DateTime.UtcNow, csttzi);
return String.Format("DaylightSavingTime: {0}", cstTime.IsDaylightSavingTime().ToString());
}

UTC DateTime to Local and Back gives incorrect time

I need to be able to convert UTC time to Local Time, add one hour and would like to convert back to UTC for comparison. However, the basic to TimeZone and back to UTC gives incorrect time.
DateTime utcDateTime = DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Utc);
var localTime = utcDateTime.ToZone("Central Time (US & Canada)"); // CORRECT TIME
// INCORRECT when converted back to utc. It should be the same as utcDateTime
var convertedBack = TimeZoneInfo.ConvertTimeToUtc(localTime);
What am I missing?
// This works
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
var convertedBack =TimeZoneInfo.ConvertTimeToUtc(localTime, tz);

How can I create a new instance of DateTime in specific time zone?

Given a specific TimeZoneInfo instance how can I create a new DateTime instance in the specified time zone? For example if I have:
var tz = TimeZoneInfo.FindSystemTimeZoneById("US Eastern Standard Time");
var date = new DateTime(2017, 1, 1, 0, 0, 0, DateTimeKind.Unspecified);
Console.WriteLine(TimeZoneInfo.ConvertTime(date, tz));
I am always getting 12/31/2016 7:00:00 PM regardless of what DateTimeKind I define (Utc, Local or Unspecified).
How can I declare a new DateTime that will be January 1st of 2017 at 0:00:00 in US Eastern Standard Time?
You can use TimeZoneInfo to retrieve your zone
You can find timezones here
var zn = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
to express that you are using a local eastern standard time use DateTimeOffset struct instead of DateTime
DateTimeOffset dateTimeOffset = new DateTimeOffset(new DateTime(2017, 1, 1, 0, 0, 0, DateTimeKind.Unspecified), zn.BaseUtcOffset);
Why DateTimeOffset
DateTimeOffset is a representation of instantaneous time (also known as absolute time).
you can use the timezoneID as you are using it to specify what timezone you want to create your datetime object.
TimeZoneInfo tzone= TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard
Time");
DateTime dt = DateTime.Now;
later you just convert the datetime to your required timezone.
var datetime2 = TimeZoneInfo.ConvertTimeFromUtc(dt , tzone);
this is the link where you can find all timezones ID. TimeZoneIDs
Thank you, hope this can help you.

How to handle UTC difference while converting DateTime

When I am trying to convert a date to AST and then convert it back to IST, why is there a difference of 1 hour?
var tesDate = DateTime.Parse("2015-09-01T03:30:00+05:30");
TimeZoneInfo tmz = TimeZoneInfo.FindSystemTimeZoneById("Atlantic Standard Time");
DateTime tesDate1 = DateTime.SpecifyKind(tesDate, DateTimeKind.Unspecified);
var earliestStartTime = TimeZoneInfo.ConvertTime(tesDate1, tmz, TimeZoneInfo.Utc);
//Local Time is Now in IS
var localEarliestStartTime = earliestStartTime.ToLocalTime();
Actual OutPut {9/1/2015 12:00:00 PM}
Expected Output {9/1/2015 01:00:00 PM}
When I am trying to convert a date to AST and then convert it back to IST, why is there a difference of 1 hour?
Because you're not actually doing that conversion. You're doing something much more convoluted, and I'm not sure why. I'll comment your code so you can see what you are doing:
// Parse a string with a fixed +05:30 Indian offset
// This is converting to the local time zone in the process (Kind == DateTimeKind.Local)
var tesDate = DateTime.Parse("2015-09-01T03:30:00+05:30");
// Get the Atlantic time zone
TimeZoneInfo tmz = TimeZoneInfo.FindSystemTimeZoneById("Atlantic Standard Time");
// Assign DateTimeKind.Unspecified, which removes the existing Local kind (why?)
DateTime tesDate1 = DateTime.SpecifyKind(tesDate, DateTimeKind.Unspecified);
// Convert to UTC, pretending the time is in AST, when actually it's in the local zone
var earliestStartTime = TimeZoneInfo.ConvertTime(tesDate1, tmz, TimeZoneInfo.Utc);
// Convert from UTC back to the local zone
var localEarliestStartTime = earliestStartTime.ToLocalTime();
This is a bit silly, and of course it will lead to the wrong values. If your goal is to convert from IST to AST and back, then you should just do this:
TimeZoneInfo tzAtlantic = TimeZoneInfo.FindSystemTimeZoneById("Atlantic Standard Time");
TimeZoneInfo tzIndian = TimeZoneInfo.FindSystemTimeZoneById("India Standard Time");
DateTimeOffset original = DateTimeOffset.Parse("2015-09-01T03:30:00+05:30");
DateTimeOffset atlantic = TimeZoneInfo.ConvertTime(original, tzAtlantic);
DateTimeOffset indian = TimeZoneInfo.ConvertTime(atlantic, tzIndian);
There's no need to involve either UTC or the local time zone, unless you have some other purpose that you didn't explain.

datetime to string with time zone

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.

Categories

Resources