class Fugit::Nat::SlotGroup

def determine_hms

def determine_hms
  return [ [ [ '*' ], [ '*' ] ] ] if @hms.empty?
  hms = @hms.dup
    #
  while ig = (hms.count > 1 && hms.index { |hm| hm.graded? }) do
    sg = hms[ig]
    so = hms.delete_at(ig == 0 ? 1 : ig - 1)
    sg.append(so)
  end
  hms
    .collect(&:a)
    .inject({}) { |r, hm|
      hm[1].each { |m| (r[m] ||= []).concat(hm[0]) }
      r }
    .inject({}) { |r, (m, hs)|
      (r[hs.sort] ||= []) << m
      r }
    .to_a
end

def initialize(slots)

def initialize(slots)
"SlotGroup.new " + slots.inspect
  @slots = {}
  @hms = []
  slots.each do |s|
    if s.key == :hm
      #ls = @hms.last; @hms.pop if ls && ls.key == :hm && ls.weak == true
      @hms << s
    elsif hs = @slots[s.key]
      hs.append(s)
    else
      @slots[s.key] = s
    end
  end
  if @slots[:monthday] || @slots[:weekday]
    @hms << make_slot(:hm, 0, 0) if @hms.empty?
  elsif @slots[:month]
    @hms << make_slot(:hm, 0, 0) if @hms.empty?
    @slots[:monthday] ||= make_slot(:monthday, 1)
  end
end

def make_slot(key, data0, data1=nil)

def make_slot(key, data0, data1=nil)
  Fugit::Nat::Slot.new(key, data0, data1)
end

def parse_cron(hm, opts)

def parse_cron(hm, opts)
  a = [
    slot(:second, '0'),
    hm[1],
    hm[0],
    slot(:monthday, '*'),
    slot(:month, '*'),
    slot(:weekday, '*') ]
  tz = @slots[:tz]
  a << tz.data0 if tz
  a.shift if a.first == [ '0' ]
  letters_last = lambda { |x| x.is_a?(Numeric) ? x : 999_999 }
  s = a
    .collect { |e|
      e.uniq.sort_by(&letters_last).collect(&:to_s).join(',') }
    .join(' ')
  c = Fugit::Cron.parse(s)
  if opts[:strict]
    restrict(a, c)
  else
    c
  end
end

def restrict(a, cron)


It happens here because it's nat being strict, not cron.

from "every 17 hours".
cron string, but makes not much sense when derived via `.parse_nat`
For example, "0 0/17 * * *" (gh-86) is a perfectly valid

Return nil if the cron is "not strict"
def restrict(a, cron)
  if m = ((a[1] && a[1][0]) || '').match(/^(\d+|\*)\/(\d+)$/)
     sla = m[1].to_i
    return nil unless [ 1, 2, 3, 4, 5, 6, 8, 12 ].include?(sla)
  end
  cron
end

def slot(key, default)

def slot(key, default)
  s = @slots[key]
  s ? s.data0 : [ default ]
end

def to_crons(opts)

def to_crons(opts)
  multi = opts.has_key?(:multi) ? opts[:multi] : false
  hms = determine_hms
  if multi == :fail && hms.count > 1
    fail(ArgumentError.new(
      "multiple crons in #{opts[:_s].inspect} - #{@slots.inspect}"))
  elsif multi == true
    hms.collect { |hm| parse_cron(hm, opts) }
  else
    parse_cron(hms.first, opts)
  end
end