Strange behavior from DateTime objects - c#

I do the following:
var now = DateTime.Now;
var utc = DateTime.UtcNow;
var timeSpan = now - utc;
Console.WriteLine("Now is: " + now.ToString("yyyy-MM-dd HH:mm:ss:ms") + " utc is: " + utc.ToString("yyyy-MM-dd HH:mm:ss:ms") + " timeSpan: " +
timeSpan.TotalMinutes);
Console.ReadKey();
It gives the following result:
And if I take the timespan.hours (which is the one I actually use) it revelas 1?
Should be 2 What am I doing wrong there?

There is some time passes between you get times (system can even switch processes between these two calls):
var now = DateTime.Now;
// some time passes here
var utc = DateTime.UtcNow;
Thats why you have less than 2 hours between two values. You should get time only once and then convert it to local time:
var utc = DateTime.UtcNow;
var now = utc.ToLocalTime();
// timeSpan: 120
Or use TimeZoneInfo.ToUniversalTime to convert local time to UTC time:
var now = DateTime.Now;
var utc = TimeZone.CurrentTimeZone.ToUniversalTime(now);

The reason is simple: your calculation introduces an error by taking the time twice. The final result is slightly wrong, but the time taken to make the calls.
The solution is simpler.
Console.WriteLine(TimeZoneInfo.Local.BaseUtcOffset.Hours);
Console.WriteLine(TimeZoneInfo.Local.GetUtcOffset(DateTime.Now).Hours);
There is no need to get or convert times when what you actually want is time zone info. The first line gets the "normal" local time offset; the second gets the offset for 'now' in case your timezone is subject to daylight saving or other adjustments.

Related

NodaTime - Calculate duration between two LocalTime variables that span midnight

I have a requirement to calculate the duration between two times that may span midnight.
The use case is to allow the user to set up shift plans e.g. “09:00 to 17:00” or “22:00 to 06:00” and calculate the contracted time
For “09:00 to 17:00” I can use:
LocalTime inAt = new LocalTime(9, 0);
LocalTime outAt = new LocalTime(17, 0);
var period = Period.Between(inAt, outAt);
Which results in 8 hours, the answer I am looking for.
For “22:00 to 06:00” the period returns 16 hours (regardless of the order of the parameters).
LocalTime inAt = new LocalTime(22, 0);
LocalTime outAt = new LocalTime(6, 0);
var period = Period.Between(inAt, outAt);
var period2 = Period.Between(outAt, inAt);
I am guessing that this is related to daylight saving time, unless you know the dates you cannot be sure that the answer will always the 8 hours. If the clocks go forward it would be 7, backwards would be 9.
How can I ensure that no matter what LocalTime values are used the period would disregard any daylight savings? Should I use LocalDateTime with an arbitrary date such as 2021-01-01?
Also, am I correct in using Period or should I be using Duration?
Update
This seems to work however I am still wondering if there is an eaiser way of doing it?
LocalTime inAt = new LocalTime(22, 0);
LocalTime outAt = new LocalTime(6, 0);
var period = Period.Between(inAt, outAt, PeriodUnits.Ticks);
LocalTime? midnightAdjustedTime = null;
if (period.Ticks < 0)
{
midnightAdjustedTime = LocalTime.Midnight + period;
}
var duration = Duration.FromTicks(midnightAdjustedTime?.TickOfDay ?? period.Ticks);
This has nothing to do with daylight savings - it can't do, given that everything is in terms of LocalTime. It's about negative periods.
For “22:00 to 06:00” the period returns 16 hours (regardless of the order of the parameters)
No, it doesn't. One returns 16 hours, the other returns -16 hours:
using NodaTime;
var start = new LocalTime(22, 0);
var end = new LocalTime(6, 0);
var startToEnd = Period.Between(start, end);
var endToStart = Period.Between(end, start);
Console.WriteLine(startToEnd);
Console.WriteLine(endToStart);
Output:
PT-16H
PT16H
It's not clear to me how you came to the conclusion that both returned the same value, but the fact that they don't return the same value is crucial to fixing the problem. The simple approach is just to add 1 day (24 hours) if the period is negative. The code you've got is almost right, but it can be done much simpler - and without using the Ticks unit at all:
// Note: Period.ToDuration() isn't "generally safe" due to variable lengths
// of months etc, but is okay here.
var duration = startToEnd.ToDuration();
if (duration < Duration.Zero)
{
duration += Duration.FromDays(1);
}
Console.WriteLine(duration);

How to get time from another zone with correct time in minutes and seconds C#

So, I have a problem with time zone
var currentTime = DateTime.Now;
var time = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(currentTime, TimeZoneInfo.Local.Id, "Eastern Standard Time").ToString("M/d/yyyy h:mm");
var expectedResult = new List<string>(){"aaa", "bbb", $"{time}"};
var elements = element.Select(x => x.Text).ToList();
Time that I get from system is different from timezone. That time is about five minutes and it changes, probably depending on the moment my record is created. After that I have to verify, that values in lists are equal
For example: I get list of strings with actual result, which equal
var actualResult = new List<string>(){"aaa", "bbb", "{timeFromSystem}"};
Now I'm still getting different result with time and timeFromSystem.
I hope you can help me.

Calculation with DateTime and TimeSpan in C#

I need to make a calculation of passed and remaining time of an operation in C#.
I have the start of the operation saved in a string format of HH:MM:SS
I have a default time length of the operation in a string format of HH:MM:SS
Now I would like to calculate:
The remaining time / extra time: For example if the operation is still below the default length, it should display -HH:MM:SS, and if the operation took longer than the default time, it should display +HH:MM:SS
If the operation took longer, I would also like to have a double value of HH,MM in % style. For example: 3hours and 30 minutes should be displayed as 3,5
Both results to be displayed next to each other.
I know I have to translate the string values into DateTime and/or TimeSpan values to do calculations, but currently I have no idea how to calculate since the first operation for example would not give me a negative value, but just get back in time [22:30:00 of yesterday].
Try this..
var start = "17:05:11"; // Pass this as a parameter
var startTime = DateTime.Parse(start);
var defaultDuration = TimeSpan.FromMinutes(2);
TimeSpan operationDuration = startTime - DateTime.Now;
TimeSpan diff = defaultDuration - operationDuration;
if (operationDuration > defaultDuration)
{
Console.Out.WriteLine($"+{diff.Hours}:{diff.Minutes}:{diff.Seconds}");
}
else
{
Console.Out.WriteLine($"-{diff.Hours}:{diff.Minutes}:{diff.Seconds}");
Console.Out.WriteLine($"{diff.Hours},{Math.Round(((double)(diff.Minutes * 100 / 60)),0, MidpointRounding.AwayFromZero)}");//example: 3hours and 30 minutes should be displayed as 3,5
}
At first save the default time as TimeSpan. Then you can take DateTime.Now and save it when the operation starts. Take another DateTime.Now later when it finished. After this point you can calculate the TimeSpan for the current operation. Then you can calculate the difference from these two TimeSpans as another TimeSpan. It can be positive or negativ and with these values you can do whatever you want.
TimeSpan defaultDuration = new TimeSpan(3, 30, 0);
DateTime begin = DateTime.Now;
//Do some work
DateTime end = DateTime.Now;
TimeSpan thisDuration = end - begin;
Console.WriteLine("Default: " + defaultDuration.ToString("hh\\:mm\\:ss"));
Console.WriteLine("This time: " + thisDuration.ToString("hh\\:mm\\:ss"));
Console.Write("Difference: ");
if (thisDuration > defaultDuration)
Console.Write("-");
Console.WriteLine((thisDuration - defaultDuration).ToString("hh\\:mm\\:ss"));

Converting DateTime in UTC to my "local" time?

I am working with a MongoDB trying to deserialize a BsonDate to a DateTime with the proper Kind. When a DateTime is serialized to MongoDB it is saved in UTC format, when it is deserialized it is returned as a UTC format too.
My issue is that I cannot seem to be able to convert the DateTime I am given back from Mongo to my local DateTime (UTC to EST). I am pretty sure I can get around my issue by just removing the UTC time offset (AddHours) for my timezone but I am pretty sure that DateTime can do this for me and would handle any time zone the application is ran in.
// Deserializing the DateTime object
DateTime eventTimeStamp = (DateTime)aDoc[MongoStrings.Log_Field_TimeStamp];
Console.Out.WriteLine("UtcDate: " + eventTimeStamp);
Console.Out.WriteLine("Locale : " + eventTimeStamp.Kind);
// First attempt at conversion
DateTime localTime = DateTime.SpecifyKind(eventTimeStamp, DateTimeKind.Local);
Console.Out.WriteLine("NewTime: " + localTime);
Console.Out.WriteLine("Locale : " + localTime.Kind);
// Another attempt at conversion
DateTime localTime2 = new DateTime(eventTimeStamp.Ticks, DateTimeKind.Local);
Console.Out.WriteLine("NewTim2: " + localTime2);
Console.Out.WriteLine("Locale : " + localTime2.Kind);
The code above produces the following output
UtcDate: 1/29/2016 2:54:05 PM
Locale : Utc
NewTime: 1/29/2016 2:54:05 PM
Locale : Local
NewTim2: 1/29/2016 2:54:05 PM
Locale : Local
My local time when that log was produced is 9:54:05 AM.
This might work for you:
// This is your code
// Deserializing the DateTime object
DateTime eventTimeStamp = (DateTime)aDoc[MongoStrings.Log_Field_TimeStamp];
Console.Out.WriteLine("UtcDate: " + eventTimeStamp);
Console.Out.WriteLine("Locale : " + eventTimeStamp.Kind);
// This is new code
Console.Out.WriteLine("LocalDate: " + eventTimeStamp.ToLocalTime());
The reasoning would be that your localTime variables are set to the exactly same time stamp as the one in UTC, you just say that it should be taken as a local time. But this setting as local time does no conversion of times, it just says what kind of time this is so that other methods (like ToLocalTime) know what to do ...
If you want that local time in a variable, then it might be like this:
DateTime localTime = eventTimeStamp.ToLocalTime();
And I guess it will work without setting the kind. If not, you know how to set the kind ...

What is the easiest method to remove the Millisecond's part of a DateTime.UctNow.TimeOfDay?

I have a theory why the following code is not producing the results I need:
endDate = DateTime.UtcNow.AddDays(1).ToShortDateString() + " " +
DateTime.UtcNow.TimeOfDay.Subtract(
new TimeSpan(0, 0, 0, 0, DateTime.UtcNow.TimeOfDay.Milliseconds));
The processor must calculate the DateTime.UtcNow.TimeOfDay.Milliseconds, and due to the time length of a single tick of the CPU (and the time to process this property and return a result), It does not denote that DateTime.UtcNow.TimeOfDay.Milliseconds will subtract the exact amount of milliseconds specified by the DateTime.UtcNow.TimeOfDay
I need to know, what is the simplest and most effective method to remove the amount of milliseconds from DateTime.UtcNow.TimeOfDay, without having to use a huge amount of processor time? This application of mine is pretty big, and this problem is pretty simple. But when this application gets deployed, there are no room for it to be unstable. This milliseconds must be trimmed because it is sent to a stored procedure in SQL Server, and this specific stored procedure does not support milliseconds on DateTimes. I also run into this problem commonly, but I normally convert the date to string (which is a cast on its own), split the string at the full stop at milliseconds, and use the index position 0 to get the time i need. Is there a shorter, more effective way?
Stability and speed is most important to me.
Thanks in advance
Don't repeatedly use the Now/UtcNow property in the same expression. Get the value once, and use the same value in the different places:
DateTime now = DateTime.Now;
endDate = now.AddDays(1).ToShortDateString() + " " +
now.TimeOfDay.Subtract(
new TimeSpan(0, 0, 0, 0, now.TimeOfDay.Milliseconds));
If you only want the date formatted in a special way, and don't need the actual DateTime value, you can just skip the milliseconds in the format, for example:
endDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
As you are sending the value to the database, you should not send it as a string, but as a DateTime value:
DateTime now = DateTime.Now;
DateTime endDate = now - new TimeSpan(0, 0, 0, 0, now.TimeOfDay.Milliseconds);
Everything you need to know about customising the DateTime ToString format is here on MSDN.
In simple terms, something like this:
endDate = DateTime.Now.AddDays(1).ToString("yyyy-MM-dd hh:mm:ss");
(alter the format as desired)
If you want to remove milliseconds without having any problem on ticks.
DateTime d = DateTime.Now;
var newDate = new DateTime(d.Year, d.Month, d.Day, d.Hour, d.Minute, d.Second);
Basically, you create a new DateTime instance from an existing one, but set everything "smaller" then Milliseconds to zero. You can use an extensions method:
public static class DateTimeExtensions
{
public static DateTime ZeroMilliseconds(this DateTime dt)
{
return new DateTime(((dt.Ticks / 10000000) * 10000000), dt.Kind);
}
}
Or for a full example using your code:
var now = DateTime.Now;
endDate = now.AddDays(1).ToShortDateString() + " " + now.ZeroMilliseconds().TimeOfDay;
Use C# DateTime formatting as described very well in the MSDN. Your analysis on milisecond calculation is quite possibly wrong. Also for string concatenation use a StringBuilder

Categories

Resources