Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Localizing Date Ranges

Does anyone know how to localize date ranges using C#?

In particular, I want to generate "smart" date ranges, so that redundant information is eliminated.

Here are some examples in US English

  1. August - Sept, 2009
  2. August 2009
  3. August 1 - 9, 2009
  4. January 1 - March 3, 2009
  5. December 6, 2009 - January 8, 2010

Form what I can tell the .NET Framework has support for localizing dates, but not ranges of dates.

Using the information in System.Globalization.DateTimeFormatInfo for the Windows locales that CultureInfo supports, I was able to figure out (most of the time) how to do items #1 and #2. Item #2 is just DateTime.ToString(DateTimeFormatInfo.YearMonthFormat). Using YearMonthFormat I was also able to deduce formats to use for #1 for most languages. For the few that I couldn't I just duplicate the year.

Unfortunately, I can't figure out how to do items #3-#5 using the .NET Framework. Outlook formats ranges using those formats, so I was hoping there might be some Win32 APIs that would do it, but a Google search for "Win32 Date Range localization" yielded nothing useful.

I like the enhanced usability provided by "smart range formating", and I would like my customers that are not using English versions of Windows to get the same benefits.

Does any one know how to do that in a culture-dependent manner?

like image 694
Scott Wisniewski Avatar asked Jul 22 '09 04:07

Scott Wisniewski


People also ask

What is date localization?

Date and time localization is quite an important topic when it comes to developing applications and software systems in multiple languages. Date and time are written and read in different ways in different countries.

What is locale date format?

DateFormat is used for formatting a date into String based on specific locale that is provided as input. The locale is used for specifying the region and language for making the code more locale to the user. The way of writing date is different in different regions of the world.

What is locale time?

Locale determines the human language and cultural norms used when generating a String to represent a date-time value. The time zone determines the wall-clock time of a particular region used to represent a moment on the timeline.


1 Answers

Good question and it does seem to be something that the .NET framework and other languages seem to be missing too. I would guess the presentation of the results depends upon your application.

Outlook has very good date understanding as an input (as does Google calenders), but I haven't personally seen this form of expression as an output (e.g. displayed to the user).

Some recommendations:

  1. Stick with displaying a start date and an end date and don't worry about redundancy
  2. Roll your own..... :-(

I would guess that from your term 'localize' you plan to internationalize the output, if that is the case you are going to have a hard job catching all of the cases, it doesn't seem to be information stored in most of the typical internationalization data sets.

Concentrating on English from your example, you seem to have a number of rules already defined in the code:

  1. Year always displayed.
  2. Month (at least one of them) is also always displayed.
  3. Days are not displayed in case 3, this I would condition to mean that the start date is the 1st and the end is the 31st (e.g. every day of the month)
  4. Day and Month are shown if the start/end are different months.
  5. Year is additionally shown against the start date if the two dates differ by year.

I know the above is only looking at English, but in my view, it doesn't look too hard to roll yourself, too much to write in this editor though!

EDIT - I know this doesn't answer the original - but if anyone using a search engine finds this question and wants an English version...

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(DateRange.Generate(2009, 08, 01, 2009, 09, 31));
        Console.WriteLine(DateRange.Generate(2009, 08, 01, 2009, 08, 31));
        Console.WriteLine(DateRange.Generate(2009, 08, 01, 2009, 08, 09));
        Console.WriteLine(DateRange.Generate(2009, 01, 01, 2009, 03, 03));
        Console.WriteLine(DateRange.Generate(2009, 12, 06, 2010, 01, 08));

        // Same dates
        Console.WriteLine(DateRange.Generate(2009, 08, 01, 2009, 08, 01));
    }
}

static class DateRange
{
    private static string[] Months = {
                                         "January", "February", "March", "April",
                                         "May", "June", "July", "August",
                                         "September", "October", "November", "December"
                                     };
    public static string Generate(
        int startYear, int startMonth, int startDay,
        int endYear, int endMonth, int endDay)
    {
        bool yearsSame = startYear == endYear;
        bool monthsSame = startMonth == endMonth;
        bool wholeMonths = (startDay == 1 && IsLastDay(endDay, endMonth));

        if ( monthsSame && yearsSame && startDay == endDay)
        {
            return string.Format("{0} {1}, {2}", startDay, Month(startMonth), startYear);
        }

        if (monthsSame)
        {
            if (yearsSame)
            {
                return wholeMonths
                           ? string.Format("{0} {1}", Month(startMonth), endYear)
                           : string.Format("{0} {1} - {2}, {3}", Month(endMonth), startDay, endDay, endYear);
            }
            return wholeMonths
                       ? string.Format("{0}, {1} - {2}, {3}",
                                       Month(startMonth), startYear,
                                       Month(endMonth), endYear)
                       : string.Format("{0} {1}, {2} - {3} {4}, {5}",
                                       Month(startMonth), startDay, startYear,
                                       Month(endMonth), endDay, endYear);
        }

        if (yearsSame)
        {
            return wholeMonths
                       ? string.Format("{0} - {1}, {2}", Month(startMonth), Month(endMonth), endYear)
                       : string.Format("{0} {1} - {2} {3}, {4}",
                                       Month(startMonth), startDay,
                                       Month(endMonth), endDay,
                                       endYear);
        }
        return wholeMonths
                   ? string.Format("{0}, {1} - {2}, {3}",
                                   Month(startMonth), startYear,
                                   Month(endMonth), endYear)
                   : string.Format("{0} {1}, {2} - {3} {4}, {5}",
                                   Month(startMonth), startDay, startYear,
                                   Month(endMonth), endDay, endYear);
    }

    private static string Month(int month)
    {
        return Months[month - 1];
    }

    public static bool IsLastDay(int day, int month)
    {
        switch (month+1)
        {
            case 2:
                // Not leap-year aware
                return (day == 28 || day == 29);
            case 1: case 3: case 5: case 7: case 8: case 10: case 12:
                return (day == 31);
            case 4: case 6: case 9: case 11:
                return (day == 30);
            default:
                return false;
        }
    }
}

This yields the same output (almost, Sept becomes September in mine) as the original question:

August - September, 2009
August 1 - 31, 2009
August 1 - 9, 2009
January 1 - March 3, 2009
December 6, 2009 - January 8, 2010
like image 194
Ray Hayes Avatar answered Sep 28 '22 00:09

Ray Hayes