aliyousefian
aliyousefian

Reputation: 449

Convert string Persian date to Gregorian DateTime

To convert a string Persian date to gregorian DateTime, I use date time picker and it sends me a string like "۱۳۹۴/۰۲/۲۰"

PersianCalendar p = new PersianCalendar();               
string[] d = start.Split('/');           
DateTime dt = new DateTime(int.Parse(d[0]), 
                           int.Parse(d[1]),
                           int.Parse(d[2]),
                           new HijriCalendar());

and my function that converts is

public static DateTime ToGregorianDate(this DateTime dt)
{
     PersianCalendar pc = new PersianCalendar();
     return pc.ToDateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, 0, 0);    
}

It gives DateTime how can I send correct DateTime when it wants to convert shows this error:

Input string was not in a correct format.

Upvotes: 8

Views: 8944

Answers (4)

xanatos
xanatos

Reputation: 111860

You have two different problems:

  • Arabic digits aren't supported by DateTime parsing

  • PersianCalendar isn't part of any CultureInfo, so you can't use it directly while parsing the string to DateTime (and you can't set it to a preexisting CultureInfo).

Possible solution:

string date = "۱۳۹۴/۰۲/۲۰";
string date2 = Regex.Replace(date, "[۰-۹]", x => ((char)(x.Value[0] - '۰' + '0')).ToString());

Replace the digits from Arabic to decimal

DateTime dt = DateTime.ParseExact(date2, "yyyy/MM/dd", CultureInfo.InvariantCulture);

Then parse the date ignoring the calendar

PersianCalendar pc = new PersianCalendar();
DateTime dt2 = pc.ToDateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second, dt.Millisecond);

Then convert the date to the right calendar.

This solution is broken because Persian Calendar months have a different number of days than Gregorian months

Upvotes: 7

Dmitrii Bychenko
Dmitrii Bychenko

Reputation: 186688

You have to convert Persian digits into int *manually, e.g.

    private static int ParsePersianNumber(String value) {
      int result = 0;

      // Persian digits are Unicode characters 0x06F0-0x06F9 
      foreach (var ch in value)
        result = result * 10 + ch - 0x06F0;

      return result;
    }

    ...

    String start = "۱۳۹۴/۰۲/۲۰";

    PersianCalendar p = new PersianCalendar();
    string[] d = start.Split('/');

    // int.Parse -> ParsePersianNumber
    // dt == 14 Mar 1974
    DateTime dt = new DateTime(
      ParsePersianNumber(d[0]), 
      ParsePersianNumber(d[1]), 
      ParsePersianNumber(d[2]), 
      new HijriCalendar());

Upvotes: 3

Soner Gönül
Soner Gönül

Reputation: 98750

As I commented; Eastern Arabic numerals does not supported by DateTime parsing methods, it only accepts Arabic numerals.

However, char type has a GetNumericValue method which converts any numeric Unicode character to a double.

Let's use a combination of char.GetNumericValue, string.Join and Int32.Parse methods;

string d = "۱۳۹۴/۰۲/۲۰";
int year = Int32.Parse(string.Join("", 
    d.Split('/')[0].Select(c => char.GetNumericValue(c)))); // 1394
int month = Int32.Parse(string.Join("", 
    d.Split('/')[1].Select(c => char.GetNumericValue(c)))); // 2
int day = Int32.Parse(string.Join("", 
    d.Split('/')[2].Select(c => char.GetNumericValue(c)))); // 20

And then you can create a DateTime based on this values;

DateTime dt = new DateTime(year, month, day);

Then you can use;

PersianCalendar pc = new PersianCalendar();
var dt1 = pc.ToDateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second, dt.Millisecond);
// {10.05.2015 00:00:00}

Upvotes: 6

Kjartan
Kjartan

Reputation: 19111

Map to Arabic numerals first:

var mapToArabic = 
  new Dictionary<char, char>{ 
    {'۰', '0'}, {'۱', '1'}, {'۲', '2'}, 
    {'۳', '3'}, {'۴', '4'}, {'۵', '5'}, 
    {'۶', '6'}, {'۷', '7'}, {'۸', '8'}, {'۹', '9'}};

var p = new System.Globalization.PersianCalendar(); 
var start = "۱۳۹۴/۰۲/۲۰";
string[] persian = start.Split('/');    

The main change from your original code: Mapping from an array of Persian numbers (year, month, date), to an equivalent array containing the values in Arabic numerals:

var arabic = persian
                .Select(persianWord => 
                    new string(persianWord
                                .Select(persianChar => mapToArabic[persianChar])
                                .ToArray()))
                .ToList();

Now you can use that as you used the other one before:

DateTime dt = new DateTime(int.Parse(arabic[0]),
                           int.Parse(arabic[1]),
                           int.Parse(arabic[2]),
                           new System.Globalization.HijriCalendar());

var pc = new System.Globalization.PersianCalendar();
var result = pc.ToDateTime(dt.Year, dt.Month, dt.Day, 
                            dt.Hour, dt.Minute, 0, 0); 

Upvotes: 2

Related Questions