【问题标题】:Timespan Year and Month in decimal c#十进制的时间跨度年和月c#
【发布时间】:2018-09-26 07:57:57
【问题描述】:

我有两个decimal

3.10m = 3 years 10 months 
 2.8m = 2 years  8 months.

我正在尝试sum 这些值,结果是5.9 这是不正确

预期的结果应该是

6.6m = 6 years 6 months

任何人都可以建议我们如何使用TimeSpan 或 C# 中的任何其他方式来实现这一点。提前致谢

【问题讨论】:

  • 为什么要对 DateTime 使用 decimal 响应?
  • 你如何区分3.131月)和3.10310月)?跨度>
  • 恒等式 3.10 = 3 年 10 个月对我来说似乎是错误的。根据该逻辑,3.12 将是 3 年 12 个月或 4 年。
  • 我们如何在 C# 中使用 TimeSpan 或任何其他方式来实现这一点? 使用 TimeSpan 你可以做到 TimeSpan total = span1 + span2; (其中 span1span2 是也属于TimeSpan)。
  • @Matthew Watson:是的,你说的很对:给30 Jan 2018 加上一个月是个问题(是28 Feb 2018?如果我们减去1 一个月后呢? ?);但是,这通常是业务请求(应在一个月内等发货)

标签: c# datetime timestamp


【解决方案1】:

首先,我会避免使用这样的小数来表示年/月值。如 cmets 中所述,您将无法轻松区分 1 个月和 10 个月...而decimal 可以表示 3.1 和 3.10 之间的区别,这将是一个非常奇怪的用法。只需将这两个值保存在单独的整数中即可。

接下来,你不能用TimeSpan 做这样的算术运算。 TimeSpan 只存储“a number of ticks” - 它不处理“1 个月”或“1 年”之类的想法,因为它们不代表固定数量的 Tick。

我建议改用我的 Noda Time 库。您可以将这些值表示为 Period 对象。您仍然无法直接将年份和月份相加 - Period 不假设公历系统,并且这样的算术对于某些非公历不起作用,尤其是那些改变数量的非公历基于年份的月份。但是,您可以轻松编写自己的 Period 规范化方法:

using System;
using NodaTime;

class Test
{
    static void Main()
    {
        Period p1 = new PeriodBuilder { Years = 3, Months = 10 }.Build();
        Period p2 = new PeriodBuilder { Years = 5, Months = -12 }.Build();
        Period sum = p1 + p2;
        Period normalized = NormalizeYearsAndMonths(sum);
        Console.WriteLine($"{normalized.Years} years; {normalized.Months} months");
    }

    static Period NormalizeYearsAndMonths(Period period)
    {
        // TODO: Handle negative years and months however you want.
        int years = period.Years;
        int months = period.Months;
        years += months / 12;
        months = months % 12;
        var builder = period.ToBuilder();
        builder.Years = years;
        builder.Months = months;
        return builder.Build();
    }
}

现在您当然可以将所有内容保存在两个整数中 - 使用 Period 的好处是您可以轻松地将其添加到 LocalDateLocalDateTime 等。如果你愿意的话,你甚至可以在没有标准化的情况下做到这一点。

【讨论】:

    【解决方案2】:

    不要这样做! decimal 不适用于 year.month。从技术上讲,您可以解决问题并区分 3.1m3.10mdecimal.GetBits 帮忙);以下是学术用途的代码:

    private static decimal EerieArithmetics(decimal d1, decimal d2) {
      int y1 = (int)d1;
      int m1 = (int)((d1 % 1m) * ((((decimal.GetBits(d1)[3] >> 16) & 31) == 2) ? 100m : 10m));
    
      int y2 = (int)d2;
      int m2 = (int)((d2 % 1m) * ((((decimal.GetBits(d2)[3] >> 16) & 31) == 2) ? 100m : 10m));
    
      int y = y1 + y2 + (m1 + m2) / 12;
      int m = (m1 + m2) % 12;
    
      return y + (m == 10 ? 0.10m : m > 10 ? m / 100.00m : m / 10.0m);
    }
    

    演示:

    Tuple<decimal, decimal>[] tests = new Tuple<decimal, decimal>[] {
      Tuple.Create(3.10m,  2.8m),
      Tuple.Create( 3.1m,  2.8m),
      Tuple.Create( 3.0m,  2.8m),
      Tuple.Create( 3.0m,  2.0m),
      Tuple.Create(   3m,    2m),
      Tuple.Create( 3.8m,  2.4m),
      Tuple.Create(3.10m, 3.10m),
      Tuple.Create( 2.8m,  2.2m),
      Tuple.Create(2.11m,  2.2m),
    };
    
    string report = string.Join(Environment.NewLine, tests
      .Select(test => 
         $"{test.Item1,5} + {test.Item2,5} == {EerieArithmetics(test.Item1, test.Item2),5}"));
    
    Console.Write(report);
    

    结果:

     3.10 +   2.8 ==   6.6
      3.1 +   2.8 ==   5.9
      3.0 +   2.8 ==   5.8
      3.0 +   2.0 ==     5
        3 +     2 ==     5
      3.8 +   2.4 ==     6
     3.10 +  3.10 ==   7.8
      2.8 +   2.2 ==  4.10
     2.11 +   2.2 ==   5.1
    

    我希望,我把你吓到这样使用decimal。作为一个又快又脏的补丁(如果你不能使用除标准库之外的任何库),你可以试试DateTime class:

     DateTime d1 = new DateTime(3, 10, 1); // 1 Oct 3 AD
     DateTime d2 = new DateTime(2, 8, 1);  // 1 Aug 2 AD
    
     DateTime result = d1
       .AddYears(d2.Year)
       .AddMonths(d2.Month);               // 1 Jun 6 AD
    
     // 6.6
     Console.Write($"{result.Year}.{result.Month}"); 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-05-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多