diff --git a/Library/Homebrew/dev-cmd/audit.rb b/Library/Homebrew/dev-cmd/audit.rb
index c8cf5f803902ad704ed7d5eb711b53e419f6c21a..ab667f09b5fbc6ccac1788e28b0a466ca2b9510d 100644
--- a/Library/Homebrew/dev-cmd/audit.rb
+++ b/Library/Homebrew/dev-cmd/audit.rb
@@ -261,9 +261,8 @@ module Homebrew
             !(versioned_formulae = formula.versioned_formulae).empty?
         versioned_aliases = formula.aliases.grep(/.@\d/)
         _, last_alias_version = versioned_formulae.map(&:name).last.split("@")
-        major, minor, = formula.version.to_s.split(".")
-        alias_name_major = "#{formula.name}@#{major}"
-        alias_name_major_minor = "#{alias_name_major}.#{minor}"
+        alias_name_major = "#{formula.name}@#{formula.version.major}"
+        alias_name_major_minor = "#{alias_name_major}.#{formula.version.minor}"
         alias_name = if last_alias_version.split(".").length == 1
           alias_name_major
         else
@@ -488,11 +487,7 @@ module Homebrew
       return unless formula.name == "postgresql"
       return unless @core_tap
 
-      major_version = formula.version
-                             .to_s
-                             .split(".")
-                             .first
-                             .to_i
+      major_version = formula.version.major.to_i
       previous_major_version = major_version - 1
       previous_formula_name = "postgresql@#{previous_major_version}"
       begin
@@ -689,7 +684,7 @@ module Homebrew
     }.freeze
 
     # version_prefix = stable_version_string.sub(/\d+$/, "")
-    # version_prefix = stable_version_string.split(".")[0..1].join(".")
+    # version_prefix = stable.version.major_minor
 
     def audit_specs
       problem "Head-only (no stable download)" if head_only?(formula)
@@ -753,11 +748,9 @@ module Homebrew
 
       stable_version_string = stable.version.to_s
       stable_url_version = Version.parse(stable.url)
-      _, stable_url_minor_version, = stable_url_version.to_s
-                                                       .split(".", 3)
-                                                       .map(&:to_i)
+      stable_url_minor_version = stable_url_version.minor.to_i
 
-      formula_suffix = stable_version_string.split(".").last.to_i
+      formula_suffix = stable.version.patch.to_i
       throttled_rate = THROTTLED_FORMULAE[formula.name]
       if throttled_rate && formula_suffix.modulo(throttled_rate).nonzero?
         problem "should only be updated every #{throttled_rate} releases on multiples of #{throttled_rate}"
@@ -771,7 +764,7 @@ module Homebrew
 
         problem "Stable version URLs should not contain #{matched}"
       when %r{download\.gnome\.org/sources}, %r{ftp\.gnome\.org/pub/GNOME/sources}i
-        version_prefix = stable_version_string.split(".")[0..1].join(".")
+        version_prefix = stable.version.major_minor
         return if GNOME_DEVEL_ALLOWLIST[formula.name] == version_prefix
         return if stable_url_version < Version.create("1.0")
         return if stable_url_minor_version.even?
diff --git a/Library/Homebrew/pkg_version.rb b/Library/Homebrew/pkg_version.rb
index 5b2e7522ef833e6a04b6667f7c3ca76f9044eec9..b4b13796536877e3084fe9215b527a3caeee1cc3 100644
--- a/Library/Homebrew/pkg_version.rb
+++ b/Library/Homebrew/pkg_version.rb
@@ -4,11 +4,20 @@ require "version"
 
 class PkgVersion
   include Comparable
+  extend Forwardable
 
   RX = /\A(.+?)(?:_(\d+))?\z/.freeze
 
   attr_reader :version, :revision
 
+  delegate [ # rubocop:disable Layout/HashAlignment
+    :major,
+    :minor,
+    :patch,
+    :major_minor,
+    :major_minor_patch,
+  ] => :version
+
   def self.parse(path)
     _, version, revision = *path.match(RX)
     version = Version.create(version)
diff --git a/Library/Homebrew/test/pkg_version_spec.rb b/Library/Homebrew/test/pkg_version_spec.rb
index 474485ffe3f4ce4ca933fc47969ff485b451c55d..ee8bedd610d72df8b08afe25eccfecca46972b09 100644
--- a/Library/Homebrew/test/pkg_version_spec.rb
+++ b/Library/Homebrew/test/pkg_version_spec.rb
@@ -85,4 +85,46 @@ describe PkgVersion do
       expect(p1.hash).not_to eq(p4.hash)
     end
   end
+
+  describe "#version" do
+    it "returns package version" do
+      expect(described_class.parse("1.2.3_4").version).to be == Version.create("1.2.3")
+    end
+  end
+
+  describe "#revision" do
+    it "returns package revision" do
+      expect(described_class.parse("1.2.3_4").revision).to be == 4
+    end
+  end
+
+  describe "#major" do
+    it "returns major version token" do
+      expect(described_class.parse("1.2.3_4").major).to be == Version::Token.create("1")
+    end
+  end
+
+  describe "#minor" do
+    it "returns minor version token" do
+      expect(described_class.parse("1.2.3_4").minor).to be == Version::Token.create("2")
+    end
+  end
+
+  describe "#patch" do
+    it "returns patch version token" do
+      expect(described_class.parse("1.2.3_4").patch).to be == Version::Token.create("3")
+    end
+  end
+
+  describe "#major_minor" do
+    it "returns major.minor version" do
+      expect(described_class.parse("1.2.3_4").major_minor).to be == Version.create("1.2")
+    end
+  end
+
+  describe "#major_minor_patch" do
+    it "returns major.minor.patch version" do
+      expect(described_class.parse("1.2.3_4").major_minor_patch).to be == Version.create("1.2.3")
+    end
+  end
 end
diff --git a/Library/Homebrew/test/version_spec.rb b/Library/Homebrew/test/version_spec.rb
index d83b25d8695439657d550af21564e4cd33df0222..51a8641b820798eb91d7897603b56a18f220acb5 100644
--- a/Library/Homebrew/test/version_spec.rb
+++ b/Library/Homebrew/test/version_spec.rb
@@ -183,6 +183,15 @@ describe Version do
     expect(described_class.create("1")).to be == 1
   end
 
+  it "can be compared against tokens" do
+    expect(described_class.create("2.1.0-p194")).to be > Version::Token.create("2")
+    expect(described_class.create("1")).to be == Version::Token.create("1")
+  end
+
+  it "can be compared against Version::NULL_TOKEN" do
+    expect(described_class.create("2.1.0-p194")).to be > Version::NULL_TOKEN
+  end
+
   specify "comparison returns nil for non-version" do
     v = described_class.create("1.0")
     expect(v <=> Object.new).to be nil
@@ -276,6 +285,76 @@ describe Version do
     expect(v2.to_str).to eq("HEAD-ffffff")
   end
 
+  describe "#major" do
+    it "returns major version token" do
+      expect(described_class.create("1").major).to be == Version::Token.create("1")
+      expect(described_class.create("1.2").major).to be == Version::Token.create("1")
+      expect(described_class.create("1.2.3").major).to be == Version::Token.create("1")
+      expect(described_class.create("1.2.3alpha").major).to be == Version::Token.create("1")
+      expect(described_class.create("1.2.3alpha4").major).to be == Version::Token.create("1")
+      expect(described_class.create("1.2.3beta4").major).to be == Version::Token.create("1")
+      expect(described_class.create("1.2.3pre4").major).to be == Version::Token.create("1")
+      expect(described_class.create("1.2.3rc4").major).to be == Version::Token.create("1")
+      expect(described_class.create("1.2.3-p4").major).to be == Version::Token.create("1")
+    end
+  end
+
+  describe "#minor" do
+    it "returns minor version token" do
+      expect(described_class.create("1").minor).to be nil
+      expect(described_class.create("1.2").minor).to be == Version::Token.create("2")
+      expect(described_class.create("1.2.3").minor).to be == Version::Token.create("2")
+      expect(described_class.create("1.2.3alpha").minor).to be == Version::Token.create("2")
+      expect(described_class.create("1.2.3alpha4").minor).to be == Version::Token.create("2")
+      expect(described_class.create("1.2.3beta4").minor).to be == Version::Token.create("2")
+      expect(described_class.create("1.2.3pre4").minor).to be == Version::Token.create("2")
+      expect(described_class.create("1.2.3rc4").minor).to be == Version::Token.create("2")
+      expect(described_class.create("1.2.3-p4").minor).to be == Version::Token.create("2")
+    end
+  end
+
+  describe "#patch" do
+    it "returns patch version token" do
+      expect(described_class.create("1").patch).to be nil
+      expect(described_class.create("1.2").patch).to be nil
+      expect(described_class.create("1.2.3").patch).to be == Version::Token.create("3")
+      expect(described_class.create("1.2.3alpha").patch).to be == Version::Token.create("3")
+      expect(described_class.create("1.2.3alpha4").patch).to be == Version::Token.create("3")
+      expect(described_class.create("1.2.3beta4").patch).to be == Version::Token.create("3")
+      expect(described_class.create("1.2.3pre4").patch).to be == Version::Token.create("3")
+      expect(described_class.create("1.2.3rc4").patch).to be == Version::Token.create("3")
+      expect(described_class.create("1.2.3-p4").patch).to be == Version::Token.create("3")
+    end
+  end
+
+  describe "#major_minor" do
+    it "returns major.minor version" do
+      expect(described_class.create("1").major_minor).to be == described_class.create("1")
+      expect(described_class.create("1.2").major_minor).to be == described_class.create("1.2")
+      expect(described_class.create("1.2.3").major_minor).to be == described_class.create("1.2")
+      expect(described_class.create("1.2.3alpha").major_minor).to be == described_class.create("1.2")
+      expect(described_class.create("1.2.3alpha4").major_minor).to be == described_class.create("1.2")
+      expect(described_class.create("1.2.3beta4").major_minor).to be == described_class.create("1.2")
+      expect(described_class.create("1.2.3pre4").major_minor).to be == described_class.create("1.2")
+      expect(described_class.create("1.2.3rc4").major_minor).to be == described_class.create("1.2")
+      expect(described_class.create("1.2.3-p4").major_minor).to be == described_class.create("1.2")
+    end
+  end
+
+  describe "#major_minor_patch" do
+    it "returns major.minor.patch version" do
+      expect(described_class.create("1").major_minor_patch).to be == described_class.create("1")
+      expect(described_class.create("1.2").major_minor_patch).to be == described_class.create("1.2")
+      expect(described_class.create("1.2.3").major_minor_patch).to be == described_class.create("1.2.3")
+      expect(described_class.create("1.2.3alpha").major_minor_patch).to be == described_class.create("1.2.3")
+      expect(described_class.create("1.2.3alpha4").major_minor_patch).to be == described_class.create("1.2.3")
+      expect(described_class.create("1.2.3beta4").major_minor_patch).to be == described_class.create("1.2.3")
+      expect(described_class.create("1.2.3pre4").major_minor_patch).to be == described_class.create("1.2.3")
+      expect(described_class.create("1.2.3rc4").major_minor_patch).to be == described_class.create("1.2.3")
+      expect(described_class.create("1.2.3-p4").major_minor_patch).to be == described_class.create("1.2.3")
+    end
+  end
+
   describe "::parse" do
     it "returns a NULL version when the URL cannot be parsed" do
       expect(described_class.parse("https://brew.sh/blah.tar")).to be_null
diff --git a/Library/Homebrew/version.rb b/Library/Homebrew/version.rb
index 50f1b67082f8882a792201ba8537fb02e34df607..589bb7a30468924c74a44ce8567e5b18a71c893f 100644
--- a/Library/Homebrew/version.rb
+++ b/Library/Homebrew/version.rb
@@ -46,6 +46,18 @@ class Version
       "#<#{self.class.name} #{value.inspect}>"
     end
 
+    def hash
+      value.hash
+    end
+
+    def to_f
+      value.to_f
+    end
+
+    def to_i
+      value.to_i
+    end
+
     def to_s
       value.to_s
     end
@@ -75,6 +87,10 @@ class Version
       end
     end
 
+    def null?
+      true
+    end
+
     def inspect
       "#<#{self.class.name}>"
     end
@@ -429,8 +445,9 @@ class Version
     # Used by the *_build_version comparisons, which formerly returned Fixnum
     other = Version.new(other.to_s) if other.is_a? Integer
     return 1 if other.nil?
-
     return 1 if other.respond_to?(:null?) && other.null?
+
+    other = Version.new(other.to_s) if other.is_a? Token
     return unless other.is_a?(Version)
     return 0 if version == other.version
     return 1 if head? && !other.head?
@@ -469,6 +486,26 @@ class Version
   end
   alias eql? ==
 
+  def major
+    tokens.first
+  end
+
+  def minor
+    tokens.second
+  end
+
+  def patch
+    tokens.third
+  end
+
+  def major_minor
+    Version.new([major, minor].compact.join("."))
+  end
+
+  def major_minor_patch
+    Version.new([major, minor, patch].compact.join("."))
+  end
+
   def empty?
     version.empty?
   end
diff --git a/Library/Homebrew/version/null.rb b/Library/Homebrew/version/null.rb
index 075c14808730380faa74087fff21e6f3836fe779..b15546a6cb914b09cb4dc6a676bd663e8cbb1063 100644
--- a/Library/Homebrew/version/null.rb
+++ b/Library/Homebrew/version/null.rb
@@ -37,6 +37,26 @@ class Version
     alias_method :requires_sse42?, :requires_nehalem_cpu?
     alias_method :requires_popcnt?, :requires_nehalem_cpu?
 
+    def major
+      NULL_TOKEN
+    end
+
+    def minor
+      NULL_TOKEN
+    end
+
+    def patch
+      NULL_TOKEN
+    end
+
+    def major_minor
+      self
+    end
+
+    def major_minor_patch
+      self
+    end
+
     def to_f
       Float::NAN
     end