Implement inverse time conversion functions for Unix and XT epochs
Some checks failed
Builds / ExectOS (amd64, debug) (push) Successful in -59m21s
Builds / ExectOS (amd64, release) (push) Successful in -59m23s
Builds / ExectOS (i686, debug) (push) Failing after -59m32s
Builds / ExectOS (i686, release) (push) Failing after -59m34s

This commit is contained in:
2026-04-25 23:32:07 +02:00
parent 439ea891ca
commit 4d12f7ac01
2 changed files with 224 additions and 0 deletions

View File

@@ -161,3 +161,223 @@ RTL::Time::TimeFieldsToXtEpoch(IN PTIME_FIELDS TimeFields,
/* Return success */
return STATUS_SUCCESS;
}
/**
* Converts a 64-bit Unix timestamp into a TIME_FIELDS calendar structure.
*
* @param UnixTime
* Supplies a pointer to a 64-bit integer that contains the number of
* seconds elapsed since January 1, 1970.
*
* @param TimeFields
* Supplies a pointer to a TIME_FIELDS structure that receives the converted calendar data.
*
* @return This routine returns a status code.
*
* @since XT 1.0
*/
XTAPI
XTSTATUS
RTL::Time::UnixEpochToTimeFields(IN PLONGLONG UnixTime,
OUT PTIME_FIELDS TimeFields)
{
LONG CalculatedDay, CalculatedMonth, CalculatedYear;
LONGLONG Era, SecondsOfDay, TotalSeconds, UnixDays;
ULONG DayOfEra, DayOfYear, YearOfEra;
/* Validate pointers */
if(UnixTime == NULLPTR || TimeFields == NULLPTR)
{
/* Invalid pointers provided, return error code */
return STATUS_INVALID_PARAMETER;
}
/* Extract total seconds */
TotalSeconds = *UnixTime;
/* Check sign */
if(TotalSeconds >= 0)
{
/* Divide TotalSeconds by seconds per day and compute the remainder */
UnixDays = TotalSeconds / TIME_SECONDS_PER_DAY;
SecondsOfDay = TotalSeconds % TIME_SECONDS_PER_DAY;
}
else
{
/* Subtract seconds per day minus one from TotalSeconds and divide by seconds per day */
UnixDays = (TotalSeconds - (TIME_SECONDS_PER_DAY - 1)) / TIME_SECONDS_PER_DAY;
SecondsOfDay = TotalSeconds % TIME_SECONDS_PER_DAY;
/* Check if SecondsOfDay is less than zero */
if(SecondsOfDay < 0)
{
/* Add seconds per day to SecondsOfDay */
SecondsOfDay += TIME_SECONDS_PER_DAY;
}
}
/* Unix timestamps do not natively support milliseconds */
TimeFields->Milliseconds = 0;
/* Shift epoch from 1970-01-01 to 0000-03-01 to align with leap year cycles */
UnixDays += 719468LL;
/* Calculate the 400-year era */
Era = (UnixDays >= 0 ? UnixDays : UnixDays - 146096) / 146097;
/* Calculate the day within the current era */
DayOfEra = (ULONG)(UnixDays - Era * 146097);
/* Calculate the year within the current era */
YearOfEra = (DayOfEra - (DayOfEra / 1460) + (DayOfEra / 36524) - (DayOfEra / 146096)) / 365;
/* Combine era and year-of-era to get the absolute year */
CalculatedYear = (LONG)(YearOfEra + Era * 400);
/* Calculate the day of the year */
DayOfYear = DayOfEra - (365 * YearOfEra + (YearOfEra / 4) - (YearOfEra / 100));
/* Calculate the month */
CalculatedMonth = (5 * DayOfYear + 2) / 153;
/* Calculate the exact day of the month */
CalculatedDay = DayOfYear - (153 * CalculatedMonth + 2) / 5 + 1;
/* Shift the month back to the standard Gregorian calendar */
CalculatedMonth += (CalculatedMonth < 10 ? 3 : -9);
/* If the month is January or February, it belongs to the next computational year */
CalculatedYear += (CalculatedMonth <= 2 ? 1 : 0);
/* Validate computed year */
if(CalculatedYear < 0 || CalculatedYear > 30827)
{
/* Year is out of bounds, return error code */
return STATUS_NOT_SUPPORTED;
}
/* Populate the output structure */
TimeFields->Day = (SHORT)CalculatedDay;
TimeFields->Month = (SHORT)CalculatedMonth;
TimeFields->Year = (SHORT)CalculatedYear;
TimeFields->Hour = (SHORT)(SecondsOfDay / TIME_SECONDS_PER_HOUR);
TimeFields->Minute = (SHORT)((SecondsOfDay % TIME_SECONDS_PER_HOUR) / TIME_SECONDS_PER_MINUTE);
TimeFields->Second = (SHORT)(SecondsOfDay % TIME_SECONDS_PER_MINUTE);
TimeFields->Milliseconds = 0;
TimeFields->Weekday = (SHORT)(((UnixDays + 4) % 7 + 7) % 7);
/* Return success */
return STATUS_SUCCESS;
}
/**
* Converts a 64-bit XT timestamp into a TIME_FIELDS calendar structure.
*
* @param XtTime
* Supplies a pointer to a variable that contains the time value in
* 100-nanosecond intervals since January 1, 1601.
*
* @param TimeFields
* Supplies a pointer to a TIME_FIELDS structure that receives the converted calendar data.
*
* @return This routine returns a status code.
*
* @since XT 1.0
*/
XTAPI
XTSTATUS
RTL::Time::XtEpochToTimeFields(IN PLARGE_INTEGER XtTime,
OUT PTIME_FIELDS TimeFields)
{
ULONG CurrentYear, Days, Leap, Periods1Year, Periods4Years, Periods100Years, Periods400Years;
ULONGLONG TotalSeconds, ElapsedDays, TotalSecondsOfDay;
/* Validate pointers */
if(XtTime == NULLPTR || TimeFields == NULLPTR)
{
/* Invalid pointers provided, return error code */
return STATUS_INVALID_PARAMETER;
}
/* The XT Epoch starts at 1601, negative absolute system time is not supported */
if(XtTime->QuadPart < 0)
{
/* Invalid time value, return error code */
return STATUS_INVALID_PARAMETER;
}
/* Extract ticks into whole seconds and remaining milliseconds */
TotalSeconds = XtTime->QuadPart / TIME_TICKS_PER_SECOND;
TimeFields->Milliseconds = (SHORT)((XtTime->QuadPart % TIME_TICKS_PER_SECOND) / TIME_TICKS_PER_MILLISECOND);
/* Split total seconds into absolute elapsed days and time of the current day */
ElapsedDays = TotalSeconds / TIME_SECONDS_PER_DAY;
TotalSecondsOfDay = TotalSeconds % TIME_SECONDS_PER_DAY;
/* Calculate hour, minute, and second */
TimeFields->Hour = (SHORT)(TotalSecondsOfDay / TIME_SECONDS_PER_HOUR);
TimeFields->Minute = (SHORT)((TotalSecondsOfDay % TIME_SECONDS_PER_HOUR) / TIME_SECONDS_PER_MINUTE);
TimeFields->Second = (SHORT)(TotalSecondsOfDay % TIME_SECONDS_PER_MINUTE);
/* Calculate weekday */
TimeFields->Weekday = (SHORT)((ElapsedDays + 1) % 7);
/* Calculate the year using the Gregorian leap year cycles */
Days = (ULONG)ElapsedDays;
CurrentYear = 1601;
/* Calculate completed 400-year periods */
Periods400Years = Days / 146097;
CurrentYear += Periods400Years * 400;
Days %= 146097;
/* Calculate completed 100-year periods */
Periods100Years = Days / 36524;
if(Periods100Years == 4)
{
/* The leap year at the very end of a 400-year cycle */
Periods100Years = 3;
}
/* Update the current year and remaining days */
CurrentYear += Periods100Years * 100;
Days -= Periods100Years * 36524;
/* Calculate completed 4-year periods */
Periods4Years = Days / 1461;
CurrentYear += Periods4Years * 4;
Days %= 1461;
/* Calculate completed 1-year periods */
Periods1Year = Days / 365;
if(Periods1Year == 4)
{
/* The leap year at the end of a normal 4-year cycle */
Periods1Year = 3;
}
/* Update the current year and remaining days */
CurrentYear += Periods1Year;
Days -= Periods1Year * 365;
/* Set the calculated year */
TimeFields->Year = (SHORT)CurrentYear;
/* Check if the calculated year is a leap year */
Leap = LeapYear(TimeFields->Year) ? 1 : 0;
TimeFields->Month = 1;
/* Subtract days of each month to find the current month */
while(Days >= DaysInMonth[Leap][TimeFields->Month - 1])
{
/* Calculate the days of the current month and advance the month index */
Days -= DaysInMonth[Leap][TimeFields->Month - 1];
TimeFields->Month++;
}
/* Store the remainder as the 1-based day of the month */
TimeFields->Day = (SHORT)(Days + 1);
/* Return success */
return STATUS_SUCCESS;
}