你能围绕.NET TimeSpan
对象吗?
我的Timespan
值为:00:00:00.6193789
是否有一种简单的方法可以将它保持为TimeSpan
对象,但将其舍入为
00:00:00.62?
对不起,伙计们,但双方的问题和普遍的回答至今都错了:-)
这个问题是错误的,因为廷德尔要求采用圆形的方法,但显示了截断的一个例子.
Dean的回答是错误的,因为它也解决了截断而不是舍入.(我想有人可能会认为答案适合于两个问题中的一个,但是让我们暂时搁置哲学...)
这是一个简单的舍入技术:
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);
使用示例代码中的输入值和位数,这是输出:
Input: 00:00:01.9365678 Truncated: 00:00:01.9300000 Rounded: 00:00:01.9400000
将精度从2位数字更改为5位数,然后改为:
Input: 00:00:01.9365678 Truncated: 00:00:01.9365600 Rounded: 00:00:01.9365700
甚至将其更改为0以获得此结果:
Input: 00:00:01.9365678 Truncated: 00:00:01 Rounded: 00:00:02
最后,如果您只想对输出进行更多控制,请添加一些格式.下面是一个示例,显示您可以将精度与显示的数字分开.精度再次设置为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
如果您正在寻找想法,上述材料非常有用; 我已经有时间为那些寻找即用型代码的人实现一个打包的解决方案.
请注意,这是未注释的代码.具有XML-doc-comments的完全注释版本将在本季度末在我的开源库中提供.虽然我犹豫是否像这样张贴"原始",但我认为这对感兴趣的读者来说仍然有一些好处.
这段代码改进了我上面的代码,尽管它已经四舍五入,仍然显示了7个位置,用零填充.此完成版本将舍入并修剪为指定的位数.
这是一个示例调用:
Console.Write(new RoundedTimeSpan(19365678, 2).ToString()); // Result = 00:00:01.94
这是完整的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); } } }
我刚刚发布了一个新版本的开源库,比预期的要早,包括我上面描述的RoundedTimeSpan.代码在这里 ; 对于API开始在这里然后导航到RoundedTimeSpan
下CleanCode.Data
的命名空间.CleanCode.DLL库包含上面显示的代码,但是在完成的包中提供它.请注意,ToString(int)
自从我在2010.01.06发布以来,我对上述方法略有改进.
TimeSpan只不过是"Ticks"成员的包装.从另一个TimeSpan的Ticks的圆形版本创建一个新的TimeSpan非常容易.
TimeSpan t1 = new TimeSpan(2345678); Console.WriteLine(t1); TimeSpan t2 = new TimeSpan(t1.Ticks - (t1.Ticks % 100000)); Console.WriteLine(t2);
得到:
00:00:00.2345678 00:00:00.2300000
如果你想要一个TimeSpan,它是一个单行:
public static TimeSpan RoundSeconds( TimeSpan span ) { return TimeSpan.FromSeconds( Math.Round( span.TotalSeconds ) ); }
如果你想要一个字符串:
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 ) ); }
积分:
cc1960的答案显示了FromSeconds的使用,但他整理了几秒钟.我的答案概括为指定的位数.
Ed的回答建议使用格式字符串,并包含格式文档的链接.
要舍入到其他单位的倍数,例如1/30秒:
public static TimeSpan RoundSeconds( TimeSpan span, int nDigits ) { return TimeSpan.FromTicks( (long)( Math.Round( span.TotalSeconds, nDigits ) * TimeSpan.TicksPerSecond) ); }
要使用其中一个来舍入到1/30秒的倍数:
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 ); }
在调试器中查看结果:
public static TimeSpan RoundMinutes(TimeSpan span) { return TimeSpan.FromMinutes(Math.Round(span.TotalMinutes)); }
考虑到一些关于舍入到秒的注释,我认为舍入到任何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 + rndticks / 2; return TimeSpan.FromTicks(ansTicks - ansTicks % rndticks); } } public static TimeSpan Round(this TimeSpan ts) => ts.Round(TimeSpan.FromSeconds(1));
鉴于在处理小数单位时(按@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));