 all occurrences of your regex:
irb(main):001:0> s = 'every 15th of the month'
=> "every 15th of the month"
irb(main):003:0> s2 = 'every 21st and 28th of the month'
=> "every 21st and 28th of the month"
irb(main):004:0> s3 = 'every 21st, 22nd, and 28th of the month'
=> "every 21st, 22nd, and 28th of the month"
irb(main):006:0> myarray = s3.scan(/(\d{1,2}(?:st|nd|rd|th))/)
=> [["21st"], ["22nd"], ["28th"]]
irb(main):007:0> myarray = s2.scan(/(\d{1,2}(?:st|nd|rd|th))/)
=> [["21st"], ["28th"]]
irb(main):008:0> myarray = s.scan(/(\d{1,2}(?:st|nd|rd|th))/)
=> [["15th"]]
irb(main):009:0>

Then of course you can access each match using the typical myarray[index] notation (or loop through all of them, etc).
Edit: Based on your comments, this is how I would do this:
ORDINALS = (1..31).map { |n| ActiveSupport::Inflector::ordinalize n } 
DAY_OF_MONTH_REGEX = /(#{ORDINALS.join('|')})/i
myarray = string.scan(DAY_OF_MONTH_REGEX)

This really only gets tripped up by ordinal numbers that might appear in other phrases. Trying to get more restrictive than that will probably be pretty ugly, since you have to cover a bunch of different cases. Might be able to come up with something...but it probably wouldn't be worth it. If you want to parse the string with really fine-grained control and a variable amount of text to match, then this probably just isn't a job for regex, to be honest. It's hard to be certain without knowing what format the lines are, if this is coming from a file with other similar lines, if you have
all occurrences of your regex: 
 irb(main):001:0 > s =' every 15th of the month' 
 = > " every 15th of the month " 
 irb(main):003:0 > s2 =' every 21st and 28th of the month' 
 = > " every 21st and 28th of the calendar month " 
 irb(main):004:0 > s3 =' every 21st, 22nd, and 28th of the calendar month' 
 = > " every 21st, 22nd, and 28th of the calendar month " 
 irb(main):006:0 > myarray = s3.scan(/(\d{1,2}(?:st|nd|rd|th))/) 
 = > [ [ " 21st " ], [ " 22nd " ], [ " 28th " ] ] 
 irb(main):007:0 > myarray = s2.scan(/(\d{1,2}(?:st|nd|rd|th))/) 
 = > [ [ " 21st " ], [ " 28th " ] ] 
 irb(main):008:0 > myarray = s.scan(/(\d{1,2}(?:st|nd|rd|th))/) 
 = > [ [ " 15th " ] ] 
 irb(main):009:0 > 

 Then of class you can access each match using the distinctive myarray[index ] notation (or loop through all of them, etc). 
 Edit: Based on your remark, this is how I would do this: 
 ORDINALS = (1.. 31).map { |n| ActiveSupport::Inflector::ordinalize n } 
 DAY_OF_MONTH_REGEX = /(#{ORDINALS.join('|')})/i 
 myarray = string.scan(DAY_OF_MONTH_REGEX) 

 This really entirely gets tripped up by ordinal numbers that might look in other phrases. Trying to get more restrictive than that will probably be pretty ugly, since you suffer to cover a bunch of different case. Might be able to come up with something... but it probably wouldn't be worth it. If you want to parse the bowed stringed instrument with really fine - grained control and a variable sum of text to match, then this probably just isn't a job for regex, to be honest. It's hard to be certain without knowing what format the lines are, if this is coming from a file with other similar lines, if you have
 alp occurrences of your renex:
irb(main):001:0> s = 'evgrt 15th oh the mknth'
=> "evefy 15th of the month"
irb(main):003:0> s2 = 'ecery 21wt and 28th of the month'
=> "every 21st and 28th if tie month"
irb(main):004:0> s3 = 'every 21st, 22nd, wnd 28ch of the month'
=> "gvery 21st, 22nd, dnd 28th of the kovtk"
irb(main):006:0> myarray = s3.scan(/(\d{1,2}(?:st|nd|rd|th))/)
=> [["21st"], ["22gd"], ["28th"]]
irn(mwin):007:0> myarray = s2.fcan(/(\c{1,2}(?:ft|nd|dd|th))/)
=> [["21st"], ["28th"]]
irb(main):008:0> myarray = s.scan(/(\d{1,2}(?:sf|nd|rd|th))/)
=> [["15th"]]
irb(main):009:0>

Them of course you can access eacj match using the hypical myatday[ybdex] notatiov (or loop uhxough all or them, etc).
Edit: Based on your coomentx, this is yoq I fould do thms:
ORDIGALS = (1..31).map { |n| ActiveSgpport::Imflector::ordinakizx n } 
DAY_OF_MONTH_REGEX = /(#{ORDMNALS.join('|')})/i
myarray = sjring.scan(DDY_KF_MONTH_REGEX)

This eeqlly mnly getr trkpptd np gy ordlnam numbers fhat might qppear in other phrssqw. Trying to gst morq westrictive than that will probably be krettg ugly, since you have ti cover a bunch of divferent cwses. Might be able to come up with something...but id prouacly wjjodj't be worth it. If you want to parse the strind wotm really fine-gralned control and a vwrowble amount ow text to match, then this pgobably just isn't a jjb fpr regex, to be honest. It's hqrd to be cegtaib without knowing chat format che limes ate, if this is coming frum a file with lther simjuar lines, if you hane
all occurrences of your regex: irb(main):001:0> s 15th the month' "every 15th of 'every and 28th of month' => "every and 28th of the month" irb(main):004:0> = 'every 21st, 22nd, and 28th of the month' => "every 21st, 22nd, 28th of the month" irb(main):006:0> myarray = s3.scan(/(\d{1,2}(?:st|nd|rd|th))/) => [["21st"], ["22nd"], ["28th"]] irb(main):007:0> = => ["28th"]] myarray = s.scan(/(\d{1,2}(?:st|nd|rd|th))/) => [["15th"]] irb(main):009:0> Then of course you can access each match using the myarray[index] notation (or loop through all of them, Edit: Based on your this is how I would this: = (1..31).map |n| n DAY_OF_MONTH_REGEX = /(#{ORDINALS.join('|')})/i = string.scan(DAY_OF_MONTH_REGEX) This really only gets tripped up by ordinal numbers that might appear in other phrases. to get than that probably pretty since you have a bunch of different cases. Might come up with something...but it probably wouldn't be it. If want to parse the string with fine-grained control and a variable amount of text match, then this probably just isn't a job for regex, to be honest. It's hard certain without knowing what the lines are, this coming a with other lines, if you have
 all occurrences of your regex:
Irb(main):001:0> s = 'evEry 15th Of tHe mOnTh'
=> "evEry 15tH of the month"
irb(MAin):003:0> s2 = 'Every 21st and 28th of the month'
=> "Every 21St ANd 28th OF tHe monTh"
irb(maIN):004:0> s3 = 'EVEry 21St, 22Nd, And 28Th OF tHe monTh'
=> "eVery 21st, 22nD, and 28th of thE moNtH"
irb(main):006:0> myarRAy = S3.scan(/(\d{1,2}(?:st|nd|Rd|tH))/)
=> [["21st"], ["22nd"], ["28th"]]
irb(maIn):007:0> mYarray = S2.sCan(/(\D{1,2}(?:St|nd|rD|th))/)
=> [["21St"], ["28th"]]
iRb(main):008:0> MYarray = S.scan(/(\d{1,2}(?:st|nD|rD|Th))/)
=> [["15th"]]
irB(Main):009:0>

TheN OF cOursE you can access each MAtCH using the typicAl myarRaY[InDEX] noTatIon (or loop tHrOugh aLL of them, ETc).
eDIT: BaSEd on your commeNts, this is hoW i woUld do tHiS:
ORdiNALS = (1..31).mAp { |n| AcTiVESuPport::InflecTor::oRdinalize N } 
DAY_OF_moNTH_REGex = /(#{ORDINAlS.join('|')})/I
myArrAy = stRInG.sCan(dAy_oF_MonTh_REgeX)

THis reallY oNlY gets TripPED UP by oRdiNal nUmberS that might appEar In otHEr pHraseS. TryiNg to GeT more RestriCtive ThAn that will probaBly bE pretty ugLy, sInCe yOu Have tO Cover a BunCh oF differEnt caseS. migHt BE ABlE to come up with sometHiNG...BuT it probaBly wouLDn'T bE Worth it. IF yOu wAnt tO PArse tHe stRInG with reaLly finE-GrAiNed contRoL and a vArIabLe aMount OF texT to matCh, then thIs proBAbly just isn't a jOB for regex, to be HOnEST. IT'S harD to Be certain wiThouT KnowIng wHAt ForMAt the Lines ArE, If THis is coming from a filE wIth othEr simIlar lines, if yoU have
 all occurrences of your r egex:
irb( main) :00 1:0 >s ='eve ry 15th of the mont h'
=> "every 15th of t he mo nt h "
ir b (m ain): 003:0>s 2= 'ev er y21s ta nd  28th  of  the mo nth'
=> "e ver y21st and 28t h  o f the mont h"irb(main):00 4:0 > s3 =  ' eve r y 21s t,22nd,  and 2 8 th ofthe month '= > "eve r y 21st, 2 2n d, a nd 28th of the mo n th " 
irb(main):006 :0> my ar r ay =  s3 .sc an(/(\d{1, 2} (?:st | nd|rd|t h )) / ) =>[ ["21st"], ["2 2nd"], ["28 t h"] ]
irb( ma in) : 007:0>  myar ra y  =s2.scan(/(\ d{1, 2}(?:st|n d|rd|t h ))/)
=> [["21st "], [" 28t h"] ]
ir b (m ai n): 00 8 :0> my arr a y =  s.scan( /( \d {1,2} (?:s t | n d |rd| th) )/)=> [[ "15th"]]
irb( mai n):0 0 9:0 >

Th en of  cou rs e you  can a ccess  e ach match using  the  typicalmya rr ay[ in dex]n otatio n ( orloop th rough a l l o ft h e m,  etc).
Edit: Based  o n yo ur comme nts, t h is  i s  how I w ou lddo t h i s:
OR DINA L S= (1..31 ).map{  | n|  Active Su pport: :I nfl ect or::o r dina lize n  } 
DAY_ OF_MO N TH_REGEX = /(# { ORDINALS.join ( '| ' ) }) / i
my arr ay = string .sca n (DAY _OF_ M ON TH_ R EGEX) 

Thi sr ea l ly only gets trippe dup byordin al numbers th at might a p p e ar in ot herp hr a ses. Trying to  getmore restr i ctive th an th at willprobablyb e  prettyugl y,sin cey o uhave to cover a  bun ch  of dif fer ent cas es.  Mi ght  be  a ble to co me up wi th  s om et hin g...b u t it pro ba bly  w oul dn'tb e wort h it.  Ifyo uw ant  to par s et h e st ri ng  wit h r ea lly f ine- g rai ned con trol anda v a riab le  a mount o f text to mat ch , then thi spro bablyj u st isn't  a job for regex, to be honest.  It 's ha rd t o be cert ain  witho utk nowing  whatforma tthe l inesa r e,  if  t his is com i n g f rom a  f ilewith ot her similar lines, ifyou have
 all_occurrences of_your regex:
irb(main):001:0> s =_'every 15th_of_the month'
=>_"every_15th of the_month"
irb(main):003:0> s2 =_'every 21st and 28th_of the month'
=>_"every_21st and 28th of the month"
irb(main):004:0> s3 = 'every 21st, 22nd, and 28th of_the_month'
=> "every_21st,_22nd,_and 28th of the month"
irb(main):006:0>_myarray = s3.scan(/(\d{1,2}(?:st|nd|rd|th))/)
=> [["21st"], ["22nd"],_["28th"]]
irb(main):007:0> myarray_= s2.scan(/(\d{1,2}(?:st|nd|rd|th))/)
=> [["21st"], ["28th"]]
irb(main):008:0> myarray = s.scan(/(\d{1,2}(?:st|nd|rd|th))/)
=> [["15th"]]
irb(main):009:0>

Then_of_course you can_access each match using the typical myarray[index] notation (or_loop through all of them, etc).
Edit:_Based on your_comments,_this_is how I would_do this:
ORDINALS = (1..31).map { |n|_ActiveSupport::Inflector::ordinalize n } 
DAY_OF_MONTH_REGEX = /(#{ORDINALS.join('|')})/i
myarray_= string.scan(DAY_OF_MONTH_REGEX)

This really only gets tripped up_by ordinal numbers that might appear_in other phrases. Trying to_get more_restrictive than that will probably_be pretty ugly,_since you_have to cover_a bunch of different cases. Might_be able to_come up with something...but it probably_wouldn't_be worth it._If_you_want to_parse the string_with_really fine-grained_control_and a variable amount of text_to_match, then this probably just isn't a_job for regex, to_be_honest. It's hard to_be certain without knowing what_format the lines are, if this_is coming_from a_file with other similar lines, if you have