In 2018 I wrote an article titled “I Want to Validate Phone Numbers.” It was a brief post introducing the newly started go-tel-num-parser-jp project and showing how to use IsTelNumber. (source)

I made a few more tweaks afterward, but the repository’s updates stopped in 2020. When I revisited it after a long time, I found that the phone number categories were still the same as back then, and there were some issues with the validation method. So, by looking at the currently published number information, I updated the number categories and validation logic (see pull request #4). (PR #4)

What I did this time wasn’t just adding new regular expressions for numbers. I also clarified what should count as a success when only part of an input string looks like a phone number.

What the library validates

It’s a small Go package with two main entry points.

ok, kind := tnp.IsTelNumber("060-1234-5678")
// ok == true, kind == tnp.MobilePhone

number, err := tnp.CropTelNumber("連絡先: 0800-123-4567")
// number == "0800-123-4567", err == nil

IsTelNumber determines whether the entire input matches a phone number format. CropTelNumber extracts the first phone number found in a block of text. Even though they use the same regular expression, the two functions serve different purposes.

In this context, “validation” refers only to checking format and category. It does not verify whether the number is actually assigned to a carrier, whether it is currently in use, or whether a fixed‑line area code exists. It is a tool for detecting strings that look like phone numbers, not a guarantee that the number can be called and reached.

Changes and fixes since 2020

This time I also rewrote the reference table in the README. Among the updated categories, the most noticeable changes are the following.

Example formatCurrent handling
060-1234-5678Added to the mobile‑phone category
0200-12345-67890Added as a 14‑digit data‑transmission mobile phone
0800-123-4567Adjusted incoming‑call‑charge numbers to the 11‑digit format
0990-123-456Added a category for information‑fee proxy collection
0600-123-4567Added a category for FMC telephone numbers

Numbers in the 020 category can be either 11 digits long or 14 digits long and start with 0200. Even though they look similar, the digit counts are not the same. 0800 was also corrected, as the previous code treated it as a 10‑digit form. It also supports representations without hyphens.

Another point to watch out for are 060 and 0600. Just because the former was added to the mobile‑phone category does not mean the latter should be treated as a mobile phone either. 0600 is classified as a separate FMC category. If you roughly categorize based only on the first few characters, you’ll make mistakes like this.

However, the ability to recognize 060 and the fact that numbers in the 060 range are already in widespread use are separate matters. These have been added as implementations that handle the formats defined in the telephone numbering plan. Links to the Ministry of Internal Affairs and Communications’ guidance and the allocation status have also been compiled in the README. (source)

Matching a regular expression alone isn’t enough

Previously, IsTelNumber was validated using the regular expression MatchString. This returns true if any part of the string matches, which is not the same as the intended meaning of “the entire string is a phone number.”

For example, call 03-5321-1111 is a sentence that contains a phone number, but the sentence itself is not a phone number. IsTelNumber should be treated as false, while CropTelNumber is the input that should be extracted. This time, while handling both cases with a common search routine, we added a step to IsTelNumber that checks whether the match spans the entire string from start to end.

There’s also a pitfall with CropTelNumber. If a string that is one digit longer—like 01201234567—has its leading 0120123456 portion extracted as a phone number, it can pick up an incorrect number. We avoid selecting candidates when digits are immediately before or after the candidate. If multiple numbers appear in a text, we return the first complete candidate that occurs in the string, not according to the order of the categories.

When hyphens are removed, the broader pattern for fixed‑line numbers can overlap with parts of service numbers. We first examine patterns such as 0120 and 0570 with their specific categories to prevent misclassifying them as fixed‑line numbers. Inspecting each regular expression individually makes it hard to notice overlaps between categories.

We also continue to handle bracketed notations like 03(5321)1111. Since this is replaced by 03-5321-1111 before validation, the string returned by CropTelNumber will also include hyphens.

Exclusion settings and testing

There has long been a SetIgnoreTypes feature that excludes certain categories from validation. This time, instead of accumulating excluded categories each time it is called, we changed it to replace the categories with the specified ones. You can clear the exclusion with SetIgnoreTypes(), which takes no arguments.

tnp.SetIgnoreTypes(tnp.MobilePhone)
// Exclude the mobile phone category from classification

tnp.SetIgnoreTypes()
// Remove the exclusion

Because the setting is shared, read and write access are protected by sync.RWMutex. However, it remains a package‑wide setting. Since the API does not accept different exclusion criteria on each call, users need to be aware of this.

This time we also added regression tests (see tnp_test.go). In addition to confirming acceptance of the new categories, the tests verify that numbers with one extra digit, the previously incorrect 0800 format, the out‑of‑scope 0170 and 0180, and that 0600 are not treated as mobile phones. On my local Go 1.27.1, go test ./... and go test -race ./... pass. (source code)

I also reorganized the Japanese README and added an English version (README.en.md) and an MIT license (LICENSE). The minimum required Go version for go.mod has been raised to Go 1.27.1, so if you were using it with an older Go environment, that is also a change. (source code) (source code)

There are multiple levels of “correctness” for a phone number

While updating the code this time, I was reminded that a phone number’s “correctness” isn’t a single concept. Does the entire string have the proper length and format? Which number category does it belong to? Is the number actually assigned? Is it still in use and dialable? To verify the latter aspects, regular expressions alone are insufficient.

This package only addresses the first two points. Therefore, even when IsTelNumber returns true, it does not guarantee that the number actually exists or can be reached, and international numbers or short emergency‑call numbers are excluded. This boundary is clearly stated in the README.

In the 2018 article I could only say “I’ve just started working on it.” This time I aligned the number categories with the current documentation and fixed the distinction between validation and extraction through tests. It’s a small library, but revisiting it after a long time revealed more changes than I expected.