{"record":{"id":"830d3523368ee6d1","repo":"ruby/ruby","slug":"yday-yday-out-of-range","errorCode":null,"errorMessage":"yday #{yday} out of range","messagePattern":"yday #(.+?) out of range","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/time.rb","lineNumber":214,"sourceCode":"      end\n      return year, mon, day, hour, min, sec\n    end\n    private :apply_offset\n\n    def make_time(date, year, yday, mon, day, hour, min, sec, sec_fraction, zone, now)\n      if !year && !yday && !mon && !day && !hour && !min && !sec && !sec_fraction\n        raise ArgumentError, \"no time information in #{date.inspect}\"\n      end\n\n      off = nil\n      if year || now\n        off_year = year || now.year\n        off = zone_offset(zone, off_year) if zone\n      end\n\n      if yday\n        unless (1..366) === yday\n          raise ArgumentError, \"yday #{yday} out of range\"\n        end\n        mon, day = (yday-1).divmod(31)\n        mon += 1\n        day += 1\n        t = make_time(date, year, nil, mon, day, hour, min, sec, sec_fraction, zone, now)\n        diff = yday - t.yday\n        return t if diff.zero?\n        day += diff\n        if day > 28 and day > (mday = month_days(off_year, mon))\n          if (mon += 1) > 12\n            raise ArgumentError, \"yday #{yday} out of range\"\n          end\n          day -= mday\n        end\n        return make_time(date, year, nil, mon, day, hour, min, sec, sec_fraction, zone, now)\n      end\n\n      if now and now.respond_to?(:getlocal)","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/ruby/ruby/blob/0e5b888e1c355f3f728f2659f085820937dada48/lib/time.rb#L196-L232","documentation":"When Date._parse recognizes an ordinal-date component (:yday), make_time validates it against 1..366 before doing any calendar math. A day-of-year outside that window (0, negative, or >366) raises ArgumentError 'yday N out of range' immediately.","triggerScenarios":"Time.parse(\"2021-400\"); Time.parse(\"2021-0\"); strings like \"21-367\" where the parser reads a day-of-year; exporting tools that emit DDD without clamping.","commonSituations":"Malformed ordinal dates from data exports or industrial loggers (YYYY-DDD format); user-typed day-of-year treated as 0-based; hand-rolled date arithmetic producing yday 0 or 367+.","solutions":["Validate before parsing: yday must be in 1..366 (1..365 for non-leap years)","Build ordinal dates with the standard API: Date.ordinal(year, yday).to_time instead of string round-trips","Clamp or reject the value at the producer that generates the string","Rescue ArgumentError and report the offending record id"],"exampleFix":"# before\nt = Time.parse(\"2021-400\")           # raises: yday 400 out of range\n\n# after\nraise ArgumentError, \"bad yday #{doy}\" unless (1..366).cover?(doy)\nt = Date.ordinal(2021, doy)&.to_time","handlingStrategy":"validation","validationCode":"raise ArgumentError, \"yday #{doy} out of range\" unless (1..366).cover?(doy)\nt = Date.ordinal(year, doy).to_time","typeGuard":null,"tryCatchPattern":"begin\n  Time.parse(ordinal_str)\nrescue ArgumentError\n  quarantine(record)  # log the bad ordinal date and move on\nend","preventionTips":["Validate day-of-year at ingestion: 1..366, tighter for non-leap years","Generate ordinal dates with Date.ordinal, never string interpolation","Add a max-DDD sanity check in exporters"],"tags":["ruby","time","date-parsing","day-of-year","ordinal-date"],"backgroundTag":"day-of-year-out-of-range","analyzedSha":"0e5b888e1c355f3f728f2659f085820937dada48","analyzedAt":"2026-08-21T14:25:43.473Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}