12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708 |
- import re
- cimport numpy as cnp
- from cpython.object cimport (
- Py_EQ,
- Py_NE,
- PyObject,
- PyObject_RichCompare,
- PyObject_RichCompareBool,
- )
- from numpy cimport (
- int32_t,
- int64_t,
- ndarray,
- )
- import numpy as np
- cnp.import_array()
- cimport cython
- from cpython.datetime cimport (
- PyDate_Check,
- PyDateTime_Check,
- datetime,
- import_datetime,
- )
- from libc.stdlib cimport (
- free,
- malloc,
- )
- from libc.string cimport (
- memset,
- strlen,
- )
- from libc.time cimport (
- strftime,
- tm,
- )
- # import datetime C API
- import_datetime()
- cimport pandas._libs.tslibs.util as util
- from pandas._libs.missing cimport C_NA
- from pandas._libs.tslibs.np_datetime cimport (
- NPY_DATETIMEUNIT,
- NPY_FR_D,
- astype_overflowsafe,
- check_dts_bounds,
- get_timedelta64_value,
- npy_datetimestruct,
- npy_datetimestruct_to_datetime,
- pandas_datetime_to_datetimestruct,
- )
- from pandas._libs.tslibs.timestamps import Timestamp
- from pandas._libs.tslibs.ccalendar cimport (
- dayofweek,
- get_day_of_year,
- get_days_in_month,
- get_week_of_year,
- is_leapyear,
- )
- from pandas._libs.tslibs.timedeltas cimport (
- delta_to_nanoseconds,
- is_any_td_scalar,
- )
- from pandas._libs.tslibs.conversion import DT64NS_DTYPE
- from pandas._libs.tslibs.dtypes cimport (
- FR_ANN,
- FR_BUS,
- FR_DAY,
- FR_HR,
- FR_MIN,
- FR_MS,
- FR_MTH,
- FR_NS,
- FR_QTR,
- FR_SEC,
- FR_UND,
- FR_US,
- FR_WK,
- PeriodDtypeBase,
- attrname_to_abbrevs,
- freq_group_code_to_npy_unit,
- )
- from pandas._libs.tslibs.parsing cimport quarter_to_myear
- from pandas._libs.tslibs.parsing import parse_datetime_string_with_reso
- from pandas._libs.tslibs.nattype cimport (
- NPY_NAT,
- c_NaT as NaT,
- c_nat_strings as nat_strings,
- checknull_with_nat,
- )
- from pandas._libs.tslibs.offsets cimport (
- BaseOffset,
- is_offset_object,
- is_tick_object,
- to_offset,
- )
- from pandas._libs.tslibs.offsets import INVALID_FREQ_ERR_MSG
- cdef:
- enum:
- INT32_MIN = -2_147_483_648LL
- ctypedef struct asfreq_info:
- int64_t intraday_conversion_factor
- int is_end
- int to_end
- int from_end
- ctypedef int64_t (*freq_conv_func)(int64_t, asfreq_info*) nogil
- cdef extern from *:
- """
- // must use npy typedef b/c int64_t is aliased in cython-generated c
- // unclear why we need LL for that row.
- // see https://github.com/pandas-dev/pandas/pull/34416/
- static npy_int64 daytime_conversion_factor_matrix[7][7] = {
- {1, 24, 1440, 86400, 86400000, 86400000000, 86400000000000},
- {0LL, 1LL, 60LL, 3600LL, 3600000LL, 3600000000LL, 3600000000000LL},
- {0, 0, 1, 60, 60000, 60000000, 60000000000},
- {0, 0, 0, 1, 1000, 1000000, 1000000000},
- {0, 0, 0, 0, 1, 1000, 1000000},
- {0, 0, 0, 0, 0, 1, 1000},
- {0, 0, 0, 0, 0, 0, 1}};
- """
- int64_t daytime_conversion_factor_matrix[7][7]
- cdef int max_value(int left, int right) nogil:
- if left > right:
- return left
- return right
- cdef int min_value(int left, int right) nogil:
- if left < right:
- return left
- return right
- cdef int64_t get_daytime_conversion_factor(int from_index, int to_index) nogil:
- cdef:
- int row = min_value(from_index, to_index)
- int col = max_value(from_index, to_index)
- # row or col < 6 means frequency strictly lower than Daily, which
- # do not use daytime_conversion_factors
- if row < 6:
- return 0
- elif col < 6:
- return 0
- return daytime_conversion_factor_matrix[row - 6][col - 6]
- cdef int64_t nofunc(int64_t ordinal, asfreq_info *af_info) nogil:
- return INT32_MIN
- cdef int64_t no_op(int64_t ordinal, asfreq_info *af_info) nogil:
- return ordinal
- cdef freq_conv_func get_asfreq_func(int from_freq, int to_freq) nogil:
- cdef:
- int from_group = get_freq_group(from_freq)
- int to_group = get_freq_group(to_freq)
- if from_group == FR_UND:
- from_group = FR_DAY
- if from_group == FR_BUS:
- if to_group == FR_ANN:
- return <freq_conv_func>asfreq_BtoA
- elif to_group == FR_QTR:
- return <freq_conv_func>asfreq_BtoQ
- elif to_group == FR_MTH:
- return <freq_conv_func>asfreq_BtoM
- elif to_group == FR_WK:
- return <freq_conv_func>asfreq_BtoW
- elif to_group == FR_BUS:
- return <freq_conv_func>no_op
- elif to_group in [FR_DAY, FR_HR, FR_MIN, FR_SEC, FR_MS, FR_US, FR_NS]:
- return <freq_conv_func>asfreq_BtoDT
- else:
- return <freq_conv_func>nofunc
- elif to_group == FR_BUS:
- if from_group == FR_ANN:
- return <freq_conv_func>asfreq_AtoB
- elif from_group == FR_QTR:
- return <freq_conv_func>asfreq_QtoB
- elif from_group == FR_MTH:
- return <freq_conv_func>asfreq_MtoB
- elif from_group == FR_WK:
- return <freq_conv_func>asfreq_WtoB
- elif from_group in [FR_DAY, FR_HR, FR_MIN, FR_SEC, FR_MS, FR_US, FR_NS]:
- return <freq_conv_func>asfreq_DTtoB
- else:
- return <freq_conv_func>nofunc
- elif from_group == FR_ANN:
- if to_group == FR_ANN:
- return <freq_conv_func>asfreq_AtoA
- elif to_group == FR_QTR:
- return <freq_conv_func>asfreq_AtoQ
- elif to_group == FR_MTH:
- return <freq_conv_func>asfreq_AtoM
- elif to_group == FR_WK:
- return <freq_conv_func>asfreq_AtoW
- elif to_group in [FR_DAY, FR_HR, FR_MIN, FR_SEC, FR_MS, FR_US, FR_NS]:
- return <freq_conv_func>asfreq_AtoDT
- else:
- return <freq_conv_func>nofunc
- elif from_group == FR_QTR:
- if to_group == FR_ANN:
- return <freq_conv_func>asfreq_QtoA
- elif to_group == FR_QTR:
- return <freq_conv_func>asfreq_QtoQ
- elif to_group == FR_MTH:
- return <freq_conv_func>asfreq_QtoM
- elif to_group == FR_WK:
- return <freq_conv_func>asfreq_QtoW
- elif to_group in [FR_DAY, FR_HR, FR_MIN, FR_SEC, FR_MS, FR_US, FR_NS]:
- return <freq_conv_func>asfreq_QtoDT
- else:
- return <freq_conv_func>nofunc
- elif from_group == FR_MTH:
- if to_group == FR_ANN:
- return <freq_conv_func>asfreq_MtoA
- elif to_group == FR_QTR:
- return <freq_conv_func>asfreq_MtoQ
- elif to_group == FR_MTH:
- return <freq_conv_func>no_op
- elif to_group == FR_WK:
- return <freq_conv_func>asfreq_MtoW
- elif to_group in [FR_DAY, FR_HR, FR_MIN, FR_SEC, FR_MS, FR_US, FR_NS]:
- return <freq_conv_func>asfreq_MtoDT
- else:
- return <freq_conv_func>nofunc
- elif from_group == FR_WK:
- if to_group == FR_ANN:
- return <freq_conv_func>asfreq_WtoA
- elif to_group == FR_QTR:
- return <freq_conv_func>asfreq_WtoQ
- elif to_group == FR_MTH:
- return <freq_conv_func>asfreq_WtoM
- elif to_group == FR_WK:
- return <freq_conv_func>asfreq_WtoW
- elif to_group in [FR_DAY, FR_HR, FR_MIN, FR_SEC, FR_MS, FR_US, FR_NS]:
- return <freq_conv_func>asfreq_WtoDT
- else:
- return <freq_conv_func>nofunc
- elif from_group in [FR_DAY, FR_HR, FR_MIN, FR_SEC, FR_MS, FR_US, FR_NS]:
- if to_group == FR_ANN:
- return <freq_conv_func>asfreq_DTtoA
- elif to_group == FR_QTR:
- return <freq_conv_func>asfreq_DTtoQ
- elif to_group == FR_MTH:
- return <freq_conv_func>asfreq_DTtoM
- elif to_group == FR_WK:
- return <freq_conv_func>asfreq_DTtoW
- elif to_group in [FR_DAY, FR_HR, FR_MIN, FR_SEC, FR_MS, FR_US, FR_NS]:
- if from_group > to_group:
- return <freq_conv_func>downsample_daytime
- else:
- return <freq_conv_func>upsample_daytime
- else:
- return <freq_conv_func>nofunc
- else:
- return <freq_conv_func>nofunc
- # --------------------------------------------------------------------
- # Frequency Conversion Helpers
- cdef int64_t DtoB_weekday(int64_t unix_date) nogil:
- return ((unix_date + 4) // 7) * 5 + ((unix_date + 4) % 7) - 4
- cdef int64_t DtoB(npy_datetimestruct *dts, int roll_back,
- int64_t unix_date) nogil:
- # calculate the current week (counting from 1970-01-01) treating
- # sunday as last day of a week
- cdef:
- int day_of_week = dayofweek(dts.year, dts.month, dts.day)
- if roll_back == 1:
- if day_of_week > 4:
- # change to friday before weekend
- unix_date -= (day_of_week - 4)
- else:
- if day_of_week > 4:
- # change to Monday after weekend
- unix_date += (7 - day_of_week)
- return DtoB_weekday(unix_date)
- cdef int64_t upsample_daytime(int64_t ordinal, asfreq_info *af_info) nogil:
- if af_info.is_end:
- return (ordinal + 1) * af_info.intraday_conversion_factor - 1
- else:
- return ordinal * af_info.intraday_conversion_factor
- cdef int64_t downsample_daytime(int64_t ordinal, asfreq_info *af_info) nogil:
- return ordinal // af_info.intraday_conversion_factor
- cdef int64_t transform_via_day(int64_t ordinal,
- asfreq_info *af_info,
- freq_conv_func first_func,
- freq_conv_func second_func) nogil:
- cdef:
- int64_t result
- result = first_func(ordinal, af_info)
- result = second_func(result, af_info)
- return result
- # --------------------------------------------------------------------
- # Conversion _to_ Daily Freq
- cdef int64_t asfreq_AtoDT(int64_t ordinal, asfreq_info *af_info) nogil:
- cdef:
- int64_t unix_date
- npy_datetimestruct dts
- ordinal += af_info.is_end
- dts.year = ordinal + 1970
- dts.month = 1
- adjust_dts_for_month(&dts, af_info.from_end)
- unix_date = unix_date_from_ymd(dts.year, dts.month, 1)
- unix_date -= af_info.is_end
- return upsample_daytime(unix_date, af_info)
- cdef int64_t asfreq_QtoDT(int64_t ordinal, asfreq_info *af_info) nogil:
- cdef:
- int64_t unix_date
- npy_datetimestruct dts
- ordinal += af_info.is_end
- dts.year = ordinal // 4 + 1970
- dts.month = (ordinal % 4) * 3 + 1
- adjust_dts_for_month(&dts, af_info.from_end)
- unix_date = unix_date_from_ymd(dts.year, dts.month, 1)
- unix_date -= af_info.is_end
- return upsample_daytime(unix_date, af_info)
- cdef int64_t asfreq_MtoDT(int64_t ordinal, asfreq_info *af_info) nogil:
- cdef:
- int64_t unix_date
- int year, month
- ordinal += af_info.is_end
- year = ordinal // 12 + 1970
- month = ordinal % 12 + 1
- unix_date = unix_date_from_ymd(year, month, 1)
- unix_date -= af_info.is_end
- return upsample_daytime(unix_date, af_info)
- cdef int64_t asfreq_WtoDT(int64_t ordinal, asfreq_info *af_info) nogil:
- ordinal = (ordinal * 7 + af_info.from_end - 4 +
- (7 - 1) * (af_info.is_end - 1))
- return upsample_daytime(ordinal, af_info)
- # --------------------------------------------------------------------
- # Conversion _to_ BusinessDay Freq
- cdef int64_t asfreq_AtoB(int64_t ordinal, asfreq_info *af_info) nogil:
- cdef:
- int roll_back
- npy_datetimestruct dts
- int64_t unix_date = asfreq_AtoDT(ordinal, af_info)
- pandas_datetime_to_datetimestruct(unix_date, NPY_FR_D, &dts)
- roll_back = af_info.is_end
- return DtoB(&dts, roll_back, unix_date)
- cdef int64_t asfreq_QtoB(int64_t ordinal, asfreq_info *af_info) nogil:
- cdef:
- int roll_back
- npy_datetimestruct dts
- int64_t unix_date = asfreq_QtoDT(ordinal, af_info)
- pandas_datetime_to_datetimestruct(unix_date, NPY_FR_D, &dts)
- roll_back = af_info.is_end
- return DtoB(&dts, roll_back, unix_date)
- cdef int64_t asfreq_MtoB(int64_t ordinal, asfreq_info *af_info) nogil:
- cdef:
- int roll_back
- npy_datetimestruct dts
- int64_t unix_date = asfreq_MtoDT(ordinal, af_info)
- pandas_datetime_to_datetimestruct(unix_date, NPY_FR_D, &dts)
- roll_back = af_info.is_end
- return DtoB(&dts, roll_back, unix_date)
- cdef int64_t asfreq_WtoB(int64_t ordinal, asfreq_info *af_info) nogil:
- cdef:
- int roll_back
- npy_datetimestruct dts
- int64_t unix_date = asfreq_WtoDT(ordinal, af_info)
- pandas_datetime_to_datetimestruct(unix_date, NPY_FR_D, &dts)
- roll_back = af_info.is_end
- return DtoB(&dts, roll_back, unix_date)
- cdef int64_t asfreq_DTtoB(int64_t ordinal, asfreq_info *af_info) nogil:
- cdef:
- int roll_back
- npy_datetimestruct dts
- int64_t unix_date = downsample_daytime(ordinal, af_info)
- pandas_datetime_to_datetimestruct(unix_date, NPY_FR_D, &dts)
- # This usage defines roll_back the opposite way from the others
- roll_back = 1 - af_info.is_end
- return DtoB(&dts, roll_back, unix_date)
- # ----------------------------------------------------------------------
- # Conversion _from_ Daily Freq
- cdef int64_t asfreq_DTtoA(int64_t ordinal, asfreq_info *af_info) nogil:
- cdef:
- npy_datetimestruct dts
- ordinal = downsample_daytime(ordinal, af_info)
- pandas_datetime_to_datetimestruct(ordinal, NPY_FR_D, &dts)
- return dts_to_year_ordinal(&dts, af_info.to_end)
- cdef int DtoQ_yq(int64_t ordinal, asfreq_info *af_info, npy_datetimestruct* dts) nogil:
- cdef:
- int quarter
- pandas_datetime_to_datetimestruct(ordinal, NPY_FR_D, dts)
- adjust_dts_for_qtr(dts, af_info.to_end)
- quarter = month_to_quarter(dts.month)
- return quarter
- cdef int64_t asfreq_DTtoQ(int64_t ordinal, asfreq_info *af_info) nogil:
- cdef:
- int quarter
- npy_datetimestruct dts
- ordinal = downsample_daytime(ordinal, af_info)
- quarter = DtoQ_yq(ordinal, af_info, &dts)
- return <int64_t>((dts.year - 1970) * 4 + quarter - 1)
- cdef int64_t asfreq_DTtoM(int64_t ordinal, asfreq_info *af_info) nogil:
- cdef:
- npy_datetimestruct dts
- ordinal = downsample_daytime(ordinal, af_info)
- pandas_datetime_to_datetimestruct(ordinal, NPY_FR_D, &dts)
- return dts_to_month_ordinal(&dts)
- cdef int64_t asfreq_DTtoW(int64_t ordinal, asfreq_info *af_info) nogil:
- ordinal = downsample_daytime(ordinal, af_info)
- return unix_date_to_week(ordinal, af_info.to_end)
- cdef int64_t unix_date_to_week(int64_t unix_date, int to_end) nogil:
- return (unix_date + 3 - to_end) // 7 + 1
- # --------------------------------------------------------------------
- # Conversion _from_ BusinessDay Freq
- cdef int64_t asfreq_BtoDT(int64_t ordinal, asfreq_info *af_info) nogil:
- ordinal = ((ordinal + 3) // 5) * 7 + (ordinal + 3) % 5 - 3
- return upsample_daytime(ordinal, af_info)
- cdef int64_t asfreq_BtoA(int64_t ordinal, asfreq_info *af_info) nogil:
- return transform_via_day(ordinal, af_info,
- <freq_conv_func>asfreq_BtoDT,
- <freq_conv_func>asfreq_DTtoA)
- cdef int64_t asfreq_BtoQ(int64_t ordinal, asfreq_info *af_info) nogil:
- return transform_via_day(ordinal, af_info,
- <freq_conv_func>asfreq_BtoDT,
- <freq_conv_func>asfreq_DTtoQ)
- cdef int64_t asfreq_BtoM(int64_t ordinal, asfreq_info *af_info) nogil:
- return transform_via_day(ordinal, af_info,
- <freq_conv_func>asfreq_BtoDT,
- <freq_conv_func>asfreq_DTtoM)
- cdef int64_t asfreq_BtoW(int64_t ordinal, asfreq_info *af_info) nogil:
- return transform_via_day(ordinal, af_info,
- <freq_conv_func>asfreq_BtoDT,
- <freq_conv_func>asfreq_DTtoW)
- # ----------------------------------------------------------------------
- # Conversion _from_ Annual Freq
- cdef int64_t asfreq_AtoA(int64_t ordinal, asfreq_info *af_info) nogil:
- return transform_via_day(ordinal, af_info,
- <freq_conv_func>asfreq_AtoDT,
- <freq_conv_func>asfreq_DTtoA)
- cdef int64_t asfreq_AtoQ(int64_t ordinal, asfreq_info *af_info) nogil:
- return transform_via_day(ordinal, af_info,
- <freq_conv_func>asfreq_AtoDT,
- <freq_conv_func>asfreq_DTtoQ)
- cdef int64_t asfreq_AtoM(int64_t ordinal, asfreq_info *af_info) nogil:
- return transform_via_day(ordinal, af_info,
- <freq_conv_func>asfreq_AtoDT,
- <freq_conv_func>asfreq_DTtoM)
- cdef int64_t asfreq_AtoW(int64_t ordinal, asfreq_info *af_info) nogil:
- return transform_via_day(ordinal, af_info,
- <freq_conv_func>asfreq_AtoDT,
- <freq_conv_func>asfreq_DTtoW)
- # ----------------------------------------------------------------------
- # Conversion _from_ Quarterly Freq
- cdef int64_t asfreq_QtoQ(int64_t ordinal, asfreq_info *af_info) nogil:
- return transform_via_day(ordinal, af_info,
- <freq_conv_func>asfreq_QtoDT,
- <freq_conv_func>asfreq_DTtoQ)
- cdef int64_t asfreq_QtoA(int64_t ordinal, asfreq_info *af_info) nogil:
- return transform_via_day(ordinal, af_info,
- <freq_conv_func>asfreq_QtoDT,
- <freq_conv_func>asfreq_DTtoA)
- cdef int64_t asfreq_QtoM(int64_t ordinal, asfreq_info *af_info) nogil:
- return transform_via_day(ordinal, af_info,
- <freq_conv_func>asfreq_QtoDT,
- <freq_conv_func>asfreq_DTtoM)
- cdef int64_t asfreq_QtoW(int64_t ordinal, asfreq_info *af_info) nogil:
- return transform_via_day(ordinal, af_info,
- <freq_conv_func>asfreq_QtoDT,
- <freq_conv_func>asfreq_DTtoW)
- # ----------------------------------------------------------------------
- # Conversion _from_ Monthly Freq
- cdef int64_t asfreq_MtoA(int64_t ordinal, asfreq_info *af_info) nogil:
- return transform_via_day(ordinal, af_info,
- <freq_conv_func>asfreq_MtoDT,
- <freq_conv_func>asfreq_DTtoA)
- cdef int64_t asfreq_MtoQ(int64_t ordinal, asfreq_info *af_info) nogil:
- return transform_via_day(ordinal, af_info,
- <freq_conv_func>asfreq_MtoDT,
- <freq_conv_func>asfreq_DTtoQ)
- cdef int64_t asfreq_MtoW(int64_t ordinal, asfreq_info *af_info) nogil:
- return transform_via_day(ordinal, af_info,
- <freq_conv_func>asfreq_MtoDT,
- <freq_conv_func>asfreq_DTtoW)
- # ----------------------------------------------------------------------
- # Conversion _from_ Weekly Freq
- cdef int64_t asfreq_WtoA(int64_t ordinal, asfreq_info *af_info) nogil:
- return transform_via_day(ordinal, af_info,
- <freq_conv_func>asfreq_WtoDT,
- <freq_conv_func>asfreq_DTtoA)
- cdef int64_t asfreq_WtoQ(int64_t ordinal, asfreq_info *af_info) nogil:
- return transform_via_day(ordinal, af_info,
- <freq_conv_func>asfreq_WtoDT,
- <freq_conv_func>asfreq_DTtoQ)
- cdef int64_t asfreq_WtoM(int64_t ordinal, asfreq_info *af_info) nogil:
- return transform_via_day(ordinal, af_info,
- <freq_conv_func>asfreq_WtoDT,
- <freq_conv_func>asfreq_DTtoM)
- cdef int64_t asfreq_WtoW(int64_t ordinal, asfreq_info *af_info) nogil:
- return transform_via_day(ordinal, af_info,
- <freq_conv_func>asfreq_WtoDT,
- <freq_conv_func>asfreq_DTtoW)
- # ----------------------------------------------------------------------
- @cython.cdivision
- cdef char* c_strftime(npy_datetimestruct *dts, char *fmt):
- """
- Generate a nice string representation of the period
- object, originally from DateObject_strftime
- Parameters
- ----------
- dts : npy_datetimestruct*
- fmt : char*
- Returns
- -------
- result : char*
- """
- cdef:
- tm c_date
- char *result
- int result_len = strlen(fmt) + 50
- c_date.tm_sec = dts.sec
- c_date.tm_min = dts.min
- c_date.tm_hour = dts.hour
- c_date.tm_mday = dts.day
- c_date.tm_mon = dts.month - 1
- c_date.tm_year = dts.year - 1900
- c_date.tm_wday = (dayofweek(dts.year, dts.month, dts.day) + 1) % 7
- c_date.tm_yday = get_day_of_year(dts.year, dts.month, dts.day) - 1
- c_date.tm_isdst = -1
- result = <char*>malloc(result_len * sizeof(char))
- strftime(result, result_len, fmt, &c_date)
- return result
- # ----------------------------------------------------------------------
- # Conversion between date_info and npy_datetimestruct
- cdef int get_freq_group(int freq) nogil:
- # See also FreqGroup.get_freq_group
- return (freq // 1000) * 1000
- cdef int get_freq_group_index(int freq) nogil:
- return freq // 1000
- cdef void adjust_dts_for_month(npy_datetimestruct* dts, int from_end) nogil:
- if from_end != 12:
- dts.month += from_end
- if dts.month > 12:
- dts.month -= 12
- else:
- dts.year -= 1
- cdef void adjust_dts_for_qtr(npy_datetimestruct* dts, int to_end) nogil:
- if to_end != 12:
- dts.month -= to_end
- if dts.month <= 0:
- dts.month += 12
- else:
- dts.year += 1
- # Find the unix_date (days elapsed since datetime(1970, 1, 1)
- # for the given year/month/day.
- # Assumes GREGORIAN_CALENDAR */
- cdef int64_t unix_date_from_ymd(int year, int month, int day) nogil:
- # Calculate the absolute date
- cdef:
- npy_datetimestruct dts
- int64_t unix_date
- memset(&dts, 0, sizeof(npy_datetimestruct))
- dts.year = year
- dts.month = month
- dts.day = day
- unix_date = npy_datetimestruct_to_datetime(NPY_FR_D, &dts)
- return unix_date
- cdef int64_t dts_to_month_ordinal(npy_datetimestruct* dts) nogil:
- # AKA: use npy_datetimestruct_to_datetime(NPY_FR_M, &dts)
- return <int64_t>((dts.year - 1970) * 12 + dts.month - 1)
- cdef int64_t dts_to_year_ordinal(npy_datetimestruct *dts, int to_end) nogil:
- cdef:
- int64_t result
- result = npy_datetimestruct_to_datetime(NPY_DATETIMEUNIT.NPY_FR_Y, dts)
- if dts.month > to_end:
- return result + 1
- else:
- return result
- cdef int64_t dts_to_qtr_ordinal(npy_datetimestruct* dts, int to_end) nogil:
- cdef:
- int quarter
- adjust_dts_for_qtr(dts, to_end)
- quarter = month_to_quarter(dts.month)
- return <int64_t>((dts.year - 1970) * 4 + quarter - 1)
- cdef int get_anchor_month(int freq, int freq_group) nogil:
- cdef:
- int fmonth
- fmonth = freq - freq_group
- if fmonth == 0:
- fmonth = 12
- return fmonth
- # specifically _dont_ use cdvision or else ordinals near -1 are assigned to
- # incorrect dates GH#19643
- @cython.cdivision(False)
- cdef int64_t get_period_ordinal(npy_datetimestruct *dts, int freq) nogil:
- """
- Generate an ordinal in period space
- Parameters
- ----------
- dts : npy_datetimestruct*
- freq : int
- Returns
- -------
- period_ordinal : int64_t
- """
- cdef:
- int64_t unix_date
- int freq_group, fmonth
- NPY_DATETIMEUNIT unit
- freq_group = get_freq_group(freq)
- if freq_group == FR_ANN:
- fmonth = get_anchor_month(freq, freq_group)
- return dts_to_year_ordinal(dts, fmonth)
- elif freq_group == FR_QTR:
- fmonth = get_anchor_month(freq, freq_group)
- return dts_to_qtr_ordinal(dts, fmonth)
- elif freq_group == FR_WK:
- unix_date = npy_datetimestruct_to_datetime(NPY_FR_D, dts)
- return unix_date_to_week(unix_date, freq - FR_WK)
- elif freq == FR_BUS:
- unix_date = npy_datetimestruct_to_datetime(NPY_FR_D, dts)
- return DtoB(dts, 0, unix_date)
- unit = freq_group_code_to_npy_unit(freq)
- return npy_datetimestruct_to_datetime(unit, dts)
- cdef void get_date_info(int64_t ordinal, int freq, npy_datetimestruct *dts) nogil:
- cdef:
- int64_t unix_date, nanos
- npy_datetimestruct dts2
- unix_date = get_unix_date(ordinal, freq)
- nanos = get_time_nanos(freq, unix_date, ordinal)
- pandas_datetime_to_datetimestruct(unix_date, NPY_FR_D, dts)
- pandas_datetime_to_datetimestruct(nanos, NPY_DATETIMEUNIT.NPY_FR_ns, &dts2)
- dts.hour = dts2.hour
- dts.min = dts2.min
- dts.sec = dts2.sec
- dts.us = dts2.us
- dts.ps = dts2.ps
- cdef int64_t get_unix_date(int64_t period_ordinal, int freq) nogil:
- """
- Returns the proleptic Gregorian ordinal of the date, as an integer.
- This corresponds to the number of days since Jan., 1st, 1970 AD.
- When the instance has a frequency less than daily, the proleptic date
- is calculated for the last day of the period.
- Parameters
- ----------
- period_ordinal : int64_t
- freq : int
- Returns
- -------
- unix_date : int64_t number of days since datetime(1970, 1, 1)
- """
- cdef:
- asfreq_info af_info
- freq_conv_func toDaily = NULL
- if freq == FR_DAY:
- return period_ordinal
- toDaily = get_asfreq_func(freq, FR_DAY)
- get_asfreq_info(freq, FR_DAY, True, &af_info)
- return toDaily(period_ordinal, &af_info)
- @cython.cdivision
- cdef int64_t get_time_nanos(int freq, int64_t unix_date, int64_t ordinal) nogil:
- """
- Find the number of nanoseconds after midnight on the given unix_date
- that the ordinal represents in the given frequency.
- Parameters
- ----------
- freq : int
- unix_date : int64_t
- ordinal : int64_t
- Returns
- -------
- int64_t
- """
- cdef:
- int64_t sub, factor
- int64_t nanos_in_day = 24 * 3600 * 10**9
- freq = get_freq_group(freq)
- if freq <= FR_DAY:
- return 0
- elif freq == FR_NS:
- factor = 1
- elif freq == FR_US:
- factor = 10**3
- elif freq == FR_MS:
- factor = 10**6
- elif freq == FR_SEC:
- factor = 10 **9
- elif freq == FR_MIN:
- factor = 10**9 * 60
- else:
- # We must have freq == FR_HR
- factor = 10**9 * 3600
- sub = ordinal - unix_date * (nanos_in_day / factor)
- return sub * factor
- cdef int get_yq(int64_t ordinal, int freq, npy_datetimestruct* dts):
- """
- Find the year and quarter of a Period with the given ordinal and frequency
- Parameters
- ----------
- ordinal : int64_t
- freq : int
- dts : *npy_datetimestruct
- Returns
- -------
- quarter : int
- describes the implied quarterly frequency associated with `freq`
- Notes
- -----
- Sets dts.year in-place.
- """
- cdef:
- asfreq_info af_info
- int qtr_freq
- int64_t unix_date
- int quarter
- unix_date = get_unix_date(ordinal, freq)
- if get_freq_group(freq) == FR_QTR:
- qtr_freq = freq
- else:
- qtr_freq = FR_QTR
- get_asfreq_info(FR_DAY, qtr_freq, True, &af_info)
- quarter = DtoQ_yq(unix_date, &af_info, dts)
- return quarter
- cdef int month_to_quarter(int month) nogil:
- return (month - 1) // 3 + 1
- # ----------------------------------------------------------------------
- # Period logic
- @cython.wraparound(False)
- @cython.boundscheck(False)
- def periodarr_to_dt64arr(const int64_t[:] periodarr, int freq):
- """
- Convert array to datetime64 values from a set of ordinals corresponding to
- periods per period convention.
- """
- cdef:
- int64_t[::1] out
- Py_ssize_t i, N
- if freq < 6000: # i.e. FR_DAY, hard-code to avoid need to cast
- N = len(periodarr)
- out = np.empty(N, dtype="i8")
- # We get here with freqs that do not correspond to a datetime64 unit
- for i in range(N):
- out[i] = period_ordinal_to_dt64(periodarr[i], freq)
- return out.base # .base to access underlying np.ndarray
- else:
- # Short-circuit for performance
- if freq == FR_NS:
- # TODO: copy?
- return periodarr.base
- if freq == FR_US:
- dta = periodarr.base.view("M8[us]")
- elif freq == FR_MS:
- dta = periodarr.base.view("M8[ms]")
- elif freq == FR_SEC:
- dta = periodarr.base.view("M8[s]")
- elif freq == FR_MIN:
- dta = periodarr.base.view("M8[m]")
- elif freq == FR_HR:
- dta = periodarr.base.view("M8[h]")
- elif freq == FR_DAY:
- dta = periodarr.base.view("M8[D]")
- return astype_overflowsafe(dta, dtype=DT64NS_DTYPE)
- cdef void get_asfreq_info(int from_freq, int to_freq,
- bint is_end, asfreq_info *af_info) nogil:
- """
- Construct the `asfreq_info` object used to convert an ordinal from
- `from_freq` to `to_freq`.
- Parameters
- ----------
- from_freq : int
- to_freq int
- is_end : bool
- af_info : *asfreq_info
- """
- cdef:
- int from_group = get_freq_group(from_freq)
- int to_group = get_freq_group(to_freq)
- af_info.is_end = is_end
- af_info.intraday_conversion_factor = get_daytime_conversion_factor(
- get_freq_group_index(max_value(from_group, FR_DAY)),
- get_freq_group_index(max_value(to_group, FR_DAY)))
- if from_group == FR_WK:
- af_info.from_end = calc_week_end(from_freq, from_group)
- elif from_group == FR_ANN:
- af_info.from_end = calc_a_year_end(from_freq, from_group)
- elif from_group == FR_QTR:
- af_info.from_end = calc_a_year_end(from_freq, from_group)
- if to_group == FR_WK:
- af_info.to_end = calc_week_end(to_freq, to_group)
- elif to_group == FR_ANN:
- af_info.to_end = calc_a_year_end(to_freq, to_group)
- elif to_group == FR_QTR:
- af_info.to_end = calc_a_year_end(to_freq, to_group)
- @cython.cdivision
- cdef int calc_a_year_end(int freq, int group) nogil:
- cdef:
- int result = (freq - group) % 12
- if result == 0:
- return 12
- else:
- return result
- cdef int calc_week_end(int freq, int group) nogil:
- return freq - group
- cpdef int64_t period_asfreq(int64_t ordinal, int freq1, int freq2, bint end):
- """
- Convert period ordinal from one frequency to another, and if upsampling,
- choose to use start ('S') or end ('E') of period.
- """
- cdef:
- int64_t retval
- _period_asfreq(&ordinal, &retval, 1, freq1, freq2, end)
- return retval
- @cython.wraparound(False)
- @cython.boundscheck(False)
- def period_asfreq_arr(ndarray[int64_t] arr, int freq1, int freq2, bint end):
- """
- Convert int64-array of period ordinals from one frequency to another, and
- if upsampling, choose to use start ('S') or end ('E') of period.
- """
- cdef:
- Py_ssize_t n = len(arr)
- Py_ssize_t increment = arr.strides[0] // 8
- ndarray[int64_t] result = cnp.PyArray_EMPTY(
- arr.ndim, arr.shape, cnp.NPY_INT64, 0
- )
- _period_asfreq(
- <int64_t*>cnp.PyArray_DATA(arr),
- <int64_t*>cnp.PyArray_DATA(result),
- n,
- freq1,
- freq2,
- end,
- increment,
- )
- return result
- @cython.wraparound(False)
- @cython.boundscheck(False)
- cdef void _period_asfreq(
- int64_t* ordinals,
- int64_t* out,
- Py_ssize_t length,
- int freq1,
- int freq2,
- bint end,
- Py_ssize_t increment=1,
- ):
- """See period_asfreq.__doc__"""
- cdef:
- Py_ssize_t i
- freq_conv_func func
- asfreq_info af_info
- int64_t val
- if length == 1 and ordinals[0] == NPY_NAT:
- # fastpath avoid calling get_asfreq_func
- out[0] = NPY_NAT
- return
- func = get_asfreq_func(freq1, freq2)
- get_asfreq_info(freq1, freq2, end, &af_info)
- for i in range(length):
- val = ordinals[i * increment]
- if val != NPY_NAT:
- val = func(val, &af_info)
- out[i] = val
- cpdef int64_t period_ordinal(int y, int m, int d, int h, int min,
- int s, int us, int ps, int freq):
- """
- Find the ordinal representation of the given datetime components at the
- frequency `freq`.
- Parameters
- ----------
- y : int
- m : int
- d : int
- h : int
- min : int
- s : int
- us : int
- ps : int
- Returns
- -------
- ordinal : int64_t
- """
- cdef:
- npy_datetimestruct dts
- dts.year = y
- dts.month = m
- dts.day = d
- dts.hour = h
- dts.min = min
- dts.sec = s
- dts.us = us
- dts.ps = ps
- return get_period_ordinal(&dts, freq)
- cdef int64_t period_ordinal_to_dt64(int64_t ordinal, int freq) except? -1:
- cdef:
- npy_datetimestruct dts
- if ordinal == NPY_NAT:
- return NPY_NAT
- get_date_info(ordinal, freq, &dts)
- check_dts_bounds(&dts)
- return npy_datetimestruct_to_datetime(NPY_DATETIMEUNIT.NPY_FR_ns, &dts)
- cdef str period_format(int64_t value, int freq, object fmt=None):
- cdef:
- int freq_group
- if value == NPY_NAT:
- return "NaT"
- if isinstance(fmt, str):
- # Encode using current locale, in case fmt contains non-utf8 chars
- fmt = <bytes>util.string_encode_locale(fmt)
- if fmt is None:
- freq_group = get_freq_group(freq)
- if freq_group == FR_ANN:
- fmt = b"%Y"
- elif freq_group == FR_QTR:
- fmt = b"%FQ%q"
- elif freq_group == FR_MTH:
- fmt = b"%Y-%m"
- elif freq_group == FR_WK:
- left = period_asfreq(value, freq, FR_DAY, 0)
- right = period_asfreq(value, freq, FR_DAY, 1)
- return f"{period_format(left, FR_DAY)}/{period_format(right, FR_DAY)}"
- elif freq_group == FR_BUS or freq_group == FR_DAY:
- fmt = b"%Y-%m-%d"
- elif freq_group == FR_HR:
- fmt = b"%Y-%m-%d %H:00"
- elif freq_group == FR_MIN:
- fmt = b"%Y-%m-%d %H:%M"
- elif freq_group == FR_SEC:
- fmt = b"%Y-%m-%d %H:%M:%S"
- elif freq_group == FR_MS:
- fmt = b"%Y-%m-%d %H:%M:%S.%l"
- elif freq_group == FR_US:
- fmt = b"%Y-%m-%d %H:%M:%S.%u"
- elif freq_group == FR_NS:
- fmt = b"%Y-%m-%d %H:%M:%S.%n"
- else:
- raise ValueError(f"Unknown freq: {freq}")
- return _period_strftime(value, freq, fmt)
- cdef list extra_fmts = [(b"%q", b"^`AB`^"),
- (b"%f", b"^`CD`^"),
- (b"%F", b"^`EF`^"),
- (b"%l", b"^`GH`^"),
- (b"%u", b"^`IJ`^"),
- (b"%n", b"^`KL`^")]
- cdef list str_extra_fmts = ["^`AB`^", "^`CD`^", "^`EF`^",
- "^`GH`^", "^`IJ`^", "^`KL`^"]
- cdef str _period_strftime(int64_t value, int freq, bytes fmt):
- cdef:
- Py_ssize_t i
- npy_datetimestruct dts
- char *formatted
- bytes pat, brepl
- list found_pat = [False] * len(extra_fmts)
- int quarter
- int32_t us, ps
- str result, repl
- get_date_info(value, freq, &dts)
- # Find our additional directives in the pattern and replace them with
- # placeholders that are not processed by c_strftime
- for i in range(len(extra_fmts)):
- pat = extra_fmts[i][0]
- brepl = extra_fmts[i][1]
- if pat in fmt:
- fmt = fmt.replace(pat, brepl)
- found_pat[i] = True
- # Execute c_strftime to process the usual datetime directives
- formatted = c_strftime(&dts, <char*>fmt)
- # Decode result according to current locale
- result = util.char_to_string_locale(formatted)
- free(formatted)
- # Now we will fill the placeholders corresponding to our additional directives
- # First prepare the contents
- # Save these to local vars as dts can be modified by get_yq below
- us = dts.us
- ps = dts.ps
- if any(found_pat[0:3]):
- # Note: this modifies `dts` in-place so that year becomes fiscal year
- # However it looses the us and ps
- quarter = get_yq(value, freq, &dts)
- else:
- quarter = 0
- # Now do the filling per se
- for i in range(len(extra_fmts)):
- if found_pat[i]:
- if i == 0: # %q, 1-digit quarter.
- repl = f"{quarter}"
- elif i == 1: # %f, 2-digit 'Fiscal' year
- repl = f"{(dts.year % 100):02d}"
- elif i == 2: # %F, 'Fiscal' year with a century
- repl = str(dts.year)
- elif i == 3: # %l, milliseconds
- repl = f"{(us // 1_000):03d}"
- elif i == 4: # %u, microseconds
- repl = f"{(us):06d}"
- elif i == 5: # %n, nanoseconds
- repl = f"{((us * 1000) + (ps // 1000)):09d}"
- result = result.replace(str_extra_fmts[i], repl)
- return result
- # ----------------------------------------------------------------------
- # period accessors
- ctypedef int (*accessor)(int64_t ordinal, int freq) except INT32_MIN
- cdef int pyear(int64_t ordinal, int freq):
- cdef:
- npy_datetimestruct dts
- get_date_info(ordinal, freq, &dts)
- return dts.year
- cdef int pqyear(int64_t ordinal, int freq):
- cdef:
- npy_datetimestruct dts
- get_yq(ordinal, freq, &dts)
- return dts.year
- cdef int pquarter(int64_t ordinal, int freq):
- cdef:
- int quarter
- npy_datetimestruct dts
- quarter = get_yq(ordinal, freq, &dts)
- return quarter
- cdef int pmonth(int64_t ordinal, int freq):
- cdef:
- npy_datetimestruct dts
- get_date_info(ordinal, freq, &dts)
- return dts.month
- cdef int pday(int64_t ordinal, int freq):
- cdef:
- npy_datetimestruct dts
- get_date_info(ordinal, freq, &dts)
- return dts.day
- cdef int pweekday(int64_t ordinal, int freq):
- cdef:
- npy_datetimestruct dts
- get_date_info(ordinal, freq, &dts)
- return dayofweek(dts.year, dts.month, dts.day)
- cdef int pday_of_year(int64_t ordinal, int freq):
- cdef:
- npy_datetimestruct dts
- get_date_info(ordinal, freq, &dts)
- return get_day_of_year(dts.year, dts.month, dts.day)
- cdef int pweek(int64_t ordinal, int freq):
- cdef:
- npy_datetimestruct dts
- get_date_info(ordinal, freq, &dts)
- return get_week_of_year(dts.year, dts.month, dts.day)
- cdef int phour(int64_t ordinal, int freq):
- cdef:
- npy_datetimestruct dts
- get_date_info(ordinal, freq, &dts)
- return dts.hour
- cdef int pminute(int64_t ordinal, int freq):
- cdef:
- npy_datetimestruct dts
- get_date_info(ordinal, freq, &dts)
- return dts.min
- cdef int psecond(int64_t ordinal, int freq):
- cdef:
- npy_datetimestruct dts
- get_date_info(ordinal, freq, &dts)
- return <int>dts.sec
- cdef int pdays_in_month(int64_t ordinal, int freq):
- cdef:
- npy_datetimestruct dts
- get_date_info(ordinal, freq, &dts)
- return get_days_in_month(dts.year, dts.month)
- @cython.wraparound(False)
- @cython.boundscheck(False)
- def get_period_field_arr(str field, const int64_t[:] arr, int freq):
- cdef:
- Py_ssize_t i, sz
- int64_t[::1] out
- func = _get_accessor_func(field)
- if func is NULL:
- raise ValueError(f"Unrecognized field name: {field}")
- sz = len(arr)
- out = np.empty(sz, dtype=np.int64)
- for i in range(sz):
- if arr[i] == NPY_NAT:
- out[i] = -1
- continue
- out[i] = func(arr[i], freq)
- return out.base # .base to access underlying np.ndarray
- cdef accessor _get_accessor_func(str field):
- if field == "year":
- return <accessor>pyear
- elif field == "qyear":
- return <accessor>pqyear
- elif field == "quarter":
- return <accessor>pquarter
- elif field == "month":
- return <accessor>pmonth
- elif field == "day":
- return <accessor>pday
- elif field == "hour":
- return <accessor>phour
- elif field == "minute":
- return <accessor>pminute
- elif field == "second":
- return <accessor>psecond
- elif field == "week":
- return <accessor>pweek
- elif field == "day_of_year":
- return <accessor>pday_of_year
- elif field == "weekday" or field == "day_of_week":
- return <accessor>pweekday
- elif field == "days_in_month":
- return <accessor>pdays_in_month
- return NULL
- @cython.wraparound(False)
- @cython.boundscheck(False)
- def from_ordinals(const int64_t[:] values, freq):
- cdef:
- Py_ssize_t i, n = len(values)
- int64_t[::1] result = np.empty(len(values), dtype="i8")
- int64_t val
- freq = to_offset(freq)
- if not isinstance(freq, BaseOffset):
- raise ValueError("freq not specified and cannot be inferred")
- for i in range(n):
- val = values[i]
- if val == NPY_NAT:
- result[i] = NPY_NAT
- else:
- result[i] = Period(val, freq=freq).ordinal
- return result.base
- @cython.wraparound(False)
- @cython.boundscheck(False)
- def extract_ordinals(ndarray values, freq) -> np.ndarray:
- # values is object-dtype, may be 2D
- cdef:
- Py_ssize_t i, n = values.size
- int64_t ordinal
- ndarray ordinals = cnp.PyArray_EMPTY(
- values.ndim, values.shape, cnp.NPY_INT64, 0
- )
- cnp.broadcast mi = cnp.PyArray_MultiIterNew2(ordinals, values)
- object p
- if values.descr.type_num != cnp.NPY_OBJECT:
- # if we don't raise here, we'll segfault later!
- raise TypeError("extract_ordinals values must be object-dtype")
- freqstr = Period._maybe_convert_freq(freq).freqstr
- for i in range(n):
- # Analogous to: p = values[i]
- p = <object>(<PyObject**>cnp.PyArray_MultiIter_DATA(mi, 1))[0]
- ordinal = _extract_ordinal(p, freqstr, freq)
- # Analogous to: ordinals[i] = ordinal
- (<int64_t*>cnp.PyArray_MultiIter_DATA(mi, 0))[0] = ordinal
- cnp.PyArray_MultiIter_NEXT(mi)
- return ordinals
- cdef int64_t _extract_ordinal(object item, str freqstr, freq) except? -1:
- """
- See extract_ordinals.
- """
- cdef:
- int64_t ordinal
- if checknull_with_nat(item) or item is C_NA:
- ordinal = NPY_NAT
- elif util.is_integer_object(item):
- if item == NPY_NAT:
- ordinal = NPY_NAT
- else:
- raise TypeError(item)
- else:
- try:
- ordinal = item.ordinal
- if item.freqstr != freqstr:
- msg = DIFFERENT_FREQ.format(cls="PeriodIndex",
- own_freq=freqstr,
- other_freq=item.freqstr)
- raise IncompatibleFrequency(msg)
- except AttributeError:
- item = Period(item, freq=freq)
- if item is NaT:
- # input may contain NaT-like string
- ordinal = NPY_NAT
- else:
- ordinal = item.ordinal
- return ordinal
- def extract_freq(ndarray[object] values) -> BaseOffset:
- # TODO: Change type to const object[:] when Cython supports that.
- cdef:
- Py_ssize_t i, n = len(values)
- object value
- for i in range(n):
- value = values[i]
- if is_period_object(value):
- return value.freq
- raise ValueError("freq not specified and cannot be inferred")
- # -----------------------------------------------------------------------
- # period helpers
- DIFFERENT_FREQ = ("Input has different freq={other_freq} "
- "from {cls}(freq={own_freq})")
- class IncompatibleFrequency(ValueError):
- pass
- cdef class PeriodMixin:
- # Methods shared between Period and PeriodArray
- @property
- def start_time(self) -> Timestamp:
- """
- Get the Timestamp for the start of the period.
- Returns
- -------
- Timestamp
- See Also
- --------
- Period.end_time : Return the end Timestamp.
- Period.dayofyear : Return the day of year.
- Period.daysinmonth : Return the days in that month.
- Period.dayofweek : Return the day of the week.
- Examples
- --------
- >>> period = pd.Period('2012-1-1', freq='D')
- >>> period
- Period('2012-01-01', 'D')
- >>> period.start_time
- Timestamp('2012-01-01 00:00:00')
- >>> period.end_time
- Timestamp('2012-01-01 23:59:59.999999999')
- """
- return self.to_timestamp(how="start")
- @property
- def end_time(self) -> Timestamp:
- """
- Get the Timestamp for the end of the period.
- Returns
- -------
- Timestamp
- See Also
- --------
- Period.start_time : Return the start Timestamp.
- Period.dayofyear : Return the day of year.
- Period.daysinmonth : Return the days in that month.
- Period.dayofweek : Return the day of the week.
- """
- return self.to_timestamp(how="end")
- def _require_matching_freq(self, other, base=False):
- # See also arrays.period.raise_on_incompatible
- if is_offset_object(other):
- other_freq = other
- else:
- other_freq = other.freq
- if base:
- condition = self.freq.base != other_freq.base
- else:
- condition = self.freq != other_freq
- if condition:
- msg = DIFFERENT_FREQ.format(
- cls=type(self).__name__,
- own_freq=self.freqstr,
- other_freq=other_freq.freqstr,
- )
- raise IncompatibleFrequency(msg)
- cdef class _Period(PeriodMixin):
- cdef readonly:
- int64_t ordinal
- PeriodDtypeBase _dtype
- BaseOffset freq
- # higher than np.ndarray, np.matrix, np.timedelta64
- __array_priority__ = 100
- dayofweek = _Period.day_of_week
- dayofyear = _Period.day_of_year
- def __cinit__(self, int64_t ordinal, BaseOffset freq):
- self.ordinal = ordinal
- self.freq = freq
- # Note: this is more performant than PeriodDtype.from_date_offset(freq)
- # because from_date_offset cannot be made a cdef method (until cython
- # supported cdef classmethods)
- self._dtype = PeriodDtypeBase(freq._period_dtype_code)
- @classmethod
- def _maybe_convert_freq(cls, object freq) -> BaseOffset:
- """
- Internally we allow integer and tuple representations (for now) that
- are not recognized by to_offset, so we convert them here. Also, a
- Period's freq attribute must have `freq.n > 0`, which we check for here.
- Returns
- -------
- DateOffset
- """
- if isinstance(freq, int):
- # We already have a dtype code
- dtype = PeriodDtypeBase(freq)
- freq = dtype._freqstr
- freq = to_offset(freq)
- if freq.n <= 0:
- raise ValueError("Frequency must be positive, because it "
- f"represents span: {freq.freqstr}")
- return freq
- @classmethod
- def _from_ordinal(cls, ordinal: int64_t, freq) -> "Period":
- """
- Fast creation from an ordinal and freq that are already validated!
- """
- if ordinal == NPY_NAT:
- return NaT
- else:
- freq = cls._maybe_convert_freq(freq)
- self = _Period.__new__(cls, ordinal, freq)
- return self
- def __richcmp__(self, other, op):
- if is_period_object(other):
- if other.freq != self.freq:
- if op == Py_EQ:
- return False
- elif op == Py_NE:
- return True
- self._require_matching_freq(other)
- return PyObject_RichCompareBool(self.ordinal, other.ordinal, op)
- elif other is NaT:
- return op == Py_NE
- elif util.is_array(other):
- # GH#44285
- if cnp.PyArray_IsZeroDim(other):
- return PyObject_RichCompare(self, other.item(), op)
- else:
- # in particular ndarray[object]; see test_pi_cmp_period
- return np.array([PyObject_RichCompare(self, x, op) for x in other])
- return NotImplemented
- def __hash__(self):
- return hash((self.ordinal, self.freqstr))
- def _add_timedeltalike_scalar(self, other) -> "Period":
- cdef:
- int64_t inc
- if not is_tick_object(self.freq):
- raise IncompatibleFrequency("Input cannot be converted to "
- f"Period(freq={self.freqstr})")
- if (
- util.is_timedelta64_object(other) and
- get_timedelta64_value(other) == NPY_NAT
- ):
- # i.e. np.timedelta64("nat")
- return NaT
- try:
- inc = delta_to_nanoseconds(other, reso=self.freq._creso, round_ok=False)
- except ValueError as err:
- raise IncompatibleFrequency("Input cannot be converted to "
- f"Period(freq={self.freqstr})") from err
- # TODO: overflow-check here
- ordinal = self.ordinal + inc
- return Period(ordinal=ordinal, freq=self.freq)
- def _add_offset(self, other) -> "Period":
- # Non-Tick DateOffset other
- cdef:
- int64_t ordinal
- self._require_matching_freq(other, base=True)
- ordinal = self.ordinal + other.n
- return Period(ordinal=ordinal, freq=self.freq)
- def __add__(self, other):
- if not is_period_object(self):
- # cython semantics; this is analogous to a call to __radd__
- # TODO(cython3): remove this
- if self is NaT:
- return NaT
- return other.__add__(self)
- if is_any_td_scalar(other):
- return self._add_timedeltalike_scalar(other)
- elif is_offset_object(other):
- return self._add_offset(other)
- elif other is NaT:
- return NaT
- elif util.is_integer_object(other):
- ordinal = self.ordinal + other * self.freq.n
- return Period(ordinal=ordinal, freq=self.freq)
- elif is_period_object(other):
- # can't add datetime-like
- # GH#17983; can't just return NotImplemented bc we get a RecursionError
- # when called via np.add.reduce see TestNumpyReductions.test_add
- # in npdev build
- sname = type(self).__name__
- oname = type(other).__name__
- raise TypeError(f"unsupported operand type(s) for +: '{sname}' "
- f"and '{oname}'")
- elif util.is_array(other):
- if other.dtype == object:
- # GH#50162
- return np.array([self + x for x in other], dtype=object)
- return NotImplemented
- def __radd__(self, other):
- return self.__add__(other)
- def __sub__(self, other):
- if not is_period_object(self):
- # cython semantics; this is like a call to __rsub__
- # TODO(cython3): remove this
- if self is NaT:
- return NaT
- return NotImplemented
- elif (
- is_any_td_scalar(other)
- or is_offset_object(other)
- or util.is_integer_object(other)
- ):
- return self + (-other)
- elif is_period_object(other):
- self._require_matching_freq(other)
- # GH 23915 - mul by base freq since __add__ is agnostic of n
- return (self.ordinal - other.ordinal) * self.freq.base
- elif other is NaT:
- return NaT
- elif util.is_array(other):
- if other.dtype == object:
- # GH#50162
- return np.array([self - x for x in other], dtype=object)
- return NotImplemented
- def __rsub__(self, other):
- if other is NaT:
- return NaT
- elif util.is_array(other):
- if other.dtype == object:
- # GH#50162
- return np.array([x - self for x in other], dtype=object)
- return NotImplemented
- def asfreq(self, freq, how="E") -> "Period":
- """
- Convert Period to desired frequency, at the start or end of the interval.
- Parameters
- ----------
- freq : str, BaseOffset
- The desired frequency.
- how : {'E', 'S', 'end', 'start'}, default 'end'
- Start or end of the timespan.
- Returns
- -------
- resampled : Period
- """
- freq = self._maybe_convert_freq(freq)
- how = validate_end_alias(how)
- base1 = self._dtype._dtype_code
- base2 = freq_to_dtype_code(freq)
- # self.n can't be negative or 0
- end = how == "E"
- if end:
- ordinal = self.ordinal + self.freq.n - 1
- else:
- ordinal = self.ordinal
- ordinal = period_asfreq(ordinal, base1, base2, end)
- return Period(ordinal=ordinal, freq=freq)
- def to_timestamp(self, freq=None, how="start") -> Timestamp:
- """
- Return the Timestamp representation of the Period.
- Uses the target frequency specified at the part of the period specified
- by `how`, which is either `Start` or `Finish`.
- Parameters
- ----------
- freq : str or DateOffset
- Target frequency. Default is 'D' if self.freq is week or
- longer and 'S' otherwise.
- how : str, default 'S' (start)
- One of 'S', 'E'. Can be aliased as case insensitive
- 'Start', 'Finish', 'Begin', 'End'.
- Returns
- -------
- Timestamp
- Examples
- --------
- >>> period = pd.Period('2023-1-1', freq='D')
- >>> timestamp = period.to_timestamp()
- >>> timestamp
- Timestamp('2023-01-01 00:00:00')
- """
- how = validate_end_alias(how)
- end = how == "E"
- if end:
- if freq == "B" or self.freq == "B":
- # roll forward to ensure we land on B date
- adjust = np.timedelta64(1, "D") - np.timedelta64(1, "ns")
- return self.to_timestamp(how="start") + adjust
- endpoint = (self + self.freq).to_timestamp(how="start")
- return endpoint - np.timedelta64(1, "ns")
- if freq is None:
- freq = self._dtype._get_to_timestamp_base()
- base = freq
- else:
- freq = self._maybe_convert_freq(freq)
- base = freq._period_dtype_code
- val = self.asfreq(freq, how)
- dt64 = period_ordinal_to_dt64(val.ordinal, base)
- return Timestamp(dt64)
- @property
- def year(self) -> int:
- """
- Return the year this Period falls on.
- """
- base = self._dtype._dtype_code
- return pyear(self.ordinal, base)
- @property
- def month(self) -> int:
- """
- Return the month this Period falls on.
- """
- base = self._dtype._dtype_code
- return pmonth(self.ordinal, base)
- @property
- def day(self) -> int:
- """
- Get day of the month that a Period falls on.
- Returns
- -------
- int
- See Also
- --------
- Period.dayofweek : Get the day of the week.
- Period.dayofyear : Get the day of the year.
- Examples
- --------
- >>> p = pd.Period("2018-03-11", freq='H')
- >>> p.day
- 11
- """
- base = self._dtype._dtype_code
- return pday(self.ordinal, base)
- @property
- def hour(self) -> int:
- """
- Get the hour of the day component of the Period.
- Returns
- -------
- int
- The hour as an integer, between 0 and 23.
- See Also
- --------
- Period.second : Get the second component of the Period.
- Period.minute : Get the minute component of the Period.
- Examples
- --------
- >>> p = pd.Period("2018-03-11 13:03:12.050000")
- >>> p.hour
- 13
- Period longer than a day
- >>> p = pd.Period("2018-03-11", freq="M")
- >>> p.hour
- 0
- """
- base = self._dtype._dtype_code
- return phour(self.ordinal, base)
- @property
- def minute(self) -> int:
- """
- Get minute of the hour component of the Period.
- Returns
- -------
- int
- The minute as an integer, between 0 and 59.
- See Also
- --------
- Period.hour : Get the hour component of the Period.
- Period.second : Get the second component of the Period.
- Examples
- --------
- >>> p = pd.Period("2018-03-11 13:03:12.050000")
- >>> p.minute
- 3
- """
- base = self._dtype._dtype_code
- return pminute(self.ordinal, base)
- @property
- def second(self) -> int:
- """
- Get the second component of the Period.
- Returns
- -------
- int
- The second of the Period (ranges from 0 to 59).
- See Also
- --------
- Period.hour : Get the hour component of the Period.
- Period.minute : Get the minute component of the Period.
- Examples
- --------
- >>> p = pd.Period("2018-03-11 13:03:12.050000")
- >>> p.second
- 12
- """
- base = self._dtype._dtype_code
- return psecond(self.ordinal, base)
- @property
- def weekofyear(self) -> int:
- """
- Get the week of the year on the given Period.
- Returns
- -------
- int
- See Also
- --------
- Period.dayofweek : Get the day component of the Period.
- Period.weekday : Get the day component of the Period.
- Examples
- --------
- >>> p = pd.Period("2018-03-11", "H")
- >>> p.weekofyear
- 10
- >>> p = pd.Period("2018-02-01", "D")
- >>> p.weekofyear
- 5
- >>> p = pd.Period("2018-01-06", "D")
- >>> p.weekofyear
- 1
- """
- base = self._dtype._dtype_code
- return pweek(self.ordinal, base)
- @property
- def week(self) -> int:
- """
- Get the week of the year on the given Period.
- Returns
- -------
- int
- See Also
- --------
- Period.dayofweek : Get the day component of the Period.
- Period.weekday : Get the day component of the Period.
- Examples
- --------
- >>> p = pd.Period("2018-03-11", "H")
- >>> p.week
- 10
- >>> p = pd.Period("2018-02-01", "D")
- >>> p.week
- 5
- >>> p = pd.Period("2018-01-06", "D")
- >>> p.week
- 1
- """
- return self.weekofyear
- @property
- def day_of_week(self) -> int:
- """
- Day of the week the period lies in, with Monday=0 and Sunday=6.
- If the period frequency is lower than daily (e.g. hourly), and the
- period spans over multiple days, the day at the start of the period is
- used.
- If the frequency is higher than daily (e.g. monthly), the last day
- of the period is used.
- Returns
- -------
- int
- Day of the week.
- See Also
- --------
- Period.day_of_week : Day of the week the period lies in.
- Period.weekday : Alias of Period.day_of_week.
- Period.day : Day of the month.
- Period.dayofyear : Day of the year.
- Examples
- --------
- >>> per = pd.Period('2017-12-31 22:00', 'H')
- >>> per.day_of_week
- 6
- For periods that span over multiple days, the day at the beginning of
- the period is returned.
- >>> per = pd.Period('2017-12-31 22:00', '4H')
- >>> per.day_of_week
- 6
- >>> per.start_time.day_of_week
- 6
- For periods with a frequency higher than days, the last day of the
- period is returned.
- >>> per = pd.Period('2018-01', 'M')
- >>> per.day_of_week
- 2
- >>> per.end_time.day_of_week
- 2
- """
- base = self._dtype._dtype_code
- return pweekday(self.ordinal, base)
- @property
- def weekday(self) -> int:
- """
- Day of the week the period lies in, with Monday=0 and Sunday=6.
- If the period frequency is lower than daily (e.g. hourly), and the
- period spans over multiple days, the day at the start of the period is
- used.
- If the frequency is higher than daily (e.g. monthly), the last day
- of the period is used.
- Returns
- -------
- int
- Day of the week.
- See Also
- --------
- Period.dayofweek : Day of the week the period lies in.
- Period.weekday : Alias of Period.dayofweek.
- Period.day : Day of the month.
- Period.dayofyear : Day of the year.
- Examples
- --------
- >>> per = pd.Period('2017-12-31 22:00', 'H')
- >>> per.dayofweek
- 6
- For periods that span over multiple days, the day at the beginning of
- the period is returned.
- >>> per = pd.Period('2017-12-31 22:00', '4H')
- >>> per.dayofweek
- 6
- >>> per.start_time.dayofweek
- 6
- For periods with a frequency higher than days, the last day of the
- period is returned.
- >>> per = pd.Period('2018-01', 'M')
- >>> per.dayofweek
- 2
- >>> per.end_time.dayofweek
- 2
- """
- # Docstring is a duplicate from dayofweek. Reusing docstrings with
- # Appender doesn't work for properties in Cython files, and setting
- # the __doc__ attribute is also not possible.
- return self.dayofweek
- @property
- def day_of_year(self) -> int:
- """
- Return the day of the year.
- This attribute returns the day of the year on which the particular
- date occurs. The return value ranges between 1 to 365 for regular
- years and 1 to 366 for leap years.
- Returns
- -------
- int
- The day of year.
- See Also
- --------
- Period.day : Return the day of the month.
- Period.day_of_week : Return the day of week.
- PeriodIndex.day_of_year : Return the day of year of all indexes.
- Examples
- --------
- >>> period = pd.Period("2015-10-23", freq='H')
- >>> period.day_of_year
- 296
- >>> period = pd.Period("2012-12-31", freq='D')
- >>> period.day_of_year
- 366
- >>> period = pd.Period("2013-01-01", freq='D')
- >>> period.day_of_year
- 1
- """
- base = self._dtype._dtype_code
- return pday_of_year(self.ordinal, base)
- @property
- def quarter(self) -> int:
- """
- Return the quarter this Period falls on.
- """
- base = self._dtype._dtype_code
- return pquarter(self.ordinal, base)
- @property
- def qyear(self) -> int:
- """
- Fiscal year the Period lies in according to its starting-quarter.
- The `year` and the `qyear` of the period will be the same if the fiscal
- and calendar years are the same. When they are not, the fiscal year
- can be different from the calendar year of the period.
- Returns
- -------
- int
- The fiscal year of the period.
- See Also
- --------
- Period.year : Return the calendar year of the period.
- Examples
- --------
- If the natural and fiscal year are the same, `qyear` and `year` will
- be the same.
- >>> per = pd.Period('2018Q1', freq='Q')
- >>> per.qyear
- 2018
- >>> per.year
- 2018
- If the fiscal year starts in April (`Q-MAR`), the first quarter of
- 2018 will start in April 2017. `year` will then be 2017, but `qyear`
- will be the fiscal year, 2018.
- >>> per = pd.Period('2018Q1', freq='Q-MAR')
- >>> per.start_time
- Timestamp('2017-04-01 00:00:00')
- >>> per.qyear
- 2018
- >>> per.year
- 2017
- """
- base = self._dtype._dtype_code
- return pqyear(self.ordinal, base)
- @property
- def days_in_month(self) -> int:
- """
- Get the total number of days in the month that this period falls on.
- Returns
- -------
- int
- See Also
- --------
- Period.daysinmonth : Gets the number of days in the month.
- DatetimeIndex.daysinmonth : Gets the number of days in the month.
- calendar.monthrange : Returns a tuple containing weekday
- (0-6 ~ Mon-Sun) and number of days (28-31).
- Examples
- --------
- >>> p = pd.Period('2018-2-17')
- >>> p.days_in_month
- 28
- >>> pd.Period('2018-03-01').days_in_month
- 31
- Handles the leap year case as well:
- >>> p = pd.Period('2016-2-17')
- >>> p.days_in_month
- 29
- """
- base = self._dtype._dtype_code
- return pdays_in_month(self.ordinal, base)
- @property
- def daysinmonth(self) -> int:
- """
- Get the total number of days of the month that this period falls on.
- Returns
- -------
- int
- See Also
- --------
- Period.days_in_month : Return the days of the month.
- Period.dayofyear : Return the day of the year.
- Examples
- --------
- >>> p = pd.Period("2018-03-11", freq='H')
- >>> p.daysinmonth
- 31
- """
- return self.days_in_month
- @property
- def is_leap_year(self) -> bool:
- """
- Return True if the period's year is in a leap year.
- """
- return bool(is_leapyear(self.year))
- @classmethod
- def now(cls, freq):
- """
- Return the period of now's date.
- Parameters
- ----------
- freq : str, BaseOffset
- Frequency to use for the returned period.
- """
- return Period(datetime.now(), freq=freq)
- @property
- def freqstr(self) -> str:
- """
- Return a string representation of the frequency.
- """
- return self.freq.freqstr
- def __repr__(self) -> str:
- base = self._dtype._dtype_code
- formatted = period_format(self.ordinal, base)
- return f"Period('{formatted}', '{self.freqstr}')"
- def __str__(self) -> str:
- """
- Return a string representation for a particular DataFrame
- """
- base = self._dtype._dtype_code
- formatted = period_format(self.ordinal, base)
- value = str(formatted)
- return value
- def __setstate__(self, state):
- self.freq = state[1]
- self.ordinal = state[2]
- def __reduce__(self):
- object_state = None, self.freq, self.ordinal
- return (Period, object_state)
- def strftime(self, fmt: str) -> str:
- r"""
- Returns a formatted string representation of the :class:`Period`.
- ``fmt`` must be a string containing one or several directives.
- The method recognizes the same directives as the :func:`time.strftime`
- function of the standard Python distribution, as well as the specific
- additional directives ``%f``, ``%F``, ``%q``, ``%l``, ``%u``, ``%n``.
- (formatting & docs originally from scikits.timeries).
- +-----------+--------------------------------+-------+
- | Directive | Meaning | Notes |
- +===========+================================+=======+
- | ``%a`` | Locale's abbreviated weekday | |
- | | name. | |
- +-----------+--------------------------------+-------+
- | ``%A`` | Locale's full weekday name. | |
- +-----------+--------------------------------+-------+
- | ``%b`` | Locale's abbreviated month | |
- | | name. | |
- +-----------+--------------------------------+-------+
- | ``%B`` | Locale's full month name. | |
- +-----------+--------------------------------+-------+
- | ``%c`` | Locale's appropriate date and | |
- | | time representation. | |
- +-----------+--------------------------------+-------+
- | ``%d`` | Day of the month as a decimal | |
- | | number [01,31]. | |
- +-----------+--------------------------------+-------+
- | ``%f`` | 'Fiscal' year without a | \(1) |
- | | century as a decimal number | |
- | | [00,99] | |
- +-----------+--------------------------------+-------+
- | ``%F`` | 'Fiscal' year with a century | \(2) |
- | | as a decimal number | |
- +-----------+--------------------------------+-------+
- | ``%H`` | Hour (24-hour clock) as a | |
- | | decimal number [00,23]. | |
- +-----------+--------------------------------+-------+
- | ``%I`` | Hour (12-hour clock) as a | |
- | | decimal number [01,12]. | |
- +-----------+--------------------------------+-------+
- | ``%j`` | Day of the year as a decimal | |
- | | number [001,366]. | |
- +-----------+--------------------------------+-------+
- | ``%m`` | Month as a decimal number | |
- | | [01,12]. | |
- +-----------+--------------------------------+-------+
- | ``%M`` | Minute as a decimal number | |
- | | [00,59]. | |
- +-----------+--------------------------------+-------+
- | ``%p`` | Locale's equivalent of either | \(3) |
- | | AM or PM. | |
- +-----------+--------------------------------+-------+
- | ``%q`` | Quarter as a decimal number | |
- | | [1,4] | |
- +-----------+--------------------------------+-------+
- | ``%S`` | Second as a decimal number | \(4) |
- | | [00,61]. | |
- +-----------+--------------------------------+-------+
- | ``%l`` | Millisecond as a decimal number| |
- | | [000,999]. | |
- +-----------+--------------------------------+-------+
- | ``%u`` | Microsecond as a decimal number| |
- | | [000000,999999]. | |
- +-----------+--------------------------------+-------+
- | ``%n`` | Nanosecond as a decimal number | |
- | | [000000000,999999999]. | |
- +-----------+--------------------------------+-------+
- | ``%U`` | Week number of the year | \(5) |
- | | (Sunday as the first day of | |
- | | the week) as a decimal number | |
- | | [00,53]. All days in a new | |
- | | year preceding the first | |
- | | Sunday are considered to be in | |
- | | week 0. | |
- +-----------+--------------------------------+-------+
- | ``%w`` | Weekday as a decimal number | |
- | | [0(Sunday),6]. | |
- +-----------+--------------------------------+-------+
- | ``%W`` | Week number of the year | \(5) |
- | | (Monday as the first day of | |
- | | the week) as a decimal number | |
- | | [00,53]. All days in a new | |
- | | year preceding the first | |
- | | Monday are considered to be in | |
- | | week 0. | |
- +-----------+--------------------------------+-------+
- | ``%x`` | Locale's appropriate date | |
- | | representation. | |
- +-----------+--------------------------------+-------+
- | ``%X`` | Locale's appropriate time | |
- | | representation. | |
- +-----------+--------------------------------+-------+
- | ``%y`` | Year without century as a | |
- | | decimal number [00,99]. | |
- +-----------+--------------------------------+-------+
- | ``%Y`` | Year with century as a decimal | |
- | | number. | |
- +-----------+--------------------------------+-------+
- | ``%Z`` | Time zone name (no characters | |
- | | if no time zone exists). | |
- +-----------+--------------------------------+-------+
- | ``%%`` | A literal ``'%'`` character. | |
- +-----------+--------------------------------+-------+
- Notes
- -----
- (1)
- The ``%f`` directive is the same as ``%y`` if the frequency is
- not quarterly.
- Otherwise, it corresponds to the 'fiscal' year, as defined by
- the :attr:`qyear` attribute.
- (2)
- The ``%F`` directive is the same as ``%Y`` if the frequency is
- not quarterly.
- Otherwise, it corresponds to the 'fiscal' year, as defined by
- the :attr:`qyear` attribute.
- (3)
- The ``%p`` directive only affects the output hour field
- if the ``%I`` directive is used to parse the hour.
- (4)
- The range really is ``0`` to ``61``; this accounts for leap
- seconds and the (very rare) double leap seconds.
- (5)
- The ``%U`` and ``%W`` directives are only used in calculations
- when the day of the week and the year are specified.
- Examples
- --------
- >>> from pandas import Period
- >>> a = Period(freq='Q-JUL', year=2006, quarter=1)
- >>> a.strftime('%F-Q%q')
- '2006-Q1'
- >>> # Output the last month in the quarter of this date
- >>> a.strftime('%b-%Y')
- 'Oct-2005'
- >>>
- >>> a = Period(freq='D', year=2001, month=1, day=1)
- >>> a.strftime('%d-%b-%Y')
- '01-Jan-2001'
- >>> a.strftime('%b. %d, %Y was a %A')
- 'Jan. 01, 2001 was a Monday'
- """
- base = self._dtype._dtype_code
- return period_format(self.ordinal, base, fmt)
- class Period(_Period):
- """
- Represents a period of time.
- Parameters
- ----------
- value : Period or str, default None
- The time period represented (e.g., '4Q2005'). This represents neither
- the start or the end of the period, but rather the entire period itself.
- freq : str, default None
- One of pandas period strings or corresponding objects. Accepted
- strings are listed in the
- :ref:`offset alias section <timeseries.offset_aliases>` in the user docs.
- ordinal : int, default None
- The period offset from the proleptic Gregorian epoch.
- year : int, default None
- Year value of the period.
- month : int, default 1
- Month value of the period.
- quarter : int, default None
- Quarter value of the period.
- day : int, default 1
- Day value of the period.
- hour : int, default 0
- Hour value of the period.
- minute : int, default 0
- Minute value of the period.
- second : int, default 0
- Second value of the period.
- Examples
- --------
- >>> period = pd.Period('2012-1-1', freq='D')
- >>> period
- Period('2012-01-01', 'D')
- """
- def __new__(cls, value=None, freq=None, ordinal=None,
- year=None, month=None, quarter=None, day=None,
- hour=None, minute=None, second=None):
- # freq points to a tuple (base, mult); base is one of the defined
- # periods such as A, Q, etc. Every five minutes would be, e.g.,
- # ('T', 5) but may be passed in as a string like '5T'
- # ordinal is the period offset from the gregorian proleptic epoch
- if freq is not None:
- freq = cls._maybe_convert_freq(freq)
- nanosecond = 0
- if ordinal is not None and value is not None:
- raise ValueError("Only value or ordinal but not both should be "
- "given but not both")
- elif ordinal is not None:
- if not util.is_integer_object(ordinal):
- raise ValueError("Ordinal must be an integer")
- if freq is None:
- raise ValueError("Must supply freq for ordinal value")
- elif value is None:
- if (year is None and month is None and
- quarter is None and day is None and
- hour is None and minute is None and second is None):
- ordinal = NPY_NAT
- else:
- if freq is None:
- raise ValueError("If value is None, freq cannot be None")
- # set defaults
- month = 1 if month is None else month
- day = 1 if day is None else day
- hour = 0 if hour is None else hour
- minute = 0 if minute is None else minute
- second = 0 if second is None else second
- ordinal = _ordinal_from_fields(year, month, quarter, day,
- hour, minute, second, freq)
- elif is_period_object(value):
- other = value
- if freq is None or freq._period_dtype_code == other.freq._period_dtype_code:
- ordinal = other.ordinal
- freq = other.freq
- else:
- converted = other.asfreq(freq)
- ordinal = converted.ordinal
- elif checknull_with_nat(value) or (isinstance(value, str) and
- (value in nat_strings or len(value) == 0)):
- # explicit str check is necessary to avoid raising incorrectly
- # if we have a non-hashable value.
- ordinal = NPY_NAT
- elif isinstance(value, str) or util.is_integer_object(value):
- if util.is_integer_object(value):
- if value == NPY_NAT:
- value = "NaT"
- value = str(value)
- value = value.upper()
- freqstr = freq.rule_code if freq is not None else None
- try:
- dt, reso = parse_datetime_string_with_reso(value, freqstr)
- except ValueError as err:
- match = re.search(r"^\d{4}-\d{2}-\d{2}/\d{4}-\d{2}-\d{2}", value)
- if match:
- # Case that cannot be parsed (correctly) by our datetime
- # parsing logic
- dt, freq = _parse_weekly_str(value, freq)
- else:
- raise err
- else:
- if reso == "nanosecond":
- nanosecond = dt.nanosecond
- if dt is NaT:
- ordinal = NPY_NAT
- if freq is None and ordinal != NPY_NAT:
- # Skip NaT, since it doesn't have a resolution
- freq = attrname_to_abbrevs[reso]
- freq = to_offset(freq)
- elif PyDateTime_Check(value):
- dt = value
- if freq is None:
- raise ValueError("Must supply freq for datetime value")
- if isinstance(dt, Timestamp):
- nanosecond = dt.nanosecond
- elif util.is_datetime64_object(value):
- dt = Timestamp(value)
- if freq is None:
- raise ValueError("Must supply freq for datetime value")
- nanosecond = dt.nanosecond
- elif PyDate_Check(value):
- dt = datetime(year=value.year, month=value.month, day=value.day)
- if freq is None:
- raise ValueError("Must supply freq for datetime value")
- else:
- msg = "Value must be Period, string, integer, or datetime"
- raise ValueError(msg)
- if ordinal is None:
- base = freq_to_dtype_code(freq)
- ordinal = period_ordinal(dt.year, dt.month, dt.day,
- dt.hour, dt.minute, dt.second,
- dt.microsecond, 1000*nanosecond, base)
- return cls._from_ordinal(ordinal, freq)
- cdef bint is_period_object(object obj):
- return isinstance(obj, _Period)
- cpdef int freq_to_dtype_code(BaseOffset freq) except? -1:
- try:
- return freq._period_dtype_code
- except AttributeError as err:
- raise ValueError(INVALID_FREQ_ERR_MSG.format(freq)) from err
- cdef int64_t _ordinal_from_fields(int year, int month, quarter, int day,
- int hour, int minute, int second,
- BaseOffset freq):
- base = freq_to_dtype_code(freq)
- if quarter is not None:
- year, month = quarter_to_myear(year, quarter, freq.freqstr)
- return period_ordinal(year, month, day, hour,
- minute, second, 0, 0, base)
- def validate_end_alias(how: str) -> str: # Literal["E", "S"]
- how_dict = {"S": "S", "E": "E",
- "START": "S", "FINISH": "E",
- "BEGIN": "S", "END": "E"}
- how = how_dict.get(str(how).upper())
- if how not in {"S", "E"}:
- raise ValueError("How must be one of S or E")
- return how
- cdef _parse_weekly_str(value, BaseOffset freq):
- """
- Parse e.g. "2017-01-23/2017-01-29", which cannot be parsed by the general
- datetime-parsing logic. This ensures that we can round-trip with
- Period.__str__ with weekly freq.
- """
- # GH#50803
- start, end = value.split("/")
- start = Timestamp(start)
- end = Timestamp(end)
- if (end - start).days != 6:
- # We are interested in cases where this is str(period)
- # of a Week-freq period
- raise ValueError("Could not parse as weekly-freq Period")
- if freq is None:
- day_name = end.day_name()[:3].upper()
- freqstr = f"W-{day_name}"
- freq = to_offset(freqstr)
- # We _should_ have freq.is_on_offset(end)
- return end, freq
|