2010-10-15 9 views
6

Przeprosiny, jeśli wcześniej zostały zadane. Mam niektóre dane, które muszę przechowywać jako ciągi, niektóre z tych danych są datami. Dane zaczynają się od ciągów takich jak "01/02/10" (format uk). Teraz, później, te dane są analizowane i, w zależności od tego, co jest analizowane, wyniki są różne (na przykład 01-Feb-10 vs. 02-Jan-10). Biorąc pod uwagę, że dane zaczynają się jako łańcuchy, zanim je zapiszę, chciałbym powiedzieć, "jeśli to wygląda jak data, sformatuj ją jako dd-mmm-rr".Pisanie odpowiednika IsDate() w języku C#?

Problem polegający na tym, że wiele rzeczy wygląda jak data w funkcji DateTime.Parse().

Zastosowałem więc pewne reguły i akceptuję tylko "rozsądne" formaty dat dla moich kontroli i napisałem funkcję IsDate(). Szukam sugestii, jak to zrobić, ponieważ, podczas gdy działa, moje rozwiązanie wydaje się bardzo nieporęczne.

Cały powód, dla którego zrobiłem to zamiast przejść do zwykłej procedury DateTime.TryParse jest jasne, jeśli kiedykolwiek zacząłeś wyrzucać losowe ciągi znaków (jak "3/4" i "6.12").

Oto co mam do tej pory:

class Program 
{ 
    static void Main(string[] args) 
    { 
    Debug.Assert(IsDate(6.12) == false); 
    Debug.Assert(IsDate("3/4") == false); 
    Debug.Assert(IsDate(010210) == false); 
    Debug.Assert(IsDate("010210") == false); 
    Debug.Assert(IsDate("12-jan-2000") == true); 
    Debug.Assert(IsDate("12-12-20") == true); 
    Debug.Assert(IsDate("1/1/34") == true); 
    Debug.Assert(IsDate("09/30/20") == false); 
    Debug.Assert(IsDate(DateTime.Now) == true); 
    } 

    static Boolean IsDate(Object value) 
    { 
    DateTimeFormatInfo DateTimeFormatGB = new CultureInfo("en-GB").DateTimeFormat; // new CultureInfo("en-US").DateTimeFormat; 
    return IsDate(value, DateTimeFormatGB); 
    } 

    static private List<String> AcceptableDateFormats = new List<String>(72); 
    static Boolean IsDate(Object value, DateTimeFormatInfo formatInfo) 
    { 
    if (AcceptableDateFormats.Count == 0) 
    { 
     foreach (var dateFormat in new[] { "d", "dd" }) 
     { 
      foreach (var monthFormat in new[] { "M", "MM", "MMM" }) 
      { 
       foreach (var yearFormat in new[] { "yy", "yyyy" }) 
       { 
       foreach (var separator in new[] { "-", "/" }) // formatInfo.DateSeparator ? 
       { 
        String shortDateFormat; 
        shortDateFormat = dateFormat + separator + monthFormat + separator + yearFormat; 
        AcceptableDateFormats.Add(shortDateFormat); 
        AcceptableDateFormats.Add(shortDateFormat + " " + "HH:mm"); // formatInfo.TimeSeparator 
        AcceptableDateFormats.Add(shortDateFormat + " " + "HH:mm:ss"); 
       } 
       } 
      } 
     } 
    } 

    String sValue = value.ToString().Trim(); 
    DateTime unused; 

    foreach (String format in AcceptableDateFormats) 
    { 
     if (DateTime.TryParseExact(sValue, format, formatInfo, DateTimeStyles.None, out unused) == true) return true; 
    } 

    return false; 
    } 
} 

nie używać separatorów data/czas od informacji hodowli, bo chciałem przyjąć zarówno „/” i „-”. Sądzę, że mogłem wykorzystać ten czas, ponieważ to mało prawdopodobne, aby się zmieniło (dla mnie).

+0

Dlaczego nie korzystać z DateTime.TryParse, a następnie wykonać pewne badanie poprawności, na przykład na części roku zwrócony DateTime? –

Odpowiedz

1

w końcu poszedłem z wersją z następujących powodów:

static private List<String> AcceptableDateFormats = new List<String>(180); 
    static Boolean IsDate(Object value, DateTimeFormatInfo formatInfo) 
    { 
    if (AcceptableDateFormats.Count == 0) 
    { 
     foreach (var dateFormat in new[] { "d", "dd" }) 
     { 
      foreach (var monthFormat in new[] { "M", "MM", "MMM" }) 
      { 
       foreach (var yearFormat in new[] { "yy", "yyyy" }) 
       { 
       foreach (var separator in new[] { "-", "/", formatInfo.DateSeparator }) 
       { 
        String shortDateFormat; 
        shortDateFormat = dateFormat + separator + monthFormat + separator + yearFormat; 
        AcceptableDateFormats.Add(shortDateFormat); 
        AcceptableDateFormats.Add(shortDateFormat + " " + "HH:mm"); 
        AcceptableDateFormats.Add(shortDateFormat + " " + "HH:mm:ss"); 
        AcceptableDateFormats.Add(shortDateFormat + " " + "HH" + formatInfo.TimeSeparator + "mm"); 
        AcceptableDateFormats.Add(shortDateFormat + " " + "HH" + formatInfo.TimeSeparator + "mm" + formatInfo.TimeSeparator + "ss"); 
       } 
       } 
      } 
     } 
     AcceptableDateFormats = AcceptableDateFormats.Distinct().ToList(); 
    } 

    DateTime unused; 
    return DateTime.TryParseExact(value.ToString(), AcceptableDateFormats.ToArray(), formatInfo, DateTimeStyles.AllowWhiteSpaces, out unused); 
    } 
7

Czy sprawdziłeś alternatywne zastąpienie DateTime.TryParse(), w którym daje on o wiele większą kontrolę nad tym, co uważa za datę?

+0

To nie daje ci dodatkowej kontroli. –

1

Czy sprawdziłeś, próbując przeciążać DateTime.TryParse, które akceptuje argumenty IFormatProvider i DateTimeStyles? Być może będziesz w stanie użyć tego, by być bardziej wybrednym w stosunku do tego, co akceptujesz jako rzeczywistą datę, bez niepotrzebnego wyrzucania wyjątku tylko po to, aby przetestować ciągi.

+0

Tak, mam. Nie mogę znaleźć sposobu, aby uniemożliwić mu myślenie, że "3/4" lub "6.12" nie jest datą (i nie rzucam wyjątków). –

6

Rozważ użycie DateTime.TryParseExact

+0

Myślałem, że tak. Problem polegał na tym, że nie kontroluję formatu danych, ale jest to uzasadnione oczekiwanie, że jest to data w formacie brytyjskim, która może, ale nie musi, mieć część czasu. –

6

Aby przekonwertować ciąg na datę albo podać kultury korzystające z tego konkretnego formatu: jak chcemy przekonwertować datę ciąg „dd/mm/rrrr” Do tej pory ..

datetime mydate = Convert.ToDateTime(
txtdate.Text, CultureInfo.GetCulture("en-GB") 
); 

lub użyć metody ParseExact:

datetime mydate = DateTime.ParseExact(
txtdate.Text, "dd/MM/yyyy", CultureInfo.Invariant 
); 

Sposób ParseExact akceptuje tylko tej formy, podczas gdy Convert.ToDateTim Metoda nadal pozwala na pewne odmiany formatu, a także akceptuje inne formaty dat.

Aby złapać nielegalne wejście, można użyć metody TryParseExact:

DateTime d; 
if (DateTime.TryParseExact(txtdate.Text, "dd/MM/yyyy", CultureInfo.Invariant, DateTimeStyles.None, out d)) { 
datetime mydate = d; 
} else { 
// communcate the failure to the user 
} 

mam nadzieję poniżej linków zapewni Ci pomoc:

http://dotnetacademy.blogspot.com/2010/09/convert-string-to-date.html

http://msdn.microsoft.com/en-us/library/system.datetime.tryparse.aspx

http://msdn.microsoft.com/en-us/library/9h21f14e.aspx

http://dotnetacademy.blogspot.com/2009/10/get-current-system-date-format.html

Jest to przykład dla tryParse: http://dotnetperls.com/datetime-tryparse

+0

Pachnie jak spam. Możesz promować własne rzeczy, ale dołącz prawdziwą treść, a nie tylko link do własnego bloga. –

+0

@Joel: Wysłałem to na moim blogu wiele miesięcy wstecz .. Mam również linki MSDN ... więc nie możesz tak powiedzieć –

+0

@Joel: Przepraszam, nie wiedziałem, że aby podać link do własnego bloga dla rozwiązanie nie jest akceptowane na stackoverflow. –

0

Ewentualnie można mieć własne wyrażenie regularne sprawdzanie po znalazłeś potencjalną datę jeśli wymagają większego stopnia kontroli. coś takiego.

^(0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])[- /.](19|20)\d\d$ 

obejmuje xx-yy-zz a xx/yy/zz jak na swoje wymagania

+0

Problem polega na tym, że mój prosty przykład sprawdza około 70 prawidłowych formatów daty. Przy założeniu, że regex może przechwycić więcej niż jeden format, ale wciąż masz dużo ciągów regex. Dodatkowo, dla większości ludzi wyglądają jak szum linii. : o) –

+0

Wygląda na to, że w tym przypadku nie ma prostej odpowiedzi. Twoje poszukiwanie większego stopnia kontroli, ale łatwość użycia. To jest bardziej decyzja dotycząca projektu tutaj. Należy rozważyć ograniczenie liczby akceptowanych formatów lub połączyć kilka prostych sprawdzeń Regex dla takich elementów jak "3/4" lub "6.12 "i po użyciu DateTime.TryParse, gdy ciąg minął wyrażenia regex – Terrance

+0

Aha i z regex, komentarze mogą przejść długą drogę – Terrance

1

Spróbuj

DateTime result; 
DateTime.TryParseExact(value.ToString(), new string[] { "dd/MM/yyyy", "d/M/yyyy" }, null, DateTimeStyles.None, out result) 
+0

Cześć, Dzięki. Nie zauważyłem, że mogę przekazać tablicę akceptowalnych formatów –

0
using System.Globalization; 

CultureInfo ukCI = CultureInfo.CreateSpecificCulture("en-GB"); 
Console.WriteLine(DateTime.Parse("1/2/2010", ukCI).ToString("dd-MMM-yyyy")); 

Można użyć TryParse w miejsce Parse, jeśli chcesz aby sprawdzić, czy argument jest datą.

+0

, ale czy spojrzałeś na przykładowy kod? –

1

Jest to oczywiście hack, ale to, co skończyło się robi było dodać VisualBasic Reference i po prostu korzystać z funkcji w C# IsDate:

using Microsoft.VisualBasic; 
//...other code... 
if (Information.IsDate(YourDateObject)) { 
    //...more code... 
} 
+1

Nice - i odwoływanie się do VB jest bardziej problemem kulturowym niż problem z dystrybucją/zależnościami. –