难题:需要一个计算日期的函数,不可用正则表达式,用普通的数学方法实现。
公司财年每年3月份(P1)开始,到转年3月份结束(P12);
每个P月都从周五开始,周四结束;
3个月一个季度,每个季度的第一个月是5周,后二个月各4周;见下描述(注意,没有输入表,要在程序里计算):2011季度4...P11 2011-01-07周五 2011-02-03周四 (4周后)
P12 2011-02-04周五 2011-03-03周四 (4周后)2012季度1
P1 2011-03-04周五 2011-04-07周四 (5周后)
P2 2011-04-08周五 2011-05-05周四 (4周后)
P3 2011-05-06周五 2011-06-02周四 (4周后)2012季度2
P4 2011-06-03周五 2011-07-07周四 (5周后)
P5 2011-07-08周五 2011-08-04周四 (4周后)
P6 2011-08-05周五 2011-09-01周四 (4周后)...
现在需要一个console app,提示输入日期,比如:2011-03-08
结果返回:
P值 (p1-p12),开始日期,结束日期。比如:P1, 2011-03-04,2011-04-07

解决方案 »

  1.   

    呵呵,给你一个普通的“非数学方法”的例子:using System;
    using System.Collections.Generic;
    using System.Linq;namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                var input = "2011-03-08";
                var result = (from 我的P p in P表
                              let tst = DateTime.Parse(input)
                              where tst >= p.开始日期 && tst < p.结束日期
                              select p).FirstOrDefault();
                if (result == null)
                    Console.WriteLine("没有对应的P表项目。");
                else
                    Console.WriteLine("{0}, {1:D},{2:D}", result.代号, result.开始日期, result.结束日期.AddDays(-1));
                Console.ReadKey();
            }        private static List<我的P> P表 = new List<我的P>{ 
                 new 我的P{ 代号="P11", 开始日期 = DateTime.Parse (" 2011-01-07"), 结束日期= DateTime.Parse("2011-02-04")},
                 new 我的P {  代号="P12", 开始日期=  DateTime.Parse ( "2011-02-04"), 结束日期=DateTime.Parse( "2011-03-04")},
                 new 我的P{ 代号="P1",  开始日期= DateTime.Parse( "2011-03-04"), 结束日期= DateTime.Parse  ("2011-04-08")}
            };        public class 我的P
            {
                public string 代号;
                public DateTime 开始日期;
                public DateTime 结束日期;   //注意应该是生活中日期 + 1天以后
            }    }
    }
      

  2.   


    static void Main(string[] args)
    {
    DateTime p0 = DateTime.Today;
    Func<int, DateTime> p = m => p0.AddDays((m - 1) * 28 + (m + 1) / 3 * 7); while (true)
    {
    Console.Write("输入日期:");
    string s = Console.ReadLine();
    try
    {
    DateTime d = Convert.ToDateTime(s);
    p0 = new DateTime(d.Year, 3, 1);
    p0 = p0.AddDays((12 - (int)p0.DayOfWeek) % 7);
    p0 = d >= p0 ? p0 : p0.AddDays(-52 * 7);
    }
    catch (Exception)
    {
    Console.WriteLine("** 输入错误 **");
    continue;
    } for (int m = 1; m <= 12; m++)
    {
    string fmt = "P{0}, {1:yyyy-MM-dd}, {2:yyyy-MM-dd}";
    Console.WriteLine(string.Format(fmt, m, p(m), p(m + 1).AddDays(-1)));
    }
    }
    }
      

  3.   


    static void Main(string[] args)
    {
    DateTime p0 = DateTime.Today;
    Func<int, DateTime> p = m => p0.AddDays((m - 1) * 28 + (m + 1) / 3 * 7); while (true)
    {
    Console.Write("输入日期:");
    string s = Console.ReadLine();
    try
    {
    DateTime d = Convert.ToDateTime(s);
    p0 = new DateTime(d.Year, 3, 1);
    p0 = p0.AddDays((12 - (int)p0.DayOfWeek) % 7);
    p0 = d >= p0 ? p0 : p0.AddDays(-52 * 7);
    }
    catch (Exception)
    {
    Console.WriteLine("** 输入错误 **");
    continue;
    } for (int m = 1; m <= 12; m++)
    {
    string fmt = "P{0}, {1:yyyy-MM-dd}, {2:yyyy-MM-dd}";
    Console.WriteLine(string.Format(fmt, m, p(m), p(m + 1).AddDays(-1)));
    }
    }
    }
      

  4.   

    同求正则..static void Main(string[] args)
            {
                DateTime dt;
                Console.WriteLine("请输入日期..");
                while (!DateTime.TryParse(Console.ReadLine(), out dt))
                {
                    Console.WriteLine("输入的日期非法,请重新输入!");                
                }
                //取得3月第一天
                 DateTime firstOfMarch = new DateTime(dt.Year, 3, 1);
                int dw = (int)firstOfMarch.DayOfWeek;
                DateTime p1 = firstOfMarch.AddDays((int)DayOfWeek.Friday - dw);
                DateTime p12 = p1.AddDays(7 * (5 + 4 + 4) * 4);
                Console.WriteLine("本年度P1:{0}\tP12:{1}", p1.ToString("d"), p12.ToString("d"));
            }
    /*
    请输入日期..
    fsfsfs
    输入的日期非法,请重新输入!
    2011-6-12
    本年度P1:3/4/2011      P12:3/2/2012
    Press any key to continue . . .
    */没仔细想,不知道是不是你需要的。只需要输入年份就可以吧,好像是规定了每年3月份开始
      

  5.   


    static void Main(string[] args)
    {
    DateTime p0 = DateTime.Today;
    Func<int, DateTime> p = m => p0.AddDays((m - 1) * 28 + (m + 1) / 3 * 7); while (true)
    {
    Console.Write("输入日期:");
    string s = Console.ReadLine();
    try
    {
    DateTime d = Convert.ToDateTime(s);
    p0 = new DateTime(d.Year, 3, 1);
    p0 = p0.AddDays((12 - (int)p0.DayOfWeek) % 7);
    p0 = d >= p0 ? p0 : p0.AddDays(-52 * 7);
    }
    catch (Exception)
    {
    Console.WriteLine("** 输入错误 **");
    continue;
    } for (int m = 1; m <= 12; m++)
    {
    string fmt = "P{0}, {1:yyyy-MM-dd}, {2:yyyy-MM-dd}";
    Console.WriteLine(string.Format(fmt, m, p(m), p(m + 1).AddDays(-1)));
    }
    }
    }
      

  6.   

    jshi123 正解,目前只需要把最后这段代码添加到一个List里面,然后返回当前输入日期对应的的P值即可。
      

  7.   

    想请问一下jshi123下面几句如何理解?其中的数字“3” 作何解释?Func<int, DateTime> p = m => p0.AddDays((m - 1) * 28 + (m + 1) / 3 * 7); p0 = new DateTime(d.Year, 3, 1);
     p0 = p0.AddDays((12 - (int)p0.DayOfWeek) % 7);
     p0 = d >= p0 ? p0 : p0.AddDays(-52 * 7);
      

  8.   

    还有其中的p0, p, m 各做何解?
      

  9.   

    比如说 2013年3月1号是P1的开始。但是按此打印,P12是2014年1月31~2014年2月27,而2014年3月第一个周五其实是3月7号,显然差出了好几天。而假设你从2005年开始推断,那么2011年1月28~2011年2月24是P12,离题目中所说的2011年3月4日开始P1也查出了好几天。可见这个问题描述是自相矛盾的,按照这个描述写出的代码必定是不准确的。
      

  10.   

    18#回答得很好了,再补充一些:// 定义一个lambda方法,传入月份m, 得到该月的起始日期
    // m从1到12
    // p0是指该财年的第一天
    // 计算公式:从p0开始,每过一个月加4周,每过一个季度多加一周
    //  => p0 + (m-1)*4 + (m+1)/3 
    // 上式乘7为总天数
    Func<int, DateTime> p = m => p0.AddDays((m - 1) * 28 + (m + 1) / 3 * 7);// 财年起始日计算方法:

    // 该年份的3月1号
    p0 = new DateTime(d.Year, 3, 1);

    // p0.DayOfWeek: 3月1号是星期几,星期天=0,星期一=1,...
    // 5 - p0.DayOfWeek: 星期五到3月1的日间隔天数
    // (5 - p0.DayOfWeek + 7) % 7: 3月1日的下个星期5(3月1号为星期6时,取余数1,其他日期等于上式)
    p0 = p0.AddDays((12 - (int)p0.DayOfWeek) % 7);

    // 如果输入日期小于财年起始日,则算到上一财年(减52周*7天)
    p0 = d >= p0 ? p0 : p0.AddDays(-52 * 7);p.s. @sp1234:
    这是一定的,因为52*7=364
    会计年度不等于自然年份,这个在某些公司的财务计算上是这样做的。
    参见:http://baike.soso.com/ShowLemma.e?sp=l409718&ch=w.search.baike.unelite
      

  11.   

    既然“这是一定的”,那么lz的描述就一定错的了?!那么正确的描述是什么?很明显,输入2013和2014年进行两次计算,得到的前面结果中的P12跟后边结果中的P1根本不能自圆其说了。
      

  12.   

    嗯,上面的算法有错误,修改一下static void Main(string[] args)
    {
    Func<DateTime, int, DateTime> p = (p0, m) => p0.AddDays((m - 1) * 28 + (m + 1) / 3 * 7);
    Func<int, DateTime> day0 = year =>
    {
    var d = new DateTime(year, 3, 1);
    return d.AddDays((12 - (int)d.DayOfWeek) % 7);
    }; while (true)
    {
    Console.Write("输入日期:");
    string s = Console.ReadLine();
    try
    {
    DateTime d = Convert.ToDateTime(s);
    DateTime lastyear = day0(d.Year - 1);
    DateTime thisyear = day0(d.Year);
    if (d >= p(lastyear, 13) && d < thisyear)
    {
    Console.WriteLine("输入的日期不在会计年度范围内");
    continue;
    }
    DateTime d0 = d >= thisyear ? thisyear : lastyear;
    for (int m = 1; m <= 12; m++)
    {
    string fmt = "P{0}, {1:yyyy-MM-dd}, {2:yyyy-MM-dd}";
    Console.WriteLine(string.Format(fmt, m, p(d0, m), p(d0, m + 1).AddDays(-1)));
    }
    }
    catch (Exception)
    {
    Console.WriteLine("** 输入错误 **");
    continue;
    }
    }
    }
      

  13.   

    多谢大家讨论,关于sp1234说的“2014年3月第一个周五其实是3月7号”,的确是,这个2014年的P1应该从2月28日开始计算,这个的确是一个特例,我之前没有注意,多谢提醒。也就是说,从2014年2月28日开始的几个星期应该算是2015的P1!
    如果是这样的话,怎样调整程序呢?
      

  14.   

    多谢jshi123,我输入 2014-02-28 说:输入的日期不在会计年度范围内。
    应该归为2005 p1
      

  15.   

    2015 P1是5周还是6周,如果按5周算,是从2014/2/28 - 2015/2/26,再下一下从2月份就开始了?
    P1, 2014-02-28, 2014-04-03
    P2, 2014-04-04, 2014-05-01
    P3, 2014-05-02, 2014-05-29
    P4, 2014-05-30, 2014-07-03
    P5, 2014-07-04, 2014-07-31
    P6, 2014-08-01, 2014-08-28
    P7, 2014-08-29, 2014-10-02
    P8, 2014-10-03, 2014-10-30
    P9, 2014-10-31, 2014-11-27
    P10, 2014-11-28, 2015-01-01
    P11, 2015-01-02, 2015-01-29
    P12, 2015-01-30, 2015-02-26
      

  16.   

    这是个算法问题,请教如何循环数出来?我一开始的描述有错误,应该是:财年开始是从2月底或者3月初。
    而且有一个规律就是每个财年的P1必须至少包含4月份1天。所以正常情况下,3个月一个季度,每个季度的第一个月是5周,后二个月各4周;
    但是如果计算的P1不包括4月份,则P1增加一周,变为6周,P2,P3还是各4周。
    这条永远不变:每个P月都从周五开始,周四结束。这种算法就是要搞定364天的问题,比如说2016年2月25日周四为P12,从2016年2月26日周五起为P1,计算5周,正好到2016年3月31日周四,但这个日期不包括4月份的任何一天,所以2016年的P1为6周。
    这样的话,有没有算法可以实现?
    多谢!!
      

  17.   

    那么到底是从3月份算起,还是从2月底算起?
    比如说2016年2月26日开始:
    P1, 2016-02-26, 2016-04-07 (6周)
    P2, 2016-04-08, 2016-05-05
    P3, 2016-05-06, 2016-06-02
    P4, 2016-06-03, 2016-07-07
    P5, 2016-07-08, 2016-08-04
    P6, 2016-08-05, 2016-09-01
    P7, 2016-09-02, 2016-10-06
    P8, 2016-10-07, 2016-11-03
    P9, 2016-11-04, 2016-12-01
    P10, 2016-12-02, 2017-01-05
    P11, 2017-01-06, 2017-02-02
    P12, 2017-02-03, 2017-03-02
    如果从2016年3月4日开始:
    P1, 2016-03-04, 2016-04-07 (5周)
    P2, 2016-04-08, 2016-05-05
    P3, 2016-05-06, 2016-06-02
    P4, 2016-06-03, 2016-07-07
    P5, 2016-07-08, 2016-08-04
    P6, 2016-08-05, 2016-09-01
    P7, 2016-09-02, 2016-10-06
    P8, 2016-10-07, 2016-11-03
    P9, 2016-11-04, 2016-12-01
    P10, 2016-12-02, 2017-01-05
    P11, 2017-01-06, 2017-02-02
    P12, 2017-02-03, 2017-03-02
      

  18.   

    jshi123 说的是,2016年2月26日开始6周:P1, 2016-02-26, 2016-04-07 
      

  19.   

    那是不是这样static void Main(string[] args)
    {
    while (true)
    {
    Console.Write("输入日期:");
    string s = Console.ReadLine();
    try
    {
    DateTime d = Convert.ToDateTime(s);
    DateTime[] p = P(d);
    for (int m = 0; m < 12; m++)
    {
    string fmt = "P{0}, {1:yyyy-MM-dd}, {2:yyyy-MM-dd}";
    Console.WriteLine(fmt, m+1, p[m], p[m+1].AddDays(-1));
    }
    }
    catch (Exception)
    {
    Console.WriteLine("** 输入错误 **");
    }
    }
    }static DateTime[] P(DateTime d)
    {
    Func<int, DateTime> day0 = year =>
    {
    var d0 = new DateTime(year-1, 3, 1);
    d0 = d0.AddDays((-1 - (int)d0.DayOfWeek) % 7 - 1);
    int w = d0.AddDays(34).Month < 4? 53 : 52;
    return d0.AddDays(w * 7);
    }; DateTime p0 = d >= day0(d.Year) ? day0(d.Year) : day0(d.Year - 1);
    Func<int, DateTime> p = m => p0.AddDays((m * 4 + (m + 2) / 3 + (4 - p0.AddDays(34).Month) * (m + 11) / 12) * 7);
    return Enumerable.Range(0, 13).Select(m => p(m)).ToArray();
    }
      

  20.   

    有一个问题请教,如果题目稍微改动一个条件:即把:“但是如果计算的P1不包括4月份,则P1增加一周,变为6周,P2,P3还是各4周。”改成:“不论计算的P1是否包括4月份,P1永远是5周,P2,P3还永远各4周。”,比如:2016年2月26日开始5周:P1, 2016-02-26, 2016-03-31  那就是说 P1 的开始时间越来越靠前,这样的算法可以做么?
      

  21.   

    如果是母公司的规定,我看你不如请他们把从2000年到2050年的所有Pxx都发给你,你放在一个表里边直接查表来计算日期的P值,这又快又准确。其实我一开始就觉得如无必要,不必搞“数学方法”。
      

  22.   


    只要有精确的定义就可以做,如果P1一直是5周,P1就会越来越靠前,所以你要规定一个P1的起始值是在哪一年,不过业务上这么做是否欠合理,因为年份越到后面,P1开始越早,甚至会提前到1月份或上年度就开始。
      

  23.   

    因为52周只有364天,所以每过一年就会提前一天,遇到闰年提前2天,
    所以需要一个调整,楼主说了,P1六周,触发条件是前五周没有跨过4月。
    那就是说,这个P是连续的,所以需要一个时间基准P,例如
    2011-03-04周五,后面的每一个Pn都需要前面一个Pn-1的结果来接着计算。
      

  24.   

    结贴了,以后如有需要再新开一个帖子继续讨论。非常感谢大家热烈讨论,非常感谢jshi123的算法!