您可以舍入 .NET TimeSpan 对象吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/338658/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Can you round a .NET TimeSpan object?
提问by BuddyJoe
Can you round a .NET TimeSpanobject?
你能舍入一个 .NETTimeSpan对象吗?
I have a Timespanvalue of: 00:00:00.6193789
我有一个Timespan值:00:00:00.6193789
Is there a simple way to keep it a TimeSpanobject but round it to something like
00:00:00.62?
有没有一种简单的方法可以将其保留为TimeSpan对象,但将其舍入为
00:00:00.62 之类的内容?
回答by Michael Sorens
Sorry, guys, but boththe question and the popular answer so far are wrong :-)
对不起,伙计们,但双方的问题和普遍的回答至今都错了:-)
The question is wrong because Tyndall asks for a way to roundbut shows an example of truncation.
这个问题是错误的,因为 Tyndall 要求一种方法来舍入但显示了一个截断的例子。
Will Dean's answer is wrong because it also addresses truncationrather than rounding. (I suppose one could argue the answer is right for one of the two questions, but let's leave philosophy aside for the moment...)
Will Dean 的回答是错误的,因为它还解决了truncation而不是四舍五入问题。(我想人们可能会争辩说,这两个问题之一的答案是正确的,但让我们暂时将哲学放在一边……)
Here is a simple technique for rounding:
这是一个简单的四舍五入技巧:
int precision = 2; // Specify how many digits past the decimal point
TimeSpan t1 = new TimeSpan(19365678); // sample input value
const int TIMESPAN_SIZE = 7; // it always has seven digits
// convert the digitsToShow into a rounding/truncating mask
int factor = (int)Math.Pow(10,(TIMESPAN_SIZE - precision));
Console.WriteLine("Input: " + t1);
TimeSpan truncatedTimeSpan = new TimeSpan(t1.Ticks - (t1.Ticks % factor));
Console.WriteLine("Truncated: " + truncatedTimeSpan);
TimeSpan roundedTimeSpan =
new TimeSpan(((long)Math.Round((1.0*t1.Ticks/factor))*factor));
Console.WriteLine("Rounded: " + roundedTimeSpan);
With the input value and number of digits in the sample code, this is the output:
使用示例代码中的输入值和位数,这是输出:
Input: 00:00:01.9365678
Truncated: 00:00:01.9300000
Rounded: 00:00:01.9400000
Change the precision from 2 digits to 5 digits and get this instead:
将精度从 2 位数更改为 5 位数并改为:
Input: 00:00:01.9365678
Truncated: 00:00:01.9365600
Rounded: 00:00:01.9365700
And even change it to 0 to get this result:
甚至将其更改为 0 以获得此结果:
Input: 00:00:01.9365678
Truncated: 00:00:01
Rounded: 00:00:02
Finally, if you want just a bit more control over the output, add some formatting. Here is one example, showing that you can separate the precision from the number of displayed digits. The precision is again set to 2 but 3 digits are displayed, as specified in the last argument of the formatting control string:
最后,如果您只想对输出进行更多控制,请添加一些格式。这是一个示例,表明您可以将精度与显示的位数分开。精度再次设置为 2,但显示 3 位数字,如格式控制字符串的最后一个参数中所指定:
Console.WriteLine("Rounded/formatted: " +
string.Format("{0:00}:{1:00}:{2:00}.{3:000}",
roundedTimeSpan.Hours, roundedTimeSpan.Minutes,
roundedTimeSpan.Seconds, roundedTimeSpan.Milliseconds));
// Input: 00:00:01.9365678
// Truncated: 00:00:01.9300000
// Rounded: 00:00:01.9400000
// Rounded/formatted: 00:00:01.940
2010.01.06 UPDATE: An Out-of-the-box Solution
2010.01.06 更新:开箱即用的解决方案
The above material is useful if you are looking for ideas; I have since had time to implement a packaged solution for those looking for ready-to-use code.
如果您正在寻找想法,上述材料很有用;从那以后,我有时间为那些寻找即用型代码的人实施打包的解决方案。
Note that this is uncommented code. The fully commented version with XML-doc-comments will be available in my open source libraryby the end of the quarter. Though I hesitated to post it "raw" like this, I figure that it could still be of some benefit to interested readers.
请注意,这是未注释的代码。到本季度末,我的开源库中将提供带有 XML-doc-comments 的完整评论版本。尽管我犹豫是否像这样“原始”地发布它,但我认为它仍然可以对感兴趣的读者有所帮助。
This code improves upon my code above which, though it rounded, still showed 7 places, padded with zeroes. This finished version rounds and trims to the specified number of digits.
这段代码改进了我上面的代码,虽然它四舍五入,但仍然显示了 7 个位置,用零填充。这个完成的版本四舍五入并修剪到指定的位数。
Here is a sample invocation:
这是一个示例调用:
Console.Write(new RoundedTimeSpan(19365678, 2).ToString());
// Result = 00:00:01.94
And here is the complete RoundedTimeSpan.cs file:
这是完整的 RoundedTimeSpan.cs 文件:
using System;
namespace CleanCode.Data
{
public struct RoundedTimeSpan
{
private const int TIMESPAN_SIZE = 7; // it always has seven digits
private TimeSpan roundedTimeSpan;
private int precision;
public RoundedTimeSpan(long ticks, int precision)
{
if (precision < 0) { throw new ArgumentException("precision must be non-negative"); }
this.precision = precision;
int factor = (int)System.Math.Pow(10, (TIMESPAN_SIZE - precision));
// This is only valid for rounding milliseconds-will *not* work on secs/mins/hrs!
roundedTimeSpan = new TimeSpan(((long)System.Math.Round((1.0 * ticks / factor)) * factor));
}
public TimeSpan TimeSpan { get { return roundedTimeSpan; } }
public override string ToString()
{
return ToString(precision);
}
public string ToString(int length)
{ // this method revised 2010.01.31
int digitsToStrip = TIMESPAN_SIZE - length;
string s = roundedTimeSpan.ToString();
if (!s.Contains(".") && length == 0) { return s; }
if (!s.Contains(".")) { s += "." + new string('0', TIMESPAN_SIZE); }
int subLength = s.Length - digitsToStrip;
return subLength < 0 ? "" : subLength > s.Length ? s : s.Substring(0, subLength);
}
}
}
2010.02.01 UPDATE: Packaged solution now available
2010.02.01 更新:现在提供打包解决方案
I just released a new version of my open-source libraries yesterday, sooner than anticipated, including the RoundedTimeSpan I described above. Code is here; for the API start herethen navigate to RoundedTimeSpanunder the CleanCode.Datanamespace. The CleanCode.DLL library includes the code shown above but provides it in a finished package. Note that I have made a slight improvement in the ToString(int)method above since I posted it on 2010.01.06.
我昨天刚刚发布了我的开源库的新版本,比预期的要早,包括我上面描述的 RoundedTimeSpan。代码在这里;对于API开始在这里然后导航到RoundedTimeSpan下CleanCode.Data的命名空间。CleanCode.DLL 库包括上面显示的代码,但在完成的包中提供它。请注意,ToString(int)自从我在 2010.01.06 发布它之后,我对上面的方法做了一些改进。
回答by Will Dean
TimeSpan is little more than a wrapper around the 'Ticks' member. It's pretty easy to create a new TimeSpan from a rounded version of another TimeSpan's Ticks.
TimeSpan 只不过是围绕“Ticks”成员的包装。从另一个 TimeSpan's Ticks 的圆形版本创建一个新的 TimeSpan 非常容易。
TimeSpan t1 = new TimeSpan(2345678);
Console.WriteLine(t1);
TimeSpan t2 = new TimeSpan(t1.Ticks - (t1.Ticks % 100000));
Console.WriteLine(t2);
Gives:
给出:
00:00:00.2345678
00:00:00.2300000
回答by ToolmakerSteve
Simplest one-liner if you are rounding to whole seconds:
如果您四舍五入到整秒,则最简单的单行:
public static TimeSpan RoundSeconds( TimeSpan span ) {
return TimeSpan.FromSeconds( Math.Round( span.TotalSeconds ) );
}
To round to up to 3 digits (e.g. tenths, hundredths of second, or milliseconds:
要四舍五入到 3 位数(例如,十分之一、百分之一或毫秒:
public static TimeSpan RoundSeconds( TimeSpan span, int nDigits ) {
// TimeSpan.FromSeconds rounds to nearest millisecond, so nDigits should be 3 or less - won't get good answer beyond 3 digits.
Debug.Assert( nDigits <= 3 );
return TimeSpan.FromSeconds( Math.Round( span.TotalSeconds, nDigits ) );
}
For more than 3 digits, its slightly more complex - but still a one-liner. This can also be used for 3 or less digits - it is a replacement for the version shown above:
对于超过 3 位数字,它稍微复杂一些 - 但仍然是单行的。这也可以用于 3 位或更少的数字 - 它是上面显示的版本的替代品:
public static TimeSpan RoundSeconds( TimeSpan span, int nDigits ) {
return TimeSpan.FromTicks( (long)( Math.Round( span.TotalSeconds, nDigits ) * TimeSpan.TicksPerSecond) );
}
If you want a string (according to a comment, this technique only works up to 7 digits):
如果您想要一个字符串(根据注释,此技术最多只能使用 7 位数字):
public static string RoundSecondsAsString( TimeSpan span, int nDigits ) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < nDigits; i++)
sb.Append( "f" );
return span.ToString( @"hh\:mm\:ss\." + sb );
}
For time of day, as hours and minutes, rounded:
对于一天中的时间,如 hours 和minutes,四舍五入:
public static TimeSpan RoundMinutes(TimeSpan span)
{
return TimeSpan.FromMinutes(Math.Round(span.TotalMinutes));
}
If you have a DateTime, and want to extract time of day rounded:
如果您有 DateTime,并且想要提取四舍五入的一天中的时间:
DateTime dt = DateTime.Now();
TimeSpan hhmm = RoundMinutes(dt.TimeOfDay);
To display rounded 24 hour time:
显示四舍五入的 24 小时制时间:
string hhmmStr = RoundMinutes(dt.TimeOfDay).ToString(@"hh\:mm");
To display time of day in current culture:
在当前文化中显示时间:
string hhmmStr = new DateTime().Add(RoundMinutes(dt.TimeOfDay)).ToShortTimeString();
Credits:
学分:
cc1960's answer shows use of FromSeconds, but he rounded to whole seconds. My answer generalizes to specified number of digits.
cc1960 的回答显示了 FromSeconds 的使用,但他四舍五入到整秒。我的答案概括为指定的位数。
Ed's answer suggests using a format string, and includes a link to the formatting document.
Ed 的回答建议使用格式字符串,并包含指向格式文档的链接。
Chris Marisicshows how to apply ToShortTimeStringto a TimeSpan (by first converting to a DateTime).
Chris Marisic展示了如何应用ToShortTimeString到 TimeSpan(首先转换为 a DateTime)。
To round to multiple of some other unit, such as 1/30 second:
四舍五入为其他单位的倍数,例如 1/30 秒:
// Rounds span to multiple of "unitInSeconds".
// NOTE: This will be close to the requested multiple,
// but is not exact when unit cannot be exactly represented by a double.
// e.g. "unitInSeconds = 1/30" isn't EXACTLY 1/30,
// so the returned value won't be exactly a multiple of 1/30.
public static double RoundMultipleAsSeconds( TimeSpan span, double unitInSeconds )
{
return unitInSeconds * Math.Round( span.TotalSeconds / unitInSeconds );
}
public static TimeSpan RoundMultipleAsTimeSpan( TimeSpan span, double unitInSeconds )
{
return TimeSpan.FromTicks( (long)(RoundMultipleAsSeconds( span, unitInSeconds ) * TimeSpan.TicksPerSecond) );
// IF USE THIS: TimeSpan.FromSeconds rounds the result to nearest millisecond.
//return TimeSpan.FromSeconds( RoundMultipleAsSeconds( span, unitInSeconds ) );
}
// Rounds "span / n".
// NOTE: This version might be a hair closer in some cases,
// but probably not enough to matter, and can only represent units that are "1 / N" seconds.
public static double RoundOneOverNAsSeconds( TimeSpan span, double n )
{
return Math.Round( span.TotalSeconds * n ) / n;
}
public static TimeSpan RoundOneOverNAsTimeSpan( TimeSpan span, double n )
{
return TimeSpan.FromTicks( (long)(RoundOneOverNAsSeconds( span, n ) * TimeSpan.TicksPerSecond) );
// IF USE THIS: TimeSpan.FromSeconds rounds the result to nearest millisecond.
//return TimeSpan.FromSeconds( RoundOneOverNAsSeconds( span, n ) );
}
To use one of these to round to multiples of 1/30 second:
要使用其中之一四舍五入为 1/30 秒的倍数:
private void Test()
{
long ticks = (long) (987.654321 * TimeSpan.TicksPerSecond);
TimeSpan span = TimeSpan.FromTicks( ticks );
TestRound( span, 30 );
TestRound( TimeSpan.FromSeconds( 987 ), 30 );
}
private static void TestRound(TimeSpan span, int n)
{
var answer1 = RoundMultipleAsSeconds( span, 1.0 / n );
var answer2 = RoundMultipleAsTimeSpan( span, 1.0 / n );
var answer3 = RoundOneOverNAsSeconds( span, n );
var answer4 = RoundOneOverNAsTimeSpan( span, n );
}
Results viewed in debugger:
在调试器中查看的结果:
// for 987.654321 seconds:
answer1 987.66666666666663 double
answer2 {00:16:27.6666666} System.TimeSpan
answer3 987.66666666666663 double
answer4 {00:16:27.6666666} System.TimeSpan
// for 987 seconds:
answer1 987 double
answer2 {00:16:27} System.TimeSpan
answer3 987 double
answer4 {00:16:27} System.TimeSpan
回答by NetMage
Given some of the comments about rounding to seconds, I thought rounding to any TimeSpan would be nice:
鉴于一些关于四舍五入到秒的评论,我认为四舍五入到任何 TimeSpan 会很好:
public static TimeSpan Round(this TimeSpan ts, TimeSpan rnd) {
if (rnd == TimeSpan.Zero)
return ts;
else {
var rndTicks = rnd.Ticks;
var ansTicks = ts.Ticks + Math.Sign(ts.Ticks) * rndTicks / 2;
return TimeSpan.FromTicks(ansTicks - ansTicks % rndTicks);
}
}
public static TimeSpan Round(this TimeSpan ts) => ts.Round(TimeSpan.FromSeconds(1));
Given the potential inaccuracies rounding to ticks when dealing with fractional units (per @ToolmakerSteve), I am adding a fractional rounding option for when you need higher accuracy and are rounded to a computer fractional seconds:
考虑到在处理小数单位时四舍五入到刻度的潜在不准确度(每个 @ToolmakerSteve),我添加了一个小数四舍五入选项,用于当您需要更高的精度并四舍五入到计算机小数秒时:
public static TimeSpan RoundToFraction(this TimeSpan ts, long num, long den) => (den == 0.0) ? TimeSpan.Zero : TimeSpan.FromTicks((long)Math.Round(Math.Round((double)ts.Ticks * (double)den / num / TimeSpan.TicksPerSecond) * (double)num / den * TimeSpan.TicksPerSecond));
public static TimeSpan RoundToFraction(this TimeSpan ts, long den) => (den == 0.0) ? TimeSpan.Zero : TimeSpan.FromTicks((long)(Math.Round((double)ts.Ticks * den / TimeSpan.TicksPerSecond) / den * TimeSpan.TicksPerSecond));
回答by mapache
new TimeSpan(tmspan.Hours, tmspan.Minutes, tmspan.Seconds, (int)Math.Round(Convert.ToDouble(tmspan.Milliseconds / 10)));
回答by Joel Coehoorn
Not sure about TimeSpan, but you might check this post on DateTimes:
http://mikeinmadison.wordpress.com/2008/03/12/datetimeround/
不确定 TimeSpan,但您可以查看 DateTimes 上的这篇文章:http:
//mikeinmadison.wordpress.com/2008/03/12/datetimeround/
回答by Gewalius
Here is a nice Extention-Method:
这是一个很好的扩展方法:
public static TimeSpan RoundToSeconds(this TimeSpan timespan, int seconds = 1)
{
long offset = (timespan.Ticks >= 0) ? TimeSpan.TicksPerSecond / 2 : TimeSpan.TicksPerSecond / -2;
return TimeSpan.FromTicks((timespan.Ticks + offset) / TimeSpan.TicksPerSecond * TimeSpan.TicksPerSecond);
}
And here are some Examples:
这里有一些例子:
DateTime dt1 = DateTime.Now.RoundToSeconds(); // round to full seconds
DateTime dt2 = DateTime.Now.RoundToSeconds(5 * 60); // round to full 5 minutes
回答by cc1960
My solution:
我的解决方案:
static TimeSpan RoundToSec(TimeSpan ts)
{
return TimeSpan.FromSeconds((int)(ts.TotalSeconds));
}
回答by Buzz
Yet another way to round milliseconds to the nearest second.
将毫秒舍入到最接近的秒的另一种方法。
private const long TicksPer1000Milliseconds = 1000 * TimeSpan.TicksPerMillisecond;
// Round milliseconds to nearest second
// To round up, add the sub-second ticks required to reach the next second
// To round down, subtract the sub-second ticks
elapsedTime = new TimeSpan(elapsedTime.Ticks + (elapsedTime.Milliseconds >= 500 ? TicksPer1000Milliseconds - (elapsedTime.Ticks % TicksPer1000Milliseconds) : -(elapsedTime.Ticks % TicksPer1000Milliseconds)));
回答by Math M.
An extension method if you need to work with DateTimeinstead, but still want to round the time. In my case, I wanted to round to the minute.
如果您需要使用一种扩展方法DateTime,但仍想调整时间。就我而言,我想精确到分钟。
public static DateTime RoundToMinute(this DateTime date)
{
var roundedTimeSpan = TimeSpan.FromMinutes(Math.Round(date.TimeOfDay.TotalMinutes));
return new DateTime(date.Year, date.Month, date.Day, roundedTimeSpan.Hours, roundedTimeSpan.Minutes, 0);
}

