通缉:DateTime.TryNew(年,月,日)或DateTime.IsValidDate(年,月,日) [英] Wanted: DateTime.TryNew(year, month, day) or DateTime.IsValidDate(year, month, day)

查看:145
本文介绍了通缉:DateTime.TryNew(年,月,日)或DateTime.IsValidDate(年,月,日)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

标题基本上都是这样说的。我得到三个用户提供的整数( day 1 从遗留数据库(我不能改变)。目前,我使用以下代码将这些整数解析成一个 DateTime 结构:

  try {
return new DateTime(year,month,day);
} catch(ArgumentException ex){
return DateTime.MinValue;
}

有时,值不表示有效的日期(是的,用户输入像1999-06-31那样的东西,不,遗留的应用程序没有验证这一点)。由于在数据验证失败时抛出异常被认为是坏的练习,我更愿意将其替换为无异常代码。然而,我可以找到的唯一的解决方案是将整数转换成一个字符串,而 TryParseExact 这个字符串,这对我来说似乎更加丑陋。我想念一些明显的更好的解决方案吗?






1 实际上,它是格式的一个整数YYYYMMDD,但将其转换为年,月和日是微不足道的...

解决方案

没有静态函数 IsValidDate()所以你必须自己写,首先天真的执行可能是:

  public static bool IsValidDate(int year,int month,int day)
{
if(year< DateTime.MinValue.Year || year> DateTime.MaxValue.Year)
返回假

if(month< 1 || month> 12)
return false;

返回日> 0&&天< = DateTime.DaysInMonth(年,月);
}

我说这是一个天真的实现,因为(除了参数范围)查看日期是否存在是闰年。在实践中,如果您正在使用非格里高利日历(即使在公历中也是错过了日期,用于将日期从朱利安日历调整为一致),这可能会因为日历问题而失败。



使用日历



非公历日历可能会损坏这些假设:




  • 1月1日01是最小的有效日期。这不是真的。不同的日历具有不同的最小日期。这个限制只是 DateTime 技术限制,但可能有不同的最小(和最大)日期的日历(或日历内的时代)。

  • 一年中的月数少于或等于12.这是不正确的,在某些日历上限是13,每年都不一样。

  • 如果日期有效(根据所有其他规则),那么它是有效的日期。这不是真的,一个日历可能有不止一个时代,并不是所有的日期都是有效的(甚至可能在时代的范围内)。



管理这个的规则很复杂,很容易忘记一些事情,所以在这种情况下,捕捉一个例外可能不是一个坏主意。以前的验证功能的更好的版本可能只是提供基本的验证,并依靠 DateTime 来检查其他规则:

  public static DateTime? TryNew(int year,
int month,
int day,
日历日历)
{
if(calendar == null)
calendar = new GregorianCalendar ();

if(year< calendar.MinSupportedDateTime.Year)
return null;

if(year> calendar.MaxSupportedDateTime.Year)
return null;

//注意,即使使用这个支票,我们也不能断言这是一个有效的
//月,因为一年可能是共享两个时间,而且
/ /我们假设现在的时代。
if(month< 1 || month> calendar.GetMonthsInYear(year))
return null;

if(day< = 0 || day> DateTime.DaysInMonth(year,month))
return null;

//现在,可能,日期是有效的,但由于日历的更改,仍然可能会出现关于时代和缺少日期的问题
//。
//对于所有这些检查,我们依赖于DateTime实现。
尝试
{
返回新的DateTime(年,月,日,日历);
}
catch(ArgumentOutOfRangeException)
{
return null;
}
}

然后,给定这个新功能,你的原始代码应该是:

 返回TryNew(年,月,日)? DateTime.MinValue; 


The title basically says it all. I'm getting three user-supplied integers (year, month, day)1 from a legacy database (which I cannot change). Currently, I use the following code to parse those integers into a DateTime structure:

try {
    return new DateTime(year, month, day);
} catch (ArgumentException ex) {
    return DateTime.MinValue;
}

Sometimes, the values don't represent a valid date (yes, users enter stuff like 1999-06-31, and no, the legacy app did not verify this). Since throwing an exception when data validation fails is considered bad practice, I'd prefer to replace this with exception-less code. However, the only solution I could find was to convert the integers into one string and TryParseExact this string, which seems even uglier to me. Did I miss some obvious better solution?


1 Actually, it's one integer in the format YYYYMMDD, but converting that to year, month and day is trivial...

解决方案

There is not a static function IsValidDate() so you have to write it by yourself, first naive implementation may be:

public static bool IsValidDate(int year, int month, int day)
{
    if (year < DateTime.MinValue.Year || year > DateTime.MaxValue.Year)
        return false;

    if (month < 1 || month > 12)
        return false;

    return day > 0 && day <= DateTime.DaysInMonth(year, month);
}

I said this is a naive implementation because (besides arguments range) the only check to see if a date exists is for leap year. In practice this may fail because of calendar issues if you're working with non Gregorian calendars (and missing days even in Gregorian calendar that has been used to align date from Julian calendar).

Working With Calendars

These assumptions may be broken for non Gregorian calendars:

  • 1 January 01 is smallest valid date. It's not true. Different calendars have a different smallest date. This limit is just DateTime technical limit but there may be a calendar (or an Era within a calendar) with a different minimum (and maximum) date.
  • Number of months in one year is less or equal than 12. It's not true, in some calendars upper bound is 13 and it's not always the same for every year.
  • If a date is valid (according all other rules) then it's a valid date. It's not true, a calendar may have more than one era and not all dates are valid (possibly even within era date range).

Rules to manage this are pretty complex and it's too easy to forget something so, in this case, catching an exception may not be such bad idea. A better version of previous validation function may just provide basic validation and relying on DateTime to check other rules:

public static DateTime? TryNew(int year,
                               int month,
                               int day,
                               Calendar calendar)
{
    if (calendar == null)
        calendar = new GregorianCalendar();

    if (year < calendar.MinSupportedDateTime.Year)
        return null;

    if (year > calendar.MaxSupportedDateTime.Year)
        return null;

    // Note that even with this check we can't assert this is a valid
    // month because one year may be "shared" for two eras moreover here
    // we're assuming current era.
    if (month < 1 || month > calendar.GetMonthsInYear(year))
        return null;

    if (day <= 0 || day > DateTime.DaysInMonth(year, month))
        return null;

    // Now, probably, date is valid but there may still be issues
    // about era and missing days because of calendar changes.
    // For all this checks we rely on DateTime implementation.        
    try
    {
        return new DateTime(year, month, day, calendar);
    }
    catch (ArgumentOutOfRangeException)
    {
        return null;
    }
}

Then, given this new function, your original code should be:

return TryNew(year, month, day) ?? DateTime.MinValue;

这篇关于通缉:DateTime.TryNew(年,月,日)或DateTime.IsValidDate(年,月,日)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆