Skip to content

Validate and convert Julian dates

Validate a Julian-format date string or integer and convert it to a DateTime, or convert a DateTime into Julian format. This operation is triggered when a consumer calls Julian.IsValid, Julian.GetYear, Julian.ToDate, or the DateTime.ToJulian() extension method.

Field Value
Operation ID OP-04
Trigger A consumer calls Julian.IsValid, Julian.GetYear, Julian.ToDate, or the DateTime.ToJulian() extension method.
Inputs julian (string or int), optional millenium (int), yearLength (int, for ToJulian)
Outputs IsValid returns bool with an out message; ToDate returns DateTime; ToJulian returns string.
Primary components Julian.IsValid, Julian.GetYear, Julian.ToDate, Julian.ToJulian
Entry point src/SAIF.NetCore.Common/SAIF.NetCore.Common/SAIF.NetCore.Common/Helpers/Julian.cs

Steps

  1. Validate that the input contains at least 4 digits. (BR-017)
  2. On failure: IsValid returns false with the message "The julian must contain at least 4 numbers". ToDate throws ApplicationException.
  3. Derive the year: if millenium < 1000, derive it from the first digit of the current year times 1000; the year digits are those before the final 3, with millenium added when under 1000.
  4. Compute isLeapYear using System.Globalization.JulianCalendar.IsLeapYear.
  5. JulianCalendar treats every 4th year as a leap year, which does not match Gregorian century-year rules (for example 2100)*.
  6. For ToDate: reject a day-of-year greater than 365 (or 366 in a leap year). (BR-017)
  7. On failure: throws ApplicationException with a message that says "must be < {validDaysInYear}" even though the actual check is >, so a day equal to 365/366 is accepted*.
  8. For ToDate: return 1 January of the derived year plus (day − 1) days.
  9. A day-of-year value of 000 is not rejected and yields 31 December of the previous year*.
  10. For ToJulian(date, yearLength=2): when yearLength equals exactly 2, convert date.ToString("yy") to an int and concatenate it with the 3-digit day of year as a string — the int conversion drops any leading zero from the two-digit year (for example January 1, 2005 produces "5001", not "05001"). For any other yearLength value, the full numeric date.Year, without zero padding, is concatenated with the 3-digit day instead (for example January 1, 2005 produces "2005001", while January 1 of year 1 produces "1001", not a zero-padded "0001001"). This member is independent of IsValid/ToDate and does not call either.

Process flow

The following diagram visualizes this operation's control flow, split into its four independent entry points — IsValid ends at its own bool return and is never followed by year/day conversion on its own; ToDate separately re-derives the year and builds a DateTime; ToJulian is a wholly independent forward conversion.

flowchart TD
    subgraph IV["Julian.IsValid(julian)"]
        IV_Start([Call IsValid]) --> IV_Check{At least 4 digits?}
        IV_Check -->|No| IV_False(["Return false<br>with message"])
        IV_Check -->|Yes| IV_True(["Return true"])
    end

    subgraph GY["Julian.GetYear(julian)"]
        GY_Start([Call GetYear]) --> GY_Derive["Derive year from<br>digits before final 3"]
        GY_Derive --> GY_Leap["Compute isLeapYear<br>via JulianCalendar"]
        GY_Leap --> GY_Return(["Return year,<br>out isLeapYear"])
    end

    subgraph TD["Julian.ToDate(julian)"]
        TD_Start([Call ToDate]) --> TD_Valid{IsValid?}
        TD_Valid -->|No| TD_Throw1([Throw ApplicationException])
        TD_Valid -->|Yes| TD_Year["Call GetYear"]
        TD_Year --> TD_DayCheck{day-of-year within<br>valid days in year?}
        TD_DayCheck -->|No| TD_Throw2([Throw ApplicationException])
        TD_DayCheck -->|Yes| TD_Build(["Return Jan 1 of year<br>plus day minus 1"])
    end

    subgraph TJ["DateTime.ToJulian(yearLength)"]
        TJ_Start([Call ToJulian]) --> TJ_Check{yearLength == 2?}
        TJ_Check -->|Yes| TJ_Short["year = int of two-digit<br>yy, no leading zero"]
        TJ_Check -->|No| TJ_Full["year = full numeric<br>date.Year, no padding"]
        TJ_Short --> TJ_Build["Concatenate year with<br>3-digit day of year"]
        TJ_Full --> TJ_Build
        TJ_Build --> TJ_Return(["Return string"])
    end

Failure and edge-case behavior

  • Fewer than 4 digits: IsValid returns false; ToDate throws ApplicationException.
  • Day-of-year greater than the valid days in year: throws ApplicationException, though the message text does not match the actual > comparison used*.
  • Day-of-year 000: accepted, yielding 31 December of the previous year*.
  • Leap-year detection via JulianCalendar can diverge from Gregorian rules on century years such as 2100*.

Note: Statements marked with * were inferred from code structure, naming conventions, or contextual evidence where direct confirmation was not available in source artifacts. Statements marked with ** could not be confirmed; treat as unknown until verified.

Sources

  • src/SAIF.NetCore.Common/SAIF.NetCore.Common/SAIF.NetCore.Common/Helpers/Julian.cs