Convert current local time to any other timezone - c#

I want to test some time-zone related code by comparing local time to UTC. However this test relies on local time being different to UTC and I'm in the UK so 6 months of the year, local time is UTC according to DateTime comparison tests (due to summer time).
I could hard-code my test to convert UTCNow to a certain timezone like EST but on the off-chance my code was used in an American system, now I have the same issue.
So is there a way I can easily convert DateTime.UtcNow to a timezone that's definitely different to my local timezone, without hard-coding it or making assumptions what timezone I'm in?

Ok, as I mentoined in comments, if you want to get timezones which differs from yours, you can do it in that way:
var zone = TimeZoneInfo.GetSystemTimeZones()
.Where(x=>x.BaseUtcOffset != TimeZoneInfo.Local.BaseUtcOffset)
.First();
To convert UTC DateTime to another timezone, you have to use TimeZoneInfo.ConvertTimeFromUtc, sample:
var datetime = // datetime in UTC, for example, DateTime.UtcNow
var zone = // target zone
return TimeZoneInfo.ConvertTimeFromUtc(datetime, zone);
You can check samples in my pet project here

Time zones in .net are a very confusing matter. DateTime only supports local and UTC timezones really, it has no concept of different zones as it only gets the current offset from the machine and applies it to a tick count value which is in UTC.
So at a first there's no way to change to another timezone, but you can simulate something. If you want per example simulate a timezone GMT+2, you must first retrieve the current zone offset and add the difference of this offset and the desired offset to the local date, something like this:
TimeSpan targetOffset = new TimeSpan(2, 0, 0) - TimeZoneInfo.Local.BaseUtcOffset; //target to GMT+2
DateTime targetNow = DateTime.Now + targetOffset;
In this way the Date will have the values like if it were on that timezone, but for calculations using the datetime object and not just the year/month/day/hour/minute/second all of them will be wrong as the object will be marked as local and thus will have the wrong values.
To solve this you have two options, reverse your logic (convert non-local time to local time) or just work with UTC dates.
The first approach is very easy:
DateTime nonLocal = new DateTime(2016, 10, 21, 13, 33, 0); //We suppose we want 2016-10-21 13:33:00 at GMT+2
DateTime local = nonLocal + (TimeZoneInfo.Local.BaseUtcOffset - new TimeSpan(2, 0, 0));
This will yield to correct DateTime operations, but, here we go again with problems, if you use the year/month/day/hour/minute they will be in your local zone, not in the supposed zone and code using these properties will fail.
Finally, the best approach is the second one, you just forget about timezones and whenever you get a DateTime you convert it to UTC, all will work flawlesly and when you need to represent the data just convert it to local and all is done, no need to worry about DateTime arithmetic or properties differing just because they're on another timezone.

public static class DateTimeExtensions
{
public static DateTime As(this DateTime source, string timeZoneName)
{
DateTime utcTime = DateTime.SpecifyKind(source, DateTimeKind.Unspecified);
TimeZoneInfo newTimeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneName);
return TimeZoneInfo.ConvertTimeFromUtc(utcTime, newTimeZone);
}
}
Usage:
DateTime date1 = DateTime.UtcNow;
DateTime date2 = date1.As("Eastern Standard Time");

Related

DateTimeKind is Unspecified Even Though I Set it

When I check optionDate's DateTime property's DateTimeKind value, I see Unspecified, even though I set dt's DateTimeKind as UTC in below code. I expect optionDate has a DateTime which has a DateTimeKind property set to UTC. Where am I wrong here?
var dt = new DateTime(Convert.ToInt32(optionDateInfo.dateTime.year),
Convert.ToInt32(optionDateInfo.dateTime.month), Convert.ToInt32(optionDateInfo.dateTime.day),
Convert.ToInt32(optionDateInfo.dateTime.hour), Convert.ToInt32(optionDateInfo.dateTime.minutes),
0, DateTimeKind.Utc);
var optionDate = new DateTimeOffset(dt);
This is documented:
DateTimeOffset.DateTime
The value of the DateTime.Kind property of the returned DateTime object is DateTimeKind.Unspecified.
Note that a DateTimeOffset does not have a "kind". It has a date, time, and offset. When you pass your DateTime with kind Utc, to it, it sets its offset to 0, and its date & time to the DateTime given. At this point, your DateTimeKind is "lost".
An offset of 0 does not necessarily mean that its kind is DateTimeKind.Utc. It could be the local time in London, or somewhere in Africa too. So it can't give you a DateTime with kind Utc just because its offset is 0 either.
In addition, DateTime being able to represent 3 kinds of things is already a questionable design, and if the DateTime property can now return 3 different kinds of DateTime depending on whether offset matches the local time, is UTC, or something else, that's just even worse.
Instead, it is designed to have 3 properties that give you DateTimes with different kinds.
DateTime gives you the date & time part of the DateTimeOffset, with kind Unspecified
LocalDateTime converts the date & time part of the DateTimeOffset to the current timezone, and gives you a DateTime with kind Local.
UtcDateTime converts the date & time part of the DateTimeOffset to UTC, and gives you a DateTime with kind Utc.
If you want a DateTime with kind Utc, you should use that last one.
Use the SpecifyKind
var myUtcZeroOffset = DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Utc)
//If constructing a datetime offset to be not utc you can supply the offset instead
var myOffSetExplicitLocal = new DateTimeOffset(DateTime.Now, new TimeSpan(1, 0, 0));
var localDateTime = myOffSetExplicitLocal.DateTime;
var utcZeroOffSetDateTime = myOffSetExplicitLocal.UtcDateTime;
To make matters worse of cause it is a criticisable implementation from Microsoft, because Universally Coordinated Time is not a timezone but a notation, as per ISO 8601, so in fact toUTC as a concept is flawed because '2021-11-02T10:16:25.12345+01:00' is completely valid in the UTC format and UTC Zero offset, popularily called Zulu being the '2021-11-02T09:16:25.12345Z' equivalent which then gets datetimekind UTC is actually just in coordinated time the zero line around GMT latitude, but what makes it coordinated is the + part which in +00:00 can be abbreviated to Z, so lots of stuff is done to mitigate the inherent conflict and with build servers and cloud providers the .Local is especially dubious, so I would recommend always to persist in ISO 8601 strings instead, unless you actually need to use them in with date operations in Your DB, in said case to name fields appropriate like DateTimeCreatedUtcZero column e.g.
just my five cents of reason on the topic in general, hope it helps.

How to get "today's local midnight expressed in UTC" using .NET core?

I am trying to get a timestamp for "Local midnight in UTC" using .NET Core.
I wrote this code:
var utcTimeStartLocalDay =
TimeZoneInfo.ConvertTimeToUtc(convertToLocalTimezone(DateTime.UtcNow).Date);
where
public DateTime ConvertToLocalTimezone(DateTime dateTime)
{
return TimeZoneInfo.ConvertTime(dateTime, TimeZoneInfo.Utc,TimeZoneInfo.FindSystemTimeZoneById("FLE Standard Time"));
}
So, the idea is to get Utc timestamp, convert it to local TZ, then take Date (that is, discard time part and effectively make a midnight timestamp) and convert back to Utc - that should have given me local time.
However, it did not happen, as the result of the first expression is Utc midnight when printed.
Fiddle: https://dotnetfiddle.net/wJIXve
What do I need to correct here to get as the answer the local midnight for the day expressed as UTC (something like 22:00 UTC previous day)?
Edit: just to clarify an unclearness in the question: by "local" TZ I meant known local timezone (found in the code)
To ensure that you are definitely using local time, I would use this code:
DateTime localMidnight = DateTime.SpecifyKind(DateTime.Now.Date, DateTimeKind.Local);
Then simply use .ToUniversalTime() to get the UTC date:
DateTime localMidnightUtc = localMidnight.ToUniversalTime();
Here's a working example:
static async Task Main(string[] args)
{
DateTime localMidnight = DateTime.SpecifyKind(DateTime.Now.Date, DateTimeKind.Local);
DateTime localMidnightUtc = localMidnight.ToUniversalTime();
Console.WriteLine($"localMidnight: {localMidnight}");
Console.WriteLine($"localMidnightUTC: {localMidnightUtc}");
}
// Output:
// localMidnight: 29.01.2020 00:00:00
// localMidnightUTC: 28.01.2020 23:00:00
(And now you know which timezone I'm in ;)
This should work.
var result = DateTime.Now.Date.ToUniversalTime();
This one-liner is the same as .NET Framework:
DateTime.Today.ToUniversalTime();

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.

.NET Save DateTime and completely ignore timezone

Maybe the answer is so obvious, I'm not seeing it, but I have a question that I'll risk asking anyway.
I want to allow users of a .NET Web application to enter a date/time, store it in an Oracle database, and no matter which timezone they are in, it always displays as the raw, original version as typed in by the user. So if one user in California enters 2PM and another in Maryland enters 2PM, they would both show as 2PM to a user in Japan. Two types of clients are possible, a web user and a windows client user (connected via web service).
Think of it like I want to completely break all timezone smarts that most applications worry about.
I don't want to save as a string though. I want a datetime that I can add/remove hours and minutes from.
Edit:
This was basically the exact problem I was having.
You should always store DateTime in UTC format (universal). When you display it you can choose which ever timezone you wish, in your case this can be fixed for all users, rather than based on location.
// when recording date time
DateTime utcDateTime = DateTime.UtcNow;
// parse DateTime back out from string
DateTime utcDateTime = DateTime.SpecifyKind(DateTime.Parse(dateStr),
DateTimeKind.Utc);
// localized DateTime
DateTime localDate = utcDateTime.ToLocalTime();
// fixed DateTime based on timezone
string timeZoneKey = "New Zealand Standard Time";
TimeZoneInfo timeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneKey);
DateTime nzlocalDate = TimeZoneInfo.ConvertTimeFromUtc(utcDateTime, timeZone);
This takes into account things like day-light savings which can trip you up if start saving localized dates.
If you just store the DateTime as entered/picked by the user then it will be stored as just that. There will be time zone informations stored in it, but you are in control of what you do with it.
For example, when you want to output it to the screen/file etc. you will need to format it to a string. If you use this ToString overload with CultureInfo.InvariantCulture then this should ignore the current culture and output the date as is:
DateTime date = DateTime.Now;
string output = date.ToString("d", CultureInfo.InvariantCulture);
Other operations will require different handling, but you will need to specify what happens in each case.
I had a time sensitive fix and the SpecifyKind did not allow me to make proper TimeSpan comparisons. In my situation, the quickest solution was to simply remove the Hours, Minutes, and Seconds from the time, as the DateTime I was comparing with was at midnight (00:00:00),
DateTime eventDateTime = (DateTime)row["event_date"];
eventDateTime = eventDateTime.AddHours(-1 * eventDateTime.Hour).AddMinutes(-1 * eventDateTime.Minute).AddSeconds(-1 * eventDateTime.Second);
If you really want discard time zone:
public static DateTime WithoutTimeZone(this DateTime dateTime)
{
if (dateTime.Kind != DateTimeKind.Unspecified)
return new DateTime(dateTime.Ticks, DateTimeKind.Unspecified);
return dateTime;
}

C#: Making sure DateTime.Now returns a GMT + 1 time

I am using DateTime.Now to show something according to today's date, and when working locally (Malta, Europe) the times appear correctly (obviously because of the Time Zone) but ofcourse when I upload it to my hosting server (USA), DateTime.Now does not represent the correct time zone.
Therefore, in my code, how can I convert DateTime.Now to correctly return the time from a GMT + 1 timezone ?
Use the TimeZoneInfo class found in System.Core;
You must set the DateTimeKind to DateTimeKind.Utc for this.
DateTime MyTime = new DateTime(1990, 12, 02, 19, 31, 30, DateTimeKind.Utc);
DateTime MyTimeInWesternEurope = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(MyTime, "W. Europe Standard Time");
Only if you're using .Net 3.5 though!
It depends on what you mean by "a GMT + 1 timezone". Do you mean permanently UTC+1, or do you mean UTC+1 or UTC+2 depending on DST?
If you're using .NET 3.5, use TimeZoneInfo to get an appropriate time zone, then use:
// Store this statically somewhere
TimeZoneInfo maltaTimeZone = TimeZoneInfo.FindSystemTimeZoneById("...");
DateTime utc = DateTime.UtcNow;
DateTime malta = TimeZoneInfo.ConvertTimeFromUtc(utc, maltaTimeZone );
You'll need to work out the system ID for the Malta time zone, but you can do that easily by running this code locally:
Console.WriteLine(TimeZoneInfo.Local.Id);
Judging by your comments, this bit will be irrelevant, but just for others...
If you're not using .NET 3.5, you'll need to work out the daylight savings yourself. To be honest, the easiest way to do that is going to be a simple lookup table. Work out the DST changes for the next few years, then write a simple method to return the offset at a particular UTC time with that list hardcoded. You might just want a sorted List<DateTime> with the known changes in, and alternate between 1 and 2 hours until your date is after the last change:
// Be very careful when building this list, and make sure they're UTC times!
private static readonly IEnumerable<DateTime> DstChanges = ...;
static DateTime ConvertToLocalTime(DateTime utc)
{
int hours = 1; // Or 2, depending on the first entry in your list
foreach (DateTime dstChange in DstChanges)
{
if (utc < dstChange)
{
return DateTime.SpecifyKind(utc.AddHours(hours), DateTimeKind.Local);
}
hours = 3 - hours; // Alternate between 1 and 2
}
throw new ArgumentOutOfRangeException("I don't have enough DST data!");
}
I don't think that you can set a property in your code that will make DateTime.Now return anything else than the current time of the computer in which the code executes. If you want to have a way of always getting another time, you will probably need to wrap in another function. You can can do the round-trip over UTC and add the desired offset:
private static DateTime GetMyTime()
{
return DateTime.UtcNow.AddHours(1);
}
(code sample updated after Luke's comment on the inner workings of DateTime.Now)

Categories

Resources