C#编程实践–帮老婆计算产假方案
摘要
今天中午午休时,和老婆聊天,老婆还过几天就要请产假了,她在网上问我让我帮她数一下该怎么请假最划算,老婆是个会过日子的人,面对此种要求我当然义不容辞,不过想到这个问题我的第一反应是:这个怎么可以用数的呢?于是,我开始去了解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#编程实践–帮老婆计算产假方案的更多相关文章
- C#编程实践–产假方案优化版
前言 既然作为一个踏踏实实学习技术的人,就要有一颗谦卑.虚心和追求卓越的心,我不能一次就写出很完美的代码,但我相信,踏踏实实一步一步的优化,代码就可以变得趋近完美,至少在某一个特定场景下相对完美,这和 ...
- Python入门经典. 以解决计算问题为导向的Python编程实践
Python入门经典. 以解决计算问题为导向的Python编程实践(高清版)PDF 百度网盘 链接:https://pan.baidu.com/s/1juLsew8UiOErRheQPOuTaw 提取 ...
- Storm实时计算:流操作入门编程实践
转自:http://shiyanjun.cn/archives/977.html Storm实时计算:流操作入门编程实践 Storm是一个分布式是实时计算系统,它设计了一种对流和计算的抽象,概念比 ...
- 编程实践中C语言的一些常见细节
对于C语言,不同的编译器采用了不同的实现,并且在不同平台上表现也不同.脱离具体环境探讨C的细节行为是没有意义的,以下是我所使用的环境,大部分内容都经过测试,且所有测试结果基于这个环境获得,为简化起见, ...
- 试读《JavaScript语言精髓与编程实践》
有幸看到iteye的活动,有幸读到<JavaScript语言精髓与编程实践_第2版>的试读版本,希望更有幸能完整的读到此书. 说来读这本书的冲动,来得很诡异,写一篇读后感,赢一本书,其实奖 ...
- HBase Coprocessor 剖析与编程实践(转载http://www.cnblogs.com/ventlam/archive/2012/10/30/2747024.html)
HBase Coprocessor 剖析与编程实践 1.起因(Why HBase Coprocessor) HBase作为列族数据库最经常被人诟病的特性包括:无法轻易建立“二级索引”,难以执行求和. ...
- Socket编程实践(10) --select的限制与poll的使用
select的限制 用select实现的并发服务器,能达到的并发数一般受两方面限制: 1)一个进程能打开的最大文件描述符限制.这可以通过调整内核参数.可以通过ulimit -n(number)来调整或 ...
- 03--(二)编程实践中C语言的一些常见细节
编程实践中C语言的一些常见细节(转载) 对于C语言,不同的编译器采用了不同的实现,并且在不同平台上表现也不同.脱离具体环境探讨C的细节行为是没有意义的,以下是我所使用的环境,大部分内容都经过测试,且所 ...
- <读书笔记>001-以解决问题为导向的python编程实践
以解决问题为导向的python编程实践 0.第0章:计算机科学 思考:计算机科学是否为计算机编程的简称? 编程的困难点:1.同时做2件事(编程语言的语法.语义+利用其解决问题) 2.什么是好程序(解 ...
随机推荐
- POJ 2762 Going from u to v or from v to u?(强连通分量+拓扑排序)
职务地址:id=2762">POJ 2762 先缩小点.进而推断网络拓扑结构是否每个号码1(排序我是想不出来这点的. .. ).由于假如有一层为2的话,那么从此之后这两个岔路的点就不可 ...
- Python基本语法[二],python入门到精通[四] (转)
写在前面 python你不去认识它,可能没什么,一旦你认识了它,你就会爱上它 回到顶部 v正文开始:Python基本语法 1.定义常量: 之所以上篇博客介绍了定义变量没有一起介绍定义常量,是因为Pyt ...
- PL/SQL程序中调用Java代码(转)
主要是学习PL/SQL调用JAVA的方法. 平台:WINDOWS 1.首先使用IDE写好需要调用的java代码,再添加"create or replace and compile java ...
- Android经常使用的布局类整理(一)
Android经常使用的布局类整理 近期又回头做了一下android的项目,发觉越来越不从心,非常多东西都忘了,简单的页面布局也非常多写不出来,首先还是先整理一下一些会混淆的概念先 layout_wi ...
- PHP从零单排(十八)图像处理
1.打开现有的图像 <?php header("Content-type:image/jpeg"); $img=imagecreatefromjpeg("cc.jp ...
- javascript活动
在javascript倘若有知识的三个方面.事件的第一,流程,其次,事件处理,第三,事件对象.下面就我个人的理解,,分别讲述一下这三个方面的内容. 第一.事件流 事件流指的是事件依照一定的顺序触发.它 ...
- JavaScript编写了一个计时器
初学JavaScript,用JavaScript编写了一个计时器. 设计思想: 1.借助于Date()对象,来不断获取时间点: 2.然后用两次时间点的毫秒数相减,算出时间差: 3.累加时间差,这样就能 ...
- EF中的EntityState几个状态的说明
之前使用EF,我们都是通过调用SaveChanges方法把增加/修改/删除的数据提交到数据库,但是上下文是如何知道实体对象是增加.修改还是删除呢?答案是通过EntityState枚举来判断的,我们看一 ...
- 你也可以玩转Skype -- 基于Skype API开发外壳程序入门
原文:你也可以玩转Skype -- 基于Skype API开发外壳程序入门 Skype是目前这个星球上最厉害的IM+VOIP软件,Skype现在已经改变了全球2.8亿人的生活方式.你,值得拥有! :) ...
- django csrf_token生成
django模板中生成csrf_token的不同方式 系统环境 CENTOS 6.4 python 2.7.6 django 1.7.1 当post提交表单的的时候,是需要 csrf_token的, ...