[SOLVED] Advanced string formatter with pattern support in C#


I would like to have a flexible template that can translate cases similar to:

  • WHnnn => WH001, WH002, WH003… (nnn is just a number indicated 3 digits)
  • INVyyyyMMdd => INV20220228
  • ORDERyyyyMMdd-nnn => ORDER20220228-007

I know that I can use the following code to achieve a specific template:

string.Format("INV{0:yyyy-MM-dd}", DateTime.Now)

Which should have the same result as case 2 above. But that’s not flexible. As the customer may customize their own template as long as I can understand/support, like the third case above.

I know even for the third case, I can do something like this:

string.Format("ORDER{0:yyyy-MM-dd}-{1:d3}", DateTime.Now, 124)

But that’s clumsy, as I would like the template (input) to be just like this:


The requirement is to support all the supported patterns by string.Format in C#, but the template can be any combination of those patterns.


I would probably use a custom formatter for this case.

Create a new class that will contain date/time & number and will implement IFormattable interface.

There is one tip: use some internal format in style INV{nnn} or INV[nnn] where only the part in {} or [] will be replaced with the value.

Otherwise there could be unwanted changes like in Inv contains ‘n’. You could get output as I7v.

In your examples the N is upper case, but will it be the case even after each customisation?

Code (simplified version):

internal sealed class InvoiceNumberInfo : IFormattable
    private static readonly Regex formatMatcher = new Regex(@"^(?<before>.*?)\[(?<code>\w+?)\](?<after>.*)$");

    private readonly DateTime date;

    private readonly int number;

    public InvoiceNumberInfo(DateTime date, int number)
        this.date = date;
        this.number = number;

    public string ToString(string format, IFormatProvider formatProvider)
        var output = format;
        while (true)
            var match = formatMatcher.Match(output);
            if (!match.Success)
                return output;

            output = match.Groups["before"].Value + FormatValue(match.Groups["code"].Value) + match.Groups["after"].Value;

    private string FormatValue(string code)
        if (code[0] == 'n')
            var numberFormat = "D" + code.Length.ToString(CultureInfo.InvariantCulture);
            return this.number.ToString(numberFormat);
            return this.date.ToString(code);


internal static class Program
    public static void Main(string[] args)
        if (args.Length == 0)
            Console.WriteLine("No format to display");

        var inv = new InvoiceNumberInfo(DateTime.Now, number: 7);
        foreach (string format in args)
            Console.WriteLine("Format: '{0}' is formatted as {1:" + format + "}", format, inv);

And output:

Format: 'WH[nnn]' is formatted as WH007
Format: 'INV[yyyyMMdd]' is formatted as INV20220227
Format: 'ORDER[yyyyMMdd]-[nnn]' is formatted as ORDER20220227-007

NOTE This is only a simplified version, proof of concept. Use proper error checking for your code.

Answered By – Julo

Answer Checked By – David Marino (BugsFixing Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *