How to deal with Rounding-off TimeSpan? - c#

I take the difference between two DateTime fields, and store it in a TimeSpan variable, Now I have to round-off the TimeSpan by the following rules:
if the minutes in TimeSpan is less than 30 then Minutes and Seconds must be set to zero,
if the minutes in TimeSpan is equal to or greater than 30 then hours must be incremented by 1 and Minutes and Seconds must be set to zero.
TimeSpan can also be a negative value, so in that case I need to preserve the sign..
I could be able to achieve the requirement if the TimeSpan wasn't a negative value, though I have written a code I am not happy with its inefficiency as it is more bulky ..
Please suggest me a simpler and efficient method.
Thanks regards,
This is my code which works fine, when TimeSpan is not negative value ..
TimeSpan time_span = endTime.Subtract(startTime);
TimeSpan time_span1;
if (time_span.Minutes >= 30)
{
time_span1 = new TimeSpan(time_span.Hours + 1, 0, 0);
}
else
{
time_span1 = new TimeSpan(time_span.Hours, 0, 0);
}
time_span1 will contain the result ..

How about:
public static TimeSpan Round(TimeSpan input)
{
if (input < TimeSpan.Zero)
{
return -Round(-input);
}
int hours = (int) input.TotalHours;
if (input.Minutes >= 30)
{
hours++;
}
return TimeSpan.FromHours(hours);
}

You can use
double v = span.TotalHours;
v = Math.Round(v, MidpointRounding.AwayFromZero);
span = TimeSpan.FromHours(v);
It depends on whether I understood your rules for negative values correctly.

TimeSpan is immutable, so you have to create a new one. This is also a perfect case for using extension methods in C#:
public static class TimeSpanUtility
{
public static TimeSpan Round( this TimeSpan ts )
{
var sign = ts < TimeSpan.Zero ? -1 : 1;
var roundBy = Math.Abs(ts.Minutes) >= 30 ? 1 : 0;
return TimeSpan.FromHours( ts.TotalHours + (sign * roundBy) );
}
}
// usage would be:
var someTimeSpan = new TimeSpan( 2, 45, 15 );
var roundedTime = someTimeSpan.Round();

Related

Parametrized remaining seconds until next interval

I wanted to return the remaining time until the next interval (KlineInterval). Basically, I want to avoid hard-coding stuff. My code works fine for 1-hour interval but it doesn't support the rest of the intervals. I want it to support all of them and if there is a way to do that in a not hard-coded way (those ugly ifs).
Is this possible?
public enum KlineInterval
{
OneMinute = 0,
ThreeMinutes = 1,
FiveMinutes = 2,
FifteenMinutes = 3,
ThirtyMinutes = 4,
OneHour = 5,
TwoHour = 6,
FourHour = 7,
SixHour = 8,
EightHour = 9,
TwelveHour = 10,
OneDay = 11,
ThreeDay = 12,
OneWeek = 13,
OneMonth = 14
}
public static double RemainingSecondsUntilNextInterval(KlineInterval interval)
{
if (interval == KlineInterval.FiveMinutes)
{
double currentTimeUnixTimestamp = DateTimeToUnixTimestamp(DateTime.Now);
int minutesInSeconds = 5 * 60;
return minutesInSeconds - (currentTimeUnixTimestamp % minutesInSeconds);
}
else if (interval == KlineInterval.OneHour)
{
var timeOfDay = DateTime.Now.TimeOfDay;
var nextFullHour = TimeSpan.FromHours(Math.Ceiling(timeOfDay.TotalHours));
return (nextFullHour - timeOfDay).TotalSeconds;
}
else
{
throw new NotSupportedException("Interval not supported.");
}
}
Edit:
using System;
class Program
{
private static double DateTimeToUnixTimestamp(DateTime dateTime)
{
return (TimeZoneInfo.ConvertTimeToUtc(dateTime) -
new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
}
private static double RemainingSecondsUntilXMinutes(int minutes)
{
double currentTimeUnixTimestamp = DateTimeToUnixTimestamp(DateTime.Now);
return (minutes * 60) - (currentTimeUnixTimestamp % (minutes * 60));
}
static void Main(string[] args)
{
// Remaining seconds until the next hour
var nextHour = RemainingSecondsUntilXMinutes(60);
// Remaining seconds until the next 5 minutes
var nextFiveMinutes = RemainingSecondsUntilXMinutes(5);
Console.ReadKey();
}
}
#Max, is that what you wanted to say?
It's important to be aware of the fact that there will need to be some place where each of the enums you have declared are mapped to their expected behaviour. (There is also the option of parsing the string values of the enums. This is very hacky and I would advise against it.) This means that you will need to have some control flow that splits based on the enum type. You can do this with either an if statement or a switch statement (switch statements are more common for this purpose, but both work fine).
Still, it's not necessary to have 3 lines of code within each branch. You can bring it down to just one line per branch. When trying to reduce the amount of code duplication, it is helpful to look at which parts are the same in the different cases and which are different.
In this case, it's useful to first convert the enum into a Timespan and then implement some logic based on this Timespan that computes the number of remaining seconds. One method could deal with converting the enum to a Timespan, and the other could calculate the remaining seconds in the interval based on the Timespan, like so:
private static double RemainingSecondsUntilNextInterval(Timespan interval) {
// ...
}
private static Timespan TimespanFromKlineInterval(KlineInterval interval) {
switch(interval){
case KlineInterval.OneMinute:
// ...
}
public static double RemainingSecondsUntilNextInterval(KlineInterval interval) {
return RemainingSecondsUntilNextInterval(TimespanFromKlineInterval(interval));
}

Convert TimeSpan to float

How do I convert a TimeSpan to a float , taking into account all of the processing unit (hour minute) for example
if (unit = hour)
convert TimeSpan to a float hours
In another context, is there not a data type "Timespan" in SQL Server ?
Use the Total* properties on TimeSpan, e.g. TimeSpan.TotalHours.
Example for minutes:
var t = new TimeSpan();
var total = t.TotalMinutes;
You can do something like this
TimeSpan elapsedTime = new TimeSpan(125000);
float floatTimeSpan;
int seconds, milliseconds;
seconds = elapsedTime.Seconds;
milliseconds = elapsedTime.Milliseconds;
floatTimeSpan = (float)seconds + ((float)milliseconds / 1000);
Console.WriteLine("Time Span: {0}", floatTimeSpan);
The program output looks like this:
Time Span: 0.012
internal static string TimeSpanToDouble(TimeSpan timeSpan, string unit)
{
double result = 0;
if (unit.Equals("MINUTES"))
result = timeSpan.TotalMinutes;
else if (unit.Equals("HOURS"))
result = timeSpan.TotalHours;
else if (unit.Equals("DAYS"))
result = timeSpan.TotalHours / 24;
else
throw new Exception();
return Convert.ToString(result);
}
You can use Convert.ToSingle, like this:
var ts = new Timespan(0, 1, 1, 30);
var minutes = Convert.ToSingle(ts.TotalMinutes);
var hours = Convert.ToSingle(ts.TotalHours);
The resulting minutes and hours will be, respectively, 61.5 and 1.025
Check out this dotnetfiddle: https://dotnetfiddle.net/uSQfk0
double TsToHoursDouble(TimeSpan ts) => ts.TotalMinutes / 60;
double TsToMinsDouble(TimeSpan ts) => ts.TotalMinutes;
using:
var hrs = TsToHoursDouble(someTimeSpan);
if you need more accurate results, you can use TotalSeconds in such calculations

How to calculate remaining minutes to "next" half an hour or hour?

I would like to calculate the remaining minutes to the "next" half an hour or hour.
Say i get a start time string of 07:15, i want it to calculate the remaining minutes to the nearest half an hour (07:30).
That would be 15min.
Then i can also have an instance where the start time can be 07:45 and i want it to calculate the remaining minutes to the nearest hour (08:00).
That would also be 15min.
So any string less then 30min in a hour would calculate to the nearest half an hour (..:30) and any string over 30min would calculate to the nearest hour (..:00).
I don't want to do a bunch of if statements, because i get from time strings that can start from and minute in an hour.
This is what i do not want to do:
if (int.Parse(fromTimeString.Right(2)) < 30)
{
//Do Calculation
}
else
{
//Do Calculation
}
public static string Right(this String stringValue, int noOfCharacters)
{
string result = null;
if (stringValue.Length >= noOfCharacters)
{
result = stringValue.Substring(stringValue.Length - noOfCharacters, noOfCharacters);
}
else
{
result = "";
}
return result;
}
Is there not an easier way with linq or with the DateTime class
Use modulo operator % with 30. Your result will be equal to (60 - currentMinutes) % 30. About LINQ its used for collections so i can't realy see how it can be used in your case.
You can use this DateTime tick-round approach to get the timespan until next half hour:
var minutes = 30;
var now = DateTime.Now;
var ticksMin = TimeSpan.FromMinutes(minutes).Ticks;
DateTime rounded = new DateTime(((now.Ticks + (ticksMin/2)) / ticksMin) * ticksMin);
var diff=rounded-now;
var minUntilNext = diff.TotalMinutes > 0 ? diff.TotalMinutes : minutes + diff.TotalMinutes;
var minutesToNextHalfHour = (60 - yourDateTimeVariable.Minutes) % 30;
This should do it:
int remainingMinutes = (current.Minute >= 30)
? 60 - current.Minute
: 30 - current.Minute;
var hhmm = fromTimeString.Split(':');
var mins = int.Parse(hhmm[1]);
var remainingMins = (60 - mins) % 30;
var str = "7:16";
var datetime = DateTime.ParseExact(str, "h:mm", new CultureInfo("en-US"));
var minutesPastHalfHour = datetime.Minute % 30;
var minutesBeforeHalfHour = 30 - minutesPastHalfHour;
I would use modulo + TimeSpan.TryParse:
public static int ComputeTime(string time)
{
TimeSpan ts;
if (TimeSpan.TryParse(time, out ts))
{
return (60 - ts.Minutes) % 30;
}
throw new ArgumentException("Time is not valid", "time");
}
private static void Main(string[] args)
{
string test1 = "7:27";
string test2 = "7:42";
Console.WriteLine(ComputeTime(test1));
Console.WriteLine(ComputeTime(test2));
Console.ReadLine();
}

How can I convert seconds into (Hour:Minutes:Seconds:Milliseconds) time?

How can I convert seconds into (Hour:Minutes:Seconds:Milliseconds) time?
Let's say I have 80 seconds; are there any specialized classes/techniques in .NET that would allow me to convert those 80 seconds into (00h:00m:00s:00ms) format like Convert.ToDateTime or something?
For .Net <= 4.0 Use the TimeSpan class.
TimeSpan t = TimeSpan.FromSeconds( secs );
string answer = string.Format("{0:D2}h:{1:D2}m:{2:D2}s:{3:D3}ms",
t.Hours,
t.Minutes,
t.Seconds,
t.Milliseconds);
(As noted by Inder Kumar Rathore) For .NET > 4.0 you can use
TimeSpan time = TimeSpan.FromSeconds(seconds);
//here backslash is must to tell that colon is
//not the part of format, it just a character that we want in output
string str = time .ToString(#"hh\:mm\:ss\:fff");
(From Nick Molyneux) Ensure that seconds is less than TimeSpan.MaxValue.TotalSeconds to avoid an exception.
For .NET > 4.0 you can use
TimeSpan time = TimeSpan.FromSeconds(seconds);
//here backslash is must to tell that colon is
//not the part of format, it just a character that we want in output
string str = time .ToString(#"hh\:mm\:ss\:fff");
or if you want date time format then you can also do this
TimeSpan time = TimeSpan.FromSeconds(seconds);
DateTime dateTime = DateTime.Today.Add(time);
string displayTime = dateTime.ToString("hh:mm:tt");
For more you can check Custom TimeSpan Format Strings
If you know you have a number of seconds, you can create a TimeSpan value by calling TimeSpan.FromSeconds:
TimeSpan ts = TimeSpan.FromSeconds(80);
You can then obtain the number of days, hours, minutes, or seconds. Or use one of the ToString overloads to output it in whatever manner you like.
I did some benchmarks to see what's the fastest way and these are my results and conclusions. I ran each method 10M times and added a comment with the average time per run.
If your input milliseconds are not limited to one day (your result may be 143:59:59.999), these are the options, from faster to slower:
// 0.86 ms
static string Method1(int millisecs)
{
int hours = millisecs / 3600000;
int mins = (millisecs % 3600000) / 60000;
// Make sure you use the appropriate decimal separator
return string.Format("{0:D2}:{1:D2}:{2:D2}.{3:D3}", hours, mins, millisecs % 60000 / 1000, millisecs % 1000);
}
// 0.89 ms
static string Method2(int millisecs)
{
double s = millisecs % 60000 / 1000.0;
millisecs /= 60000;
int mins = millisecs % 60;
int hours = millisecs / 60;
return string.Format("{0:D2}:{1:D2}:{2:00.000}", hours, mins, s);
}
// 0.95 ms
static string Method3(int millisecs)
{
TimeSpan t = TimeSpan.FromMilliseconds(millisecs);
// Make sure you use the appropriate decimal separator
return string.Format("{0:D2}:{1:D2}:{2:D2}.{3:D3}",
(int)t.TotalHours,
t.Minutes,
t.Seconds,
t.Milliseconds);
}
If your input milliseconds are limited to one day (your result will never be greater then 23:59:59.999), these are the options, from faster to slower:
// 0.58 ms
static string Method5(int millisecs)
{
// Fastest way to create a DateTime at midnight
// Make sure you use the appropriate decimal separator
return DateTime.FromBinary(599266080000000000).AddMilliseconds(millisecs).ToString("HH:mm:ss.fff");
}
// 0.59 ms
static string Method4(int millisecs)
{
// Make sure you use the appropriate decimal separator
return TimeSpan.FromMilliseconds(millisecs).ToString(#"hh\:mm\:ss\.fff");
}
// 0.93 ms
static string Method6(int millisecs)
{
TimeSpan t = TimeSpan.FromMilliseconds(millisecs);
// Make sure you use the appropriate decimal separator
return string.Format("{0:D2}:{1:D2}:{2:D2}.{3:D3}",
t.Hours,
t.Minutes,
t.Seconds,
t.Milliseconds);
}
In case your input is just seconds, the methods are slightly faster. Again, if your input seconds are not limited to one day (your result may be 143:59:59):
// 0.63 ms
static string Method1(int secs)
{
int hours = secs / 3600;
int mins = (secs % 3600) / 60;
secs = secs % 60;
return string.Format("{0:D2}:{1:D2}:{2:D2}", hours, mins, secs);
}
// 0.64 ms
static string Method2(int secs)
{
int s = secs % 60;
secs /= 60;
int mins = secs % 60;
int hours = secs / 60;
return string.Format("{0:D2}:{1:D2}:{2:D2}", hours, mins, s);
}
// 0.70 ms
static string Method3(int secs)
{
TimeSpan t = TimeSpan.FromSeconds(secs);
return string.Format("{0:D2}:{1:D2}:{2:D2}",
(int)t.TotalHours,
t.Minutes,
t.Seconds);
}
And if your input seconds are limited to one day (your result will never be greater then 23:59:59):
// 0.33 ms
static string Method5(int secs)
{
// Fastest way to create a DateTime at midnight
return DateTime.FromBinary(599266080000000000).AddSeconds(secs).ToString("HH:mm:ss");
}
// 0.34 ms
static string Method4(int secs)
{
return TimeSpan.FromSeconds(secs).ToString(#"hh\:mm\:ss");
}
// 0.70 ms
static string Method6(int secs)
{
TimeSpan t = TimeSpan.FromSeconds(secs);
return string.Format("{0:D2}:{1:D2}:{2:D2}",
t.Hours,
t.Minutes,
t.Seconds);
}
As a final comment, let me add that I noticed that string.Format is a bit faster if you use D2 instead of 00.
TimeSpan.FromSeconds(80);
http://msdn.microsoft.com/en-us/library/system.timespan.fromseconds.aspx
The TimeSpan constructor allows you to pass in seconds. Simply declare a variable of type TimeSpan amount of seconds. Ex:
TimeSpan span = new TimeSpan(0, 0, 500);
span.ToString();
I'd suggest you use the TimeSpan class for this.
public static void Main(string[] args)
{
TimeSpan t = TimeSpan.FromSeconds(80);
Console.WriteLine(t.ToString());
t = TimeSpan.FromSeconds(868693412);
Console.WriteLine(t.ToString());
}
Outputs:
00:01:20
10054.07:43:32
In VB.NET, but it's the same in C#:
Dim x As New TimeSpan(0, 0, 80)
debug.print(x.ToString())
' Will print 00:01:20
For .NET < 4.0 (e.x: Unity) you can write an extension method to have the TimeSpan.ToString(string format) behavior like .NET > 4.0
public static class TimeSpanExtensions
{
public static string ToString(this TimeSpan time, string format)
{
DateTime dateTime = DateTime.Today.Add(time);
return dateTime.ToString(format);
}
}
And from anywhere in your code you can use it like:
var time = TimeSpan.FromSeconds(timeElapsed);
string formattedDate = time.ToString("hh:mm:ss:fff");
This way you can format any TimeSpanobject by simply calling ToString from anywhere of your code.
Why do people need TimeSpan AND DateTime if we have DateTime.AddSeconds()?
var dt = new DateTime(2015, 1, 1).AddSeconds(totalSeconds);
The date is arbitrary.
totalSeconds can be greater than 59 and it is a double.
Then you can format your time as you want using DateTime.ToString():
dt.ToString("H:mm:ss");
This does not work if totalSeconds < 0 or > 59:
new DateTime(2015, 1, 1, 0, 0, totalSeconds)
to get total seconds
var i = TimeSpan.FromTicks(startDate.Ticks).TotalSeconds;
and to get datetime from seconds
var thatDateTime = new DateTime().AddSeconds(i)
This will return in hh:mm:ss format
public static string ConvertTime(long secs)
{
TimeSpan ts = TimeSpan.FromSeconds(secs);
string displayTime = $"{ts.Hours}:{ts.Minutes}:{ts.Seconds}";
return displayTime;
}
TimeSpan t = TimeSpan.FromSeconds(EnergyRestoreTimer.Instance.SecondsForRestore);
string sTime = EnergyRestoreTimer.Instance.SecondsForRestore < 3600
? $"{t.Hours:D2}:{t.Minutes:D2}:{t.Seconds:D2}"
: $"{t.Minutes:D2}:{t.Seconds:D2}";
time.text = sTime;
private string ConvertTime(double miliSeconds)
{
var timeSpan = TimeSpan.FromMilliseconds(totalMiliSeconds);
// Converts the total miliseconds to the human readable time format
return timeSpan.ToString(#"hh\:mm\:ss\:fff");
}
//Test
[TestCase(1002, "00:00:01:002")]
[TestCase(700011, "00:11:40:011")]
[TestCase(113879834, "07:37:59:834")]
public void ConvertTime_ResturnsCorrectString(double totalMiliSeconds, string expectedMessage)
{
// Arrange
var obj = new Class();;
// Act
var resultMessage = obj.ConvertTime(totalMiliSeconds);
// Assert
Assert.AreEqual(expectedMessage, resultMessage);
}

DateTime.Compare how to check if a date is less than 30 days old?

I'm trying to work out if an account expires in less than 30 days. Am I using DateTime Compare correctly?
if (DateTime.Compare(expiryDate, now) < 30)
{
matchFound = true;
}
Am I using DateTime Compare correctly?
No. Compare only offers information about the relative position of two dates: less, equal or greater. What you want is something like this:
if ((expiryDate - DateTime.Now).TotalDays < 30)
matchFound = true;
This subtracts two DateTimes. The result is a TimeSpan object which has a TotalDays property.
Additionally, the conditional can be written directly as:
bool matchFound = (expiryDate - DateTime.Now).TotalDays < 30;
No if needed.
Alternatively, you can avoid naked numbers by using TimeSpan.FromDays:
bool matchFound = (expiryDate - DateTime.Now) < TimeSpan.FromDays(30);
This is slightly more verbose but I generally recommend using the appropriate types, and the appropriate type in this case is a TimeSpan, not an int.
should be
matchFound = (expiryDate - DateTime.Now).TotalDays < 30;
note the total days
otherwise you'll get werid behaviour
Well I would do it like this instead:
TimeSpan diff = expiryDate - DateTime.Today;
if (diff.Days > 30)
matchFound = true;
Compare only responds with an integer indicating weather the first is earlier, same or later...
Try this instead
if ( (expiryDate - DateTime.Now ).TotalDays < 30 ) {
matchFound = true;
}
Compare returns 1, 0, -1 for greater than, equal to, less than, respectively.
You want:
if (DateTime.Compare(expiryDate, DateTime.Now.AddDays(30)) <= 0)
{
bool matchFound = true;
}
This will give you accurate result :
if ((expiryDate.Date - DateTime.Now.Date).Days < 30)
matchFound = true;
Assuming you want to assign false (if applicable) to matchtime, a simpler way of writing it would be..
matchtime = ((expiryDate - DateTime.Now).TotalDays < 30);
No, the Compare function will return either 1, 0, or -1. 0 when the two values are equal, -1 and 1 mean less than and greater than, I believe in that order, but I often mix them up.
No you are not using it correctly.
See here for details.
DateTime t1 = new DateTime(100);
DateTime t2 = new DateTime(20);
if (DateTime.Compare(t1, t2) > 0) Console.WriteLine("t1 > t2");
if (DateTime.Compare(t1, t2) == 0) Console.WriteLine("t1 == t2");
if (DateTime.Compare(t1, t2) < 0) Console.WriteLine("t1 < t2");
What you want to do is subtract the two DateTimes (expiryDate and DateTime.Now). This will return an object of type TimeSpan. The TimeSpan has a property "Days". Compare that number to 30 for your answer.
No it's not correct, try this :
DateTime expiryDate = DateTime.Now.AddDays(-31);
if (DateTime.Compare(expiryDate, DateTime.Now.AddDays(-30)) < 1)
{
matchFound = true;
}
Actually none of these answers worked for me. I solved it by doing like this:
if ((expireDate.Date - DateTime.Now).Days > -30)
{
matchFound = true;
}
When i tried doing this:
matchFound = (expiryDate - DateTime.Now).Days < 30;
Today, 2011-11-14 and my expiryDate was 2011-10-17 i got that matchFound = -28. Instead of 28. So i inversed the last check.
// this isn't set up for good processing.
//I don't know what data set has the expiration
//dates of your accounts. I assume a list.
// matchfound is a single variablethat returns true if any 1 record is expired.
bool matchFound = false;
DateTime dateOfExpiration = DateTime.Today.AddDays(-30);
List<DateTime> accountExpireDates = new List<DateTime>();
foreach (DateTime date in accountExpireDates)
{
if (DateTime.Compare(dateOfExpiration, date) != -1)
{
matchFound = true;
}
}
You can try to do like this:
var daysPassed = (DateTime.UtcNow - expiryDate).Days;
if (daysPassed > 30)
{
// ...
}
Compare is unnecessary, Days / TotalDays are unnecessary.
All you need is
if (expireDate < DateTime.Now) {
// has expired
} else {
// not expired
}
note this will work if you decide to use minutes or months or even years as your expiry criteria.

Categories