I want to compute the time 4 hours later during the DST transition, either into DST or out of it.
Currently doing this:
DateTime now = DateTime.now();
DateTime later = now.AddHours(4);
System.Console.WriteLine(now + " isDST " + now.IsDaylightSavingTime());
System.Console.WriteLine(later + " isDST " + later.IsDaylightSavingTime());
Provides:
11/1/2009 12:45:54 AM isDST True
11/1/2009 4:45:54 AM isDST False
What I want to see is:
11/1/2009 12:45:54 AM isDST True
11/1/2009 3:45:54 AM isDST False
The "best" way I've found to do this is to take a round trip through UTC like this:
DateTime now = DateTime.now();
DateTime later = now.ToUniversalTime().AddHours(4).ToLocalTime;
System.Console.WriteLine(now + " isDST " + now.IsDaylightSavingTime());
System.Console.WriteLine(later + " isDST " + later.IsDaylightSavingTime());
It seems I should be able to specify to AddHours() that the time returned should be elapsed time or display time. What am I missing?
I don't think you're missing anything. It sounds like DateTime could have a better documented or richer interface, but MSDN recommends using UTC exactly like you are.
There's a cute time difference between .NET and win32 in displaying times from a different daylight savings' rule than the present. .NET will display the time as it would have been displayed at that time, while win32 will always give it in terms of the current daylight savings flag.
AddHours() deals with TimeSpans, which have no real concept of "display hours" or even timezones. It's essentially a whole number of ticks.
I think what you're doing is a pretty good way of handling it, if I understand your intent correctly.
Yes, you should use UTC.
You're effectively talking about an instant in time 4 hours after the current instant. Not 4 hours of a particular local time, but 4 hours in "experienced" time, if you will. (Let's leave relativity out of it.) Imagine one person calls another and says, "I'll call you again in 4 hours" - both will know what that means, even if they're in completely different time zones with different DST transitions involved.
Instants are usually best expressed in UTC to avoid confusion - they're inherently "universal" rather than being in a particular time zone.
I'd avoid using local time until you need it:
DateTime localTimeInFourHours = DateTime.UtcNow.AddHours(4).ToLocalTime;
Or you could use DateTimeOffset if you want to end up with a "local time" in a time zone other than the one your computer is set to.
Related
I'm trying to add a wrapper around DateTime to include the time zone information. Here's what I have so far:
public struct DateTimeWithZone {
private readonly DateTime _utcDateTime;
private readonly TimeZoneInfo _timeZone;
public DateTimeWithZone(DateTime dateTime, TimeZoneInfo timeZone) {
_utcDateTime = TimeZoneInfo.ConvertTimeToUtc(DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified), timeZone);
_timeZone = timeZone;
}
public DateTime UniversalTime { get { return _utcDateTime; } }
public TimeZoneInfo TimeZone { get { return _timeZone; } }
public DateTime LocalTime { get { return TimeZoneInfo.ConvertTimeFromUtc(_utcDateTime, _timeZone); } }
public DateTimeWithZone AddDays(int numDays) {
return new DateTimeWithZone(TimeZoneInfo.ConvertTimeFromUtc(UniversalTime.AddDays(numDays), _timeZone), _timeZone);
}
public DateTimeWithZone AddDaysToLocal(int numDays) {
return new DateTimeWithZone(LocalTime.AddDays(numDays), _timeZone);
}
}
This has been adapted from an answer #Jon Skeet provided in an earlier question.
I am struggling with with adding/subtracting time due to problems with daylight saving time. According to the following it is best practice to add/subtract the universal time:
https://msdn.microsoft.com/en-us/library/ms973825.aspx#datetime_topic3b
The problem I have is that if I say:
var timeZone = TimeZoneInfo.FindSystemTimeZoneById("Romance Standard Time");
var date = new DateTimeWithZone(new DateTime(2003, 10, 26, 00, 00, 00), timeZone);
date.AddDays(1).LocalTime.ToString();
This will return 26/10/2003 23:00:00. As you can see the local time has lost an hour (due to daylight saving time ending) so if I was to display this, it would say it's the same day as the day it's just added a day to. However if i was to say:
date.AddDaysToLocal(1).LocalTime.ToString();
I would get back 27/10/2003 00:00:00 and the time is preserved. This looks correct to me but it goes against the best practice to add to the universal time.
I'd appreciate it if someone could help clarify what's the correct way to do this. Please note that I have looked at Noda Time and it's currently going to take too much work to convert to it, also I'd like a better understanding of the problem.
Both ways are correct (or incorrect) depending upon what you need to do.
I like to think of these as different types of computations:
Chronological computation.
Calendrical computation.
A chronological computation involves time arithmetic in units that are regular with respect to physical time. For example the addition of seconds, nanoseconds, hours or days.
A calendrical computation involves time arithmetic in units that humans find convenient, but which don't always have the same length of physical time. For example the addition of months or years (each of which have a varying number of days).
A calendrical computation is convenient when you want to add a coarse unit that does not necessarily have a fixed number of seconds in it, and yet you still want to preserve the finer field units in the date, such as days, hours, minutes and seconds.
In your local time computation, you add a day, and presuming a calendrical computation is what you intended, you preserve the local time of day, despite the fact that 1 day is not always 24 hours in the local calendar. Be aware that arithmetic in local time has the potential to result in a local time that has two mappings to UTC, or even zero mappings to UTC. So your code should be constructed such that you know this can never happen, or be able to detect when it does and react in whatever way is correct for your application (e.g. disambiguate an ambiguous mapping).
In your UTC time computation (a chronological computation), you always add 86400 seconds, and the local calendar can react however it may due to UTC offset changes (daylight saving related or otherwise). UTC offset changes can be as large as 24h, and so adding a chronological day may not even bump the local calendar day of the month by one. Chronological computations always have a result which has a unique UTC <-> local mapping (assuming the input has a unique mapping).
Both computations are useful. Both are commonly needed. Know which you need, and know how to use the API to compute whichever you need.
Just to add to Howard's great answer, understand that the "best practice" you refer to is about incrementing by an elapsed time. Indeed, if you wanted to add 24 hours, you'd do that in UTC and you'd find you'd end up on 23:00 due to there being an extra hour in that day.
I typically consider adding a day to be a calendrical computation (using Howard's terminology), and thus it doesn't matter how many hours there are on that day or not - you increment the day in local time.
You do then have to verify that the result is a valid time on that day, as it very well may have landed you on an invalid value, in the "gap" of a forward transition. You'll have to decide how to adjust. Likewise, when you convert to UTC, you should test for ambiguous time and adjust accordingly.
Understand that by not doing any adjusting on your own, you're relying on the default behavior of the TimeZoneInfo methods, which adjust backward during an ambiguous time (even though the usually desired behavior is to adjust forward), and that ConvertTimeFromUtc will throw an exception during an invalid time.
This is the reason why ZonedDateTime in Noda Time has the concept of "resolvers" to allow you to control this behavior more specifically. Your code is missing any similar concept.
I'll also add that while you say you've looked at Noda Time and it's too much work to convert to it - I'd encourage you to look again. One doesn't necessarily need to retrofit their entire application to use it. You can, but you can also just introduce it where it's needed. For example, you might want to use it internally in this DateTimeWithZone class, in order to force you down the right path.
One more thing - When you use SpecifyKind in your input, you're basically saying to ignore whatever the input kind is. Since you're designing general purpose code for reuse, you're inviting the potential for bugs. For example, I might pass in DateTime.UtcNow, and you're going to assume it's the timezone-based time. Noda Time avoids this problem by having separate types instead of a "kind". If you're going to continue to use DateTime, then you should evaluate the kind to apply an appropriate action. Just ignoring it is going to get you into trouble for sure.
I am trying to control Daylight saving in a C# application instead of letting windows do it. (I won't come into the reason here).
So I have removed the checkmark "Automatically adjust clock for daylight saving" in Date and Time settings (Windows7)
I have written this small piece of code in order to demonstrate the problem that I am facing.
TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById(TimeZoneInfo.Local.Id);
// "Romance Standard Time"
var rule = tzi.GetAdjustmentRules()[0];
System.Globalization.CultureInfo.CurrentCulture.ClearCachedData();
var timestampToWorkOn = DateTime.Now;
Console.WriteLine("Timezone is: " + tzi.ToString());
Console.WriteLine("Timezone id is: " + tzi.Id);
Console.WriteLine("Timestamp right now: " + timestampToWorkOn.ToString("yyyy-MM-dd HH:mm"));
Console.WriteLine("Rule for change says: " + rule.DaylightTransitionEnd.TimeOfDay.ToString("HH:mm"));
Console.WriteLine("Is it dst: " + tzi.IsDaylightSavingTime(timestampToWorkOn));
Console.WriteLine("Is it ambiguous:" + tzi.IsAmbiguousTime(timestampToWorkOn));
As the transition from dst to normal time is supposed to happen at 3:00 I would suspect that the time from 2:00 to 3:00 would be Ambiguous.
But the result from running the code at 1:54 is:
Timezone is: (UTC+01:00) Brussels, Copenhagen, Madrid, Paris
Timezone id is: Romance Standard Time
Timestamp right now: 2013-10-27 01:54
Rule for change says: 03:00
Is it dst: False
Is it ambiguous:True
I might be missing something.
I would expect dst to be true and ambiguous to be false, but it is the opposite way around.
It's hard to keep an overview but why am I seeing this behavior?
You should read this blog post, which describes in detail how the Windows registry settings are affected by the time zone selection and the "Automatically adjust..." checkbox. It also describes how TimeZoneInfo uses these settings. Specifically, it states:
When daylight saving time is disabled for the local time zone, TimeZoneInfo.Local will return a TimeZoneInfo object with TimeZoneInfo.SupportsDaylightSavingTime set to False. Any TimeZoneInfo.ConvertTime(...) calls using this TimeZoneInfo instance will not take daylight saving time into account.
IMHO, there is never a good reason to clear that checkbox and disable DST. It puts the computer's clock in an artificial reality.
If you are running code on a server, you should probably set the server's time zone to UTC. This will keep Windows from having to update the computer's bios for the transition, and will let local timestamps from other servers all line up.
Regarding your code, realize that the result of DateTime.Now has .Kind == DateTimeKind.Local, and has no bearing on the time zone you used earlier. You happened to be reporting the local time zone, but if you used a different one, your code would be incorrect.
When you are getting the adjustment rules, you are assuming the local time zone will have one. Some (like Arizona) do not have any DST, so they have no adjustment rules and you get an index out of bounds exception (because of the [0]).
Also, just being nitpicky but,
// This line is self redundant.
TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById(TimeZoneInfo.Local.Id);
// It can be reduced to:
TimeZoneInfo tzi = TimeZoneInfo.Local;
But the real problem is that because you have the "adjust for DST" option turned off, the DateTime is not being converted to the right UTC moment in order to make the determination if it is ambiguous or not. If you drill in to the .Net source code (decompiled or symbolsource) for DateTime.IsAmbiguousTime(), you will find that it uses TimeZoneInfo.ConvertTime(), which (per the earlier quote) does not take DST into account when the box is unchecked, thus making the result incorrect (essentially 1 hour off).
You should also look at the MSDN notes for TimeZoneInfo.IsAmbiguousTime, which describe how the Kind of the input affects the output result.
I want get UTC 0 time which was Greenwich Mean Time (GMT).
I try to DateTime.UtcNow and DateTime.Now.ToUniversalTime() but its returns wrong times.
İts return 22:40 instead of 06:40
How can I get UTC/GMT time in C#.
I try to DateTime.UtcNow and DateTime.Now.ToUniversalTime() but its returns wrong times.
No, it really doesn't. (I would strongly advise using the former rather than the latter though. In general, converting from a local time to UTC can be ambiguous due to daylight saving transitions. I believe in this particular case it may do the right thing, but UtcNow is a better approach.)
DateTime.UtcNow does return the current UTC time. If it's giving you the wrong result, then your system clock is wrong - or you're performing some other transformation of the value somewhere (e.g. converting it to local time as part of formatting).
Basically, you're using the right property, so you need to diagnose where exactly the result is going wrong.
I suggest you start off with a small console app:
class ShowTimes
{
static void Main()
{
Console.WriteLine("Local time: {0}", DateTime.Now);
Console.WriteLine("UTC time: {0}", DateTime.UtcNow);
}
}
What does that show, and what time zone are you in?
Our webservice uses the Datetime.parse method to convert data from an xml to DateTime format. It parses Date and time strings separately and adds it together like this -
DateTime.Parse(Date_string).add(TimeSpan.Parse(Time_string)).
Code was working fine except for a few hours last week. Time was showing as 12 hours ahead of actual time. For example, 01/01/2011 10:00:00 will be parsed as 01/01/2011 22:00:00. Most of the requests during that time were processed with datetime values 12 hours ahead of actual time though some were processed correctly. It is working fine now and haven't seen it after that.
Has anyone seen a issue like this?
You say "Code was working fine except for a few hours last week", but you didn't specify exactly when that was or what time zone you are in. Any chance it was around a daylight savings time change?
You shouldn't use TimeSpan.Parse at all. A TimeSpan does NOT represent the time-of-day, despite its appearance as hh:mm:ss. A TimeSpan represents a fixed DURATION of time.
If you really are given separate date and time strings, join them together before parsing, such as:
DateTime dt = DateTime.Parse(date_string + " " + time_string);
You should also be aware of the timezone implications of the string you are sending in. See the MSDN article on DateTime.Parse for further details.
As this Question's Answer from hightechrider mentions that code block as below is more right
var start = DateTime.Parse("08/10/2011 23:50:31").Utc;
if(start.AddMinutes(20) > DateTime.UtcNow)
then using as this by TimeSpan
var start = DateTime.Now;
var oldDate = DateTime.Parse("08/10/2011 23:50:31");
if(start - oldDate).TotalMinutes >= 20)
Here since the DateTime is executed and also parsed in the same culture then, How it will make difference ??
I am feeling very Phoney by this answer.
In a nutshell: UTC is a continuous, single-valued time scale, whereas local time is not continuous or single-valued. The primary reason is Daylight Savings Time, which doesn't apply to UTC. So UTC never jumps forward or back an hour, whereas local time does. And when it jumps backward, the same time value occurs twice.
Making comparisons is best done using the continuous, single-valued time scale, unless you want to mess around with DST yourself. Even if you do, there is no way to distinguish between the first and second "2am" when Daylight Savings Time ends and the clocks are set back an hour.
Technical note: even though UTC is continuous, it does have the occasional extra leap second inserted to keep up with the slowing down of the Earth's rotation. Those seconds are usually added at the end of the day and are listed with 60 seconds. So you'd have 23:59:59, 23:59:60, 00:00:00.
The United States transitions from Daylight Savings Time to Standard Time at 2AM on November 6th, 2011. If, at 2:10AM, I ask how far in the past 1:50AM was, .NET will tell me 20 minutes. In truth, it was an hour and 20 minutes, since we set our clocks back an hour at 2AM. I won't run into these issues if I use UTC - libraries like the .NET Framework have all the logic needed to correctly deal with discontinuities like this.
The whole Daylight Savings Time scheme is a mess, and it's hard for anyone whose country, like yours, (sensibly) doesn't implement it, to understand the issues that arise. It gets even more interesting when governments start changing the switchover days around.