摘要

今天中午午休时,和老婆聊天,老婆还过几天就要请产假了,她在网上问我让我帮她数一下该怎么请假最划算,老婆是个会过日子的人,面对此种要求我当然义不容辞,不过想到这个问题我的第一反应是:这个怎么可以用数的呢?于是,我开始去了解2014年上海市最新的产假政策规定,大致概况如下:“产假加上晚育假一共128天,其中前面98天是正常产假,其中已经包括国家法定节日和双休日,后面30天是晚育假,只包含双休日,不包含国家法定节日,也就是说遇到国家法定节日则假期往后顺延”,注意黑体粗字描述,可以知道这里面的精打细算就体现在前面98天的正常产假。我们要做的就是尽量避免正常产假包含太多的国家法定节假日,否则用老婆的话说那就是“亏”了,注意我把“亏”字打引号,我的意思是在生活中我们不必太过于精打细算斤斤计较,如果过度了那么就容易失去生活情趣和心灵的自由,有句话说吃亏是福。但是,在不妨碍这种前提条件下,我们还是要努力争取,扯远了,这个问题又不复杂,所以,我何乐而不为呢?况且,最近已经准备开始写博了,经常看书看文章看博客,毕竟,纸上得来终觉浅,绝知此事要躬行,还是要经常实践实践,况且作为干这一行的,更要有开放和分享的心态,好了,废话已经很多了,开始代码的构思和实现。

更新

此文已更新,请看这里:C#编程实践--产假方案优化版

领域

初步定位,这是一个关于时间查询的应用,模型围绕时间建立,假期根据月份和日号来表示(公历和农历),另需要提供假期规则的定义,如下(DateModels.cs):

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace HelloBaby
{
// 这里提供默认的放假集合定义
public static class DefaultHoliday
{
// 元旦1天假
public static readonly Holiday NewYearsHoliday
= new Holiday(MonthDayDate.NewYearsDay, 1);
// 春节3天假,从前一天(除夕)开始放
public static readonly Holiday SpringFestivalHoliday
= new Holiday(MonthDayDate.SpringFestival, -1, 3);
// 清明1天假
public static readonly Holiday QingMingHoliday
= new Holiday(MonthDayDate.QingMingDay, 1);
// 劳动节1天假
public static readonly Holiday LabourHoliday
= new Holiday(MonthDayDate.LabourDay, 1);
// 端午节1天假
public static readonly Holiday DragonBoatHoliday
= new Holiday(MonthDayDate.DragonBoatFestival, 1);
// 中秋节1天假
public static readonly Holiday MidAutumnHoliday
= new Holiday(MonthDayDate.MidAutumnDay, 1);
// 国庆节3天假
public static readonly Holiday NationalHoliday
= new Holiday(MonthDayDate.NationalDay, 3); public static List<Holiday> Holidays = new List<Holiday>{
DefaultHoliday.NewYearsHoliday,
DefaultHoliday.SpringFestivalHoliday,
DefaultHoliday.QingMingHoliday,
DefaultHoliday.LabourHoliday,
DefaultHoliday.DragonBoatHoliday,
DefaultHoliday.MidAutumnHoliday,
DefaultHoliday.NationalHoliday
};
} // 假期,包含3个成员(农历或公历几月几日?提前几天放?共放几天)
public struct Holiday
{
public Holiday(MonthDayDate monthDay, int startOffset, int days)
: this()
{
MonthDay = monthDay;
StartOffset = startOffset;
Days = days;
} public Holiday(MonthDayDate monthDay, int days)
: this(monthDay, 0, days)
{
} public MonthDayDate MonthDay { get; private set; }
public int StartOffset { get; set; }
public int Days { get; set; } // 根据年份获取假期具体日期枚举
public IEnumerable<DateTime> ToDateTimeRange(int year)
{
DateTime gregorian = DateTime.Now; if (MonthDay.Calendar == CalendarKind.LunarCalendar)
gregorian = DateUtility.ConvertLunarYearDate(year, MonthDay.Month, MonthDay.Day);
else
gregorian = new DateTime(year, MonthDay.Month, MonthDay.Day); DateTime begin = gregorian.AddDays(StartOffset);
for (int i = 0; i < Days; i++)
{
yield return begin.AddDays((double)i);
}
}
} // 此处使用Calendar属性来区分历法
// 也可以将struct改为class使用继承的设计方式,方便扩展
public struct MonthDayDate
{
// 元旦节
public static readonly MonthDayDate NewYearsDay = new MonthDayDate(1, 1);
// 中国春节
public static readonly MonthDayDate SpringFestival =
new MonthDayDate(1, 1, CalendarKind.LunarCalendar);
// 清明节
public static readonly MonthDayDate QingMingDay = new MonthDayDate(4, 5);
// 五一劳动节
public static readonly MonthDayDate LabourDay = new MonthDayDate(5, 1);
// 端午节
public static readonly MonthDayDate DragonBoatFestival =
new MonthDayDate(5, 5, CalendarKind.LunarCalendar);
// 中秋节
public static readonly MonthDayDate MidAutumnDay =
new MonthDayDate(8, 15, CalendarKind.LunarCalendar);
// 国庆节
public static readonly MonthDayDate NationalDay =
new MonthDayDate(10, 1); public MonthDayDate(int month, int day, CalendarKind calendar)
: this()
{
Month = month;
Day = day;
Calendar = calendar;
} public MonthDayDate(int month, int day)
: this(month, day, CalendarKind.Gregorian)
{
} public int Month { get; private set; }
public int Day { get; private set; }
public CalendarKind Calendar { get; private set; } public static bool operator ==(MonthDayDate d1, MonthDayDate d2)
{
return d1.Month == d2.Month
&& d1.Day == d2.Day
&& d1.Calendar == d2.Calendar;
} public static bool operator !=(MonthDayDate d1, MonthDayDate d2)
{
return !(d1 == d2);
}
} public enum CalendarKind
{
// 公历(阳历)
Gregorian,
// 中国农历(阴历)
LunarCalendar
}
}

注意,在Model里面,我们添加了业务逻辑,比如Holiday的ToDateTimeRange方法里面依赖了外部的农历到公历的转换逻辑,这里依赖了外部逻辑,是否属于不良好的设计?于是我有必要声明,此代码为半实验性代码,非生产代码。既然此处依赖一些外部方法逻辑,那么我也把这部分代码列出(DateUtility.cs):

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace HelloBaby
{
public class DateUtility
{
/// <summary>
/// 获取时间段枚举
/// </summary>
public static IEnumerable<DateTime> RangeDay(DateTime starting, DateTime ending)
{
for (DateTime d = starting; d <= ending; d = d.AddDays(1))
{
yield return d;
}
} /// <summary>
/// 农历转公历
/// </summary>
public static DateTime ConvertLunarYearDate(int year, int month, int day)
{
ChineseLunisolarCalendar calendar = new ChineseLunisolarCalendar(); return calendar.ToDateTime(year, month, day, 0, 0, 0, 0);
}
}
}

业务

程序写到这里已经完成了一大半了,接下来就是一些判断的规则逻辑,这里使用扩展方法来实现(DateExtensions.cs):

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace HelloBaby
{
public static class DateExtensions
{
/// <summary>
/// 判断日期是否是节假日,使用默认放假规定
/// </summary>
public static bool IsHoliday(this DateTime date)
{
return date.IsHoliday(DefaultHoliday.Holidays);
} /// <summary>
/// 判断日期是否是节假日,使用指定放假规定
/// </summary>
public static bool IsHoliday(this DateTime date, IEnumerable<Holiday> holidays)
{
return holidays.Any(
d => d.ToDateTimeRange(date.Year).Contains(date)
);
}
} }

提示:在项目的实践中,我们尽量遵循TDD的开发模式,尤其是针对业务处理层的代码,单元测试代码这里就不贴了,省略掉。

应用

好了,至此我们的领域和业务规则部分已经完成了,可以开始我们的应用了,在一个以数据为中心的年代,有了数据,我们就可以发挥想象,为所欲为,在这里我还是简单的回到最初的问题点上,因为虽然借此机会练练手写写代码,但初衷还是要帮老婆解决问题啊,于是我的应用场景为计算98天产假里面包含的国家法定节日,我在此主要使用LINQ查询:

internal static void PrintSolutions(int days)
{
var sample = DateUtility.RangeDay(
new DateTime(2014, 1, 1),
new DateTime(2015, 12, 31)); var holidayCollection = DefaultHoliday.Holidays; var solutions =
from begin in sample
from end in sample
let range = DateUtility.RangeDay(begin, end)
where range.Count() == days
select new
{
Begin = begin,
End = end,
HolidayCount = range.Count(d => d.IsHoliday())
}; // 显式查询集合,避免多次查询
// 空间换时间的典型场景啊!!!
var local = solutions.ToList(); var groups =
from all in local
group all by all.HolidayCount into g
select new
{
Holidays = g.Key,
SolutionCount = g.Count()
}; var best =
from all in local
where all.HolidayCount == 0
select all; Console.WriteLine("group results:");
foreach (var group in groups)
{
Console.WriteLine("{0} solutions for {1} holidays",
group.SolutionCount, group.Holidays);
} Console.WriteLine();
Console.WriteLine("best results:");
foreach (var one in best)
{
Console.WriteLine("from {0:yyyy-MM-dd} to {1:yyyy-MM-dd}",
one.Begin, one.End);
}
}

之前和老婆聊天的时候,我对她的初步方案表示认同,因为98天假期只会跨越一天元旦假期而已,我觉得98天假期不跨越法定节日基本上不太会有吧,就算有,这种方案也不多见,虽然我已经安慰老婆了,但是我还是耐心做做测试,我使用2014年初到2015年末作为时间样本,测试结果,原来我以为不可能有98天没法定节日的,结果发现,2015年还真有这么仅有的一个时间段,98天不经过一个法定节日,算出来是:2015-06-21 到 2015-09-26。呵呵,老婆反正你是没戏咯

结语

虽然文章里面一些措辞是奔着解决问题去的,说实话,最终目的还是练手,自己比较懒,只看东西不爱动手,但还是觉得要有开放的心态。本人关于LINQ的查询还存在一些疑问,那就是涉及到它背后的执行查询的性能?就好像写SQL语句同一个查询有不同的写法,不同的写法有不同的性能考量,所以我们一般都尽量遵循规范来写查询,那么LINQ to Object是如何规范做这一切的?以后其他比较通用的查询Provider(比如LINQ to Entity,虽然我仔细阅读过一些书和案例,始终觉得还没摸透),看来,有空还可以再探究探究,欢迎有想法的朋友们交流!希望可以结交到有识之士!

最后再吐槽一下:为毛上海的陪产假只有区区3天啊,而且还是针对晚育的情况,相比其他省市的政策,太不人性化了嘛!!!

C#编程实践–帮老婆计算产假方案的更多相关文章

  1. C#编程实践–产假方案优化版

    前言 既然作为一个踏踏实实学习技术的人,就要有一颗谦卑.虚心和追求卓越的心,我不能一次就写出很完美的代码,但我相信,踏踏实实一步一步的优化,代码就可以变得趋近完美,至少在某一个特定场景下相对完美,这和 ...

  2. Python入门经典. 以解决计算问题为导向的Python编程实践

    Python入门经典. 以解决计算问题为导向的Python编程实践(高清版)PDF 百度网盘 链接:https://pan.baidu.com/s/1juLsew8UiOErRheQPOuTaw 提取 ...

  3. Storm实时计算:流操作入门编程实践

    转自:http://shiyanjun.cn/archives/977.html Storm实时计算:流操作入门编程实践   Storm是一个分布式是实时计算系统,它设计了一种对流和计算的抽象,概念比 ...

  4. 编程实践中C语言的一些常见细节

    对于C语言,不同的编译器采用了不同的实现,并且在不同平台上表现也不同.脱离具体环境探讨C的细节行为是没有意义的,以下是我所使用的环境,大部分内容都经过测试,且所有测试结果基于这个环境获得,为简化起见, ...

  5. 试读《JavaScript语言精髓与编程实践》

    有幸看到iteye的活动,有幸读到<JavaScript语言精髓与编程实践_第2版>的试读版本,希望更有幸能完整的读到此书. 说来读这本书的冲动,来得很诡异,写一篇读后感,赢一本书,其实奖 ...

  6. HBase Coprocessor 剖析与编程实践(转载http://www.cnblogs.com/ventlam/archive/2012/10/30/2747024.html)

    HBase Coprocessor 剖析与编程实践 1.起因(Why HBase  Coprocessor) HBase作为列族数据库最经常被人诟病的特性包括:无法轻易建立“二级索引”,难以执行求和. ...

  7. Socket编程实践(10) --select的限制与poll的使用

    select的限制 用select实现的并发服务器,能达到的并发数一般受两方面限制: 1)一个进程能打开的最大文件描述符限制.这可以通过调整内核参数.可以通过ulimit -n(number)来调整或 ...

  8. 03--(二)编程实践中C语言的一些常见细节

    编程实践中C语言的一些常见细节(转载) 对于C语言,不同的编译器采用了不同的实现,并且在不同平台上表现也不同.脱离具体环境探讨C的细节行为是没有意义的,以下是我所使用的环境,大部分内容都经过测试,且所 ...

  9. <读书笔记>001-以解决问题为导向的python编程实践

    以解决问题为导向的python编程实践 0.第0章:计算机科学 思考:计算机科学是否为计算机编程的简称? 编程的困难点:1.同时做2件事(编程语言的语法.语义+利用其解决问题)  2.什么是好程序(解 ...

随机推荐

  1. jvm在存储区域

    当区域执行的数据  JVM存储器的管理分为几个时间之后的数据区的实施:程序计数器.JavaVM栈.本地方法栈.Java堆.方法区(包括常量池的实现).   程序计数器 较小的内存空间,能够看作是当前线 ...

  2. 中国澳门sinox很多平台CAD制图、PCB电路板、IC我知道了、HDL硬件描述语言叙述、电路仿真和设计软件,元素分析表

    中国澳门sinox很多平台CAD制图.PCB电路板.IC我知道了.HDL硬件描述语言叙述.电路仿真和设计软件,元素分析表,可打开眼世界. 最近的研究sinox执行windows版protel,powe ...

  3. git fetch, merge, pull, push需要注意的地方(转)

    在git操作中,我们经常会用到fetch, merge, pull和push等命令,以下是一些我们需要注意的地方. 给大家准备了参考资料: 1. Whatʼs a Fast Forward Merge ...

  4. (大数据工程师学习路径)第一步 Linux 基础入门----数据流重定向

    介绍 开始对重定向这个概念感到些许陌生,但通过前面的课程中多次见过>或>>操作了,并知道他们分别是将标准输出导向一个文件或追加到一个文件中.这其实就是重定向,将原本输出到标准输出的数 ...

  5. malloc实现原理

    记得早一段时间,看到一本书上写过delete的一个..今天突然找啦一下资料: malloc()是C语言中动态存储管理 的一组标准库函数之中的一个.其作用是在内存的动态存储区中分配一个长度为size的连 ...

  6. Display Database Image using MS SQL Server 2008 Reporting Services

    原文 Display Database Image using MS SQL Server 2008 Reporting Services With the new release of MS SQL ...

  7. Weka初步

    从前年開始使用weka最数据挖掘方面的研究,到如今有一年半的时间了.看到我们同组的兄弟写了关于weka方面的总结.我也想整理一下.由于网上的资料实在是太少.记得刚接手的时候,真是硬着头皮看代码.只是到 ...

  8. 应用程序框架实战十三:DDD分层架构之我见(转)

    前面介绍了应用程序框架的一个重要组成部分——公共操作类,并提供了一个数据类型转换公共操作类作为示例进行演示.下面准备介绍应用程序框架的另一个重要组成部分,即体系架构支持.你不一定要使用DDD这样的架构 ...

  9. PS多形式的部分之间复制“笨办法”

    PS剪切页面,有时候你可能会遇到这样的情况:设计改进,但是,我们要具有相同的切片. 在此假设,可以直接用于切割片.我们可以节省大量的时间,又分为片. 但是,人们一般不会在你的上跨片设计PSD在变化,但 ...

  10. Nyoj 城市平乱(图论)

    描述 南将军统领着N个部队,这N个部队分别驻扎在N个不同的城市. 他在用这N个部队维护着M个城市的治安,这M个城市分别编号从1到M. 现在,小工军师告诉南将军,第K号城市发生了暴乱,南将军从各个部队都 ...