Timezone StartOfDay - c#

Hi i receice some random datetimeoffset utc my point is
1 - turn it into chicago time
2- Define the chicago startOfDay
3-finaly convert chicago startOfDay back to an utc datetimeOffset.
(this process is needed to query an utc api)
i writed this that seem do what i want but realy not sure it will always do the job with day saving light etc..
someone can tell or improve the code?
var UTCDate = DateTimeOffset.UtcNow; //<----------Some random utc dateTimeOffset
TimeZoneInfo zone = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
DateTimeOffset CSTDate = GetZoneDatetimeOffset(UTCDate, zone);
Console.WriteLine("Utc : {0} Chicago: {1} local : {2}", UTCDate, CSTDate, UTCDate.ToLocalTime());
Console.WriteLine(" Chicago StartOfDay: {0} Chicago StartOfDay To UTC : {1}", CSTDate.Date, CSTDate.Date.ToUniversalTime());
public DateTimeOffset GetZoneDatetimeOffset(DateTimeOffset someUTCDate , TimeZoneInfo destZone)
{
DateTimeOffset ZoneDateTimeOffset = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(someUTCDate.DateTime , TimeZoneInfo.Utc.Id, destZone.Id);
return ZoneDateTimeOffset;
}

You have a few bugs in your code:
You call TimeZoneInfo.ConvertTimeBySystemTimeZoneId and pass a DateTime value. The result will thus also be a DateTime. The Kind of that result will be Unspecified. Assigning it to a DateTimeOffset thus will perform an implicit cast operation from DateTime to DateTimeOffset, which will use the computer's local time zone - not the time zone you are working with.
Both DateTimeOffset.Date and DateTimeOffset.DateTime also return a DateTime whose Kind is Unspecified. Thus when you call ToLocalTime or ToUniversalTime, you are again using your the computer's local time zone.
Determining the start of the day in a specific time zone can be tricky. Though it doesn't happen in Chicago (or anywhere in the USA), some time zones of the world have DST transitions that occur immediately at the start of the day. Thus 00:00 might not be the first moment of the day, or it might occur twice. Your code doesn't account for that.
A good example is Cuba, where DST next starts on 2022-03-13 at midnight, and thus the start of that day in Cuba is 2022-03-13T01:00.
You can use the GetStartOfDay method I already provided in this answer to handle these edge cases.
Putting it all together:
// Input values
DateTimeOffset utc = DateTimeOffset.UtcNow;
TimeZoneInfo zone = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
// Convert with full fidelity using only DateTimeOffset values
DateTimeOffset converted = TimeZoneInfo.ConvertTime(utc, zone);
// Get the start of day using the method from https://stackoverflow.com/a/49988688
DateTimeOffset startOfDay = GetStartOfDay(converted.DateTime, zone);
// Output the results
Console.WriteLine("Original values:");
Console.WriteLine("Chicago: {0:o} UTC: {1:o} Local: {2:o}", converted, utc, utc.ToLocalTime());
Console.WriteLine();
Console.WriteLine("At start of day in Chicago:");
Console.WriteLine("Chicago: {0:o} UTC: {1:o} Local: {2:o}", startOfDay, startOfDay.ToUniversalTime(), startOfDay.ToLocalTime());
Example output (local time zone is US Pacific Time in this example):
Original values:
Chicago: 2022-03-10T10:58:47.8164830-06:00 UTC: 2022-03-10T16:58:47.8164830+00:00 Local: 2022-03-10T08:58:47.8164830-08:00
At start of day in Chicago:
Chicago: 2022-03-10T00:00:00.0000000-06:00 UTC: 2022-03-10T06:00:00.0000000+00:00 Local: 2022-03-09T22:00:00.0000000-08:00
See also the working .NET Fiddle here.

Related

Parse String into DateTimeOffset While Assuming TimeZone

Probably a super simple solution but I'm clearly missing something.
I have a string object with value "2020/07/29 13:30:00".
How can I parse that into a DateTimeOffset object and make the assumption that the time zone of that parsed time is "GMT Standard Time" for example, or any TimeZoneInfo I wish to specify preferably?
How can I then take that DateTimeOffset, and return its Utc time but to any specified time zone of my choice?
Many thanks
The easiest I could find is something like this.
I couldn't find any methods to parse a DateTimeOffset in a particular given timezone, but you can parse your string as a DateTime (with a Kind of Unspecified, which just acts as a container for the bits of information in the string, without trying to apply timezone knowledge to it).
Then you can ask a TimeZoneInfo for the UTC offset in a given timezone at the given local time, and apply this to the DateTime to create a DateTimeOffset.
Once you've got your DateTimeOffset, you can work with it using its ToOffset method, and TimeZoneInfo.ConvertTime.
string input = "2020/07/29 13:30:00";
var timezone = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
// DateTime.Parse creates a DateTime with Kind == Unspecified
var dateTime = DateTime.Parse(input);
Console.WriteLine(dateTime); // 7/29/2020 1:30:00 PM
// Since Kind == Unspecified, timezone.GetUtcOffset will give us the UTC offset in effect at
// the given local time in timezone
var dateTimeOffset = new DateTimeOffset(dateTime, timezone.GetUtcOffset(dateTime));
Console.WriteLine(dateTimeOffset); // 7/29/2020 1:30:00 PM +01:00
// Convert to UTC
Console.WriteLine(dateTimeOffset.UtcDateTime); // 7/29/2020 12:30:00 PM
Console.WriteLine(dateTimeOffset.ToOffset(TimeSpan.Zero)); // 7/29/2020 12:30:00 PM +00:00
// Convert to another timezone
var cst = TimeZoneInfo.FindSystemTimeZoneById("Central America Standard Time");
Console.WriteLine(TimeZoneInfo.ConvertTime(dateTimeOffset, cst)); // 7/29/2020 6:30:00 AM -06:00
Try the DateTimeOffset.ParseExact overload that accepts a DateTimeStyles parameter.
This code:
var dt=DateTimeOffset.ParseExact("2020/07/29 13:30:00","yyyy/MM/dd HH:mm:ss",
CultureInfo.InvariantCulture,DateTimeStyles.AssumeUniversal);
Returns 2020-07-29T13:30:00.0000000+00:00
There's no GMT Standard Time, that's a very unfortunate name used in Windows that somehow manages to mix up British and UTC time to the point that no-one knows what it means without looking at the docs. This was thoroughly discussed and explained in this question: Difference between UTC and GMT Standard Time in .NET. As one of the answers explains :
The names GMT Standard Time and GMT Daylight Time are unknown outside of Redmond. They are mythical animals that appear only in the bestiary called the Windows Registry.
If you wanted to assume British time and your machine uses a British timezone, you can use DateTimeStyles.AssumeLocal
This function should convert your date time string (with assumption this is GMT Standard Time) to any other timezone:
public static DateTime? ToUTCTimeZone(string sDate, string timeZone)
{
DateTime utcDate = DateTime.Parse(sDate);
DateTimeOffset localServerTime = DateTimeOffset.Now;
utcDate = DateTime.SpecifyKind(utcDate, DateTimeKind.Utc);
TimeZoneInfo cstZone = TimeZoneInfo.FindSystemTimeZoneById(timeZone);
if (cstZone == null)
return null;
return TimeZoneInfo.ConvertTimeFromUtc(utcDate, cstZone);
}//ToUTCTimeZone

C# Manually specify a timezone in DateTime.ParseExact

I have a log file that contains date/time strings formatted like this:
2019/10/01 15:30
As you can see, the strings don't contain any timezone information. However, I do know the timezone that the time is represented within.
If I use this C# code on my own PC, which has its local timezone set correctly, the time is parsed as expected. I'm expecting to convert the local time to UTC for inserting into a database.
Time.ParseExact("2019/10/01 15:30", "yyyy/MM/dd HH:mm", provider, DateTimeStyles.AdjustToUniversal & DateTimeStyles.AssumeLocal);
However, if I run this code on one of my Docker hosts, which has its timezone set to UTC, the times are parsed as UTC. This makes sense, since on a Docker host using UTC AssumeLocal will be the same as UTC.
Simply applying a manual offset isn't sufficient due to daylight saving time. Also, some of these logs are historical and go back to prior to the DST change, so even using an algorithm would be complex and it seems like it's something that should already be solved (i.e. given a date and timezone, determine if it's in DST or not and then adjust to UTC accordingly)
What I want to be able to do is to explicitly specify the timezone for the timestamp and have the framework use both the given timezone and the actual date to properly convert the time to UTC. I've looked at this question and this question but they all seem to depend on the system itself being set to the timezone that you want to assume the date is in.
The second question does seem to start addressing the issue of assuming a known timezone, but then the responses just use GetUtcOffset on the parsed time to determine the amount of time to offset the time. This assumes that the time was already parsed in the desired timezone.
I believe what you're looking for is TimeZoneInfo.
Using TimeZoneInfo, you can get the information you need from TimeZoneInfo.FindSystemTimeZoneById.
Those Ids are based on the operating system, but as long as all of your containers are on Linux (as an example) you will be able to use that to get a UTC time for storage. You mention that you know what timezone your string is in, so you'll likely need something to convert that to Ids readable by TimeZoneInfo.
Code example taken from TimeZoneInfo.FindSystemTimeZoneById
using System;
public class Example
{
public static void Main()
{
// Get time in local time zone
DateTime thisTime = DateTime.Now;
Console.WriteLine("Time in {0} zone: {1}", TimeZoneInfo.Local.IsDaylightSavingTime(thisTime) ?
TimeZoneInfo.Local.DaylightName : TimeZoneInfo.Local.StandardName, thisTime);
Console.WriteLine(" UTC Time: {0}", TimeZoneInfo.ConvertTimeToUtc(thisTime, TimeZoneInfo.Local));
// Get Tokyo Standard Time zone
TimeZoneInfo tst = TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time");
DateTime tstTime = TimeZoneInfo.ConvertTime(thisTime, TimeZoneInfo.Local, tst);
Console.WriteLine("Time in {0} zone: {1}", tst.IsDaylightSavingTime(tstTime) ?
tst.DaylightName : tst.StandardName, tstTime);
Console.WriteLine(" UTC Time: {0}", TimeZoneInfo.ConvertTimeToUtc(tstTime, tst));
}
}
// The example displays output like the following when run on a system in the
// U.S. Pacific Standard Time zone:
// Time in Pacific Standard Time zone: 12/6/2013 10:57:51 AM
// UTC Time: 12/6/2013 6:57:51 PM
// Time in Tokyo Standard Time zone: 12/7/2013 3:57:51 AM
// UTC Time: 12/6/2013 6:57:51 PM

I want to specify a TimeZone as GMT but create a DateTime that is local (EST);

I want to specify a time in GMT timezone, and then convert it to the local TimeZone which is EST.
This appears to do what I want, but seems a long way to get there!
Is there a simpler way to achieve this:
public static TimeZoneInfo edtZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
public static TimeZoneInfo gmtZone = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
public static CultureInfo ci = CultureInfo.InvariantCulture;
DateTime edtStartDT = TimeZoneInfo.ConvertTime(DateTime.SpecifyKind(DateTime.Now.Date.Add(new TimeSpan(18, 00, 00)), DateTimeKind.Unspecified), gmtZone, edtZone);
This is probably what you are looking for:
// A timespan isn't really a time-of-day, but we'll let that slide for now.
TimeSpan time = new TimeSpan(18, 00, 00);
// Use the current utc date, at that time.
DateTime utcDateTime = DateTime.UtcNow.Date.Add(time);
// Convert to the US Eastern time zone.
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
DateTime easternDateTime = TimeZoneInfo.ConvertTimeFromUtc(utcDateTime, tz);
Note that we're pairing the current UTC date with the time you provided. Since US Eastern time is either 5 or 4 hours behind UTC, with 18:00 you will always get the same date. But if you were using a different time, such as 00:00, you would find that the resulting Eastern time was on the prior day. This is normal.
A couple of notes about your previous code:
The Windows Time Zone ID "Eastern Standard Time" refers to both EST and EDT. It really should have been called "Eastern Time". Don't let the name confuse the issue.
GMT and UTC are mostly the same for all modern usage. Unless you are referring to the time zone used in London, you should prefer the term UTC.
The Windows Time Zone ID "GMT Standard Time" is not actually for GMT/UTC. It is specifically the time zone used in London, which alternates between GMT (UTC+00:00) and BST (UTC+01:00). If you want a TimeZoneInfo representing UTC, the ID is just "UTC". (However, you don't really need that in this case.)
Your original code assumed used DateTime.Now.Date, which would assume the date in the computer's local time zone - which might not be either UTC or Eastern.
If you find yourself using DateTime.SpecifyKind, in most situations, you're probably doing something wrong. (The exception would be when loading or deserializing.)
Regarding my note about a TimeSpan not being a true time-of-day, this is how .NET would have you handle that:
DateTime time = DateTime.Parse("18:00:00", CultureInfo.InvariantCulture);
DateTime utcDateTime = DateTime.UtcNow.Date.Add(time.TimeOfDay);
Or even uglier in one line:
DateTime utcDateTime = DateTime.Parse("18:00:00", CultureInfo.InvariantCulture,
DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
Personally, I would rather use Noda Time, which has a separate LocalTime type explicitly for a time-of-day that's not bound to a specific date. I'm also working to get System.TimeOfDate and System.Date types added to the CoreCLR.

How to parse the date and time depends upon the time zone?

I want to parse the string to time.
string inputDate = "1970-01-01T00:00:00+0000";
var dt = DateTime.Parse(inputDate, CultureInfo.InvariantCulture);
Console.WriteLine("Date==> " + dt);
It is working fine in india time(UTC +5.30).
But when I change the time zone to UTC -5 in settings in emulator, the out put is showing
12/31/1969 7:00:00 PM
The date should be same when ever i change the time zone in settings. Please help me to resolve my problem.
Let me explain what is going on here..
Usually, DateTime.Parse method returned DateTime's Kind property will be Unspecified.
But since your string has time zone information and you using DateTime.Parse method without DateTimeStyles overload (it uses DateTimeStyles.None by default), your DateTime's Kind property will be Local.
That's why when you use your code in UTC +05.30 time zone system, it will be generate a result like;
01/01/1970 05:00:00 AM
and when you use in UTC -05.00 time zone system, it will generate;
12/31/1969 7:00:00 PM // which is equal 12/31/1969 19:00:00 AM representation
which is too normal.
The date should be same when ever i change the time zone in settings.
Makind your DateTime as UTC is the best choice in such a case. Using ToUniversalTime() method is one way to do that in a Local time.
From documentation;
The Coordinated Universal Time (UTC) is equal to the local time minus
the UTC offset.
Since your code generates Local time, your ToUniversalTime() generated datetime's will be the same in both time zone.
Another way to do it, using DateTimeStyles.AdjustToUniversal as a third parameter in DateTime.Parse method.
From documentation;
Date and time are returned as a Coordinated Universal Time (UTC). If
the input string denotes a local time, through a time zone specifier
or AssumeLocal, the date and time are converted from the local time to
UTC. If the input string denotes a UTC time, through a time zone
specifier or AssumeUniversal, no conversion occurs. If the input
string does not denote a local or UTC time, no conversion occurs and
the resulting Kind property is Unspecified.
string inputDate = "1970-01-01T00:00:00+0000";
var dt = DateTime.Parse(inputDate,
CultureInfo.InvariantCulture,
DateTimeStyles.AdjustToUniversal);
That will generate 01/01/1970 00:00:00 which Kind is Utc.
Final Solution
string givenDate = ("1970-01-01T00:00:00+0000");
DateTime d = DateTime.Parse(givenDate, System.Globalization.CultureInfo.InvariantCulture);
string ouputDate = d.ToUniversalTime().ToString("MMM d, yyyy h:m:s tt", System.Globalization.CultureInfo.InvariantCulture);

Convert US-Style DateTime to local time

How do I convert US-style DateTime such as 5/1/2012 3:38:27 PM returned from the server to user's local time? I am developing for windows phone.
I've tried
DateTime localTime = serverTime.ToLocalTime();
but the result is off a couple of hours. I thought ToLocalTime() will take care of the conversion to any timezone the user are in? Perhaps I need to get the user's timezone info first?
EDIT 1
I think the serverTime is in the PST time zone
EDIT 2
My timezone is GMT +8. I tried the following, but the resulting localTime is 15 hour behind.
TimeZoneInfo localZone = TimeZoneInfo.Local;
DateTime localTime = TimeZoneInfo.ConvertTime(serverTime, localZone);
EDIT 3
This result in 7 hours behind my local time.
TimeZoneInfo localZone = TimeZoneInfo.Local;
DateTime dateTimeKind = DateTime.SpecifyKind(serverTime, DateTimeKind.Utc);
DateTime localTime = TimeZoneInfo.ConvertTime(dateTimeKind, localZone);
EDIT 4
OK I think I am getting there but not sure if this is applicable for all time zones. I think I still have to consider day light saving because the resulting local time is just one hour ahead now.
TimeZoneInfo localZone = TimeZoneInfo.Local;
double offset = localZone.GetUtcOffset(DateTime.Now).TotalHours;
DateTime dateTimeKind = DateTime.SpecifyKind(serverTime, DateTimeKind.Utc);
DateTime localTime = TimeZoneInfo.ConvertTime(dateTimeKind, localZone).AddHours(offset);
But then how do you get DLS is in effect for a particular time zone in Windows Phone? TimeZoneInfo.FindSystemTimeZoneById does not seem to be supported?
For this to work, the DateTime-object serverTime must be of the UTC-form - or at least know what Kindit is. Read all the details around this under the remarks section of this page.
Best of luck!
What does the time represent? If it is a specific moment in time, such as the date and time that something happened, then you should update your server code to return the time in one of the following formats:
// ISO8601 local time with offset.
// get from DateTimeOffset.ToString("o")
2012-05-01T15:38:27-07:00
// ISO8601 UTC time
// get from DateTime.ToString("o") when kind is UTC
2012-05-01T22:38:27Z
It's really important that you do this, because local times can be ambiguous when daylight savings ends. You must either provide the correct offset, (-8 for PST, -7 for PDT), or send as UTC.
There are very few scenarios where sending local time by itself makes sense. If you think you have one, please elaborate about what the time represents.

Categories

Resources