12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018 |
- /*++
- Copyright (c) 2013 Minoca Corp. All Rights Reserved
- Module Name:
- time.c
- Abstract:
- This module implements calendar time support functions.
- Author:
- Evan Green 6-Aug-2013
- Environment:
- Any
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include "rtlp.h"
- #include "time.h"
- #include <minoca/lib/tzfmt.h>
- //
- // ---------------------------------------------------------------- Definitions
- //
- //
- // This value defines the two digit year beginning in which the 21st century is
- // assumed.
- //
- #define TWO_DIGIT_YEAR_CUTOFF 70
- #define TWENTIETH_CENTURY 1900
- #define TWENTY_FIRST_CENTURY 2000
- //
- // Define the period of the entire Gregorian cycle.
- //
- #define GREGORIAN_CYCLE_YEARS 400
- #define GREGORIAN_CYCLE_DAYS ((365 * 400) + 100 - 4 + 1)
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- KSTATUS
- RtlpScanTimeStrings (
- PSTR Input,
- PSTR *Strings,
- ULONG StringCount,
- PLONG Index,
- PLONG Size
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // ------------------------------------------------------------------ Functions
- //
- RTL_API
- KSTATUS
- RtlSystemTimeToGmtCalendarTime (
- PSYSTEM_TIME SystemTime,
- PCALENDAR_TIME CalendarTime
- )
- /*++
- Routine Description:
- This routine converts the given system time into calendar time in the
- GMT time zone.
- Arguments:
- SystemTime - Supplies a pointer to the system time to convert.
- CalendarTime - Supplies a pointer to the calendar time to initialize based
- on the given system time.
- Return Value:
- STATUS_SUCCESS on success.
- STATUS_OUT_OF_BOUNDS if the given system time is too funky.
- --*/
- {
- LONG Day;
- LONG Hour;
- INT Leap;
- LONG Minute;
- LONG Month;
- LONG RawDays;
- LONGLONG RawTime;
- LONG Second;
- LONG Weekday;
- LONG Year;
- RtlZeroMemory(CalendarTime, sizeof(CALENDAR_TIME));
- RawTime = SystemTime->Seconds;
- if ((RawTime > MAX_TIME_ZONE_DATE) ||
- (RawTime < MIN_TIME_ZONE_DATE)) {
- return STATUS_OUT_OF_BOUNDS;
- }
- RawDays = RawTime / SECONDS_PER_DAY;
- RawTime -= (LONGLONG)RawDays * SECONDS_PER_DAY;
- if (RawTime < 0) {
- RawTime += SECONDS_PER_DAY;
- RawDays -= 1;
- }
- Weekday = (TIME_ZONE_EPOCH_WEEKDAY + RawDays) % DAYS_PER_WEEK;
- if (Weekday < 0) {
- Weekday = DAYS_PER_WEEK + Weekday;
- }
- Year = RtlpComputeYearForDays(&RawDays);
- Leap = 0;
- if (IS_LEAP_YEAR(Year)) {
- Leap = 1;
- }
- //
- // Subtract off the months.
- //
- Month = 0;
- Day = RawDays;
- while (Day >= RtlDaysPerMonth[Leap][Month]) {
- Day -= RtlDaysPerMonth[Leap][Month];
- Month += 1;
- }
- //
- // Days of the month start with 1.
- //
- Day += 1;
- //
- // Figure out the time of day.
- //
- Second = (LONG)RawTime;
- Hour = Second / SECONDS_PER_HOUR;
- Second -= Hour * SECONDS_PER_HOUR;
- Minute = Second / SECONDS_PER_MINUTE;
- Second -= Minute * SECONDS_PER_MINUTE;
- //
- // Fill in the structure.
- //
- CalendarTime->Year = Year;
- CalendarTime->Month = Month;
- CalendarTime->Day = Day;
- CalendarTime->Hour = Hour;
- CalendarTime->Minute = Minute;
- CalendarTime->Second = Second;
- CalendarTime->Nanosecond = SystemTime->Nanoseconds;
- CalendarTime->Weekday = Weekday;
- CalendarTime->YearDay = RawDays;
- CalendarTime->IsDaylightSaving = FALSE;
- return STATUS_SUCCESS;
- }
- RTL_API
- KSTATUS
- RtlCalendarTimeToSystemTime (
- PCALENDAR_TIME CalendarTime,
- PSYSTEM_TIME SystemTime
- )
- /*++
- Routine Description:
- This routine converts the given calendar time into its corresponding system
- time.
- Arguments:
- CalendarTime - Supplies a pointer to the calendar time to convert.
- SystemTime - Supplies a pointer where the system time will be returned.
- Return Value:
- STATUS_SUCCESS on success.
- STATUS_OUT_OF_BOUNDS if the given calendar time is too funky.
- --*/
- {
- LONGLONG Days;
- RtlpNormalizeCalendarTime(CalendarTime);
- if ((CalendarTime->Year > MAX_TIME_ZONE_YEAR) ||
- (CalendarTime->Year < MIN_TIME_ZONE_YEAR)) {
- return STATUS_OUT_OF_BOUNDS;
- }
- Days = RtlpComputeDaysForYear(CalendarTime->Year);
- //
- // The normalize function above ensures that the year day is correct.
- //
- Days += CalendarTime->YearDay;
- SystemTime->Nanoseconds = CalendarTime->Nanosecond;
- SystemTime->Seconds = ((LONGLONG)Days * SECONDS_PER_DAY) +
- (CalendarTime->Hour * SECONDS_PER_HOUR) +
- (CalendarTime->Minute * SECONDS_PER_MINUTE) +
- CalendarTime->Second -
- CalendarTime->GmtOffset;
- return STATUS_SUCCESS;
- }
- RTL_API
- KSTATUS
- RtlGmtCalendarTimeToSystemTime (
- PCALENDAR_TIME CalendarTime,
- PSYSTEM_TIME SystemTime
- )
- /*++
- Routine Description:
- This routine converts the given calendar time, assumed to be a GMT date and
- time, into its corresponding system time. On success, this routine will
- update the supplied calendar time to fill out all fields.
- Arguments:
- CalendarTime - Supplies a pointer to the GMT calendar time to convert.
- SystemTime - Supplies a pointer where the system time will be returned.
- Return Value:
- STATUS_SUCCESS on success.
- STATUS_OUT_OF_BOUNDS if the given GMT calendar time is too funky.
- --*/
- {
- CALENDAR_TIME GmtCalendarTime;
- KSTATUS Status;
- //
- // The supplied time is meant to be interpreted in the GMT time zone. Smash
- // the GMT offset and any time zone information.
- //
- CalendarTime->GmtOffset = 0;
- RtlZeroMemory(&(CalendarTime->TimeZone), sizeof(CalendarTime->TimeZone));
- //
- // Convert the given GMT calendar time into a system time. This normalizes
- // the calendar time as well.
- //
- Status = RtlCalendarTimeToSystemTime(CalendarTime, SystemTime);
- if (!KSUCCESS(Status)) {
- return Status;
- }
- //
- // Convert the system time back to a GMT calendar time to get all the
- // fields filled out.
- //
- Status = RtlSystemTimeToGmtCalendarTime(SystemTime, &GmtCalendarTime);
- if (!KSUCCESS(Status)) {
- return Status;
- }
- //
- // With success on the horizon, copy the fully qualified calendar time to
- // the supplied pointer.
- //
- RtlCopyMemory(CalendarTime, &GmtCalendarTime, sizeof(CALENDAR_TIME));
- return STATUS_SUCCESS;
- }
- RTL_API
- UINTN
- RtlFormatDate (
- PSTR StringBuffer,
- ULONG StringBufferSize,
- PSTR Format,
- PCALENDAR_TIME CalendarTime
- )
- /*++
- Routine Description:
- This routine converts the given calendar time into a string governed by
- the given format string.
- Arguments:
- StringBuffer - Supplies a pointer where the converted string will be
- returned, including the terminating null.
- StringBufferSize - Supplies the size of the string buffer in bytes.
- Format - Supplies the format string to govern the conversion. Ordinary
- characters in the format string will be copied verbatim to the output
- string. Conversions will be substituted for their corresponding value
- in the provided calendar time. Conversions start with a '%' character,
- followed by an optional E or O character, followed by a conversion
- specifier. The conversion specifier can take the following values:
- %a - Replaced by the abbreviated weekday.
- %A - Replaced by the full weekday.
- %b - Replaced by the abbreviated month name.
- %B - Replaced by the full month name.
- %c - Replaced by the locale's appropriate date and time representation.
- %C - Replaced by the year divided by 100 (century) [00,99].
- %d - Replaced by the day of the month [01,31].
- %D - Equivalent to "%m/%d/%y".
- %e - Replaced by the day of the month [ 1,31]. A single digit is
- preceded by a space.
- %F - Equivalent to "%Y-%m-%d" (the ISO 8601:2001 date format).
- %G - The ISO 8601 week-based year [0001,9999]. The week-based year and
- the Gregorian year can differ in the first week of January.
- %h - Equivalent to %b (abbreviated month).
- %H - Replaced by the 24 hour clock hour [00,23].
- %I - Replaced by the 12 hour clock hour [01,12].
- %J - Replaced by the nanosecond [0,999999999].
- %j - Replaced by the day of the year [001,366].
- %m - Replaced by the month number [01,12].
- %M - Replaced by the minute [00,59].
- %N - Replaced by the nanosecond [000000000,999999999]
- %n - Replaced by a newline.
- %p - Replaced by "AM" or "PM".
- %P - Replaced by "am" or "pm".
- %q - Replaced by the millisecond [0,999].
- %r - Replaced by the time in AM/PM notation: "%I:%M:%S %p".
- %R - Replaced by the time in 24 hour notation: "%H:%M".
- %S - Replaced by the second [00,60].
- %s - Replaced by the number of seconds since 1970 GMT.
- %t - Replaced by a tab.
- %T - Replaced by the time: "%H:%M:%S".
- %u - Replaced by the weekday number, with 1 representing Monday [1,7].
- %U - Replaced by the week number of the year [00,53]. The first Sunday
- of January is the first day of week 1. Days before this are week 0.
- %V - Replaced by the week number of the year with Monday as the first
- day in the week [01,53]. If the week containing January 1st has 4
- or more days in the new year, it is considered week 1. Otherwise,
- it is the last week of the previous year, and the next week is 1.
- %w - Replaced by the weekday number [0,6], with 0 representing Sunday.
- %W - Replaced by the week number [00,53]. The first Monday of January
- is the first day of week 1. Days before this are in week 0.
- %x - Replaced by the locale's appropriate date representation.
- %X - Replaced by the locale's appropriate time representation.
- %y - Replaced by the last two digits of the year [00,99].
- %Y - Replaced by the full four digit year [0001,9999].
- %z - Replaced by the offset from UTC in the standard ISO 8601:2000
- standard format (+hhmm or -hhmm), or by no characters if no
- timezone is terminable. If the "is daylight saving" member of the
- calendar structure is greater than zero, then the daylight saving
- offset is used. If the dayslight saving member of the calendar
- structure is negative, no characters are returned.
- %Z - Replaced by the timezone name or abbreviation, or by no bytes if
- no timezone information exists.
- %% - Replaced by a literal '%'.
- CalendarTime - Supplies a pointer to the calendar time value to use in the
- substitution.
- Return Value:
- Returns the number of characters written to the output buffer, including
- the null terminator.
- --*/
- {
- PSTR CopyString;
- ULONG CopyStringSize;
- ULONG DefaultFieldSize;
- BOOL Evening;
- LONG Hour12;
- LONGLONG Integer;
- LONG IsoYear;
- PSTR SavedFormat;
- CHAR Sign;
- CHAR Specifier;
- KSTATUS Status;
- PSTR String;
- ULONG StringSize;
- SYSTEM_TIME SystemTime;
- LONG WeekNumber;
- CHAR WorkingBuffer[13];
- BOOL ZeroPad;
- LONG ZoneOffset;
- LONG ZoneOffsetHours;
- LONG ZoneOffsetMinutes;
- Hour12 = CalendarTime->Hour;
- if (Hour12 == 0) {
- Hour12 = 12;
- } else if (Hour12 > 12) {
- Hour12 -= 12;
- }
- Evening = FALSE;
- if (CalendarTime->Hour >= 12) {
- Evening = TRUE;
- }
- SavedFormat = NULL;
- String = StringBuffer;
- StringSize = StringBufferSize;
- while (StringSize != 0) {
- //
- // If this is the end of the format string, then either it's really the
- // end, or it's just the end of the temporary format string.
- //
- if (*Format == '\0') {
- if (SavedFormat != NULL) {
- Format = SavedFormat;
- SavedFormat = NULL;
- continue;
- } else {
- *String = '\0';
- String += 1;
- StringSize -= 1;
- break;
- }
- }
- //
- // Handle ordinary characters in the format.
- //
- if (*Format != '%') {
- *String = *Format;
- Format += 1;
- String += 1;
- StringSize -= 1;
- continue;
- }
- Format += 1;
- //
- // Pass over an E or an O for alternate representations. At some point
- // these should be supported.
- //
- if (*Format == 'E') {
- Format += 1;
- }
- if (*Format == 'O') {
- Format += 1;
- }
- Specifier = *Format;
- Format += 1;
- CopyString = NULL;
- CopyStringSize = -1;
- Integer = -1;
- ZeroPad = FALSE;
- DefaultFieldSize = 2;
- switch (Specifier) {
- case 'a':
- if ((CalendarTime->Weekday >= TimeZoneWeekdaySunday) &&
- (CalendarTime->Weekday <= TimeZoneWeekdaySaturday)) {
- CopyString =
- RtlAbbreviatedWeekdayStrings[CalendarTime->Weekday];
- } else {
- return 0;
- }
- break;
- case 'A':
- if ((CalendarTime->Weekday >= TimeZoneWeekdaySunday) &&
- (CalendarTime->Weekday <= TimeZoneWeekdaySaturday)) {
- CopyString = RtlWeekdayStrings[CalendarTime->Weekday];
- } else {
- return 0;
- }
- break;
- case 'b':
- case 'h':
- if ((CalendarTime->Month >= TimeZoneMonthJanuary) &&
- (CalendarTime->Month <= TimeZoneMonthDecember)) {
- CopyString = RtlAbbreviatedMonthStrings[CalendarTime->Month];
- } else {
- return 0;
- }
- break;
- case 'B':
- if ((CalendarTime->Month >= TimeZoneMonthJanuary) &&
- (CalendarTime->Month <= TimeZoneMonthDecember)) {
- CopyString = RtlMonthStrings[CalendarTime->Month];
- } else {
- return 0;
- }
- break;
- case 'c':
- SavedFormat = Format;
- Format = "%a %b %e %H:%M:%S %Y";
- continue;
- case 'C':
- Integer = CalendarTime->Year / YEARS_PER_CENTURY;
- ZeroPad = TRUE;
- break;
- case 'd':
- Integer = CalendarTime->Day;
- ZeroPad = TRUE;
- break;
- case 'D':
- SavedFormat = Format;
- Format = "%m/%d/%y";
- continue;
- case 'e':
- Integer = CalendarTime->Day;
- break;
- case 'F':
- SavedFormat = Format;
- Format = "%Y-%m-%d";
- continue;
- case 'g':
- case 'G':
- Status = RtlpCalculateIsoWeekNumber(CalendarTime->Year,
- CalendarTime->YearDay,
- CalendarTime->Weekday,
- NULL,
- &IsoYear);
- if (!KSUCCESS(Status)) {
- CopyStringSize = 0;
- } else {
- if (Specifier == 'g') {
- Integer = IsoYear % YEARS_PER_CENTURY;
- DefaultFieldSize = 2;
- } else {
- Integer = IsoYear;
- DefaultFieldSize = 4;
- }
- ZeroPad = TRUE;
- }
- break;
- case 'H':
- Integer = CalendarTime->Hour;
- ZeroPad = TRUE;
- break;
- case 'I':
- Integer = Hour12;
- ZeroPad = TRUE;
- break;
- case 'J':
- Integer = CalendarTime->Nanosecond;
- ZeroPad = TRUE;
- DefaultFieldSize = 9;
- break;
- case 'j':
- Integer = CalendarTime->YearDay + 1;
- ZeroPad = TRUE;
- DefaultFieldSize = 3;
- break;
- case 'm':
- Integer = CalendarTime->Month + 1;
- ZeroPad = TRUE;
- break;
- case 'M':
- Integer = CalendarTime->Minute;
- ZeroPad = TRUE;
- break;
- case 'N':
- Integer = CalendarTime->Nanosecond;
- ZeroPad = TRUE;
- DefaultFieldSize = 9;
- break;
- case 'n':
- WorkingBuffer[0] = '\n';
- CopyString = WorkingBuffer;
- CopyStringSize = 1;
- break;
- case 'p':
- CopyString = RtlAmPmStrings[0][Evening];
- break;
- case 'P':
- CopyString = RtlAmPmStrings[1][Evening];
- break;
- case 'q':
- Integer = CalendarTime->Nanosecond / 1000000;
- ZeroPad = TRUE;
- DefaultFieldSize = 3;
- break;
- case 'r':
- SavedFormat = Format;
- Format = "%I:%M:%S %p";
- continue;
- case 'R':
- SavedFormat = Format;
- Format = "%H:%M";
- continue;
- case 's':
- Status = RtlCalendarTimeToSystemTime(CalendarTime, &SystemTime);
- if (!KSUCCESS(Status)) {
- Integer = 0;
- } else {
- Integer = SystemTime.Seconds + SYSTEM_TIME_TO_EPOCH_DELTA;
- }
- break;
- case 'S':
- Integer = CalendarTime->Second;
- ZeroPad = TRUE;
- break;
- case 't':
- WorkingBuffer[0] = '\t';
- CopyString = WorkingBuffer;
- CopyStringSize = 1;
- break;
- case 'T':
- SavedFormat = Format;
- Format = "%H:%M:%S";
- continue;
- case 'u':
- Integer = CalendarTime->Weekday;
- if (Integer == TimeZoneWeekdaySunday) {
- Integer = DAYS_PER_WEEK;
- }
- DefaultFieldSize = 1;
- break;
- case 'U':
- Status = RtlpCalculateWeekNumber(CalendarTime->Year,
- CalendarTime->YearDay,
- TimeZoneWeekdaySunday,
- &WeekNumber);
- if (!KSUCCESS(Status)) {
- CopyStringSize = 0;
- } else {
- Integer = WeekNumber;
- ZeroPad = TRUE;
- }
- break;
- case 'V':
- Status = RtlpCalculateIsoWeekNumber(CalendarTime->Year,
- CalendarTime->YearDay,
- CalendarTime->Weekday,
- &WeekNumber,
- NULL);
- if (!KSUCCESS(Status)) {
- CopyStringSize = 0;
- } else {
- Integer = WeekNumber;
- ZeroPad = TRUE;
- }
- break;
- case 'w':
- Integer = CalendarTime->Weekday;
- DefaultFieldSize = 1;
- break;
- case 'W':
- Status = RtlpCalculateWeekNumber(CalendarTime->Year,
- CalendarTime->YearDay,
- TimeZoneWeekdayMonday,
- &WeekNumber);
- if (!KSUCCESS(Status)) {
- CopyStringSize = 0;
- } else {
- Integer = WeekNumber;
- ZeroPad = TRUE;
- }
- break;
- case 'x':
- SavedFormat = Format;
- Format = "%m/%d/%y";
- continue;
- case 'X':
- SavedFormat = Format;
- Format = "%H:%M:%S";
- continue;
- case 'y':
- Integer = CalendarTime->Year % YEARS_PER_CENTURY;
- ZeroPad = TRUE;
- break;
- case 'Y':
- Integer = CalendarTime->Year;
- ZeroPad = TRUE;
- DefaultFieldSize = 4;
- break;
- case 'z':
- ZoneOffset = CalendarTime->GmtOffset;
- Sign = '+';
- if (ZoneOffset < 0) {
- Sign = '-';
- ZoneOffset = -ZoneOffset;
- }
- ZoneOffsetHours = ZoneOffset / SECONDS_PER_HOUR;
- ZoneOffset %= SECONDS_PER_HOUR;
- ZoneOffsetMinutes = ZoneOffset / SECONDS_PER_MINUTE;
- CopyStringSize = RtlPrintToString(WorkingBuffer,
- sizeof(WorkingBuffer),
- CharacterEncodingDefault,
- "%c%02d%02d",
- Sign,
- ZoneOffsetHours,
- ZoneOffsetMinutes);
- if (CopyStringSize > sizeof(WorkingBuffer)) {
- CopyStringSize = sizeof(WorkingBuffer);
- }
- if (CopyStringSize != 0) {
- CopyStringSize -= 1;
- }
- CopyString = WorkingBuffer;
- break;
- case 'Z':
- CopyStringSize = sizeof(WorkingBuffer);
- CopyString = WorkingBuffer;
- ASSERT(sizeof(CalendarTime->TimeZone) + 1 <= sizeof(WorkingBuffer));
- RtlCopyMemory(CopyString,
- CalendarTime->TimeZone,
- sizeof(CalendarTime->TimeZone));
- CopyString[CopyStringSize - 1] = '\0';
- CopyStringSize = RtlStringLength(CopyString);
- break;
- case '%':
- WorkingBuffer[0] = '%';
- CopyString = WorkingBuffer;
- CopyStringSize = 1;
- break;
- default:
- CopyStringSize = 0;
- break;
- }
- //
- // If there's an integer, use that.
- //
- if (Integer != -1) {
- if (ZeroPad != FALSE) {
- CopyStringSize = RtlPrintToString(WorkingBuffer,
- sizeof(WorkingBuffer),
- CharacterEncodingDefault,
- "%0*I64d",
- DefaultFieldSize,
- Integer);
- } else {
- CopyStringSize = RtlPrintToString(WorkingBuffer,
- sizeof(WorkingBuffer),
- CharacterEncodingDefault,
- "%*I64d",
- DefaultFieldSize,
- Integer);
- }
- if (CopyStringSize > sizeof(WorkingBuffer)) {
- CopyStringSize = sizeof(WorkingBuffer);
- }
- if (CopyStringSize != 0) {
- CopyStringSize -= 1;
- }
- CopyString = WorkingBuffer;
- //
- // If the copy string size was left untouched, take its length.
- //
- } else if (CopyStringSize == -1) {
- ASSERT(CopyString != NULL);
- CopyStringSize = RtlStringLength(CopyString);
- }
- //
- // Copy characters over to the destination buffer.
- //
- while ((StringSize != 0) && (CopyStringSize != 0)) {
- *String = *CopyString;
- String += 1;
- StringSize -= 1;
- CopyString += 1;
- CopyStringSize -= 1;
- }
- }
- //
- // Null terminate the string if it's completely filled up.
- //
- if ((StringSize == 0) && (StringBufferSize != 0)) {
- StringBuffer[StringBufferSize - 1] = '\0';
- }
- //
- // Figure out the number of bytes that were written, and returned.
- //
- StringSize = StringBufferSize - StringSize;
- return StringSize;
- }
- RTL_API
- PSTR
- RtlScanDate (
- PSTR StringBuffer,
- PSTR Format,
- PCALENDAR_TIME CalendarTime
- )
- /*++
- Routine Description:
- This routine scans the given input string into values in the calendar time,
- using the specified format.
- Arguments:
- StringBuffer - Supplies a pointer to the null terminated string to scan.
- Format - Supplies the format string to govern the conversion. Ordinary
- characters in the format string will be scanned verbatim from the input.
- Whitespace characters in the format will cause all whitespace at the
- current position in the input to be scanned. Conversions will be
- scanned for their corresponding value in the provided calendar time.
- Conversions start with a '%' character, followed by an optional E or O
- character, followed by a conversion specifier. The conversion specifier
- can take the following values:
- %a - The day of the weekday name, either the full or abbreviated name.
- %A - Equivalent to %a.
- %b - The month name, either the full or abbreviated name.
- %B - Equivalent to %b.
- %c - Replaced by the locale's appropriate date and time representation.
- %C - The year divided by 100 (century) [00,99].
- %d - The day of the month [01,31].
- %D - Equivalent to "%m/%d/%y".
- %e - Equivalent to %d.
- %h - Equivalent to %b (month name).
- %H - The 24 hour clock hour [00,23].
- %I - The 12 hour clock hour [01,12].
- %J - Replaced by the nanosecond [0,999999999].
- %j - The day of the year [001,366].
- %m - The month number [01,12].
- %M - The minute [00,59].
- %N - The microsecond [0,999999]
- %n - Any whitespace.
- %p - The equivalent of "AM" or "PM".
- %q - The millisecond [0,999].
- %r - Replaced by the time in AM/PM notation: "%I:%M:%S %p".
- %R - Replaced by the time in 24 hour notation: "%H:%M".
- %S - The second [00,60].
- %t - Any white space.
- %T - Replaced by the time: "%H:%M:%S".
- %u - Replaced by the weekday number, with 1 representing Monday [1,7].
- %U - The week number of the year [00,53]. The first Sunday of January is
- the first day of week 1. Days before this are week 0.
- %w - The weekday number [0,6], with 0 representing Sunday.
- %W - The week number [00,53]. The first Monday of January is the first
- day of week 1. Days before this are in week 0.
- %x - Replaced by the locale's appropriate date representation.
- %X - Replaced by the locale's appropriate time representation.
- %y - The last two digits of the year [00,99].
- %Y - The full four digit year [0001,9999].
- %% - Replaced by a literal '%'.
- CalendarTime - Supplies a pointer to the calendar time value to place the
- values in. Only the values that are scanned in are modified.
- Return Value:
- Returns the a pointer to the input string after the last character scanned.
- NULL if the result coult not be scanned.
- --*/
- {
- BOOL Evening;
- LONG Integer;
- LONGLONG LongLong;
- PSTR SavedFormat;
- BOOL ScanInteger;
- LONG Size;
- CHAR Specifier;
- KSTATUS Status;
- PSTR String;
- ULONG StringSize;
- Evening = FALSE;
- SavedFormat = NULL;
- String = StringBuffer;
- while (*String != '\0') {
- //
- // If this is the end of the format string, then either it's really the
- // end, or it's just the end of the temporary format string.
- //
- if (*Format == '\0') {
- if (SavedFormat != NULL) {
- Format = SavedFormat;
- SavedFormat = NULL;
- continue;
- } else {
- break;
- }
- }
- //
- // Handle whitespace in the format.
- //
- if (RtlIsCharacterSpace(*Format) != FALSE) {
- while (RtlIsCharacterSpace(*String) != FALSE) {
- String += 1;
- }
- Format += 1;
- continue;
- //
- // Handle ordinary characters in the format.
- //
- } else if (*Format != '%') {
- if (*String != *Format) {
- Status = STATUS_NO_MATCH;
- goto ScanTimeEnd;
- }
- Format += 1;
- String += 1;
- continue;
- }
- Format += 1;
- //
- // Pass over an E or an O for alternate representations. At some point
- // these should be supported.
- //
- if (*Format == 'E') {
- Format += 1;
- }
- if (*Format == 'O') {
- Format += 1;
- }
- Specifier = *Format;
- Format += 1;
- ScanInteger = FALSE;
- switch (Specifier) {
- case 'a':
- case 'A':
- Status = RtlpScanTimeStrings(String,
- RtlWeekdayStrings,
- DAYS_PER_WEEK,
- &Integer,
- &Size);
- if (!KSUCCESS(Status)) {
- Status = RtlpScanTimeStrings(String,
- RtlAbbreviatedWeekdayStrings,
- DAYS_PER_WEEK,
- &Integer,
- &Size);
- }
- if (!KSUCCESS(Status)) {
- goto ScanTimeEnd;
- }
- CalendarTime->Weekday = Integer;
- String += Size;
- break;
- case 'b':
- case 'B':
- case 'h':
- Status = RtlpScanTimeStrings(String,
- RtlMonthStrings,
- MONTHS_PER_YEAR,
- &Integer,
- &Size);
- if (!KSUCCESS(Status)) {
- Status = RtlpScanTimeStrings(String,
- RtlAbbreviatedMonthStrings,
- MONTHS_PER_YEAR,
- &Integer,
- &Size);
- }
- if (!KSUCCESS(Status)) {
- goto ScanTimeEnd;
- }
- CalendarTime->Month = Integer;
- String += Size;
- break;
- case 'c':
- SavedFormat = Format;
- Format = "%a %b %e %H:%M:%S %Y";
- continue;
- case 'D':
- SavedFormat = Format;
- Format = "%m/%d/%y";
- continue;
- case 'n':
- case 't':
- while (RtlIsCharacterSpace(*String) != FALSE) {
- String += 1;
- }
- continue;
- case 'p':
- Status = RtlpScanTimeStrings(String,
- RtlAmPmStrings[0],
- 2,
- &Integer,
- &Size);
- if (!KSUCCESS(Status)) {
- Status = RtlpScanTimeStrings(String,
- RtlAmPmStrings[1],
- 2,
- &Integer,
- &Size);
- }
- if (!KSUCCESS(Status)) {
- goto ScanTimeEnd;
- }
- if (Integer == 1) {
- Evening = TRUE;
- if ((CalendarTime->Hour >= 0) && (CalendarTime->Hour <= 12)) {
- CalendarTime->Hour += 12;
- }
- } else {
- if (CalendarTime->Hour == 12) {
- CalendarTime->Hour = 0;
- }
- }
- String += Size;
- break;
- case 'r':
- SavedFormat = Format;
- Format = "%I:%M:%S %p";
- continue;
- case 'R':
- SavedFormat = Format;
- Format = "%H:%M";
- continue;
- case 'T':
- SavedFormat = Format;
- Format = "%H:%M:%S";
- continue;
- case 'x':
- SavedFormat = Format;
- Format = "%m/%d/%y";
- continue;
- case 'X':
- SavedFormat = Format;
- Format = "%H:%M:%S";
- continue;
- case 'C':
- case 'd':
- case 'e':
- case 'H':
- case 'I':
- case 'J':
- case 'j':
- case 'm':
- case 'M':
- case 'N':
- case 'q':
- case 'S':
- case 'U':
- case 'W':
- case 'w':
- case 'y':
- case 'Y':
- ScanInteger = TRUE;
- break;
- case '%':
- if (*String != '%') {
- Status = STATUS_NO_MATCH;
- goto ScanTimeEnd;
- }
- String += 1;
- break;
- default:
- break;
- }
- //
- // Scan an integer if desired.
- //
- if (ScanInteger != FALSE) {
- StringSize = MAX_ULONG;
- Status = RtlStringScanInteger(&String,
- &StringSize,
- 10,
- TRUE,
- &LongLong);
- if (!KSUCCESS(Status)) {
- goto ScanTimeEnd;
- }
- Integer = (LONG)LongLong;
- //
- // Process now that the integer has been scanned.
- //
- switch (Specifier) {
- case 'C':
- CalendarTime->Year = (CalendarTime->Year % YEARS_PER_CENTURY) +
- Integer * YEARS_PER_CENTURY;
- break;
- case 'd':
- case 'e':
- if ((Integer <= 0) || (Integer > 31)) {
- Status = STATUS_OUT_OF_BOUNDS;
- goto ScanTimeEnd;
- }
- CalendarTime->Day = Integer;
- break;
- case 'H':
- if ((Integer < 0) || (Integer >= HOURS_PER_DAY)) {
- Status = STATUS_OUT_OF_BOUNDS;
- goto ScanTimeEnd;
- }
- CalendarTime->Hour = Integer;
- break;
- case 'I':
- if ((Integer <= 0) || (Integer > 12)) {
- Status = STATUS_OUT_OF_BOUNDS;
- goto ScanTimeEnd;
- }
- if (Evening != FALSE) {
- Integer += 12;
- } else {
- if (Integer == 12) {
- Integer = 0;
- }
- }
- CalendarTime->Hour = Integer;
- break;
- case 'J':
- if ((Integer < 0) || (Integer >= NANOSECONDS_PER_SECOND)) {
- Status = STATUS_OUT_OF_BOUNDS;
- goto ScanTimeEnd;
- }
- CalendarTime->Nanosecond = Integer;
- break;
- case 'j':
- if ((Integer <= 0) || (Integer > DAYS_PER_LEAP_YEAR)) {
- Status = STATUS_OUT_OF_BOUNDS;
- goto ScanTimeEnd;
- }
- CalendarTime->YearDay = Integer - 1;
- break;
- case 'm':
- if ((Integer <= 0) || (Integer > MONTHS_PER_YEAR)) {
- Status = STATUS_OUT_OF_BOUNDS;
- goto ScanTimeEnd;
- }
- CalendarTime->Month = Integer - 1;
- break;
- case 'M':
- if ((Integer < 0) || (Integer >= SECONDS_PER_MINUTE)) {
- Status = STATUS_OUT_OF_BOUNDS;
- goto ScanTimeEnd;
- }
- CalendarTime->Minute = Integer;
- break;
- case 'N':
- if ((Integer < 0) || (Integer >= MICROSECONDS_PER_SECOND)) {
- Status = STATUS_OUT_OF_BOUNDS;
- goto ScanTimeEnd;
- }
- CalendarTime->Nanosecond = Integer * 1000;
- break;
- case 'q':
- if ((Integer < 0) || (Integer >= MILLISECONDS_PER_SECOND)) {
- Status = STATUS_OUT_OF_BOUNDS;
- goto ScanTimeEnd;
- }
- CalendarTime->Nanosecond = Integer * 1000000;
- break;
- //
- // Seconds allows a value of 60 for the occasional leap second.
- //
- case 'S':
- if ((Integer < 0) || (Integer > SECONDS_PER_MINUTE)) {
- Status = STATUS_OUT_OF_BOUNDS;
- goto ScanTimeEnd;
- }
- CalendarTime->Second = Integer;
- break;
- case 'U':
- case 'W':
- break;
- case 'w':
- if ((Integer < TimeZoneWeekdaySunday) ||
- (Integer > TimeZoneWeekdaySaturday)) {
- Status = STATUS_OUT_OF_BOUNDS;
- goto ScanTimeEnd;
- }
- CalendarTime->Weekday = Integer;
- break;
- case 'y':
- if ((Integer < 0) || (Integer >= YEARS_PER_CENTURY)) {
- Status = STATUS_OUT_OF_BOUNDS;
- goto ScanTimeEnd;
- }
- if (Integer > TWO_DIGIT_YEAR_CUTOFF) {
- CalendarTime->Year = TWENTY_FIRST_CENTURY + Integer;
- } else {
- CalendarTime->Year = TWENTIETH_CENTURY + Integer;
- }
- break;
- case 'Y':
- if ((Integer < MIN_TIME_ZONE_YEAR) ||
- (Integer > MAX_TIME_ZONE_YEAR)) {
- Status = STATUS_OUT_OF_BOUNDS;
- goto ScanTimeEnd;
- }
- CalendarTime->Year = Integer;
- break;
- default:
- break;
- }
- }
- }
- Status = STATUS_SUCCESS;
- ScanTimeEnd:
- if (!KSUCCESS(Status)) {
- return NULL;
- }
- return String;
- }
- LONG
- RtlpComputeYearForDays (
- PLONG Days
- )
- /*++
- Routine Description:
- This routine calculates the year given a number of days from the epoch.
- Arguments:
- Days - Supplies a pointer to the number of days since the epoch. On
- completion, this will contain the number of remaining days after the
- years have been subtracted.
- Return Value:
- Returns the year that the day resides in.
- --*/
- {
- LONG Cycles;
- LONG RemainingDays;
- LONG Year;
- Year = TIME_ZONE_EPOCH_YEAR;
- RemainingDays = *Days;
- //
- // Divide by the period for truly ridiculous dates.
- //
- if ((RemainingDays >= GREGORIAN_CYCLE_DAYS) ||
- (-RemainingDays >= GREGORIAN_CYCLE_DAYS)) {
- Cycles = RemainingDays / GREGORIAN_CYCLE_DAYS;
- Year += Cycles * GREGORIAN_CYCLE_YEARS;
- RemainingDays -= (Cycles * GREGORIAN_CYCLE_DAYS);
- }
- //
- // Subtract off any years after the epoch.
- //
- while (RemainingDays > 0) {
- if (IS_LEAP_YEAR(Year)) {
- RemainingDays -= DAYS_PER_LEAP_YEAR;
- } else {
- RemainingDays -= DAYS_PER_YEAR;
- }
- Year += 1;
- }
- //
- // The subtraction may have gone one too far, or the days may have
- // started negative. Either way, get the days up to a non-negative value.
- //
- while (RemainingDays < 0) {
- Year -= 1;
- if (IS_LEAP_YEAR(Year)) {
- RemainingDays += DAYS_PER_LEAP_YEAR;
- } else {
- RemainingDays += DAYS_PER_YEAR;
- }
- }
- *Days = RemainingDays;
- return Year;
- }
- LONG
- RtlpComputeDaysForYear (
- LONG Year
- )
- /*++
- Routine Description:
- This routine calculates the number of days for the given year, relative to
- the epoch.
- Arguments:
- Year - Supplies the target year.
- Return Value:
- Returns the number of days since the epoch that January 1st of the given
- year occurred.
- --*/
- {
- LONG Cycles;
- LONG Days;
- Days = 0;
- if (((Year - TIME_ZONE_EPOCH_YEAR) >= GREGORIAN_CYCLE_YEARS) ||
- (-(Year - TIME_ZONE_EPOCH_YEAR) >= GREGORIAN_CYCLE_YEARS)) {
- Cycles = (Year - TIME_ZONE_EPOCH_YEAR) / GREGORIAN_CYCLE_YEARS;
- Year -= Cycles * GREGORIAN_CYCLE_YEARS;
- Days += Cycles * GREGORIAN_CYCLE_DAYS;
- }
- if (Year >= TIME_ZONE_EPOCH_YEAR) {
- while (Year > TIME_ZONE_EPOCH_YEAR) {
- Year -= 1;
- if (IS_LEAP_YEAR(Year)) {
- Days += DAYS_PER_LEAP_YEAR;
- } else {
- Days += DAYS_PER_YEAR;
- }
- }
- } else {
- while (Year < TIME_ZONE_EPOCH_YEAR) {
- if (IS_LEAP_YEAR(Year)) {
- Days -= DAYS_PER_LEAP_YEAR;
- } else {
- Days -= DAYS_PER_YEAR;
- }
- Year += 1;
- }
- }
- return Days;
- }
- KSTATUS
- RtlpCalculateWeekdayForMonth (
- LONG Year,
- LONG Month,
- PLONG Weekday
- )
- /*++
- Routine Description:
- This routine calculates the weekday for the first of the month on the
- given month and year.
- Arguments:
- Year - Supplies the year to calculate the weekday for.
- Month - Supplies the month to calculate the weekday for.
- Weekday - Supplies a pointer where the weekday will be returned on success.
- Return Value:
- STATUS_SUCCESS on success.
- STATUS_OUT_OF_BOUNDS on failure.
- --*/
- {
- LONG Days;
- LONG Leap;
- LONG Modulo;
- if ((Year > MAX_TIME_ZONE_YEAR) || (Year < MIN_TIME_ZONE_YEAR)) {
- return STATUS_OUT_OF_BOUNDS;
- }
- Days = RtlpComputeDaysForYear(Year);
- Leap = 0;
- if (IS_LEAP_YEAR(Year)) {
- Leap = 1;
- }
- Days += RtlMonthDays[Leap][Month];
- Modulo = ((TIME_ZONE_EPOCH_WEEKDAY + Days) % DAYS_PER_WEEK);
- if (Modulo < 0) {
- Modulo = DAYS_PER_WEEK + Modulo;
- }
- *Weekday = Modulo;
- return STATUS_SUCCESS;
- }
- VOID
- RtlpNormalizeCalendarTime (
- PCALENDAR_TIME CalendarTime
- )
- /*++
- Routine Description:
- This routine normalizes the fields in a calendar time structure, putting
- them in their proper ranges.
- Arguments:
- CalendarTime - Supplies a pointer to the calendar time structure to
- normalize.
- Return Value:
- None.
- --*/
- {
- LONG Day;
- LONG Leap;
- //
- // Get nanoseconds, seconds, minutes, and hours into range.
- //
- if ((CalendarTime->Nanosecond >= NANOSECONDS_PER_SECOND) ||
- (CalendarTime->Nanosecond < 0)) {
- CalendarTime->Second += CalendarTime->Nanosecond /
- NANOSECONDS_PER_SECOND;
- CalendarTime->Nanosecond %= NANOSECONDS_PER_SECOND;
- if (CalendarTime->Nanosecond < 0) {
- CalendarTime->Nanosecond += NANOSECONDS_PER_SECOND;
- CalendarTime->Second -= 1;
- }
- }
- if ((CalendarTime->Second >= SECONDS_PER_MINUTE) ||
- (CalendarTime->Second < 0)) {
- CalendarTime->Minute += CalendarTime->Second / SECONDS_PER_MINUTE;
- CalendarTime->Second %= SECONDS_PER_MINUTE;
- if (CalendarTime->Second < 0) {
- CalendarTime->Second += SECONDS_PER_MINUTE;
- CalendarTime->Minute -= 1;
- }
- }
- if ((CalendarTime->Minute >= MINUTES_PER_HOUR) ||
- (CalendarTime->Minute < 0)) {
- CalendarTime->Hour += CalendarTime->Minute / MINUTES_PER_HOUR;
- CalendarTime->Minute %= MINUTES_PER_HOUR;
- if (CalendarTime->Minute < 0) {
- CalendarTime->Minute += MINUTES_PER_HOUR;
- CalendarTime->Hour -= 1;
- }
- }
- Day = 0;
- if ((CalendarTime->Hour >= HOURS_PER_DAY) ||
- (CalendarTime->Hour < 0)) {
- Day = CalendarTime->Hour / HOURS_PER_DAY;
- CalendarTime->Hour %= HOURS_PER_DAY;
- if (CalendarTime->Hour < 0) {
- CalendarTime->Hour += HOURS_PER_DAY;
- Day -= 1;
- }
- }
- //
- // Skip the days for now as they're tricky. Get the month into range.
- //
- if ((CalendarTime->Month >= MONTHS_PER_YEAR) ||
- (CalendarTime->Month < 0)) {
- CalendarTime->Year += CalendarTime->Month / MONTHS_PER_YEAR;
- CalendarTime->Month %= MONTHS_PER_YEAR;
- if (CalendarTime->Month < 0) {
- CalendarTime->Month += MONTHS_PER_YEAR;
- CalendarTime->Year -= 1;
- }
- }
- //
- // Make the day positive.
- //
- Day += CalendarTime->Day - 1;
- while (Day < 0) {
- CalendarTime->Month -= 1;
- if (CalendarTime->Month < 0) {
- CalendarTime->Year -= 1;
- CalendarTime->Month = MONTHS_PER_YEAR - 1;
- }
- Leap = 0;
- if (IS_LEAP_YEAR(CalendarTime->Year)) {
- Leap = 1;
- }
- Day += RtlDaysPerMonth[Leap][CalendarTime->Month];
- }
- //
- // Now get the day in range.
- //
- while (TRUE) {
- Leap = 0;
- if (IS_LEAP_YEAR(CalendarTime->Year)) {
- Leap = 1;
- }
- if (Day < RtlDaysPerMonth[Leap][CalendarTime->Month]) {
- break;
- }
- Day -= RtlDaysPerMonth[Leap][CalendarTime->Month];
- CalendarTime->Month += 1;
- if (CalendarTime->Month == MONTHS_PER_YEAR) {
- CalendarTime->Year += 1;
- CalendarTime->Month = 0;
- }
- }
- CalendarTime->Day = Day + 1;
- CalendarTime->YearDay = RtlMonthDays[Leap][CalendarTime->Month] + Day;
- Day = CalendarTime->YearDay + RtlpComputeDaysForYear(CalendarTime->Year);
- CalendarTime->Weekday = (TIME_ZONE_EPOCH_WEEKDAY + Day) % DAYS_PER_WEEK;
- if (CalendarTime->Weekday < 0) {
- CalendarTime->Weekday = DAYS_PER_WEEK + CalendarTime->Weekday;
- }
- return;
- }
- KSTATUS
- RtlpCalculateWeekNumber (
- LONG Year,
- LONG YearDay,
- LONG StartingWeekday,
- PLONG WeekNumber
- )
- /*++
- Routine Description:
- This routine calculates the week number given a year and year day.
- Arguments:
- Year - Supplies the year the week resides in.
- YearDay - Supplies the day of the of the year. Valid values are between 0
- and 365.
- StartingWeekday - Supplies the weekday of the start of the week. Usually
- this is Sunday (0) or Monday (1).
- WeekNumber - Supplies a pointer where the week number will be returned.
- Return Value:
- STATUS_SUCCESS on success.
- STATUS_OUT_OF_BOUNDS on failure.
- --*/
- {
- KSTATUS Status;
- LONG Week1YearDay;
- LONG Weekday;
- //
- // Calculate the year day for week 1.
- //
- Status = RtlpCalculateWeekdayForMonth(Year, TimeZoneMonthJanuary, &Weekday);
- if (!KSUCCESS(Status)) {
- return Status;
- }
- Week1YearDay = StartingWeekday - Weekday;
- if (Week1YearDay < 0) {
- Week1YearDay += DAYS_PER_WEEK;
- }
- if (YearDay < Week1YearDay) {
- *WeekNumber = 0;
- } else {
- *WeekNumber = 1 + ((YearDay - Week1YearDay) / DAYS_PER_WEEK);
- }
- return STATUS_SUCCESS;
- }
- KSTATUS
- RtlpCalculateIsoWeekNumber (
- LONG Year,
- LONG YearDay,
- LONG Weekday,
- PLONG WeekNumber,
- PLONG IsoYear
- )
- /*++
- Routine Description:
- This routine calculates the ISO 8601 week-based year week number and year.
- Arguments:
- Year - Supplies the Gregorian calendar year.
- YearDay - Supplies the day of the of the year. Valid values are between 0
- and 365.
- Weekday - Supplies the day of the week. Valid values are between 0 and 6,
- with 0 as Sunday.
- WeekNumber - Supplies an optional pointer where the ISO week number will be
- returned.
- IsoYear - Supplies an optional pointer where the ISO year will be returned.
- Return Value:
- STATUS_SUCCESS on success.
- STATUS_OUT_OF_BOUNDS on failure.
- --*/
- {
- LONG Day;
- LONG FinalWeekNumber;
- KSTATUS Status;
- LONG WeekCount;
- LONG YearStartWeekday;
- //
- // Convert to an ISO weekday, where 1 is Monday and 7 is Sunday.
- //
- if (Weekday == 0) {
- Weekday = 7;
- }
- Day = YearDay - Weekday + DAYS_PER_WEEK + TimeZoneWeekdayWednesday;
- FinalWeekNumber = Day / DAYS_PER_WEEK;
- //
- // If the week number is zero, it actually belongs in the last week of the
- // previous year. If the week is 53, it might be the first week of the
- // next year.
- //
- if ((FinalWeekNumber == 0) || (FinalWeekNumber == 53)) {
- if (FinalWeekNumber == 0) {
- Year -= 1;
- }
- Status = RtlpCalculateWeekdayForMonth(Year,
- TimeZoneMonthJanuary,
- &YearStartWeekday);
- if (!KSUCCESS(Status)) {
- return Status;
- }
- WeekCount = 52;
- if ((YearStartWeekday == TimeZoneWeekdayThursday) ||
- ((IS_LEAP_YEAR(Year)) &&
- (YearStartWeekday == TimeZoneWeekdayWednesday))) {
- WeekCount = 53;
- }
- if (FinalWeekNumber == 0) {
- FinalWeekNumber = WeekCount;
- } else {
- if (FinalWeekNumber > WeekCount) {
- Year += 1;
- FinalWeekNumber = 1;
- }
- }
- }
- if (WeekNumber != NULL) {
- *WeekNumber = FinalWeekNumber;
- }
- if (IsoYear != NULL) {
- *IsoYear = Year;
- }
- return STATUS_SUCCESS;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- KSTATUS
- RtlpScanTimeStrings (
- PSTR Input,
- PSTR *Strings,
- ULONG StringCount,
- PLONG Index,
- PLONG Size
- )
- /*++
- Routine Description:
- This routine attempts to scan one of the set of given time strings, case
- insensitively.
- Arguments:
- Input - Supplies a pointer to the input string to scan.
- Strings - Supplies the array of possible strings to scan.
- StringCount - Supplies the number of strings in the array.
- Index - Supplies a pointer where the index of the matching string will be
- returned on success.
- Size - Supplies a pointer where the number of characters scanned will be
- returned on success.
- Return Value:
- STATUS_SUCCESS on success.
- STATUS_NO_MATCH if none of the strings matched.
- --*/
- {
- CHAR CompareCharacter;
- ULONG CompareIndex;
- PSTR CompareString;
- CHAR InputCharacter;
- ULONG StringIndex;
- for (StringIndex = 0; StringIndex < StringCount; StringIndex += 1) {
- CompareIndex = 0;
- CompareString = Strings[StringIndex];
- while (TRUE) {
- CompareCharacter =
- RtlConvertCharacterToLowerCase(CompareString[CompareIndex]);
- InputCharacter =
- RtlConvertCharacterToLowerCase(Input[CompareIndex]);
- if ((CompareCharacter == '\0') || (InputCharacter == '\0') ||
- (CompareCharacter != InputCharacter)) {
- break;
- }
- CompareIndex += 1;
- }
- //
- // If there was a match, return it.
- //
- if (CompareCharacter == '\0') {
- *Index = StringIndex;
- *Size = CompareIndex;
- return STATUS_SUCCESS;
- }
- }
- return STATUS_NO_MATCH;
- }
|