Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Languages / C# / June 2007

Tip: Looking for answers? Try searching our database.

Formatting dates with ordinal numbers

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
David Jackson - 28 Jun 2007 14:40 GMT
Hello,

Is there anything in the framework which will format a date to show the
ordinal representation of the day value e.g.

28th June 2007
1st August 2007

instead of

28 June 2007
01 August 2007

I realise that it would be simple enough to write a function for this, but I
wondered if there was anything built-in so as not to reinvent the wheel.

Thanks,

DJ
Ignacio Machin ( .NET/ C# MVP ) - 28 Jun 2007 15:33 GMT
Hi,

> Hello,
>
[quoted text clipped - 8 lines]
> 28 June 2007
> 01 August 2007

I dont know for sure, but look into DateTime.ToString() and more
especifically DateTimeFormatInfo provider.
David Jackson - 28 Jun 2007 17:07 GMT
Hi Ignacio,

Thanks for the reply.

> I dont know for sure, but look into DateTime.ToString() and more
> especifically DateTimeFormatInfo provider.

Those were the first two places I looked but couldn't find anything.

DJ
Ignacio Machin ( .NET/ C# MVP ) - 28 Jun 2007 18:41 GMT
Hi,

> Hi Ignacio,
>
[quoted text clipped - 4 lines]
>
> Those were the first two places I looked but couldn't find anything.

If not there I doubht you will find it somewhere else :(

Implement it and then share it with the community :)
David Jackson - 28 Jun 2007 22:20 GMT
> Implement it and then share it with the community :)

That seems fair :-)

OK, now this is my first attempt at showing any code in public, so I would
really appreciate any and all criticism. I'm still very much a newbie
compared to most who post in here.

I'm also ashamed to say that I don't really speak any language other than
English, but luckily my French wife helped me with the French formatting. I
guess the main problem here is that this function would need to be extended
to support all of the various cultures in the Framework... Anyway, be
kind...

string EnglishDate = OrdinalDate(DateTime.Now, "MMM", "yyyy", new
CultureInfo("en-GB");
string FrenchDate = OrdinalDate(DateTime.Now, "MMM", "yyyy", new
CultureInfo("fr-FR");

static string OrdinalDate(DateTime DateToFormat, string MonthFormat, string
YearFormat, CultureInfo CI)
{
   StringBuilder SB = new StringBuilder();

   SB.Append(DateToFormat.Day.ToString("d"));
   switch (CI.TwoLetterISOLanguageName)
   {
       case "en":
       {
           switch (DateToFormat.Day)
           {
               case 1:
               case 21:
               case 31:
               {
                   SB.Append("st");
                   break;
               }
               case 2:
               case 22:
               {
                   SB.Append("nd");
                   break;
               }
               case 3:
               case 23:
               {
                   SB.Append("rd");
                   break;
               }
               default:
               {
                   SB.Append("th");
                   break;
               }
           }
           break;
       }
       case "fr":
       {
           switch (DateToFormat.Day)
           {
               case 1:
               {
                   SB.Append("er");
                   break;
               }
               default:
               {
                   SB.Append("e");
                   break;
               }
           }
           break;
       }
   }
   SB.Append(" ");

   SB.Append(DateToFormat.ToString(MonthFormat, CI.DateTimeFormat));
   SB.Append(" ");
   SB.Append(DateToFormat.ToString(YearFormat, CI.DateTimeFormat));

   return SB.ToString();
}
Peter Duniho - 28 Jun 2007 23:14 GMT
> OK, now this is my first attempt at showing any code in public, so I  
> would
> really appreciate any and all criticism. I'm still very much a newbie
> compared to most who post in here.

Because of your desire to make it extensible, I would use the Dictionary<>  
class to handle the actual look-up (which is mostly what your function  
does).

For example (you will note that, counting initialization, this code is  
longer than the code you posted, but as new languages are added, two  
things are true: 1) this code stays the same length, as code based on your  
approach increases proportionally to the data being added, and 2) once  
this code is shown to be correct, adding new data is simple and less  
error-prone):

(Warning: I haven't compiled any of this code.  There may well be some  
fundamental syntax errors, like you need to cast something I forgot to  
cast.  However, I am confident that the basic design is fine, so don't let  
any little thing like compiler errors dissuade you.  :) )

Dictionary<string, Dictionary<int, string>> _dict = new Dictionary<string,  
Dictionary<int, string>>();

static string OrdinalDate(DateTime DateToFormat, string MonthFormat, string
YearFormat, CultureInfo CI)
{
    StringBuilder SB = new StringBuilder();
    Dictionary<int, string> dictLanguage;

    SB.Append(DateToFormat.Day.ToString("d"));

    try
    {
        string strPostfix;

        dictLanguage = _dict[CI.TwoLetterISOLanguageName].Value;

        try
        {
            strPostfix = dictLanguage[DateToFormat.Day].Value;
        }
        catch (KeyNotFoundException)
        {
            // The dictionary must always include key 0, which is the  
default
            // to use if the actual date number isn't found.
            strPostfix = dictLanguage[0].Value;
        }

        SB.Append(strPostfix);
    }
    catch (KeyNotFoundException exc)
    {
        throw new NotSupportedException("Unknown language in CultureInfo  
CI", exc);
    }

    SB.Append(" ");

    SB.Append(DateToFormat.ToString(MonthFormat, CI.DateTimeFormat));
    SB.Append(" ");
    SB.Append(DateToFormat.ToString(YearFormat, CI.DateTimeFormat));

    return SB.ToString();
}

The idea being that the _dict field would be initialized to contain  
dictionaries for each language you support, each dictionary containing a  
default string to append (using index 0) and then specific strings for  
specific values.  You could of course write explicit code to initialize  
the Dictionary<> instances, but in keeping with the data-driven model, I  
would do something like this:

void InitOrdinalDate()
{
    object[] objsDictInit = new object[] {
        new object[]
        { "en",
            new object[] { "th", 0 },
            new object[] { "st", 1, 21, 31 },
            new object[] { "nd", 2, 22 },
            new object[] { "rd", 3, 23 }
        },
        new object []
        { "fr",
            new object[] { "e", 0 },
            new object[] { "er", 1 }
        } };

    Dictionary<string, Dictionary<int, string>> dictLang = new  
Dictionary<string, Dictionary<int, string>>();

    foreach (object[] arrayLang in objsDictInit)
    {
        string strLanguage = arrayLang[0];
        Dictionary<int, string> dictPostfix = new Dictionary<int,  
string>();

        for (int i = 1; i < arrayLang.Length; i++)
        {
            object[] arrayPostfix = arrayLang[i];
            string strPostfix = arrayPostfix[0];

            for (int j = 1; j < arrayPostfix.Length; j++)
            {
                dictPostfix.Add(arrayPostfix[j], strPostfix);
            }

        }

        dictLang.Add(strLanguage, dictPostfix);
    }

    _dict = dictLang;
}

You'd run this code once, and then the OrdinalDate() method would simply  
reuse the results each time you call that method.  Note also that the  
awkward "arrays of arrays of arrays" design can easily be replaced with  
something that just reads an XML file or string.  IMHO, that would  
actually be a better approach than what I posted, but I wanted to get the  
illustration out without complicating things further.  While I think XML  
would be more maintainable, the above has the advantage that is uses only  
the basic built-in stuff.

Hope that helps, rather than confuses things further.  :)

Pete
David Jackson - 29 Jun 2007 12:59 GMT
Hi Peter,

Thanks for the reply.

> Because of your desire to make it extensible, I would use the Dictionary<>
> class to handle the actual look-up (which is mostly what your function
> does).

OK.

> For example (you will note that, counting initialization, this code is
> longer than the code you posted, but as new languages are added, two
> things are true: 1) this code stays the same length, as code based on your
> approach increases proportionally to the data being added, and 2) once
> this code is shown to be correct, adding new data is simple and less
> error-prone):

Yes, I can see that.

> Dictionary<string, Dictionary<int, string>> _dict = new Dictionary<string,
> Dictionary<int, string>>();

FxCop tells me that it doesn't recommend nested generics - are they really
OK?

> Hope that helps, rather than confuses things further.  :)

I found it very useful. Thanks again.

DJ
Mark Rae - 29 Jun 2007 14:37 GMT
>> Dictionary<string, Dictionary<int, string>> _dict = new
>> Dictionary<string, Dictionary<int, string>>();

> FxCop tells me that it doesn't recommend nested generics - are they really
> OK?

FxCop's recommendations really should taken very much with a pinch of
salt...

Peter's code looks fine to me, and I would have no qualms about using
"nested generics"...

In fact, until I saw your post, the concept of a "nested generic" never
crossed my mind...

Signature

http://www.markrae.net

Peter Duniho - 29 Jun 2007 17:26 GMT
> [...]
> FxCop tells me that it doesn't recommend nested generics - are they  
> really OK?

I don't know anything about FxCop.  Sounds a little fussy to me.  :)

I have no idea why it might suggest that nested generics should be  
avoided.  That's like saying one should avoid putting references to any  
class within any other class.  But, it should go without saying, since I  
wrote the code I at least thought it was okay at the time I wrote it.  :)

Pete
Mark Wilden - 29 Jun 2007 19:24 GMT
Rather than designing and implementing this from scratch, why not look at
one of the Perl or Ruby library functions that already accomplish this, and
convert it to C#?

///ark

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.