سؤال

أجد أنه من الغريب أن الطريقة الأكثر وضوحا لخلق Date الكائنات في جافا تم إهمالها ، ويبدو أن "استبدال" مع ليست واضحة جدا استخدام تساهلا التقويم.

كيف يمكن التحقق من أن موعد إعطاء مزيج من اليوم ، الشهر ، السنة ، صالح التاريخ ؟

فعلى سبيل المثال ، 2008-02-31 (كما في yyyy-mm-dd) سيكون تاريخ غير صالح.

نصائح أخرى

مفتاح df.setLenient(كاذبة) ؛ .هذا هو أكثر من كاف بالنسبة الحالات البسيطة.إذا كنت تبحث عن أكثر قوة (أشك) و/أو بديلة المكتبات مثل joda الوقت ثم ننظر في الإجابة من قبل المستخدم "tardate"

final static String DATE_FORMAT = "dd-MM-yyyy";

public static boolean isDateValid(String date) 
{
        try {
            DateFormat df = new SimpleDateFormat(DATE_FORMAT);
            df.setLenient(false);
            df.parse(date);
            return true;
        } catch (ParseException e) {
            return false;
        }
}

كما أظهرت @Maglob, النهج الأساسي هو اختبار التحويل من السلسلة حتى الآن باستخدام SimpleDateFormat.تحليل.من شأنها أن قبض صالح اليوم/الشهر تركيبات مثل 2008-02-31.

ولكن في الواقع نادرا ما يكفي منذ SimpleDateFormat.تحليل جدا الليبرالية.هناك نوعان من السلوكيات قد تكون قلقة مع:

أحرف غير صالحة في تاريخ السلسلة من المستغرب ، 2008-02-2x سوف "تمرير" صالح موعد مع لغة تنسيق = "yyyy-MM-dd" على سبيل المثال.حتى عندما isLenient==كاذبة.

سنوات:2 ، 3 أو 4 أرقام ؟ قد تحتاج أيضا إلى فرض 4 أرقام سنوات بدلا من السماح الافتراضي SimpleDateFormat السلوك (الذي سيتم تفسير "12-02-31" بشكل مختلف اعتمادا على ما إذا كان شكل "yyyy-MM-dd" أو "yy-MM-dd")

صارمة الحل مع المكتبة القياسية

لذا كاملة سلسلة تاريخ الاختبار يمكن أن تبدو مثل هذا:مزيج من regex المباراة ثم القسري تاريخ التحويل.خدعة مع regex هو أن تجعل لغة سهلة الاستخدام.

  Date parseDate(String maybeDate, String format, boolean lenient) {
    Date date = null;

    // test date string matches format structure using regex
    // - weed out illegal characters and enforce 4-digit year
    // - create the regex based on the local format string
    String reFormat = Pattern.compile("d+|M+").matcher(Matcher.quoteReplacement(format)).replaceAll("\\\\d{1,2}");
    reFormat = Pattern.compile("y+").matcher(reFormat).replaceAll("\\\\d{4}");
    if ( Pattern.compile(reFormat).matcher(maybeDate).matches() ) {

      // date string matches format structure, 
      // - now test it can be converted to a valid date
      SimpleDateFormat sdf = (SimpleDateFormat)DateFormat.getDateInstance();
      sdf.applyPattern(format);
      sdf.setLenient(lenient);
      try { date = sdf.parse(maybeDate); } catch (ParseException e) { }
    } 
    return date;
  } 

  // used like this:
  Date date = parseDate( "21/5/2009", "d/M/yyyy", false);

علما بأن regex يفترض شكل سلسلة يحتوي فقط في اليوم ، الشهر ، السنة ، فاصل الشخصيات.وبصرف النظر عن ذلك ، شكل يمكن أن يكون في أي لغة الشكل:"د/MM/yy", "yyyy-MM-dd" ، وهلم جرا.شكل سلسلة الإعدادات المحلية الحالية يمكن الحصول على مثل هذا:

Locale locale = Locale.getDefault();
SimpleDateFormat sdf = (SimpleDateFormat)DateFormat.getDateInstance(DateFormat.SHORT, locale );
String format = sdf.toPattern();

Joda الوقت بديل أفضل ؟

لقد كنت أسمع عن joda الوقت مؤخرا فكرت مقارنة.نقطتين:

  1. يبدو أفضل في كونها صارمة حول أحرف غير صالحة في تاريخ السلسلة ، على عكس SimpleDateFormat
  2. لا أرى وسيلة لفرض 4 أرقام سنوات مع ذلك حتى الآن (ولكن أعتقد أنه يمكنك إنشاء الخاصة بك DateTimeFormatter لهذا الغرض)

انها بسيطة جدا للاستخدام:

import org.joda.time.format.*;
import org.joda.time.DateTime;

org.joda.time.DateTime parseDate(String maybeDate, String format) {
  org.joda.time.DateTime date = null;
  try {
    DateTimeFormatter fmt = DateTimeFormat.forPattern(format);
    date =  fmt.parseDateTime(maybeDate);
  } catch (Exception e) { }
  return date;
}

ويمكنك استخدام SimpleDateFormat

وعلى سبيل المثال شيئا مثل:

boolean isLegalDate(String s) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    sdf.setLenient(false);
    return sdf.parse(s, new ParsePosition(0)) != null;
}

tl;dr

استخدام وضع الصارم على java.time.DateTimeFormatter إلى تحليل LocalDate.الفخ DateTimeParseException.

LocalDate.parse(                   // Represent a date-only value, without time-of-day and without time zone.
    "31/02/2000" ,                 // Input string.
    DateTimeFormatter              // Define a formatting pattern to match your input string.
    .ofPattern ( "dd/MM/uuuu" )
    .withResolverStyle ( ResolverStyle.STRICT )  // Specify leniency in tolerating questionable inputs.
)

بعد تحليل ، قد تحقق قيمة معقولة.على سبيل المثال تاريخ الميلاد خلال آخر مائة سنة.

birthDate.isAfter( LocalDate.now().minusYears( 100 ) )

تجنب إرث التاريخ-الوقت الطبقات

تجنب استخدام مزعجة القديم التاريخ-الوقت الطبقات التي يتم شحنها مع الإصدارات الأقدم من جافا.الآن قبل محل java.الوقت فصول.

LocalDate & DateTimeFormatter & ResolverStyle

على LocalDate تمثل الطبقة تاريخ فقط قيمة دون وقت من اليوم وبدون المنطقة الزمنية.

String input = "31/02/2000";
DateTimeFormatter f = DateTimeFormatter.ofPattern ( "dd/MM/uuuu" );
try {
    LocalDate ld = LocalDate.parse ( input , f );
    System.out.println ( "ld: " + ld );
} catch ( DateTimeParseException e ) {
    System.out.println ( "ERROR: " + e );
}

على java.time.DateTimeFormatter فئة يمكن تعيين تحليل السلاسل مع أي من ثلاثة التساهل وسائط المعرفة في ResolverStyle enum.نحن إدراج سطر في التعليمات البرمجية أعلاه محاولة كل من وسائط.

f = f.withResolverStyle ( ResolverStyle.LENIENT );

النتائج:

  • ResolverStyle.LENIENT
    ld:2000-03-02
  • ResolverStyle.SMART
    ld:2000-02-29
  • ResolverStyle.STRICT
    خطأ:java.الوقت.تنسيق.DateTimeParseException:نص '31/02/2000' لا يمكن تحليل:تاريخ غير صالح 'شباط / فبراير 31'

يمكننا أن نرى أن في ResolverStyle.LENIENT وضع صالح تاريخ قدما أي ما يعادل عدد أيام.في ResolverStyle.SMART وضع (الافتراضي) ، قرار منطقي للحفاظ تاريخ في الشهر و تسير مع آخر يوم من الشهر ، Feb 29 في السنة الكبيسة ، كما ليس هناك 31 يوما في الشهر.على ResolverStyle.STRICT وضع يطرح استثناء يشكون أنه لا يوجد مثل هذا التاريخ.

كل ثلاثة من هذه معقولة اعتمادا على مشكلة الأعمال والسياسات.يبدو في حال كنت تريد وضع الصارم برفض تاريخ غير صالح بدلا من تعديله.


عن java.الوقت

على java.الوقت إطار بنيت في جاوة 8 وما بعدها.هذه الفئات محل مزعجة القديمة تراث التاريخ الوقت الطبقات مثل java.util.Date, Calendar, & SimpleDateFormat.

على Joda الوقت المشروع الآن في وضع الصيانة, تنصح الهجرة إلى java.الوقت فصول.

لمعرفة المزيد ، راجع أوراكل التعليمي.والبحث تجاوز سعة مكدس على العديد من الأمثلة و الشروح.مواصفات JSR 310.

قد تبادل java.الوقت الكائنات مباشرة مع قاعدة البيانات الخاصة بك.استخدام JDBC driver متوافقة مع JDBC 4.2 أو في وقت لاحق.لا حاجة سلاسل حاجة java.sql.* فصول.

أين يمكن الحصول على جافا.الوقت الطبقات ؟

  • جافا SE 8, جافا SE 9, و في وقت لاحق
    • المدمج في.
    • جزء من معيار API جافا مع المجمعة التنفيذ.
    • جافا 9 يضيف بعض الميزات طفيفة وإصلاحات.
  • Java SE 6 و Java SE 7
    • الكثير من جافا.الوظائف في الوقت عاد استدار إلى جافا 6 & 7 في ThreeTen-Backport.
  • الروبوت
    • الإصدارات الأحدث من الروبوت حزمة تطبيقات جافا.الوقت الطبقات.
    • السابقة الروبوت (<26) ، ThreeTenABP المشروع يتكيف ThreeTen-Backport (المذكورة أعلاه).انظر كيفية استخدام ThreeTenABP....

على ThreeTen إضافية المشروع يمتد جافا.الوقت مع فصول إضافية.هذا المشروع هو إثبات أن من الممكن في المستقبل الإضافات جافا.الوقت.قد تجد بعض المعلومات المفيدة في هذه الطبقات هنا مثل Interval, YearWeek, YearQuarter, ، المزيد.

تحليل java.time

وAPI الوقت (و<لأ href = "HTTP: / /docs.oracle.com/javase/8/docs/api/java/time/package-summary.html "يختلط =" noreferrer "> الطبقات java.time ) التي بنيت في جاوة 8 و في وقت لاحق، يمكنك استخدام و LocalDate الصف.

public static boolean isDateValid(int year, int month, int day) {
    boolean dateIsValid = true;
    try {
        LocalDate.of(year, month, day);
    } catch (DateTimeException e) {
        dateIsValid = false;
    }
    return dateIsValid;
}

وهناك حل بديل الصارم باستخدام المكتبة القياسية هو القيام بما يلي:

1) إنشاء SimpleDateFormat الصارم باستخدام النمط الخاص بك

2) محاولة تحليل المستخدم القيمة التي تم إدخالها باستخدام الكائن شكل

و3) وإذا ما نجحت، إعادة التاريخ الناتجة عن (2) باستخدام تنسيق التاريخ نفسه (من (1))

و4) قارن تاريخ إخراجها مقابل القيمة الأصلية، التي أدخلها المستخدم. اذا كانا لا يزالان على قدم المساواة ثم قيمة دخل مباريات بدقة النمط الخاص بك.

وبهذه الطريقة، لا تحتاج إلى إنشاء تعبيرات العادية معقدة - في حالتي أنا في حاجة إلى دعم كل من جملة نمط SimpleDateFormat، بدلا من أن يقتصر على أنواع معينة مثل أيام فقط والشهور والسنوات

.

وبناء على إجابة لPangea لعلاج هذه المشكلة أشار <وأ href = "HTTPS: // ستاكوفيرفلوو وأضاف كوم / الأسئلة / 4528047 / فحص الحركة وصحة من واحد في تاريخ / 30528952 # comment35086192_4528094 "> @ ceklock لي طريقة للتحقق من أن dateString لا يحتوي على أي حرف غير صالح.

وهنا هو كيف أفعل:

private boolean isDateCorrect(String dateString) {
    try {
        Date date = mDateFormatter.parse(dateString);
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return matchesOurDatePattern(dateString);    //added my method
    }
    catch (ParseException e) {
        return false;
    }
}

/**
 * This will check if the provided string matches our date format
 * @param dateString
 * @return true if the passed string matches format 2014-1-15 (YYYY-MM-dd)
 */
private boolean matchesDatePattern(String dateString) {
    return dateString.matches("^\\d+\\-\\d+\\-\\d+");
}

وأنا أقترح عليك استخدام الطبقة org.apache.commons.validator.GenericValidator من اباتشي.

وGenericValidator.isDate(String value, String datePattern, boolean strict);

ملحوظة: صارمة - أم لا أن يكون هناك تطابق تام من datePattern

وأعتقد أن simpliest لهو فقط لتحويل سلسلة إلى كائن تاريخ وتحويله مرة أخرى إلى سلسلة. سلسلة تاريخ معين على ما يرام إذا كان كل من السلاسل لا يزال المباراة.

public boolean isDateValid(String dateString, String pattern)
{   
    try
    {
        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        if (sdf.format(sdf.parse(dateString)).equals(dateString))
            return true;
    }
    catch (ParseException pe) {}

    return false;
}

وإذا افترضنا أن كلا من تلك هي سلاسل (وإلا يكون لديهم بالفعل تواريخ سارية المفعول)، وهنا طريقة واحدة:

package cruft;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateValidator
{
    private static final DateFormat DEFAULT_FORMATTER;

    static
    {
        DEFAULT_FORMATTER = new SimpleDateFormat("dd-MM-yyyy");
        DEFAULT_FORMATTER.setLenient(false);
    }

    public static void main(String[] args)
    {
        for (String dateString : args)
        {
            try
            {
                System.out.println("arg: " + dateString + " date: " + convertDateString(dateString));
            }
            catch (ParseException e)
            {
                System.out.println("could not parse " + dateString);
            }
        }
    }

    public static Date convertDateString(String dateString) throws ParseException
    {
        return DEFAULT_FORMATTER.parse(dateString);
    }
}

وهنا إخراج أحصل على:

java cruft.DateValidator 32-11-2010 31-02-2010 04-01-2011
could not parse 32-11-2010
could not parse 31-02-2010
arg: 04-01-2011 date: Tue Jan 04 00:00:00 EST 2011

Process finished with exit code 0

وكما ترون، فإنه التعامل مع كل القضايا الخاصة بك بشكل جيد.

وهذا يعمل كبيرة بالنسبة لي. النهج المقترح أعلاه بن.

private static boolean isDateValid(String s) {
    SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
    try {
        Date d = asDate(s);
        if (sdf.format(d).equals(s)) {
            return true;
        } else {
            return false;
        }
    } catch (ParseException e) {
        return false;
    }
}

واثنين من التعليقات على استخدام SimpleDateFormat.

<اقتباس فقرة>   

ويجب أن يعلن عنها مثيل ثابت   إذا أعلن وصول ثابت يجب أن تكون متزامنة كما أنها ليست ذات ألوان

وIME هي أحسن أن instantiating مثيل لكل تحليل للتاريخ.

وفوق طرق تاريخ التوزيع لطيفة، أنا فقط واضاف شيك جديد في الأساليب القائمة التي تحقق ضعف تاريخ محولة مع التاريخ الأصلي باستخدام formater، لذلك يعمل على كل حالة تقريبا وأنا التحقق منها. مثلا 2013/2/29 هو تاريخ غير صالح. ظيفة معينة تحليل التاريخ وفقا لتنسيقات التاريخ مقبولة الحالية. فإنها ترجع صحيح إذا لم يتم تحليل تاريخ بنجاح.

 public final boolean validateDateFormat(final String date) {
        String[] formatStrings = {"MM/dd/yyyy"};
        boolean isInvalidFormat = false;
        Date dateObj;
        for (String formatString : formatStrings) {
            try {
                SimpleDateFormat sdf = (SimpleDateFormat) DateFormat.getDateInstance();
                sdf.applyPattern(formatString);
                sdf.setLenient(false);
                dateObj = sdf.parse(date);
                System.out.println(dateObj);
                if (date.equals(sdf.format(dateObj))) {
                    isInvalidFormat = false;
                    break;
                }
            } catch (ParseException e) {
                isInvalidFormat = true;
            }
        }
        return isInvalidFormat;
    }

إليك ما فعلته للبيئة عقدة باستخدام أي المكتبات الخارجية:

Date.prototype.yyyymmdd = function() {
   var yyyy = this.getFullYear().toString();
   var mm = (this.getMonth()+1).toString(); // getMonth() is zero-based
   var dd  = this.getDate().toString();
   return zeroPad([yyyy, mm, dd].join('-'));  
};

function zeroPad(date_string) {
   var dt = date_string.split('-');
   return dt[0] + '-' + (dt[1][1]?dt[1]:"0"+dt[1][0]) + '-' + (dt[2][1]?dt[2]:"0"+dt[2][0]);
}

function isDateCorrect(in_string) {
   if (!matchesDatePattern) return false;
   in_string = zeroPad(in_string);
   try {
      var idate = new Date(in_string);
      var out_string = idate.yyyymmdd();
      return in_string == out_string;
   } catch(err) {
      return false;
   }

   function matchesDatePattern(date_string) {
      var dateFormat = /[0-9]+-[0-9]+-[0-9]+/;
      return dateFormat.test(date_string); 
   }
}

وهنا هو كيفية استخدامه:

isDateCorrect('2014-02-23')
true
// to return valid days of month, according to month and year
int returnDaysofMonth(int month, int year) {
    int daysInMonth;
    boolean leapYear;
    leapYear = checkLeap(year);
    if (month == 4 || month == 6 || month == 9 || month == 11)
        daysInMonth = 30;
    else if (month == 2)
        daysInMonth = (leapYear) ? 29 : 28;
    else
        daysInMonth = 31;
    return daysInMonth;
}

// to check a year is leap or not
private boolean checkLeap(int year) {
    Calendar cal = Calendar.getInstance();
    cal.set(Calendar.YEAR, year);
    return cal.getActualMaximum(Calendar.DAY_OF_YEAR) > 365;
}

وهنا أود أن تحقق تنسيق التاريخ:

 public static boolean checkFormat(String dateTimeString) {
    return dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}") || dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}")
            || dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}") || dateTimeString
            .matches("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z") ||
            dateTimeString.matches("^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}:\\d{2}Z");
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top