I am trying to convert a time represented in double, something like 23.40 which means 23 hours and 40 minutes, using the following method:
private TimeSpan DoubleToTimeSpan(double time)
{
double hour = Math.Floor(time);
double minute = (time - hour) * 100d;
TimeSpan ts = new TimeSpan((int)hour, (int)minute, 0);
return ts;
}
When testing it on some times, like 23.40 for example:
Console.WriteLine(DoubleToTimeSpan(23.40));
It shows 23:39:00, a whole minute has been stolen by the system! Where is my minute?
Note: I know about TimeSpan.FromHours, this doesn't help me because this method considers the minutes as percentage, so 23.40 is 23 hours and 40% of an hour, which is 23:24:00.
Since 0.4 cannot be exactly represented in the Double floating-point format, you get its nearest representation, which in the case of (23.4-23)*100 is probably something like 39.999999999999858. When you use (int), you truncate the fraction, leaving you with 39.
You need to round instead of truncate, so use (int)Math.Round(minute).
Alternatively, you can use the Decimal type, which can exactly represent decimal numbers like 23.40.
Try rounding it instead of int
private TimeSpan DoubleToTimeSpan(double time)
{
double hour = Math.Floor(time);
hour.Dump();
double minute = (time - hour) * 100d;
minute.Dump();
TimeSpan ts = new TimeSpan((int)hour, (int)Math.Round(minute), 0);
return ts;
}
If you do just (int)minute it will take the integer part of your double.
When you cast from a double to an int, it's truncating your value for minute -- thus your lost minute.
(time - hour) * 100d
Will evaluate to 39.999999999999858, and thus 39 when casting to int.
You've used Math.Floor for the hour, so use Math.Ceiling for the minute. It seems to work ok.
private static TimeSpan DoubleToTimeSpan(double time)
{
return new TimeSpan((int)Math.Floor(time), (int)Math.Ceiling((time - Math.Floor(time)) * 100d), 0);
}
Another option to use decimals. This will prevent precision loss.
decimal minute = ((decimal)time)-((decimal)hour)*100;
Not sure if it's a better solution, but if you're on .NET 4.0 or later, you can also go through a string. It seems like "23.40" is a sequence of characters with one interpretation as a double, and another one as a TimeSpan.
If you like that idea, use:
TimeSpan DoubleToTimeSpan(double time)
{
string str = time.ToString("F2", CultureInfo.InvariantCulture);
TimeSpan ts = TimeSpan.ParseExact(str, "h'.'mm", CultureInfo.InvariantCulture);
return ts;
}
Maybe not as fast as the multiply-by-100-and-round approach, though I haven't tested that.
Related
I need to divide two TimeSpan.
I have one TimeSpan "worktime" and the other TimeSpan "productive"
What i want is to get as result of worktime/productive is a percentage. I need to get the "added value" (<- I think this is how it's called in english :)),
In .NET Core you can simply divide the TimeSpans. TimeSpan has a division operator.
This works:
var t1=TimeSpan.FromTicks(1000);
var t2=TimeSpan.FromTicks(100);
Console.WriteLine(t2/t1);
---------------------
0.1 ```
A Simple example:
DateTime d1 = DateTime.Parse("10:00:00");
DateTime d2 = DateTime.Parse("13:00:00");
TimeSpan t1 = d2 - d1; // three hours
TimeSpan t2 = new TimeSpan(0,15,0); // 15 minutes
Console.WriteLine(t1/t2);
This outputs: 12
see: https://onlinegdb.com/ImxwiWk37
var addedValue = (decimal)workTime.Ticks/productive.Ticks * 100;
The decimal cast is needed or the division will automatically round and you'll get very inaccurate percentages in certain cases.
I need to get the "added value"
By this, do you mean you want the percentage expressed as a percentage of the overall work time that was productive?
So if the total work time was 10 hours and the productive work time was 9 hours, the percentage of productive work time would be 90%?
If so, the answer is:
double percentage = double percentage = 100.0 - 100.0 * (worktime.Ticks - productive.Ticks) / worktime.Ticks;
I'm working on a project which calculates a cost of calls from two different times entered by the user. I've parsed the data into the code as DateTime.
DateTime starttime = new DateTime(Convert.ToDateTime(cal1.SelectedDate).Year,Convert.ToDateTime(cal1.SelectedDate).Month,Convert.ToDateTime(cal1.SelectedDate).Day,
Convert.ToInt32(drpdwn1.SelectedValue),
Convert.ToInt32(drpdwn2.SelectedValue),
Convert.ToInt32(drpdwn3.SelectedValue));
DateTime endtime = new DateTime(Convert.ToDateTime(cal1.SelectedDate).Year, Convert.ToDateTime(cal1.SelectedDate).Month, Convert.ToDateTime(cal1.SelectedDate).Day,
Convert.ToInt32(drpdwn4.SelectedValue),
Convert.ToInt32(drpdwn5.SelectedValue),
Convert.ToInt32(drpdwn6.SelectedValue));
And subtracts the two to get a timespan.
TimeSpan duration3 = (endtime - starttime);
txtDisplay.Text = duration3.ToString();
Can someone help me find a way to multiply $0.35 to the timespan duration3?
Here is what I'm using right now but the textbox keeps displaying 0
double mins = duration3.Minutes;
double hrs = duration3.Hours;
double cost = ((hrs * 60) + mins) * 0.35;
txtCost.Text = cost.ToString();
//$0.35/min
// ((0.35*60)*hr + 0.35*min)
Possibly something like:
static decimal Foo(TimeSpan duration, decimal perMinuteRate)
=> ((decimal)duration.TotalMinutes) * perMinuteRate;
although note that TotalMinutes is a double, so this could have unexpected rounding if your duration is very fractional.
Better to do it this way, so you don't interfer with double precision errors:
public static decimal MultiplyPerMinute(TimeSpan a, decimal b)
{
return a.Ticks * b / TimeSpan.TicksPerMinute;
}
When you work with decimal, only double or float should scare you off in any possible operator. Decimal is a type made specificaly for digital, and precision durable operations.
As written, the question is not very clear. There are two questions really:
How to multiply a TimeSpan by a decimal?
Why is my TextBox always displaying zero?
Question 1 is not clear - it can be interpreted in many ways. From reading the posted code, comments and other answers it is possible to see that you are trying to calculate the "cost of a TimeSpan given the cost per minute". Why not ask this question?
But that isn't really your question - your posted code does indicate that you have an idea about how to calculate the "cost of a TimeSpan given the cost per minute", the problem is that your "textbox keeps displaying 0".
From the code posted, I don't know why this is. However, following some good debugging practices should fairly quickly help you narrow down the problem yourself.
See this link to a post by Eric Lippert that you should find useful:
How to debug small programs
No need to calculate the minutes yourself - and multiplying is easy:
TimeSpan ts = DateTime.Now - DateTime.Today;
var cost = ((int)ts.TotalMinutes) * 0.35m; // cost per minute, edited wrongly used 0.63m
I am int ing out the decimals of TotalMinutes to avoid billing for 23.2832s as well ;)
I have this absurd situation where i need to support a Custom TimeSpan Format with a unit of Tenths of minutes with Timespan.TryParse.
i.e. hh:mm:t
where t denotes 10ths of minute (6 second intervals)
What would be the easiest way of adding this to the Custom Timespan format parsing specifies?
Is there some override facility that would make this easy?
Edit
var mask = "hmmt";
var value = "0011";
// 0 hours
// 01 minutes
// 1 tenths of minutes
TimeSpan.TryParseExact(value, mask, null, out time)
the mask is configurable by the user and i need the ability to add some sort of custom specifier like "t" to denote tenths of minutes
the user in essence adds this mask, as the value comes from various pabx phone systems that output duration's in many weird and wonderful ways. one of those ways is the use of 10ths of minutes
if I understand correctly, then for example .3 at the end means 3/10 of a minute, making it 18 seconds. If so, this is the way, in case you have time like "hh:mm:t" as you wrote:
public static class TimeSpanExtension
{
public static TimeSpan TryParseTenth(string timeSpanString)
{
//change following line to accomodate date format if needed
timeSpanString += "0";
TimeSpan ts = new TimeSpan();
if (TimeSpan.TryParse(timeSpanString, out ts))
{
// recalculate from tenth of minute into seconds
float realSeconds = ts.Seconds * 60 / 100;
//final operation to correct
return ts.Subtract(new TimeSpan(0, 0, ts.Seconds - (int)realSeconds));
}
else
return TimeSpan.Zero;
}
}
usage:
string time = "06:55:3";
var timeSpan = TimeSpanExtension.TryParseTenth(time);
resulting in 6h55m18s as I wrote at the top
I need to calculate the time difference faken for division most accurately in nano seconds. Please tell me to do this.
At Present i'm using a lower accuracy method in which the problem is that : when the first calculation is performed it shows 87 milliseconds or 65 milliseconds as answer. But when the function is called again second time or more, it only show 0 milliseconds.
The code is :
long startTick = DateTime.Now.Ticks;
double result = (double)22 / 7;
result = System.Math.Round(result, digit);
long endTick = DateTime.Now.Ticks;
long tick = endTick - startTick;
double milliseconds = tick / TimeSpan.TicksPerMillisecond;
time.Text = result + "\nThe division took " + milliseconds + " milliseconds to complete.";
digit is the parameter of function which is variable. No matter what the value of digit is the milliseconds value remains 0 after first calling of function....
Please suggest more accurate way in which calling the same function with different decimal digits will result in different time interval in c# for windows Phone.
I think the memory flush should be done before and after each calculation. But i dont know how to do this.
I don't like this tick method personally for accuracy. I've tried stopwatch also but its not working. Please suggest another method best suited in my case. I want result like : 0.0345 or 0.0714 seconds.
Thanks
You are performing integer division on this line:
double milliseconds = tick / TimeSpan.TicksPerMillisecond;
Even though you are declaring it as a double, a long divided by a long will truncate the decimal. You are better off doing:
double milliseconds = (double)tick / TimeSpan.TicksPerMillisecond;
Or better yet, just ditch the tick stuff all together:
DateTime start = DateTime.Now;
double result = (double)22 / 7;
result = System.Math.Round(result, digit);
DateTime end = DateTime.Now;
double milliseconds = (end - start).TotalMilliseconds;
time.Text = result + "\nThe division took " + milliseconds + " milliseconds to complete.";
You won't be able to get micro or nano level precision, but you will get millisecond precision with a margin of error.
You still may get zero, however. You are trying to time how long a simple division operation takes. You could do millions of division operations in less than a second. You may want to do it 1,000,000 times, then divide the result by a 1,000,000:
DateTime start = DateTime.Now;
for (var i = 0; i < 1000000; i++)
{
double result = (double)22 / 7;
result = System.Math.Round(result, digit);
}
DateTime end = DateTime.Now;
double milliseconds = (end - start).TotalMilliseconds / 1000000;
This still won't be completely realistic, but should get you an actual number.
Since you have the time in ticks, just increase the resolution by multiplying the denominator:
double microseconds = tick / (TimeSpan.TicksPerMillisecond * 1000.0);
Why are you not using StopWatch Class to do your time calulation.
It is meant to the calculate the time the you want ..
Here is a link for your reference.
http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.aspx
//if you want to get the full milliseconds you could also do something like this.
dateStartTime = Convert.ToDateTime(DateTime.Now.TimeOfDay.ToString());
//then where you end the code do this
dateEndTime = Convert.ToDateTime(DateTime.Now.TimeOfDay.ToString());
ddateDuration = (TimeSpan)(dateEndTime - dateStartTime);
then to display out what you are actually looking for in terms of miliseconds do
Console.WriteLine(ddateDuration.ToString().Substring(0, 8));
// or some other method that you are using to display the results
I want to round a DateTime to the nearest 5 seconds. This is the way I'm currently doing it but I was wondering if there was a better or more concise way?
DateTime now = DateTime.Now;
int second = 0;
// round to nearest 5 second mark
if (now.Second % 5 > 2.5)
{
// round up
second = now.Second + (5 - (now.Second % 5));
}
else
{
// round down
second = now.Second - (now.Second % 5);
}
DateTime rounded = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, second);
Please note that I've found these two previous questions, however they truncate rather than round the time.
(Sorry for the resurrection; I recognize it's an old and answered question - just adding some extra code for Google's sake.)
I started with JayMcClellan's answer, but then I wanted it to be more generic, rounding to arbitrary intervals (not just 5 seconds). So I ended up leaving Jay's method for one that uses Math.Round on ticks and put it into an extension method that can take arbitrary intervals and also offers the option of changing the rounding logic (banker's rounding versus away-from-zero). I'm posting here in case this is helpful to someone else as well:
public static TimeSpan Round(this TimeSpan time, TimeSpan roundingInterval, MidpointRounding roundingType) {
return new TimeSpan(
Convert.ToInt64(Math.Round(
time.Ticks / (decimal)roundingInterval.Ticks,
roundingType
)) * roundingInterval.Ticks
);
}
public static TimeSpan Round(this TimeSpan time, TimeSpan roundingInterval) {
return Round(time, roundingInterval, MidpointRounding.ToEven);
}
public static DateTime Round(this DateTime datetime, TimeSpan roundingInterval) {
return new DateTime((datetime - DateTime.MinValue).Round(roundingInterval).Ticks);
}
It won't win any awards for bare efficiency, but I find it easy to read and intuitive to use. Example usage:
new DateTime(2010, 11, 4, 10, 28, 27).Round(TimeSpan.FromMinutes(1)); // rounds to 2010.11.04 10:28:00
new DateTime(2010, 11, 4, 13, 28, 27).Round(TimeSpan.FromDays(1)); // rounds to 2010.11.05 00:00
new TimeSpan(0, 2, 26).Round(TimeSpan.FromSeconds(5)); // rounds to 00:02:25
new TimeSpan(3, 34, 0).Round(TimeSpan.FromMinutes(37); // rounds to 03:42:00...for all your round-to-37-minute needs
The Ticks count of a DateTime represents 100-nanosecond intervals, so you can round to the nearest 5 seconds by rounding to the nearest 50000000-tick interval like this:
DateTime now = DateTime.Now;
DateTime rounded = new DateTime(((now.Ticks + 25000000) / 50000000) * 50000000);
That's more concise, but not necessarily better. It depends on whether you prefer brevity and speed over code clarity. Yours is arguably easier to understand.
Like you mentioned, it's fairly easy to truncate. So, just add 2.5 seconds, then truncate down.
I can't think of a better way, although I would probably factor out the round method:
static int Round(int n, int r)
{
if ((n % r) <= r / 2)
{
return n - (n % r);
}
return n + (r - (n % r));
}
Also, % returns an int, so comparing it to 2.5 strikes me as a little odd, even though it is correct. I'd use >= 3.
How about this (blending a few answers together)? I think it conveys the meaning well and should handle the edge cases (rounding to the next minute) elegantly due to AddSeconds.
// truncate to multiple of 5
int second = 5 * (int) (now.Second / 5);
DateTime dt = new DateTime(..., second);
// round-up if necessary
if (now.Second % 5 > 2.5)
{
dt = dt.AddSeconds(5);
}
The Ticks approach as shown by Jay is more concise, but may be a bit less readable. If you use that approach, at least reference TimeSpan.TicksPerSecond.
I couldn't recognize the difference between C# and a bar of soap (well, I couldn't when I originally wrote this answer, things have changed quite a bit in the years since) but, if you're looking for a more concise solution, I would just put the whole thing in a function - there's little that will be more concise in your code than a simple call to said function:
DateTime rounded = roundTo5Secs (DateTime.Now);
Then you can put whatever you want in the function and just document how it works, such as (assuming these are all integer operations):
secBase = now.Second / 5;
secExtra = now.Second % 5;
if (secExtra > 2) {
return new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute,
secBase + 5);
}
return new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute,
secBase);
You may also need some extra checks if secBase goes to 60 (unless C# DateTime objects are smart enough to bump up the minute (and hour if minute goes to 60, and so on).
Technically, you can never correctly round to an odd interval given only seconds.
2, 4, 6, 8, 10 <-- are no problem
If you are 'distributing' times in intervals and if the jitter is low, truncation is a lot
more tractable.
If you can pass milliseconds and round at a 500mS mark, you will be able to to odd
seconds and also slash the effect of jitter way down or eliminate it entirely.
Most simple and accurate one-liner:
private static DateTime QuantizeToEachNthSecond(DateTime dateTime, int nthSecond = 5)
{
return dateTime.AddTicks(-(dateTime.Ticks % (nthSecond * TimeSpan.TicksPerSecond)));
}
so, if you like each 5th second, if will be truncated to e.g. "10:12:02" -> "10:12:00", "10:12:08" -> "10:12:05" and so on.