Recursive AI

Format a phone number as E.164 in C#

OneContact is a free, open-source contact data library for .NET from Recursive AI. MIT or Apache-2.0, no native dependencies, no network access.

E.164 formatting gives you a plus sign, country calling code and number without display punctuation.

Terminal
dotnet add package RecursiveAI.OneContact

Requires .NET 10 (net10.0); .NET 9 and earlier aren’t supported.

C#
using RecursiveAI.OneContact;
 
var phone = PhoneNumber.Parse(
    "(202) 555-0123", Region.US);
Console.WriteLine(phone.E164);
Text
+12025550123

Download this example (.zip): unzip, then dotnet run.

Full program

C#
using RecursiveAI.OneContact;
 
var phone = PhoneNumber.Parse("(202) 555-0123 ext. 42", Region.US);
Console.WriteLine($"E.164: {phone.E164}");
Console.WriteLine($"Extension: {phone.Extension}");
Console.WriteLine($"Canonical: {phone.Canonical}");
Console.WriteLine($"National: {phone.Format(PhoneFormat.National)}");

dotnet run prints:

Text
E.164: +12025550123
Extension: 42
Canonical: +12025550123;ext=42
National: (202) 555-0123 ext. 42

The extension stays available on the parsed value, but E164 leaves it out. “Canonical” is the E.164 number plus ;ext= and the extension, when present. If input cannot be parsed, Parse throws ContactParseException.

Variations

Print a grouped international number and a dialing URI:

C#
using RecursiveAI.OneContact;
 
var phone = PhoneNumber.Parse("+44 20 7946 0123 ext. 42");
Console.WriteLine(phone.Format(PhoneFormat.International));
Console.WriteLine(phone.Format(PhoneFormat.Rfc3966));
Text
+44 20 7946 0123 x42
tel:+44-20-7946-0123;ext=42

The short answer

Parse the input with PhoneNumber.Parse, then read phone.E164. It returns the plus sign, country calling code and national significant number without display punctuation or an extension. This is different from Canonical, which appends ;ext= when the phone value has an extension.

The full program deliberately uses an extension. It prints the E.164 number and extension on separate lines, then prints the canonical and national forms. That makes it clear which value to put into a field that accepts only the international number.

How do you format a phone number as E.164 in C#?

Start with country context rather than string replacement. The example parses a US national number with Region.US. For an input that already starts with a plus sign and calling code, you can omit the default region. For a national input, supply the region in which the number was written.

Don’t just delete punctuation and prepend a plus sign. A national prefix is not necessarily part of the national significant number. Parsing applies the region’s prefix rules; formatting then uses the parsed parts. The British example starts with 020 nationally but uses 20 after the international calling code.

Formatting and validation are separate decisions. If your application only accepts valid numbers, check IsValid after parsing and before storing the formatted value. A successfully parsed phone value is not by itself a valid subscriber number, and producing a formatted string does not test whether a call would connect.

Use TryParse instead of Parse when handling ordinary form input. It lets the form take a rejection path without catching an exception. The parsing recipe includes that version as runnable code. Decide what a missing region means in your UI: ask for a country or require an international number, rather than guessing one from punctuation.

How do I format a phone number internationally in C#?

Choose the representation for the destination. E164 is the compact international number. PhoneFormat.International is a grouped, readable international form. PhoneFormat.National is the display form within the region. PhoneFormat.Rfc3966 produces a tel: URI with the extension parameter when present. The variation prints the latter two international representations for the same value.

Store an extension in its own field if your downstream API accepts only E.164. Otherwise you risk losing information when you replace the original string with E164. Use Canonical when you need OneContact’s complete normalized phone value, including the extension, and the receiver understands that representation.

A phone’s calling code also isn’t a substitute for its region. Several regions can share a calling code. Leave parsing and region lookup to the metadata-backed phone type rather than splitting a fixed number of characters from the beginning. The phone documentation lists the supported formats and explains how the canonical form and comparison key differ. Choose deliberately between a display string, a dialing URI and a comparison value; they serve different purposes even when they refer to the same phone.

All onecontact recipes