难题:需要一个计算日期的函数,不可用正则表达式,用普通的数学方法实现。
公司财年每年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
公司财年每年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
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天以后
} }
}
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)));
}
}
}
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)));
}
}
}
{
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月份开始
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)));
}
}
}
p0 = p0.AddDays((12 - (int)p0.DayOfWeek) % 7);
p0 = d >= p0 ? p0 : p0.AddDays(-52 * 7);
// 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
{
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;
}
}
}
如果是这样的话,怎样调整程序呢?
应该归为2005 p1
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
而且有一个规律就是每个财年的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周。
这样的话,有没有算法可以实现?
多谢!!
比如说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
{
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();
}
只要有精确的定义就可以做,如果P1一直是5周,P1就会越来越靠前,所以你要规定一个P1的起始值是在哪一年,不过业务上这么做是否欠合理,因为年份越到后面,P1开始越早,甚至会提前到1月份或上年度就开始。
所以需要一个调整,楼主说了,P1六周,触发条件是前五周没有跨过4月。
那就是说,这个P是连续的,所以需要一个时间基准P,例如
2011-03-04周五,后面的每一个Pn都需要前面一个Pn-1的结果来接着计算。