12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547 |
- #----------------------------------------------------------------------
- #
- # clock.tcl --
- #
- # This file implements the portions of the [clock] ensemble that are
- # coded in Tcl. Refer to the users' manual to see the description of
- # the [clock] command and its subcommands.
- #
- #
- #----------------------------------------------------------------------
- #
- # Copyright (c) 2004,2005,2006,2007 by Kevin B. Kenny
- # See the file "license.terms" for information on usage and redistribution
- # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
- #
- #----------------------------------------------------------------------
- # We must have message catalogs that support the root locale, and we need
- # access to the Registry on Windows systems.
- uplevel \#0 {
- package require msgcat 1.6
- if { $::tcl_platform(platform) eq {windows} } {
- if { [catch { package require registry 1.1 }] } {
- namespace eval ::tcl::clock [list variable NoRegistry {}]
- }
- }
- }
- # Put the library directory into the namespace for the ensemble so that the
- # library code can find message catalogs and time zone definition files.
- namespace eval ::tcl::clock \
- [list variable LibDir [file dirname [info script]]]
- #----------------------------------------------------------------------
- #
- # clock --
- #
- # Manipulate times.
- #
- # The 'clock' command manipulates time. Refer to the user documentation for
- # the available subcommands and what they do.
- #
- #----------------------------------------------------------------------
- namespace eval ::tcl::clock {
- # Export the subcommands
- namespace export format
- namespace export clicks
- namespace export microseconds
- namespace export milliseconds
- namespace export scan
- namespace export seconds
- namespace export add
- # Import the message catalog commands that we use.
- namespace import ::msgcat::mcload
- namespace import ::msgcat::mclocale
- namespace import ::msgcat::mc
- namespace import ::msgcat::mcpackagelocale
- }
- #----------------------------------------------------------------------
- #
- # ::tcl::clock::Initialize --
- #
- # Finish initializing the 'clock' subsystem
- #
- # Results:
- # None.
- #
- # Side effects:
- # Namespace variable in the 'clock' subsystem are initialized.
- #
- # The '::tcl::clock::Initialize' procedure initializes the namespace variables
- # and root locale message catalog for the 'clock' subsystem. It is broken
- # into a procedure rather than simply evaluated as a script so that it will be
- # able to use local variables, avoiding the dangers of 'creative writing' as
- # in Bug 1185933.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::Initialize {} {
- rename ::tcl::clock::Initialize {}
- variable LibDir
- # Define the Greenwich time zone
- proc InitTZData {} {
- variable TZData
- array unset TZData
- set TZData(:Etc/GMT) {
- {-9223372036854775808 0 0 GMT}
- }
- set TZData(:GMT) $TZData(:Etc/GMT)
- set TZData(:Etc/UTC) {
- {-9223372036854775808 0 0 UTC}
- }
- set TZData(:UTC) $TZData(:Etc/UTC)
- set TZData(:localtime) {}
- }
- InitTZData
- mcpackagelocale set {}
- ::msgcat::mcpackageconfig set mcfolder [file join $LibDir msgs]
- ::msgcat::mcpackageconfig set unknowncmd ""
- ::msgcat::mcpackageconfig set changecmd ChangeCurrentLocale
- # Define the message catalog for the root locale.
- ::msgcat::mcmset {} {
- AM {am}
- BCE {B.C.E.}
- CE {C.E.}
- DATE_FORMAT {%m/%d/%Y}
- DATE_TIME_FORMAT {%a %b %e %H:%M:%S %Y}
- DAYS_OF_WEEK_ABBREV {
- Sun Mon Tue Wed Thu Fri Sat
- }
- DAYS_OF_WEEK_FULL {
- Sunday Monday Tuesday Wednesday Thursday Friday Saturday
- }
- GREGORIAN_CHANGE_DATE 2299161
- LOCALE_DATE_FORMAT {%m/%d/%Y}
- LOCALE_DATE_TIME_FORMAT {%a %b %e %H:%M:%S %Y}
- LOCALE_ERAS {}
- LOCALE_NUMERALS {
- 00 01 02 03 04 05 06 07 08 09
- 10 11 12 13 14 15 16 17 18 19
- 20 21 22 23 24 25 26 27 28 29
- 30 31 32 33 34 35 36 37 38 39
- 40 41 42 43 44 45 46 47 48 49
- 50 51 52 53 54 55 56 57 58 59
- 60 61 62 63 64 65 66 67 68 69
- 70 71 72 73 74 75 76 77 78 79
- 80 81 82 83 84 85 86 87 88 89
- 90 91 92 93 94 95 96 97 98 99
- }
- LOCALE_TIME_FORMAT {%H:%M:%S}
- LOCALE_YEAR_FORMAT {%EC%Ey}
- MONTHS_ABBREV {
- Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
- }
- MONTHS_FULL {
- January February March
- April May June
- July August September
- October November December
- }
- PM {pm}
- TIME_FORMAT {%H:%M:%S}
- TIME_FORMAT_12 {%I:%M:%S %P}
- TIME_FORMAT_24 {%H:%M}
- TIME_FORMAT_24_SECS {%H:%M:%S}
- }
- # Define a few Gregorian change dates for other locales. In most cases
- # the change date follows a language, because a nation's colonies changed
- # at the same time as the nation itself. In many cases, different
- # national boundaries existed; the dominating rule is to follow the
- # nation's capital.
- # Italy, Spain, Portugal, Poland
- ::msgcat::mcset it GREGORIAN_CHANGE_DATE 2299161
- ::msgcat::mcset es GREGORIAN_CHANGE_DATE 2299161
- ::msgcat::mcset pt GREGORIAN_CHANGE_DATE 2299161
- ::msgcat::mcset pl GREGORIAN_CHANGE_DATE 2299161
- # France, Austria
- ::msgcat::mcset fr GREGORIAN_CHANGE_DATE 2299227
- # For Belgium, we follow Southern Netherlands; Liege Diocese changed
- # several weeks later.
- ::msgcat::mcset fr_BE GREGORIAN_CHANGE_DATE 2299238
- ::msgcat::mcset nl_BE GREGORIAN_CHANGE_DATE 2299238
- # Austria
- ::msgcat::mcset de_AT GREGORIAN_CHANGE_DATE 2299527
- # Hungary
- ::msgcat::mcset hu GREGORIAN_CHANGE_DATE 2301004
- # Germany, Norway, Denmark (Catholic Germany changed earlier)
- ::msgcat::mcset de_DE GREGORIAN_CHANGE_DATE 2342032
- ::msgcat::mcset nb GREGORIAN_CHANGE_DATE 2342032
- ::msgcat::mcset nn GREGORIAN_CHANGE_DATE 2342032
- ::msgcat::mcset no GREGORIAN_CHANGE_DATE 2342032
- ::msgcat::mcset da GREGORIAN_CHANGE_DATE 2342032
- # Holland (Brabant, Gelderland, Flanders, Friesland, etc. changed at
- # various times)
- ::msgcat::mcset nl GREGORIAN_CHANGE_DATE 2342165
- # Protestant Switzerland (Catholic cantons changed earlier)
- ::msgcat::mcset fr_CH GREGORIAN_CHANGE_DATE 2361342
- ::msgcat::mcset it_CH GREGORIAN_CHANGE_DATE 2361342
- ::msgcat::mcset de_CH GREGORIAN_CHANGE_DATE 2361342
- # English speaking countries
- ::msgcat::mcset en GREGORIAN_CHANGE_DATE 2361222
- # Sweden (had several changes onto and off of the Gregorian calendar)
- ::msgcat::mcset sv GREGORIAN_CHANGE_DATE 2361390
- # Russia
- ::msgcat::mcset ru GREGORIAN_CHANGE_DATE 2421639
- # Romania (Transylvania changed earler - perhaps de_RO should show the
- # earlier date?)
- ::msgcat::mcset ro GREGORIAN_CHANGE_DATE 2422063
- # Greece
- ::msgcat::mcset el GREGORIAN_CHANGE_DATE 2423480
- #------------------------------------------------------------------
- #
- # CONSTANTS
- #
- #------------------------------------------------------------------
- # Paths at which binary time zone data for the Olson libraries are known
- # to reside on various operating systems
- variable ZoneinfoPaths {}
- foreach path {
- /usr/share/zoneinfo
- /usr/share/lib/zoneinfo
- /usr/lib/zoneinfo
- /usr/local/etc/zoneinfo
- } {
- if { [file isdirectory $path] } {
- lappend ZoneinfoPaths $path
- }
- }
- # Define the directories for time zone data and message catalogs.
- variable DataDir [file join $LibDir tzdata]
- # Number of days in the months, in common years and leap years.
- variable DaysInRomanMonthInCommonYear \
- { 31 28 31 30 31 30 31 31 30 31 30 31 }
- variable DaysInRomanMonthInLeapYear \
- { 31 29 31 30 31 30 31 31 30 31 30 31 }
- variable DaysInPriorMonthsInCommonYear [list 0]
- variable DaysInPriorMonthsInLeapYear [list 0]
- set i 0
- foreach j $DaysInRomanMonthInCommonYear {
- lappend DaysInPriorMonthsInCommonYear [incr i $j]
- }
- set i 0
- foreach j $DaysInRomanMonthInLeapYear {
- lappend DaysInPriorMonthsInLeapYear [incr i $j]
- }
- # Another epoch (Hi, Jeff!)
- variable Roddenberry 1946
- # Integer ranges
- variable MINWIDE -9223372036854775808
- variable MAXWIDE 9223372036854775807
- # Day before Leap Day
- variable FEB_28 58
- # Translation table to map Windows TZI onto cities, so that the Olson
- # rules can apply. In some cases the mapping is ambiguous, so it's wise
- # to specify $::env(TCL_TZ) rather than simply depending on the system
- # time zone.
- # The keys are long lists of values obtained from the time zone
- # information in the Registry. In order, the list elements are:
- # Bias StandardBias DaylightBias
- # StandardDate.wYear StandardDate.wMonth StandardDate.wDayOfWeek
- # StandardDate.wDay StandardDate.wHour StandardDate.wMinute
- # StandardDate.wSecond StandardDate.wMilliseconds
- # DaylightDate.wYear DaylightDate.wMonth DaylightDate.wDayOfWeek
- # DaylightDate.wDay DaylightDate.wHour DaylightDate.wMinute
- # DaylightDate.wSecond DaylightDate.wMilliseconds
- # The values are the names of time zones where those rules apply. There
- # is considerable ambiguity in certain zones; an attempt has been made to
- # make a reasonable guess, but this table needs to be taken with a grain
- # of salt.
- variable WinZoneInfo [dict create {*}{
- {-43200 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Pacific/Kwajalein
- {-39600 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Pacific/Midway
- {-36000 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Pacific/Honolulu
- {-32400 0 3600 0 11 0 1 2 0 0 0 0 3 0 2 2 0 0 0} :America/Anchorage
- {-28800 0 3600 0 11 0 1 2 0 0 0 0 3 0 2 2 0 0 0} :America/Los_Angeles
- {-28800 0 3600 0 10 0 5 2 0 0 0 0 4 0 1 2 0 0 0} :America/Tijuana
- {-25200 0 3600 0 11 0 1 2 0 0 0 0 3 0 2 2 0 0 0} :America/Denver
- {-25200 0 3600 0 10 0 5 2 0 0 0 0 4 0 1 2 0 0 0} :America/Chihuahua
- {-25200 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :America/Phoenix
- {-21600 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :America/Regina
- {-21600 0 3600 0 11 0 1 2 0 0 0 0 3 0 2 2 0 0 0} :America/Chicago
- {-21600 0 3600 0 10 0 5 2 0 0 0 0 4 0 1 2 0 0 0} :America/Mexico_City
- {-18000 0 3600 0 11 0 1 2 0 0 0 0 3 0 2 2 0 0 0} :America/New_York
- {-18000 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :America/Indianapolis
- {-14400 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :America/Caracas
- {-14400 0 3600 0 3 6 2 23 59 59 999 0 10 6 2 23 59 59 999}
- :America/Santiago
- {-14400 0 3600 0 2 0 5 2 0 0 0 0 11 0 1 2 0 0 0} :America/Manaus
- {-14400 0 3600 0 11 0 1 2 0 0 0 0 3 0 2 2 0 0 0} :America/Halifax
- {-12600 0 3600 0 10 0 5 2 0 0 0 0 4 0 1 2 0 0 0} :America/St_Johns
- {-10800 0 3600 0 2 0 2 2 0 0 0 0 10 0 3 2 0 0 0} :America/Sao_Paulo
- {-10800 0 3600 0 10 0 5 2 0 0 0 0 4 0 1 2 0 0 0} :America/Godthab
- {-10800 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :America/Buenos_Aires
- {-10800 0 3600 0 2 0 5 2 0 0 0 0 11 0 1 2 0 0 0} :America/Bahia
- {-10800 0 3600 0 3 0 2 2 0 0 0 0 10 0 1 2 0 0 0} :America/Montevideo
- {-7200 0 3600 0 9 0 5 2 0 0 0 0 3 0 5 2 0 0 0} :America/Noronha
- {-3600 0 3600 0 10 0 5 3 0 0 0 0 3 0 5 2 0 0 0} :Atlantic/Azores
- {-3600 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Atlantic/Cape_Verde
- {0 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :UTC
- {0 0 3600 0 10 0 5 2 0 0 0 0 3 0 5 1 0 0 0} :Europe/London
- {3600 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Africa/Kinshasa
- {3600 0 3600 0 10 0 5 3 0 0 0 0 3 0 5 2 0 0 0} :CET
- {7200 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Africa/Harare
- {7200 0 3600 0 9 4 5 23 59 59 0 0 4 4 5 23 59 59 0}
- :Africa/Cairo
- {7200 0 3600 0 10 0 5 4 0 0 0 0 3 0 5 3 0 0 0} :Europe/Helsinki
- {7200 0 3600 0 9 0 3 2 0 0 0 0 3 5 5 2 0 0 0} :Asia/Jerusalem
- {7200 0 3600 0 9 0 5 1 0 0 0 0 3 0 5 0 0 0 0} :Europe/Bucharest
- {7200 0 3600 0 10 0 5 3 0 0 0 0 3 0 5 2 0 0 0} :Europe/Athens
- {7200 0 3600 0 9 5 5 1 0 0 0 0 3 4 5 0 0 0 0} :Asia/Amman
- {7200 0 3600 0 10 6 5 23 59 59 999 0 3 0 5 0 0 0 0}
- :Asia/Beirut
- {7200 0 -3600 0 4 0 1 2 0 0 0 0 9 0 1 2 0 0 0} :Africa/Windhoek
- {10800 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Asia/Riyadh
- {10800 0 3600 0 10 0 1 4 0 0 0 0 4 0 1 3 0 0 0} :Asia/Baghdad
- {10800 0 3600 0 10 0 5 3 0 0 0 0 3 0 5 2 0 0 0} :Europe/Moscow
- {12600 0 3600 0 9 2 4 2 0 0 0 0 3 0 1 2 0 0 0} :Asia/Tehran
- {14400 0 3600 0 10 0 5 5 0 0 0 0 3 0 5 4 0 0 0} :Asia/Baku
- {14400 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Asia/Muscat
- {14400 0 3600 0 10 0 5 3 0 0 0 0 3 0 5 2 0 0 0} :Asia/Tbilisi
- {16200 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Asia/Kabul
- {18000 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Asia/Karachi
- {18000 0 3600 0 10 0 5 3 0 0 0 0 3 0 5 2 0 0 0} :Asia/Yekaterinburg
- {19800 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Asia/Calcutta
- {20700 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Asia/Katmandu
- {21600 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Asia/Dhaka
- {21600 0 3600 0 10 0 5 3 0 0 0 0 3 0 5 2 0 0 0} :Asia/Novosibirsk
- {23400 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Asia/Rangoon
- {25200 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Asia/Bangkok
- {25200 0 3600 0 10 0 5 3 0 0 0 0 3 0 5 2 0 0 0} :Asia/Krasnoyarsk
- {28800 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Asia/Chongqing
- {28800 0 3600 0 10 0 5 3 0 0 0 0 3 0 5 2 0 0 0} :Asia/Irkutsk
- {32400 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Asia/Tokyo
- {32400 0 3600 0 10 0 5 3 0 0 0 0 3 0 5 2 0 0 0} :Asia/Yakutsk
- {34200 0 3600 0 3 0 5 3 0 0 0 0 10 0 5 2 0 0 0} :Australia/Adelaide
- {34200 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Australia/Darwin
- {36000 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Australia/Brisbane
- {36000 0 3600 0 10 0 5 3 0 0 0 0 3 0 5 2 0 0 0} :Asia/Vladivostok
- {36000 0 3600 0 3 0 5 3 0 0 0 0 10 0 1 2 0 0 0} :Australia/Hobart
- {36000 0 3600 0 3 0 5 3 0 0 0 0 10 0 5 2 0 0 0} :Australia/Sydney
- {39600 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Pacific/Noumea
- {43200 0 3600 0 3 0 3 3 0 0 0 0 10 0 1 2 0 0 0} :Pacific/Auckland
- {43200 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Pacific/Fiji
- {46800 0 3600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0} :Pacific/Tongatapu
- }]
- # Groups of fields that specify the date, priorities, and code bursts that
- # determine Julian Day Number given those groups. The code in [clock
- # scan] will choose the highest priority (lowest numbered) set of fields
- # that determines the date.
- variable DateParseActions {
- { seconds } 0 {}
- { julianDay } 1 {}
- { era century yearOfCentury month dayOfMonth } 2 {
- dict set date year [expr { 100 * [dict get $date century]
- + [dict get $date yearOfCentury] }]
- set date [GetJulianDayFromEraYearMonthDay $date[set date {}] \
- $changeover]
- }
- { era century yearOfCentury dayOfYear } 2 {
- dict set date year [expr { 100 * [dict get $date century]
- + [dict get $date yearOfCentury] }]
- set date [GetJulianDayFromEraYearDay $date[set date {}] \
- $changeover]
- }
- { century yearOfCentury month dayOfMonth } 3 {
- dict set date era CE
- dict set date year [expr { 100 * [dict get $date century]
- + [dict get $date yearOfCentury] }]
- set date [GetJulianDayFromEraYearMonthDay $date[set date {}] \
- $changeover]
- }
- { century yearOfCentury dayOfYear } 3 {
- dict set date era CE
- dict set date year [expr { 100 * [dict get $date century]
- + [dict get $date yearOfCentury] }]
- set date [GetJulianDayFromEraYearDay $date[set date {}] \
- $changeover]
- }
- { iso8601Century iso8601YearOfCentury iso8601Week dayOfWeek } 3 {
- dict set date era CE
- dict set date iso8601Year \
- [expr { 100 * [dict get $date iso8601Century]
- + [dict get $date iso8601YearOfCentury] }]
- set date [GetJulianDayFromEraYearWeekDay $date[set date {}] \
- $changeover]
- }
- { yearOfCentury month dayOfMonth } 4 {
- set date [InterpretTwoDigitYear $date[set date {}] $baseTime]
- dict set date era CE
- set date [GetJulianDayFromEraYearMonthDay $date[set date {}] \
- $changeover]
- }
- { yearOfCentury dayOfYear } 4 {
- set date [InterpretTwoDigitYear $date[set date {}] $baseTime]
- dict set date era CE
- set date [GetJulianDayFromEraYearDay $date[set date {}] \
- $changeover]
- }
- { iso8601YearOfCentury iso8601Week dayOfWeek } 4 {
- set date [InterpretTwoDigitYear \
- $date[set date {}] $baseTime \
- iso8601YearOfCentury iso8601Year]
- dict set date era CE
- set date [GetJulianDayFromEraYearWeekDay $date[set date {}] \
- $changeover]
- }
- { month dayOfMonth } 5 {
- set date [AssignBaseYear $date[set date {}] \
- $baseTime $timeZone $changeover]
- set date [GetJulianDayFromEraYearMonthDay $date[set date {}] \
- $changeover]
- }
- { dayOfYear } 5 {
- set date [AssignBaseYear $date[set date {}] \
- $baseTime $timeZone $changeover]
- set date [GetJulianDayFromEraYearDay $date[set date {}] \
- $changeover]
- }
- { iso8601Week dayOfWeek } 5 {
- set date [AssignBaseIso8601Year $date[set date {}] \
- $baseTime $timeZone $changeover]
- set date [GetJulianDayFromEraYearWeekDay $date[set date {}] \
- $changeover]
- }
- { dayOfMonth } 6 {
- set date [AssignBaseMonth $date[set date {}] \
- $baseTime $timeZone $changeover]
- set date [GetJulianDayFromEraYearMonthDay $date[set date {}] \
- $changeover]
- }
- { dayOfWeek } 7 {
- set date [AssignBaseWeek $date[set date {}] \
- $baseTime $timeZone $changeover]
- set date [GetJulianDayFromEraYearWeekDay $date[set date {}] \
- $changeover]
- }
- {} 8 {
- set date [AssignBaseJulianDay $date[set date {}] \
- $baseTime $timeZone $changeover]
- }
- }
- # Groups of fields that specify time of day, priorities, and code that
- # processes them
- variable TimeParseActions {
- seconds 1 {}
- { hourAMPM minute second amPmIndicator } 2 {
- dict set date secondOfDay [InterpretHMSP $date]
- }
- { hour minute second } 2 {
- dict set date secondOfDay [InterpretHMS $date]
- }
- { hourAMPM minute amPmIndicator } 3 {
- dict set date second 0
- dict set date secondOfDay [InterpretHMSP $date]
- }
- { hour minute } 3 {
- dict set date second 0
- dict set date secondOfDay [InterpretHMS $date]
- }
- { hourAMPM amPmIndicator } 4 {
- dict set date minute 0
- dict set date second 0
- dict set date secondOfDay [InterpretHMSP $date]
- }
- { hour } 4 {
- dict set date minute 0
- dict set date second 0
- dict set date secondOfDay [InterpretHMS $date]
- }
- { } 5 {
- dict set date secondOfDay 0
- }
- }
- # Legacy time zones, used primarily for parsing RFC822 dates.
- variable LegacyTimeZone [dict create \
- gmt +0000 \
- ut +0000 \
- utc +0000 \
- bst +0100 \
- wet +0000 \
- wat -0100 \
- at -0200 \
- nft -0330 \
- nst -0330 \
- ndt -0230 \
- ast -0400 \
- adt -0300 \
- est -0500 \
- edt -0400 \
- cst -0600 \
- cdt -0500 \
- mst -0700 \
- mdt -0600 \
- pst -0800 \
- pdt -0700 \
- yst -0900 \
- ydt -0800 \
- hst -1000 \
- hdt -0900 \
- cat -1000 \
- ahst -1000 \
- nt -1100 \
- idlw -1200 \
- cet +0100 \
- cest +0200 \
- met +0100 \
- mewt +0100 \
- mest +0200 \
- swt +0100 \
- sst +0200 \
- fwt +0100 \
- fst +0200 \
- eet +0200 \
- eest +0300 \
- bt +0300 \
- it +0330 \
- zp4 +0400 \
- zp5 +0500 \
- ist +0530 \
- zp6 +0600 \
- wast +0700 \
- wadt +0800 \
- jt +0730 \
- cct +0800 \
- jst +0900 \
- kst +0900 \
- cast +0930 \
- jdt +1000 \
- kdt +1000 \
- cadt +1030 \
- east +1000 \
- eadt +1030 \
- gst +1000 \
- nzt +1200 \
- nzst +1200 \
- nzdt +1300 \
- idle +1200 \
- a +0100 \
- b +0200 \
- c +0300 \
- d +0400 \
- e +0500 \
- f +0600 \
- g +0700 \
- h +0800 \
- i +0900 \
- k +1000 \
- l +1100 \
- m +1200 \
- n -0100 \
- o -0200 \
- p -0300 \
- q -0400 \
- r -0500 \
- s -0600 \
- t -0700 \
- u -0800 \
- v -0900 \
- w -1000 \
- x -1100 \
- y -1200 \
- z +0000 \
- ]
- # Caches
- variable LocaleNumeralCache {}; # Dictionary whose keys are locale
- # names and whose values are pairs
- # comprising regexes matching numerals
- # in the given locales and dictionaries
- # mapping the numerals to their numeric
- # values.
- # variable CachedSystemTimeZone; # If 'CachedSystemTimeZone' exists,
- # it contains the value of the
- # system time zone, as determined from
- # the environment.
- variable TimeZoneBad {}; # Dictionary whose keys are time zone
- # names and whose values are 1 if
- # the time zone is unknown and 0
- # if it is known.
- variable TZData; # Array whose keys are time zone names
- # and whose values are lists of quads
- # comprising start time, UTC offset,
- # Daylight Saving Time indicator, and
- # time zone abbreviation.
- variable FormatProc; # Array mapping format group
- # and locale to the name of a procedure
- # that renders the given format
- }
- ::tcl::clock::Initialize
- #----------------------------------------------------------------------
- #
- # clock format --
- #
- # Formats a count of seconds since the Posix Epoch as a time of day.
- #
- # The 'clock format' command formats times of day for output. Refer to the
- # user documentation to see what it does.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::format { args } {
- variable FormatProc
- variable TZData
- lassign [ParseFormatArgs {*}$args] format locale timezone
- set locale [string tolower $locale]
- set clockval [lindex $args 0]
- # Get the data for time changes in the given zone
- if {$timezone eq ""} {
- set timezone [GetSystemTimeZone]
- }
- if {![info exists TZData($timezone)]} {
- if {[catch {SetupTimeZone $timezone} retval opts]} {
- dict unset opts -errorinfo
- return -options $opts $retval
- }
- }
- # Build a procedure to format the result. Cache the built procedure's name
- # in the 'FormatProc' array to avoid losing its internal representation,
- # which contains the name resolution.
- set procName formatproc'$format'$locale
- set procName [namespace current]::[string map {: {\:} \\ {\\}} $procName]
- if {[info exists FormatProc($procName)]} {
- set procName $FormatProc($procName)
- } else {
- set FormatProc($procName) \
- [ParseClockFormatFormat $procName $format $locale]
- }
- return [$procName $clockval $timezone]
- }
- #----------------------------------------------------------------------
- #
- # ParseClockFormatFormat --
- #
- # Builds and caches a procedure that formats a time value.
- #
- # Parameters:
- # format -- Format string to use
- # locale -- Locale in which the format string is to be interpreted
- #
- # Results:
- # Returns the name of the newly-built procedure.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::ParseClockFormatFormat {procName format locale} {
- if {[namespace which $procName] ne {}} {
- return $procName
- }
- # Map away the locale-dependent composite format groups
- EnterLocale $locale
- # Change locale if a fresh locale has been given on the command line.
- try {
- return [ParseClockFormatFormat2 $format $locale $procName]
- } trap CLOCK {result opts} {
- dict unset opts -errorinfo
- return -options $opts $result
- }
- }
- proc ::tcl::clock::ParseClockFormatFormat2 {format locale procName} {
- set didLocaleEra 0
- set didLocaleNumerals 0
- set preFormatCode \
- [string map [list @GREGORIAN_CHANGE_DATE@ \
- [mc GREGORIAN_CHANGE_DATE]] \
- {
- variable TZData
- set date [GetDateFields $clockval \
- $TZData($timezone) \
- @GREGORIAN_CHANGE_DATE@]
- }]
- set formatString {}
- set substituents {}
- set state {}
- set format [LocalizeFormat $locale $format]
- foreach char [split $format {}] {
- switch -exact -- $state {
- {} {
- if { [string equal % $char] } {
- set state percent
- } else {
- append formatString $char
- }
- }
- percent { # Character following a '%' character
- set state {}
- switch -exact -- $char {
- % { # A literal character, '%'
- append formatString %%
- }
- a { # Day of week, abbreviated
- append formatString %s
- append substituents \
- [string map \
- [list @DAYS_OF_WEEK_ABBREV@ \
- [list [mc DAYS_OF_WEEK_ABBREV]]] \
- { [lindex @DAYS_OF_WEEK_ABBREV@ \
- [expr {[dict get $date dayOfWeek] \
- % 7}]]}]
- }
- A { # Day of week, spelt out.
- append formatString %s
- append substituents \
- [string map \
- [list @DAYS_OF_WEEK_FULL@ \
- [list [mc DAYS_OF_WEEK_FULL]]] \
- { [lindex @DAYS_OF_WEEK_FULL@ \
- [expr {[dict get $date dayOfWeek] \
- % 7}]]}]
- }
- b - h { # Name of month, abbreviated.
- append formatString %s
- append substituents \
- [string map \
- [list @MONTHS_ABBREV@ \
- [list [mc MONTHS_ABBREV]]] \
- { [lindex @MONTHS_ABBREV@ \
- [expr {[dict get $date month]-1}]]}]
- }
- B { # Name of month, spelt out
- append formatString %s
- append substituents \
- [string map \
- [list @MONTHS_FULL@ \
- [list [mc MONTHS_FULL]]] \
- { [lindex @MONTHS_FULL@ \
- [expr {[dict get $date month]-1}]]}]
- }
- C { # Century number
- append formatString %02d
- append substituents \
- { [expr {[dict get $date year] / 100}]}
- }
- d { # Day of month, with leading zero
- append formatString %02d
- append substituents { [dict get $date dayOfMonth]}
- }
- e { # Day of month, without leading zero
- append formatString %2d
- append substituents { [dict get $date dayOfMonth]}
- }
- E { # Format group in a locale-dependent
- # alternative era
- set state percentE
- if {!$didLocaleEra} {
- append preFormatCode \
- [string map \
- [list @LOCALE_ERAS@ \
- [list [mc LOCALE_ERAS]]] \
- {
- set date [GetLocaleEra \
- $date[set date {}] \
- @LOCALE_ERAS@]}] \n
- set didLocaleEra 1
- }
- if {!$didLocaleNumerals} {
- append preFormatCode \
- [list set localeNumerals \
- [mc LOCALE_NUMERALS]] \n
- set didLocaleNumerals 1
- }
- }
- g { # Two-digit year relative to ISO8601
- # week number
- append formatString %02d
- append substituents \
- { [expr { [dict get $date iso8601Year] % 100 }]}
- }
- G { # Four-digit year relative to ISO8601
- # week number
- append formatString %02d
- append substituents { [dict get $date iso8601Year]}
- }
- H { # Hour in the 24-hour day, leading zero
- append formatString %02d
- append substituents \
- { [expr { [dict get $date localSeconds] \
- / 3600 % 24}]}
- }
- I { # Hour AM/PM, with leading zero
- append formatString %02d
- append substituents \
- { [expr { ( ( ( [dict get $date localSeconds] \
- % 86400 ) \
- + 86400 \
- - 3600 ) \
- / 3600 ) \
- % 12 + 1 }] }
- }
- j { # Day of year (001-366)
- append formatString %03d
- append substituents { [dict get $date dayOfYear]}
- }
- J { # Julian Day Number
- append formatString %07ld
- append substituents { [dict get $date julianDay]}
- }
- k { # Hour (0-23), no leading zero
- append formatString %2d
- append substituents \
- { [expr { [dict get $date localSeconds]
- / 3600
- % 24 }]}
- }
- l { # Hour (12-11), no leading zero
- append formatString %2d
- append substituents \
- { [expr { ( ( ( [dict get $date localSeconds]
- % 86400 )
- + 86400
- - 3600 )
- / 3600 )
- % 12 + 1 }]}
- }
- m { # Month number, leading zero
- append formatString %02d
- append substituents { [dict get $date month]}
- }
- M { # Minute of the hour, leading zero
- append formatString %02d
- append substituents \
- { [expr { [dict get $date localSeconds]
- / 60
- % 60 }]}
- }
- n { # A literal newline
- append formatString \n
- }
- N { # Month number, no leading zero
- append formatString %2d
- append substituents { [dict get $date month]}
- }
- O { # A format group in the locale's
- # alternative numerals
- set state percentO
- if {!$didLocaleNumerals} {
- append preFormatCode \
- [list set localeNumerals \
- [mc LOCALE_NUMERALS]] \n
- set didLocaleNumerals 1
- }
- }
- p { # Localized 'AM' or 'PM' indicator
- # converted to uppercase
- append formatString %s
- append preFormatCode \
- [list set AM [string toupper [mc AM]]] \n \
- [list set PM [string toupper [mc PM]]] \n
- append substituents \
- { [expr {(([dict get $date localSeconds]
- % 86400) < 43200) ?
- $AM : $PM}]}
- }
- P { # Localized 'AM' or 'PM' indicator
- append formatString %s
- append preFormatCode \
- [list set am [mc AM]] \n \
- [list set pm [mc PM]] \n
- append substituents \
- { [expr {(([dict get $date localSeconds]
- % 86400) < 43200) ?
- $am : $pm}]}
- }
- Q { # Hi, Jeff!
- append formatString %s
- append substituents { [FormatStarDate $date]}
- }
- s { # Seconds from the Posix Epoch
- append formatString %s
- append substituents { [dict get $date seconds]}
- }
- S { # Second of the minute, with
- # leading zero
- append formatString %02d
- append substituents \
- { [expr { [dict get $date localSeconds]
- % 60 }]}
- }
- t { # A literal tab character
- append formatString \t
- }
- u { # Day of the week (1-Monday, 7-Sunday)
- append formatString %1d
- append substituents { [dict get $date dayOfWeek]}
- }
- U { # Week of the year (00-53). The
- # first Sunday of the year is the
- # first day of week 01
- append formatString %02d
- append preFormatCode {
- set dow [dict get $date dayOfWeek]
- if { $dow == 7 } {
- set dow 0
- }
- incr dow
- set UweekNumber \
- [expr { ( [dict get $date dayOfYear]
- - $dow + 7 )
- / 7 }]
- }
- append substituents { $UweekNumber}
- }
- V { # The ISO8601 week number
- append formatString %02d
- append substituents { [dict get $date iso8601Week]}
- }
- w { # Day of the week (0-Sunday,
- # 6-Saturday)
- append formatString %1d
- append substituents \
- { [expr { [dict get $date dayOfWeek] % 7 }]}
- }
- W { # Week of the year (00-53). The first
- # Monday of the year is the first day
- # of week 01.
- append preFormatCode {
- set WweekNumber \
- [expr { ( [dict get $date dayOfYear]
- - [dict get $date dayOfWeek]
- + 7 )
- / 7 }]
- }
- append formatString %02d
- append substituents { $WweekNumber}
- }
- y { # The two-digit year of the century
- append formatString %02d
- append substituents \
- { [expr { [dict get $date year] % 100 }]}
- }
- Y { # The four-digit year
- append formatString %04d
- append substituents { [dict get $date year]}
- }
- z { # The time zone as hours and minutes
- # east (+) or west (-) of Greenwich
- append formatString %s
- append substituents { [FormatNumericTimeZone \
- [dict get $date tzOffset]]}
- }
- Z { # The name of the time zone
- append formatString %s
- append substituents { [dict get $date tzName]}
- }
- % { # A literal percent character
- append formatString %%
- }
- default { # An unknown escape sequence
- append formatString %% $char
- }
- }
- }
- percentE { # Character following %E
- set state {}
- switch -exact -- $char {
- E {
- append formatString %s
- append substituents { } \
- [string map \
- [list @BCE@ [list [mc BCE]] \
- @CE@ [list [mc CE]]] \
- {[dict get {BCE @BCE@ CE @CE@} \
- [dict get $date era]]}]
- }
- C { # Locale-dependent era
- append formatString %s
- append substituents { [dict get $date localeEra]}
- }
- y { # Locale-dependent year of the era
- append preFormatCode {
- set y [dict get $date localeYear]
- if { $y >= 0 && $y < 100 } {
- set Eyear [lindex $localeNumerals $y]
- } else {
- set Eyear $y
- }
- }
- append formatString %s
- append substituents { $Eyear}
- }
- default { # Unknown %E format group
- append formatString %%E $char
- }
- }
- }
- percentO { # Character following %O
- set state {}
- switch -exact -- $char {
- d - e { # Day of the month in alternative
- # numerals
- append formatString %s
- append substituents \
- { [lindex $localeNumerals \
- [dict get $date dayOfMonth]]}
- }
- H - k { # Hour of the day in alternative
- # numerals
- append formatString %s
- append substituents \
- { [lindex $localeNumerals \
- [expr { [dict get $date localSeconds]
- / 3600
- % 24 }]]}
- }
- I - l { # Hour (12-11) AM/PM in alternative
- # numerals
- append formatString %s
- append substituents \
- { [lindex $localeNumerals \
- [expr { ( ( ( [dict get $date localSeconds]
- % 86400 )
- + 86400
- - 3600 )
- / 3600 )
- % 12 + 1 }]]}
- }
- m { # Month number in alternative numerals
- append formatString %s
- append substituents \
- { [lindex $localeNumerals [dict get $date month]]}
- }
- M { # Minute of the hour in alternative
- # numerals
- append formatString %s
- append substituents \
- { [lindex $localeNumerals \
- [expr { [dict get $date localSeconds]
- / 60
- % 60 }]]}
- }
- S { # Second of the minute in alternative
- # numerals
- append formatString %s
- append substituents \
- { [lindex $localeNumerals \
- [expr { [dict get $date localSeconds]
- % 60 }]]}
- }
- u { # Day of the week (Monday=1,Sunday=7)
- # in alternative numerals
- append formatString %s
- append substituents \
- { [lindex $localeNumerals \
- [dict get $date dayOfWeek]]}
- }
- w { # Day of the week (Sunday=0,Saturday=6)
- # in alternative numerals
- append formatString %s
- append substituents \
- { [lindex $localeNumerals \
- [expr { [dict get $date dayOfWeek] % 7 }]]}
- }
- y { # Year of the century in alternative
- # numerals
- append formatString %s
- append substituents \
- { [lindex $localeNumerals \
- [expr { [dict get $date year] % 100 }]]}
- }
- default { # Unknown format group
- append formatString %%O $char
- }
- }
- }
- }
- }
- # Clean up any improperly terminated groups
- switch -exact -- $state {
- percent {
- append formatString %%
- }
- percentE {
- append retval %%E
- }
- percentO {
- append retval %%O
- }
- }
- proc $procName {clockval timezone} "
- $preFormatCode
- return \[::format [list $formatString] $substituents\]
- "
- # puts [list $procName [info args $procName] [info body $procName]]
- return $procName
- }
- #----------------------------------------------------------------------
- #
- # clock scan --
- #
- # Inputs a count of seconds since the Posix Epoch as a time of day.
- #
- # The 'clock format' command scans times of day on input. Refer to the user
- # documentation to see what it does.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::scan { args } {
- set format {}
- # Check the count of args
- if { [llength $args] < 1 || [llength $args] % 2 != 1 } {
- set cmdName "clock scan"
- return -code error \
- -errorcode [list CLOCK wrongNumArgs] \
- "wrong \# args: should be\
- \"$cmdName string\
- ?-base seconds?\
- ?-format string? ?-gmt boolean?\
- ?-locale LOCALE? ?-timezone ZONE?\""
- }
- # Set defaults
- set base [clock seconds]
- set string [lindex $args 0]
- set format {}
- set gmt 0
- set locale c
- set timezone [GetSystemTimeZone]
- # Pick up command line options.
- foreach { flag value } [lreplace $args 0 0] {
- set saw($flag) {}
- switch -exact -- $flag {
- -b - -ba - -bas - -base {
- set base $value
- }
- -f - -fo - -for - -form - -forma - -format {
- set format $value
- }
- -g - -gm - -gmt {
- set gmt $value
- }
- -l - -lo - -loc - -loca - -local - -locale {
- set locale [string tolower $value]
- }
- -t - -ti - -tim - -time - -timez - -timezo - -timezon - -timezone {
- set timezone $value
- }
- default {
- return -code error \
- -errorcode [list CLOCK badOption $flag] \
- "bad option \"$flag\",\
- must be -base, -format, -gmt, -locale or -timezone"
- }
- }
- }
- # Check options for validity
- if { [info exists saw(-gmt)] && [info exists saw(-timezone)] } {
- return -code error \
- -errorcode [list CLOCK gmtWithTimezone] \
- "cannot use -gmt and -timezone in same call"
- }
- if { [catch { expr { wide($base) } } result] } {
- return -code error "expected integer but got \"$base\""
- }
- if { ![string is boolean -strict $gmt] } {
- return -code error "expected boolean value but got \"$gmt\""
- } elseif { $gmt } {
- set timezone :GMT
- }
- if { ![info exists saw(-format)] } {
- # Perhaps someday we'll localize the legacy code. Right now, it's not
- # localized.
- if { [info exists saw(-locale)] } {
- return -code error \
- -errorcode [list CLOCK flagWithLegacyFormat] \
- "legacy \[clock scan\] does not support -locale"
- }
- return [FreeScan $string $base $timezone $locale]
- }
- # Change locale if a fresh locale has been given on the command line.
- EnterLocale $locale
- try {
- # Map away the locale-dependent composite format groups
- set scanner [ParseClockScanFormat $format $locale]
- return [$scanner $string $base $timezone]
- } trap CLOCK {result opts} {
- # Conceal location of generation of expected errors
- dict unset opts -errorinfo
- return -options $opts $result
- }
- }
- #----------------------------------------------------------------------
- #
- # FreeScan --
- #
- # Scans a time in free format
- #
- # Parameters:
- # string - String containing the time to scan
- # base - Base time, expressed in seconds from the Epoch
- # timezone - Default time zone in which the time will be expressed
- # locale - (Unused) Name of the locale where the time will be scanned.
- #
- # Results:
- # Returns the date and time extracted from the string in seconds from
- # the epoch
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::FreeScan { string base timezone locale } {
- variable TZData
- # Get the data for time changes in the given zone
- try {
- SetupTimeZone $timezone
- } on error {retval opts} {
- dict unset opts -errorinfo
- return -options $opts $retval
- }
- # Extract year, month and day from the base time for the parser to use as
- # defaults
- set date [GetDateFields $base $TZData($timezone) 2361222]
- dict set date secondOfDay [expr {
- [dict get $date localSeconds] % 86400
- }]
- # Parse the date. The parser will return a list comprising date, time,
- # time zone, relative month/day/seconds, relative weekday, ordinal month.
- try {
- set scanned [Oldscan $string \
- [dict get $date year] \
- [dict get $date month] \
- [dict get $date dayOfMonth]]
- lassign $scanned \
- parseDate parseTime parseZone parseRel \
- parseWeekday parseOrdinalMonth
- } on error message {
- return -code error \
- "unable to convert date-time string \"$string\": $message"
- }
- # If the caller supplied a date in the string, update the 'date' dict with
- # the value. If the caller didn't specify a time with the date, default to
- # midnight.
- if { [llength $parseDate] > 0 } {
- lassign $parseDate y m d
- if { $y < 100 } {
- if { $y >= 39 } {
- incr y 1900
- } else {
- incr y 2000
- }
- }
- dict set date era CE
- dict set date year $y
- dict set date month $m
- dict set date dayOfMonth $d
- if { $parseTime eq {} } {
- set parseTime 0
- }
- }
- # If the caller supplied a time zone in the string, it comes back as a
- # two-element list; the first element is the number of minutes east of
- # Greenwich, and the second is a Daylight Saving Time indicator (1 == yes,
- # 0 == no, -1 == unknown). We make it into a time zone indicator of
- # +-hhmm.
- if { [llength $parseZone] > 0 } {
- lassign $parseZone minEast dstFlag
- set timezone [FormatNumericTimeZone \
- [expr { 60 * $minEast + 3600 * $dstFlag }]]
- SetupTimeZone $timezone
- }
- dict set date tzName $timezone
- # Assemble date, time, zone into seconds-from-epoch
- set date [GetJulianDayFromEraYearMonthDay $date[set date {}] 2361222]
- if { $parseTime ne {} } {
- dict set date secondOfDay $parseTime
- } elseif { [llength $parseWeekday] != 0
- || [llength $parseOrdinalMonth] != 0
- || ( [llength $parseRel] != 0
- && ( [lindex $parseRel 0] != 0
- || [lindex $parseRel 1] != 0 ) ) } {
- dict set date secondOfDay 0
- }
- dict set date localSeconds [expr {
- -210866803200
- + ( 86400 * wide([dict get $date julianDay]) )
- + [dict get $date secondOfDay]
- }]
- dict set date tzName $timezone
- set date [ConvertLocalToUTC $date[set date {}] $TZData($timezone) 2361222]
- set seconds [dict get $date seconds]
- # Do relative times
- if { [llength $parseRel] > 0 } {
- lassign $parseRel relMonth relDay relSecond
- set seconds [add $seconds \
- $relMonth months $relDay days $relSecond seconds \
- -timezone $timezone -locale $locale]
- }
- # Do relative weekday
- if { [llength $parseWeekday] > 0 } {
- lassign $parseWeekday dayOrdinal dayOfWeek
- set date2 [GetDateFields $seconds $TZData($timezone) 2361222]
- dict set date2 era CE
- set jdwkday [WeekdayOnOrBefore $dayOfWeek [expr {
- [dict get $date2 julianDay] + 6
- }]]
- incr jdwkday [expr { 7 * $dayOrdinal }]
- if { $dayOrdinal > 0 } {
- incr jdwkday -7
- }
- dict set date2 secondOfDay \
- [expr { [dict get $date2 localSeconds] % 86400 }]
- dict set date2 julianDay $jdwkday
- dict set date2 localSeconds [expr {
- -210866803200
- + ( 86400 * wide([dict get $date2 julianDay]) )
- + [dict get $date secondOfDay]
- }]
- dict set date2 tzName $timezone
- set date2 [ConvertLocalToUTC $date2[set date2 {}] $TZData($timezone) \
- 2361222]
- set seconds [dict get $date2 seconds]
- }
- # Do relative month
- if { [llength $parseOrdinalMonth] > 0 } {
- lassign $parseOrdinalMonth monthOrdinal monthNumber
- if { $monthOrdinal > 0 } {
- set monthDiff [expr { $monthNumber - [dict get $date month] }]
- if { $monthDiff <= 0 } {
- incr monthDiff 12
- }
- incr monthOrdinal -1
- } else {
- set monthDiff [expr { [dict get $date month] - $monthNumber }]
- if { $monthDiff >= 0 } {
- incr monthDiff -12
- }
- incr monthOrdinal
- }
- set seconds [add $seconds $monthOrdinal years $monthDiff months \
- -timezone $timezone -locale $locale]
- }
- return $seconds
- }
- #----------------------------------------------------------------------
- #
- # ParseClockScanFormat --
- #
- # Parses a format string given to [clock scan -format]
- #
- # Parameters:
- # formatString - The format being parsed
- # locale - The current locale
- #
- # Results:
- # Constructs and returns a procedure that accepts the string being
- # scanned, the base time, and the time zone. The procedure will either
- # return the scanned time or else throw an error that should be rethrown
- # to the caller of [clock scan]
- #
- # Side effects:
- # The given procedure is defined in the ::tcl::clock namespace. Scan
- # procedures are not deleted once installed.
- #
- # Why do we parse dates by defining a procedure to parse them? The reason is
- # that by doing so, we have one convenient place to cache all the information:
- # the regular expressions that match the patterns (which will be compiled),
- # the code that assembles the date information, everything lands in one place.
- # In this way, when a given format is reused at run time, all the information
- # of how to apply it is available in a single place.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::ParseClockScanFormat {formatString locale} {
- # Check whether the format has been parsed previously, and return the
- # existing recognizer if it has.
- set procName scanproc'$formatString'$locale
- set procName [namespace current]::[string map {: {\:} \\ {\\}} $procName]
- if { [namespace which $procName] != {} } {
- return $procName
- }
- variable DateParseActions
- variable TimeParseActions
- # Localize the %x, %X, etc. groups
- set formatString [LocalizeFormat $locale $formatString]
- # Condense whitespace
- regsub -all {[[:space:]]+} $formatString { } formatString
- # Walk through the groups of the format string. In this loop, we
- # accumulate:
- # - a regular expression that matches the string,
- # - the count of capturing brackets in the regexp
- # - a set of code that post-processes the fields captured by the regexp,
- # - a dictionary whose keys are the names of fields that are present
- # in the format string.
- set re {^[[:space:]]*}
- set captureCount 0
- set postcode {}
- set fieldSet [dict create]
- set fieldCount 0
- set postSep {}
- set state {}
- foreach c [split $formatString {}] {
- switch -exact -- $state {
- {} {
- if { $c eq "%" } {
- set state %
- } elseif { $c eq " " } {
- append re {[[:space:]]+}
- } else {
- if { ! [string is alnum $c] } {
- append re "\\"
- }
- append re $c
- }
- }
- % {
- set state {}
- switch -exact -- $c {
- % {
- append re %
- }
- { } {
- append re "\[\[:space:\]\]*"
- }
- a - A { # Day of week, in words
- set l {}
- foreach \
- i {7 1 2 3 4 5 6} \
- abr [mc DAYS_OF_WEEK_ABBREV] \
- full [mc DAYS_OF_WEEK_FULL] {
- dict set l [string tolower $abr] $i
- dict set l [string tolower $full] $i
- incr i
- }
- lassign [UniquePrefixRegexp $l] regex lookup
- append re ( $regex )
- dict set fieldSet dayOfWeek [incr fieldCount]
- append postcode "dict set date dayOfWeek \[" \
- "dict get " [list $lookup] " " \
- \[ {string tolower $field} [incr captureCount] \] \
- "\]\n"
- }
- b - B - h { # Name of month
- set i 0
- set l {}
- foreach \
- abr [mc MONTHS_ABBREV] \
- full [mc MONTHS_FULL] {
- incr i
- dict set l [string tolower $abr] $i
- dict set l [string tolower $full] $i
- }
- lassign [UniquePrefixRegexp $l] regex lookup
- append re ( $regex )
- dict set fieldSet month [incr fieldCount]
- append postcode "dict set date month \[" \
- "dict get " [list $lookup] \
- " " \[ {string tolower $field} \
- [incr captureCount] \] \
- "\]\n"
- }
- C { # Gregorian century
- append re \\s*(\\d\\d?)
- dict set fieldSet century [incr fieldCount]
- append postcode "dict set date century \[" \
- "::scan \$field" [incr captureCount] " %d" \
- "\]\n"
- }
- d - e { # Day of month
- append re \\s*(\\d\\d?)
- dict set fieldSet dayOfMonth [incr fieldCount]
- append postcode "dict set date dayOfMonth \[" \
- "::scan \$field" [incr captureCount] " %d" \
- "\]\n"
- }
- E { # Prefix for locale-specific codes
- set state %E
- }
- g { # ISO8601 2-digit year
- append re \\s*(\\d\\d)
- dict set fieldSet iso8601YearOfCentury \
- [incr fieldCount]
- append postcode \
- "dict set date iso8601YearOfCentury \[" \
- "::scan \$field" [incr captureCount] " %d" \
- "\]\n"
- }
- G { # ISO8601 4-digit year
- append re \\s*(\\d\\d)(\\d\\d)
- dict set fieldSet iso8601Century [incr fieldCount]
- dict set fieldSet iso8601YearOfCentury \
- [incr fieldCount]
- append postcode \
- "dict set date iso8601Century \[" \
- "::scan \$field" [incr captureCount] " %d" \
- "\]\n" \
- "dict set date iso8601YearOfCentury \[" \
- "::scan \$field" [incr captureCount] " %d" \
- "\]\n"
- }
- H - k { # Hour of day
- append re \\s*(\\d\\d?)
- dict set fieldSet hour [incr fieldCount]
- append postcode "dict set date hour \[" \
- "::scan \$field" [incr captureCount] " %d" \
- "\]\n"
- }
- I - l { # Hour, AM/PM
- append re \\s*(\\d\\d?)
- dict set fieldSet hourAMPM [incr fieldCount]
- append postcode "dict set date hourAMPM \[" \
- "::scan \$field" [incr captureCount] " %d" \
- "\]\n"
- }
- j { # Day of year
- append re \\s*(\\d\\d?\\d?)
- dict set fieldSet dayOfYear [incr fieldCount]
- append postcode "dict set date dayOfYear \[" \
- "::scan \$field" [incr captureCount] " %d" \
- "\]\n"
- }
- J { # Julian Day Number
- append re \\s*(\\d+)
- dict set fieldSet julianDay [incr fieldCount]
- append postcode "dict set date julianDay \[" \
- "::scan \$field" [incr captureCount] " %ld" \
- "\]\n"
- }
- m - N { # Month number
- append re \\s*(\\d\\d?)
- dict set fieldSet month [incr fieldCount]
- append postcode "dict set date month \[" \
- "::scan \$field" [incr captureCount] " %d" \
- "\]\n"
- }
- M { # Minute
- append re \\s*(\\d\\d?)
- dict set fieldSet minute [incr fieldCount]
- append postcode "dict set date minute \[" \
- "::scan \$field" [incr captureCount] " %d" \
- "\]\n"
- }
- n { # Literal newline
- append re \\n
- }
- O { # Prefix for locale numerics
- set state %O
- }
- p - P { # AM/PM indicator
- set l [list [string tolower [mc AM]] 0 \
- [string tolower [mc PM]] 1]
- lassign [UniquePrefixRegexp $l] regex lookup
- append re ( $regex )
- dict set fieldSet amPmIndicator [incr fieldCount]
- append postcode "dict set date amPmIndicator \[" \
- "dict get " [list $lookup] " \[string tolower " \
- "\$field" \
- [incr captureCount] \
- "\]\]\n"
- }
- Q { # Hi, Jeff!
- append re {Stardate\s+([-+]?\d+)(\d\d\d)[.](\d)}
- incr captureCount
- dict set fieldSet seconds [incr fieldCount]
- append postcode {dict set date seconds } \[ \
- {ParseStarDate $field} [incr captureCount] \
- { $field} [incr captureCount] \
- { $field} [incr captureCount] \
- \] \n
- }
- s { # Seconds from Posix Epoch
- # This next case is insanely difficult, because it's
- # problematic to determine whether the field is
- # actually within the range of a wide integer.
- append re {\s*([-+]?\d+)}
- dict set fieldSet seconds [incr fieldCount]
- append postcode {dict set date seconds } \[ \
- {ScanWide $field} [incr captureCount] \] \n
- }
- S { # Second
- append re \\s*(\\d\\d?)
- dict set fieldSet second [incr fieldCount]
- append postcode "dict set date second \[" \
- "::scan \$field" [incr captureCount] " %d" \
- "\]\n"
- }
- t { # Literal tab character
- append re \\t
- }
- u - w { # Day number within week, 0 or 7 == Sun
- # 1=Mon, 6=Sat
- append re \\s*(\\d)
- dict set fieldSet dayOfWeek [incr fieldCount]
- append postcode {::scan $field} [incr captureCount] \
- { %d dow} \n \
- {
- if { $dow == 0 } {
- set dow 7
- } elseif { $dow > 7 } {
- return -code error \
- -errorcode [list CLOCK badDayOfWeek] \
- "day of week is greater than 7"
- }
- dict set date dayOfWeek $dow
- }
- }
- U { # Week of year. The first Sunday of
- # the year is the first day of week
- # 01. No scan rule uses this group.
- append re \\s*\\d\\d?
- }
- V { # Week of ISO8601 year
- append re \\s*(\\d\\d?)
- dict set fieldSet iso8601Week [incr fieldCount]
- append postcode "dict set date iso8601Week \[" \
- "::scan \$field" [incr captureCount] " %d" \
- "\]\n"
- }
- W { # Week of the year (00-53). The first
- # Monday of the year is the first day
- # of week 01. No scan rule uses this
- # group.
- append re \\s*\\d\\d?
- }
- y { # Two-digit Gregorian year
- append re \\s*(\\d\\d?)
- dict set fieldSet yearOfCentury [incr fieldCount]
- append postcode "dict set date yearOfCentury \[" \
- "::scan \$field" [incr captureCount] " %d" \
- "\]\n"
- }
- Y { # 4-digit Gregorian year
- append re \\s*(\\d\\d)(\\d\\d)
- dict set fieldSet century [incr fieldCount]
- dict set fieldSet yearOfCentury [incr fieldCount]
- append postcode \
- "dict set date century \[" \
- "::scan \$field" [incr captureCount] " %d" \
- "\]\n" \
- "dict set date yearOfCentury \[" \
- "::scan \$field" [incr captureCount] " %d" \
- "\]\n"
- }
- z - Z { # Time zone name
- append re {(?:([-+]\d\d(?::?\d\d(?::?\d\d)?)?)|([[:alnum:]]{1,4}))}
- dict set fieldSet tzName [incr fieldCount]
- append postcode \
- {if } \{ { $field} [incr captureCount] \
- { ne "" } \} { } \{ \n \
- {dict set date tzName $field} \
- $captureCount \n \
- \} { else } \{ \n \
- {dict set date tzName } \[ \
- {ConvertLegacyTimeZone $field} \
- [incr captureCount] \] \n \
- \} \n \
- }
- % { # Literal percent character
- append re %
- }
- default {
- append re %
- if { ! [string is alnum $c] } {
- append re \\
- }
- append re $c
- }
- }
- }
- %E {
- switch -exact -- $c {
- C { # Locale-dependent era
- set d {}
- foreach triple [mc LOCALE_ERAS] {
- lassign $triple t symbol year
- dict set d [string tolower $symbol] $year
- }
- lassign [UniquePrefixRegexp $d] regex lookup
- append re (?: $regex )
- }
- E {
- set l {}
- dict set l [string tolower [mc BCE]] BCE
- dict set l [string tolower [mc CE]] CE
- dict set l b.c.e. BCE
- dict set l c.e. CE
- dict set l b.c. BCE
- dict set l a.d. CE
- lassign [UniquePrefixRegexp $l] regex lookup
- append re ( $regex )
- dict set fieldSet era [incr fieldCount]
- append postcode "dict set date era \["\
- "dict get " [list $lookup] \
- { } \[ {string tolower $field} \
- [incr captureCount] \] \
- "\]\n"
- }
- y { # Locale-dependent year of the era
- lassign [LocaleNumeralMatcher $locale] regex lookup
- append re $regex
- incr captureCount
- }
- default {
- append re %E
- if { ! [string is alnum $c] } {
- append re \\
- }
- append re $c
- }
- }
- set state {}
- }
- %O {
- switch -exact -- $c {
- d - e {
- lassign [LocaleNumeralMatcher $locale] regex lookup
- append re $regex
- dict set fieldSet dayOfMonth [incr fieldCount]
- append postcode "dict set date dayOfMonth \[" \
- "dict get " [list $lookup] " \$field" \
- [incr captureCount] \
- "\]\n"
- }
- H - k {
- lassign [LocaleNumeralMatcher $locale] regex lookup
- append re $regex
- dict set fieldSet hour [incr fieldCount]
- append postcode "dict set date hour \[" \
- "dict get " [list $lookup] " \$field" \
- [incr captureCount] \
- "\]\n"
- }
- I - l {
- lassign [LocaleNumeralMatcher $locale] regex lookup
- append re $regex
- dict set fieldSet hourAMPM [incr fieldCount]
- append postcode "dict set date hourAMPM \[" \
- "dict get " [list $lookup] " \$field" \
- [incr captureCount] \
- "\]\n"
- }
- m {
- lassign [LocaleNumeralMatcher $locale] regex lookup
- append re $regex
- dict set fieldSet month [incr fieldCount]
- append postcode "dict set date month \[" \
- "dict get " [list $lookup] " \$field" \
- [incr captureCount] \
- "\]\n"
- }
- M {
- lassign [LocaleNumeralMatcher $locale] regex lookup
- append re $regex
- dict set fieldSet minute [incr fieldCount]
- append postcode "dict set date minute \[" \
- "dict get " [list $lookup] " \$field" \
- [incr captureCount] \
- "\]\n"
- }
- S {
- lassign [LocaleNumeralMatcher $locale] regex lookup
- append re $regex
- dict set fieldSet second [incr fieldCount]
- append postcode "dict set date second \[" \
- "dict get " [list $lookup] " \$field" \
- [incr captureCount] \
- "\]\n"
- }
- u - w {
- lassign [LocaleNumeralMatcher $locale] regex lookup
- append re $regex
- dict set fieldSet dayOfWeek [incr fieldCount]
- append postcode "set dow \[dict get " [list $lookup] \
- { $field} [incr captureCount] \] \n \
- {
- if { $dow == 0 } {
- set dow 7
- } elseif { $dow > 7 } {
- return -code error \
- -errorcode [list CLOCK badDayOfWeek] \
- "day of week is greater than 7"
- }
- dict set date dayOfWeek $dow
- }
- }
- y {
- lassign [LocaleNumeralMatcher $locale] regex lookup
- append re $regex
- dict set fieldSet yearOfCentury [incr fieldCount]
- append postcode {dict set date yearOfCentury } \[ \
- {dict get } [list $lookup] { $field} \
- [incr captureCount] \] \n
- }
- default {
- append re %O
- if { ! [string is alnum $c] } {
- append re \\
- }
- append re $c
- }
- }
- set state {}
- }
- }
- }
- # Clean up any unfinished format groups
- append re $state \\s*\$
- # Build the procedure
- set procBody {}
- append procBody "variable ::tcl::clock::TZData" \n
- append procBody "if \{ !\[ regexp -nocase [list $re] \$string ->"
- for { set i 1 } { $i <= $captureCount } { incr i } {
- append procBody " " field $i
- }
- append procBody "\] \} \{" \n
- append procBody {
- return -code error -errorcode [list CLOCK badInputString] \
- {input string does not match supplied format}
- }
- append procBody \}\n
- append procBody "set date \[dict create\]" \n
- append procBody {dict set date tzName $timeZone} \n
- append procBody $postcode
- append procBody [list set changeover [mc GREGORIAN_CHANGE_DATE]] \n
- # Set up the time zone before doing anything with a default base date
- # that might need a timezone to interpret it.
- if { ![dict exists $fieldSet seconds]
- && ![dict exists $fieldSet starDate] } {
- if { [dict exists $fieldSet tzName] } {
- append procBody {
- set timeZone [dict get $date tzName]
- }
- }
- append procBody {
- ::tcl::clock::SetupTimeZone $timeZone
- }
- }
- # Add code that gets Julian Day Number from the fields.
- append procBody [MakeParseCodeFromFields $fieldSet $DateParseActions]
- # Get time of day
- append procBody [MakeParseCodeFromFields $fieldSet $TimeParseActions]
- # Assemble seconds from the Julian day and second of the day.
- # Convert to local time unless epoch seconds or stardate are
- # being processed - they're always absolute
- if { ![dict exists $fieldSet seconds]
- && ![dict exists $fieldSet starDate] } {
- append procBody {
- if { [dict get $date julianDay] > 5373484 } {
- return -code error -errorcode [list CLOCK dateTooLarge] \
- "requested date too large to represent"
- }
- dict set date localSeconds [expr {
- -210866803200
- + ( 86400 * wide([dict get $date julianDay]) )
- + [dict get $date secondOfDay]
- }]
- }
- # Finally, convert the date to local time
- append procBody {
- set date [::tcl::clock::ConvertLocalToUTC $date[set date {}] \
- $TZData($timeZone) $changeover]
- }
- }
- # Return result
- append procBody {return [dict get $date seconds]} \n
- proc $procName { string baseTime timeZone } $procBody
- # puts [list proc $procName [list string baseTime timeZone] $procBody]
- return $procName
- }
- #----------------------------------------------------------------------
- #
- # LocaleNumeralMatcher --
- #
- # Composes a regexp that captures the numerals in the given locale, and
- # a dictionary to map them to conventional numerals.
- #
- # Parameters:
- # locale - Name of the current locale
- #
- # Results:
- # Returns a two-element list comprising the regexp and the dictionary.
- #
- # Side effects:
- # Caches the result.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::LocaleNumeralMatcher {l} {
- variable LocaleNumeralCache
- if { ![dict exists $LocaleNumeralCache $l] } {
- set d {}
- set i 0
- set sep \(
- foreach n [mc LOCALE_NUMERALS] {
- dict set d $n $i
- regsub -all {[^[:alnum:]]} $n \\\\& subex
- append re $sep $subex
- set sep |
- incr i
- }
- append re \)
- dict set LocaleNumeralCache $l [list $re $d]
- }
- return [dict get $LocaleNumeralCache $l]
- }
- #----------------------------------------------------------------------
- #
- # UniquePrefixRegexp --
- #
- # Composes a regexp that performs unique-prefix matching. The RE
- # matches one of a supplied set of strings, or any unique prefix
- # thereof.
- #
- # Parameters:
- # data - List of alternating match-strings and values.
- # Match-strings with distinct values are considered
- # distinct.
- #
- # Results:
- # Returns a two-element list. The first is a regexp that matches any
- # unique prefix of any of the strings. The second is a dictionary whose
- # keys are match values from the regexp and whose values are the
- # corresponding values from 'data'.
- #
- # Side effects:
- # None.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::UniquePrefixRegexp { data } {
- # The 'successors' dictionary will contain, for each string that is a
- # prefix of any key, all characters that may follow that prefix. The
- # 'prefixMapping' dictionary will have keys that are prefixes of keys and
- # values that correspond to the keys.
- set prefixMapping [dict create]
- set successors [dict create {} {}]
- # Walk the key-value pairs
- foreach { key value } $data {
- # Construct all prefixes of the key;
- set prefix {}
- foreach char [split $key {}] {
- set oldPrefix $prefix
- dict set successors $oldPrefix $char {}
- append prefix $char
- # Put the prefixes in the 'prefixMapping' and 'successors'
- # dictionaries
- dict lappend prefixMapping $prefix $value
- if { ![dict exists $successors $prefix] } {
- dict set successors $prefix {}
- }
- }
- }
- # Identify those prefixes that designate unique values, and those that are
- # the full keys
- set uniquePrefixMapping {}
- dict for { key valueList } $prefixMapping {
- if { [llength $valueList] == 1 } {
- dict set uniquePrefixMapping $key [lindex $valueList 0]
- }
- }
- foreach { key value } $data {
- dict set uniquePrefixMapping $key $value
- }
- # Construct the re.
- return [list \
- [MakeUniquePrefixRegexp $successors $uniquePrefixMapping {}] \
- $uniquePrefixMapping]
- }
- #----------------------------------------------------------------------
- #
- # MakeUniquePrefixRegexp --
- #
- # Service procedure for 'UniquePrefixRegexp' that constructs a regular
- # expresison that matches the unique prefixes.
- #
- # Parameters:
- # successors - Dictionary whose keys are all prefixes
- # of keys passed to 'UniquePrefixRegexp' and whose
- # values are dictionaries whose keys are the characters
- # that may follow those prefixes.
- # uniquePrefixMapping - Dictionary whose keys are the unique
- # prefixes and whose values are not examined.
- # prefixString - Current prefix being processed.
- #
- # Results:
- # Returns a constructed regular expression that matches the set of
- # unique prefixes beginning with the 'prefixString'.
- #
- # Side effects:
- # None.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::MakeUniquePrefixRegexp { successors
- uniquePrefixMapping
- prefixString } {
- # Get the characters that may follow the current prefix string
- set schars [lsort -ascii [dict keys [dict get $successors $prefixString]]]
- if { [llength $schars] == 0 } {
- return {}
- }
- # If there is more than one successor character, or if the current prefix
- # is a unique prefix, surround the generated re with non-capturing
- # parentheses.
- set re {}
- if {
- [dict exists $uniquePrefixMapping $prefixString]
- || [llength $schars] > 1
- } then {
- append re "(?:"
- }
- # Generate a regexp that matches the successors.
- set sep ""
- foreach { c } $schars {
- set nextPrefix $prefixString$c
- regsub -all {[^[:alnum:]]} $c \\\\& rechar
- append re $sep $rechar \
- [MakeUniquePrefixRegexp \
- $successors $uniquePrefixMapping $nextPrefix]
- set sep |
- }
- # If the current prefix is a unique prefix, make all following text
- # optional. Otherwise, if there is more than one successor character,
- # close the non-capturing parentheses.
- if { [dict exists $uniquePrefixMapping $prefixString] } {
- append re ")?"
- } elseif { [llength $schars] > 1 } {
- append re ")"
- }
- return $re
- }
- #----------------------------------------------------------------------
- #
- # MakeParseCodeFromFields --
- #
- # Composes Tcl code to extract the Julian Day Number from a dictionary
- # containing date fields.
- #
- # Parameters:
- # dateFields -- Dictionary whose keys are fields of the date,
- # and whose values are the rightmost positions
- # at which those fields appear.
- # parseActions -- List of triples: field set, priority, and
- # code to emit. Smaller priorities are better, and
- # the list must be in ascending order by priority
- #
- # Results:
- # Returns a burst of code that extracts the day number from the given
- # date.
- #
- # Side effects:
- # None.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::MakeParseCodeFromFields { dateFields parseActions } {
- set currPrio 999
- set currFieldPos [list]
- set currCodeBurst {
- error "in ::tcl::clock::MakeParseCodeFromFields: can't happen"
- }
- foreach { fieldSet prio parseAction } $parseActions {
- # If we've found an answer that's better than any that follow, quit
- # now.
- if { $prio > $currPrio } {
- break
- }
- # Accumulate the field positions that are used in the current field
- # grouping.
- set fieldPos [list]
- set ok true
- foreach field $fieldSet {
- if { ! [dict exists $dateFields $field] } {
- set ok 0
- break
- }
- lappend fieldPos [dict get $dateFields $field]
- }
- # Quit if we don't have a complete set of fields
- if { !$ok } {
- continue
- }
- # Determine whether the current answer is better than the last.
- set fPos [lsort -integer -decreasing $fieldPos]
- if { $prio == $currPrio } {
- foreach currPos $currFieldPos newPos $fPos {
- if {
- ![string is integer $newPos]
- || ![string is integer $currPos]
- || $newPos > $currPos
- } then {
- break
- }
- if { $newPos < $currPos } {
- set ok 0
- break
- }
- }
- }
- if { !$ok } {
- continue
- }
- # Remember the best possibility for extracting date information
- set currPrio $prio
- set currFieldPos $fPos
- set currCodeBurst $parseAction
- }
- return $currCodeBurst
- }
- #----------------------------------------------------------------------
- #
- # EnterLocale --
- #
- # Switch [mclocale] to a given locale if necessary
- #
- # Parameters:
- # locale -- Desired locale
- #
- # Results:
- # Returns the locale that was previously current.
- #
- # Side effects:
- # Does [mclocale]. If necessary, loades the designated locale's files.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::EnterLocale { locale } {
- if { $locale eq {system} } {
- if { $::tcl_platform(platform) ne {windows} } {
- # On a non-windows platform, the 'system' locale is the same as
- # the 'current' locale
- set locale current
- } else {
- # On a windows platform, the 'system' locale is adapted from the
- # 'current' locale by applying the date and time formats from the
- # Control Panel. First, load the 'current' locale if it's not yet
- # loaded
- mcpackagelocale set [mclocale]
- # Make a new locale string for the system locale, and get the
- # Control Panel information
- set locale [mclocale]_windows
- if { ! [mcpackagelocale present $locale] } {
- LoadWindowsDateTimeFormats $locale
- }
- }
- }
- if { $locale eq {current}} {
- set locale [mclocale]
- }
- # Eventually load the locale
- mcpackagelocale set $locale
- }
- #----------------------------------------------------------------------
- #
- # LoadWindowsDateTimeFormats --
- #
- # Load the date/time formats from the Control Panel in Windows and
- # convert them so that they're usable by Tcl.
- #
- # Parameters:
- # locale - Name of the locale in whose message catalog
- # the converted formats are to be stored.
- #
- # Results:
- # None.
- #
- # Side effects:
- # Updates the given message catalog with the locale strings.
- #
- # Presumes that on entry, [mclocale] is set to the current locale, so that
- # default strings can be obtained if the Registry query fails.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::LoadWindowsDateTimeFormats { locale } {
- # Bail out if we can't find the Registry
- variable NoRegistry
- if { [info exists NoRegistry] } return
- if { ![catch {
- registry get "HKEY_CURRENT_USER\\Control Panel\\International" \
- sShortDate
- } string] } {
- set quote {}
- set datefmt {}
- foreach { unquoted quoted } [split $string '] {
- append datefmt $quote [string map {
- dddd %A
- ddd %a
- dd %d
- d %e
- MMMM %B
- MMM %b
- MM %m
- M %N
- yyyy %Y
- yy %y
- y %y
- gg {}
- } $unquoted]
- if { $quoted eq {} } {
- set quote '
- } else {
- set quote $quoted
- }
- }
- ::msgcat::mcset $locale DATE_FORMAT $datefmt
- }
- if { ![catch {
- registry get "HKEY_CURRENT_USER\\Control Panel\\International" \
- sLongDate
- } string] } {
- set quote {}
- set ldatefmt {}
- foreach { unquoted quoted } [split $string '] {
- append ldatefmt $quote [string map {
- dddd %A
- ddd %a
- dd %d
- d %e
- MMMM %B
- MMM %b
- MM %m
- M %N
- yyyy %Y
- yy %y
- y %y
- gg {}
- } $unquoted]
- if { $quoted eq {} } {
- set quote '
- } else {
- set quote $quoted
- }
- }
- ::msgcat::mcset $locale LOCALE_DATE_FORMAT $ldatefmt
- }
- if { ![catch {
- registry get "HKEY_CURRENT_USER\\Control Panel\\International" \
- sTimeFormat
- } string] } {
- set quote {}
- set timefmt {}
- foreach { unquoted quoted } [split $string '] {
- append timefmt $quote [string map {
- HH %H
- H %k
- hh %I
- h %l
- mm %M
- m %M
- ss %S
- s %S
- tt %p
- t %p
- } $unquoted]
- if { $quoted eq {} } {
- set quote '
- } else {
- set quote $quoted
- }
- }
- ::msgcat::mcset $locale TIME_FORMAT $timefmt
- }
- catch {
- ::msgcat::mcset $locale DATE_TIME_FORMAT "$datefmt $timefmt"
- }
- catch {
- ::msgcat::mcset $locale LOCALE_DATE_TIME_FORMAT "$ldatefmt $timefmt"
- }
- return
- }
- #----------------------------------------------------------------------
- #
- # LocalizeFormat --
- #
- # Map away locale-dependent format groups in a clock format.
- #
- # Parameters:
- # locale -- Current [mclocale] locale, supplied to avoid
- # an extra call
- # format -- Format supplied to [clock scan] or [clock format]
- #
- # Results:
- # Returns the string with locale-dependent composite format groups
- # substituted out.
- #
- # Side effects:
- # None.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::LocalizeFormat { locale format } {
- # message catalog key to cache this format
- set key FORMAT_$format
- if { [::msgcat::mcexists -exactlocale -exactnamespace $key] } {
- return [mc $key]
- }
- # Handle locale-dependent format groups by mapping them out of the format
- # string. Note that the order of the [string map] operations is
- # significant because later formats can refer to later ones; for example
- # %c can refer to %X, which in turn can refer to %T.
- set list {
- %% %%
- %D %m/%d/%Y
- %+ {%a %b %e %H:%M:%S %Z %Y}
- }
- lappend list %EY [string map $list [mc LOCALE_YEAR_FORMAT]]
- lappend list %T [string map $list [mc TIME_FORMAT_24_SECS]]
- lappend list %R [string map $list [mc TIME_FORMAT_24]]
- lappend list %r [string map $list [mc TIME_FORMAT_12]]
- lappend list %X [string map $list [mc TIME_FORMAT]]
- lappend list %EX [string map $list [mc LOCALE_TIME_FORMAT]]
- lappend list %x [string map $list [mc DATE_FORMAT]]
- lappend list %Ex [string map $list [mc LOCALE_DATE_FORMAT]]
- lappend list %c [string map $list [mc DATE_TIME_FORMAT]]
- lappend list %Ec [string map $list [mc LOCALE_DATE_TIME_FORMAT]]
- set format [string map $list $format]
- ::msgcat::mcset $locale $key $format
- return $format
- }
- #----------------------------------------------------------------------
- #
- # FormatNumericTimeZone --
- #
- # Formats a time zone as +hhmmss
- #
- # Parameters:
- # z - Time zone in seconds east of Greenwich
- #
- # Results:
- # Returns the time zone formatted in a numeric form
- #
- # Side effects:
- # None.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::FormatNumericTimeZone { z } {
- if { $z < 0 } {
- set z [expr { - $z }]
- set retval -
- } else {
- set retval +
- }
- append retval [::format %02d [expr { $z / 3600 }]]
- set z [expr { $z % 3600 }]
- append retval [::format %02d [expr { $z / 60 }]]
- set z [expr { $z % 60 }]
- if { $z != 0 } {
- append retval [::format %02d $z]
- }
- return $retval
- }
- #----------------------------------------------------------------------
- #
- # FormatStarDate --
- #
- # Formats a date as a StarDate.
- #
- # Parameters:
- # date - Dictionary containing 'year', 'dayOfYear', and
- # 'localSeconds' fields.
- #
- # Results:
- # Returns the given date formatted as a StarDate.
- #
- # Side effects:
- # None.
- #
- # Jeff Hobbs put this in to support an atrocious pun about Tcl being
- # "Enterprise ready." Now we're stuck with it.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::FormatStarDate { date } {
- variable Roddenberry
- # Get day of year, zero based
- set doy [expr { [dict get $date dayOfYear] - 1 }]
- # Determine whether the year is a leap year
- set lp [IsGregorianLeapYear $date]
- # Convert day of year to a fractional year
- if { $lp } {
- set fractYear [expr { 1000 * $doy / 366 }]
- } else {
- set fractYear [expr { 1000 * $doy / 365 }]
- }
- # Put together the StarDate
- return [::format "Stardate %02d%03d.%1d" \
- [expr { [dict get $date year] - $Roddenberry }] \
- $fractYear \
- [expr { [dict get $date localSeconds] % 86400
- / ( 86400 / 10 ) }]]
- }
- #----------------------------------------------------------------------
- #
- # ParseStarDate --
- #
- # Parses a StarDate
- #
- # Parameters:
- # year - Year from the Roddenberry epoch
- # fractYear - Fraction of a year specifiying the day of year.
- # fractDay - Fraction of a day
- #
- # Results:
- # Returns a count of seconds from the Posix epoch.
- #
- # Side effects:
- # None.
- #
- # Jeff Hobbs put this in to support an atrocious pun about Tcl being
- # "Enterprise ready." Now we're stuck with it.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::ParseStarDate { year fractYear fractDay } {
- variable Roddenberry
- # Build a tentative date from year and fraction.
- set date [dict create \
- gregorian 1 \
- era CE \
- year [expr { $year + $Roddenberry }] \
- dayOfYear [expr { $fractYear * 365 / 1000 + 1 }]]
- set date [GetJulianDayFromGregorianEraYearDay $date[set date {}]]
- # Determine whether the given year is a leap year
- set lp [IsGregorianLeapYear $date]
- # Reconvert the fractional year according to whether the given year is a
- # leap year
- if { $lp } {
- dict set date dayOfYear \
- [expr { $fractYear * 366 / 1000 + 1 }]
- } else {
- dict set date dayOfYear \
- [expr { $fractYear * 365 / 1000 + 1 }]
- }
- dict unset date julianDay
- dict unset date gregorian
- set date [GetJulianDayFromGregorianEraYearDay $date[set date {}]]
- return [expr {
- 86400 * [dict get $date julianDay]
- - 210866803200
- + ( 86400 / 10 ) * $fractDay
- }]
- }
- #----------------------------------------------------------------------
- #
- # ScanWide --
- #
- # Scans a wide integer from an input
- #
- # Parameters:
- # str - String containing a decimal wide integer
- #
- # Results:
- # Returns the string as a pure wide integer. Throws an error if the
- # string is misformatted or out of range.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::ScanWide { str } {
- set count [::scan $str {%ld %c} result junk]
- if { $count != 1 } {
- return -code error -errorcode [list CLOCK notAnInteger $str] \
- "\"$str\" is not an integer"
- }
- if { [incr result 0] != $str } {
- return -code error -errorcode [list CLOCK integervalueTooLarge] \
- "integer value too large to represent"
- }
- return $result
- }
- #----------------------------------------------------------------------
- #
- # InterpretTwoDigitYear --
- #
- # Given a date that contains only the year of the century, determines
- # the target value of a two-digit year.
- #
- # Parameters:
- # date - Dictionary containing fields of the date.
- # baseTime - Base time relative to which the date is expressed.
- # twoDigitField - Name of the field that stores the two-digit year.
- # Default is 'yearOfCentury'
- # fourDigitField - Name of the field that will receive the four-digit
- # year. Default is 'year'
- #
- # Results:
- # Returns the dictionary augmented with the four-digit year, stored in
- # the given key.
- #
- # Side effects:
- # None.
- #
- # The current rule for interpreting a two-digit year is that the year shall be
- # between 1937 and 2037, thus staying within the range of a 32-bit signed
- # value for time. This rule may change to a sliding window in future
- # versions, so the 'baseTime' parameter (which is currently ignored) is
- # provided in the procedure signature.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::InterpretTwoDigitYear { date baseTime
- { twoDigitField yearOfCentury }
- { fourDigitField year } } {
- set yr [dict get $date $twoDigitField]
- if { $yr <= 37 } {
- dict set date $fourDigitField [expr { $yr + 2000 }]
- } else {
- dict set date $fourDigitField [expr { $yr + 1900 }]
- }
- return $date
- }
- #----------------------------------------------------------------------
- #
- # AssignBaseYear --
- #
- # Places the number of the current year into a dictionary.
- #
- # Parameters:
- # date - Dictionary value to update
- # baseTime - Base time from which to extract the year, expressed
- # in seconds from the Posix epoch
- # timezone - the time zone in which the date is being scanned
- # changeover - the Julian Day on which the Gregorian calendar
- # was adopted in the target locale.
- #
- # Results:
- # Returns the dictionary with the current year assigned.
- #
- # Side effects:
- # None.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::AssignBaseYear { date baseTime timezone changeover } {
- variable TZData
- # Find the Julian Day Number corresponding to the base time, and
- # find the Gregorian year corresponding to that Julian Day.
- set date2 [GetDateFields $baseTime $TZData($timezone) $changeover]
- # Store the converted year
- dict set date era [dict get $date2 era]
- dict set date year [dict get $date2 year]
- return $date
- }
- #----------------------------------------------------------------------
- #
- # AssignBaseIso8601Year --
- #
- # Determines the base year in the ISO8601 fiscal calendar.
- #
- # Parameters:
- # date - Dictionary containing the fields of the date that
- # is to be augmented with the base year.
- # baseTime - Base time expressed in seconds from the Posix epoch.
- # timeZone - Target time zone
- # changeover - Julian Day of adoption of the Gregorian calendar in
- # the target locale.
- #
- # Results:
- # Returns the given date with "iso8601Year" set to the
- # base year.
- #
- # Side effects:
- # None.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::AssignBaseIso8601Year {date baseTime timeZone changeover} {
- variable TZData
- # Find the Julian Day Number corresponding to the base time
- set date2 [GetDateFields $baseTime $TZData($timeZone) $changeover]
- # Calculate the ISO8601 date and transfer the year
- dict set date era CE
- dict set date iso8601Year [dict get $date2 iso8601Year]
- return $date
- }
- #----------------------------------------------------------------------
- #
- # AssignBaseMonth --
- #
- # Places the number of the current year and month into a
- # dictionary.
- #
- # Parameters:
- # date - Dictionary value to update
- # baseTime - Time from which the year and month are to be
- # obtained, expressed in seconds from the Posix epoch.
- # timezone - Name of the desired time zone
- # changeover - Julian Day on which the Gregorian calendar was adopted.
- #
- # Results:
- # Returns the dictionary with the base year and month assigned.
- #
- # Side effects:
- # None.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::AssignBaseMonth {date baseTime timezone changeover} {
- variable TZData
- # Find the year and month corresponding to the base time
- set date2 [GetDateFields $baseTime $TZData($timezone) $changeover]
- dict set date era [dict get $date2 era]
- dict set date year [dict get $date2 year]
- dict set date month [dict get $date2 month]
- return $date
- }
- #----------------------------------------------------------------------
- #
- # AssignBaseWeek --
- #
- # Determines the base year and week in the ISO8601 fiscal calendar.
- #
- # Parameters:
- # date - Dictionary containing the fields of the date that
- # is to be augmented with the base year and week.
- # baseTime - Base time expressed in seconds from the Posix epoch.
- # changeover - Julian Day on which the Gregorian calendar was adopted
- # in the target locale.
- #
- # Results:
- # Returns the given date with "iso8601Year" set to the
- # base year and "iso8601Week" to the week number.
- #
- # Side effects:
- # None.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::AssignBaseWeek {date baseTime timeZone changeover} {
- variable TZData
- # Find the Julian Day Number corresponding to the base time
- set date2 [GetDateFields $baseTime $TZData($timeZone) $changeover]
- # Calculate the ISO8601 date and transfer the year
- dict set date era CE
- dict set date iso8601Year [dict get $date2 iso8601Year]
- dict set date iso8601Week [dict get $date2 iso8601Week]
- return $date
- }
- #----------------------------------------------------------------------
- #
- # AssignBaseJulianDay --
- #
- # Determines the base day for a time-of-day conversion.
- #
- # Parameters:
- # date - Dictionary that is to get the base day
- # baseTime - Base time expressed in seconds from the Posix epoch
- # changeover - Julian day on which the Gregorian calendar was
- # adpoted in the target locale.
- #
- # Results:
- # Returns the given dictionary augmented with a 'julianDay' field
- # that contains the base day.
- #
- # Side effects:
- # None.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::AssignBaseJulianDay { date baseTime timeZone changeover } {
- variable TZData
- # Find the Julian Day Number corresponding to the base time
- set date2 [GetDateFields $baseTime $TZData($timeZone) $changeover]
- dict set date julianDay [dict get $date2 julianDay]
- return $date
- }
- #----------------------------------------------------------------------
- #
- # InterpretHMSP --
- #
- # Interprets a time in the form "hh:mm:ss am".
- #
- # Parameters:
- # date -- Dictionary containing "hourAMPM", "minute", "second"
- # and "amPmIndicator" fields.
- #
- # Results:
- # Returns the number of seconds from local midnight.
- #
- # Side effects:
- # None.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::InterpretHMSP { date } {
- set hr [dict get $date hourAMPM]
- if { $hr == 12 } {
- set hr 0
- }
- if { [dict get $date amPmIndicator] } {
- incr hr 12
- }
- dict set date hour $hr
- return [InterpretHMS $date[set date {}]]
- }
- #----------------------------------------------------------------------
- #
- # InterpretHMS --
- #
- # Interprets a 24-hour time "hh:mm:ss"
- #
- # Parameters:
- # date -- Dictionary containing the "hour", "minute" and "second"
- # fields.
- #
- # Results:
- # Returns the given dictionary augmented with a "secondOfDay"
- # field containing the number of seconds from local midnight.
- #
- # Side effects:
- # None.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::InterpretHMS { date } {
- return [expr {
- ( [dict get $date hour] * 60
- + [dict get $date minute] ) * 60
- + [dict get $date second]
- }]
- }
- #----------------------------------------------------------------------
- #
- # GetSystemTimeZone --
- #
- # Determines the system time zone, which is the default for the
- # 'clock' command if no other zone is supplied.
- #
- # Parameters:
- # None.
- #
- # Results:
- # Returns the system time zone.
- #
- # Side effects:
- # Stores the sustem time zone in the 'CachedSystemTimeZone'
- # variable, since determining it may be an expensive process.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::GetSystemTimeZone {} {
- variable CachedSystemTimeZone
- variable TimeZoneBad
- if {[set result [getenv TCL_TZ]] ne {}} {
- set timezone $result
- } elseif {[set result [getenv TZ]] ne {}} {
- set timezone $result
- }
- if {![info exists timezone]} {
- # Cache the time zone only if it was detected by one of the
- # expensive methods.
- if { [info exists CachedSystemTimeZone] } {
- set timezone $CachedSystemTimeZone
- } elseif { $::tcl_platform(platform) eq {windows} } {
- set timezone [GuessWindowsTimeZone]
- } elseif { [file exists /etc/localtime]
- && ![catch {ReadZoneinfoFile \
- Tcl/Localtime /etc/localtime}] } {
- set timezone :Tcl/Localtime
- } else {
- set timezone :localtime
- }
- set CachedSystemTimeZone $timezone
- }
- if { ![dict exists $TimeZoneBad $timezone] } {
- dict set TimeZoneBad $timezone [catch {SetupTimeZone $timezone}]
- }
- if { [dict get $TimeZoneBad $timezone] } {
- return :localtime
- } else {
- return $timezone
- }
- }
- #----------------------------------------------------------------------
- #
- # ConvertLegacyTimeZone --
- #
- # Given an alphanumeric time zone identifier and the system time zone,
- # convert the alphanumeric identifier to an unambiguous time zone.
- #
- # Parameters:
- # tzname - Name of the time zone to convert
- #
- # Results:
- # Returns a time zone name corresponding to tzname, but in an
- # unambiguous form, generally +hhmm.
- #
- # This procedure is implemented primarily to allow the parsing of RFC822
- # date/time strings. Processing a time zone name on input is not recommended
- # practice, because there is considerable room for ambiguity; for instance, is
- # BST Brazilian Standard Time, or British Summer Time?
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::ConvertLegacyTimeZone { tzname } {
- variable LegacyTimeZone
- set tzname [string tolower $tzname]
- if { ![dict exists $LegacyTimeZone $tzname] } {
- return -code error -errorcode [list CLOCK badTZName $tzname] \
- "time zone \"$tzname\" not found"
- }
- return [dict get $LegacyTimeZone $tzname]
- }
- #----------------------------------------------------------------------
- #
- # SetupTimeZone --
- #
- # Given the name or specification of a time zone, sets up its in-memory
- # data.
- #
- # Parameters:
- # tzname - Name of a time zone
- #
- # Results:
- # Unless the time zone is ':localtime', sets the TZData array to contain
- # the lookup table for local<->UTC conversion. Returns an error if the
- # time zone cannot be parsed.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::SetupTimeZone { timezone } {
- variable TZData
- if {! [info exists TZData($timezone)] } {
- variable MINWIDE
- if { $timezone eq {:localtime} } {
- # Nothing to do, we'll convert using the localtime function
- } elseif {
- [regexp {^([-+])(\d\d)(?::?(\d\d)(?::?(\d\d))?)?} $timezone \
- -> s hh mm ss]
- } then {
- # Make a fixed offset
- ::scan $hh %d hh
- if { $mm eq {} } {
- set mm 0
- } else {
- ::scan $mm %d mm
- }
- if { $ss eq {} } {
- set ss 0
- } else {
- ::scan $ss %d ss
- }
- set offset [expr { ( $hh * 60 + $mm ) * 60 + $ss }]
- if { $s eq {-} } {
- set offset [expr { - $offset }]
- }
- set TZData($timezone) [list [list $MINWIDE $offset -1 $timezone]]
- } elseif { [string index $timezone 0] eq {:} } {
- # Convert using a time zone file
- if {
- [catch {
- LoadTimeZoneFile [string range $timezone 1 end]
- }] && [catch {
- LoadZoneinfoFile [string range $timezone 1 end]
- }]
- } then {
- return -code error \
- -errorcode [list CLOCK badTimeZone $timezone] \
- "time zone \"$timezone\" not found"
- }
- } elseif { ![catch {ParsePosixTimeZone $timezone} tzfields] } {
- # This looks like a POSIX time zone - try to process it
- if { [catch {ProcessPosixTimeZone $tzfields} data opts] } {
- if { [lindex [dict get $opts -errorcode] 0] eq {CLOCK} } {
- dict unset opts -errorinfo
- }
- return -options $opts $data
- } else {
- set TZData($timezone) $data
- }
- } else {
- # We couldn't parse this as a POSIX time zone. Try again with a
- # time zone file - this time without a colon
- if { [catch { LoadTimeZoneFile $timezone }]
- && [catch { LoadZoneinfoFile $timezone } - opts] } {
- dict unset opts -errorinfo
- return -options $opts "time zone $timezone not found"
- }
- set TZData($timezone) $TZData(:$timezone)
- }
- }
- return
- }
- #----------------------------------------------------------------------
- #
- # GuessWindowsTimeZone --
- #
- # Determines the system time zone on windows.
- #
- # Parameters:
- # None.
- #
- # Results:
- # Returns a time zone specifier that corresponds to the system time zone
- # information found in the Registry.
- #
- # Bugs:
- # Fixed dates for DST change are unimplemented at present, because no
- # time zone information supplied with Windows actually uses them!
- #
- # On a Windows system where neither $env(TCL_TZ) nor $env(TZ) is specified,
- # GuessWindowsTimeZone looks in the Registry for the system time zone
- # information. It then attempts to find an entry in WinZoneInfo for a time
- # zone that uses the same rules. If it finds one, it returns it; otherwise,
- # it constructs a Posix-style time zone string and returns that.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::GuessWindowsTimeZone {} {
- variable WinZoneInfo
- variable NoRegistry
- variable TimeZoneBad
- if { [info exists NoRegistry] } {
- return :localtime
- }
- # Dredge time zone information out of the registry
- if { [catch {
- set rpath HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Control\\TimeZoneInformation
- set data [list \
- [expr { -60
- * [registry get $rpath Bias] }] \
- [expr { -60
- * [registry get $rpath StandardBias] }] \
- [expr { -60 \
- * [registry get $rpath DaylightBias] }]]
- set stdtzi [registry get $rpath StandardStart]
- foreach ind {0 2 14 4 6 8 10 12} {
- binary scan $stdtzi @${ind}s val
- lappend data $val
- }
- set daytzi [registry get $rpath DaylightStart]
- foreach ind {0 2 14 4 6 8 10 12} {
- binary scan $daytzi @${ind}s val
- lappend data $val
- }
- }] } {
- # Missing values in the Registry - bail out
- return :localtime
- }
- # Make up a Posix time zone specifier if we can't find one. Check here
- # that the tzdata file exists, in case we're running in an environment
- # (e.g. starpack) where tzdata is incomplete. (Bug 1237907)
- if { [dict exists $WinZoneInfo $data] } {
- set tzname [dict get $WinZoneInfo $data]
- if { ! [dict exists $TimeZoneBad $tzname] } {
- dict set TimeZoneBad $tzname [catch {SetupTimeZone $tzname}]
- }
- } else {
- set tzname {}
- }
- if { $tzname eq {} || [dict get $TimeZoneBad $tzname] } {
- lassign $data \
- bias stdBias dstBias \
- stdYear stdMonth stdDayOfWeek stdDayOfMonth \
- stdHour stdMinute stdSecond stdMillisec \
- dstYear dstMonth dstDayOfWeek dstDayOfMonth \
- dstHour dstMinute dstSecond dstMillisec
- set stdDelta [expr { $bias + $stdBias }]
- set dstDelta [expr { $bias + $dstBias }]
- if { $stdDelta <= 0 } {
- set stdSignum +
- set stdDelta [expr { - $stdDelta }]
- set dispStdSignum -
- } else {
- set stdSignum -
- set dispStdSignum +
- }
- set hh [::format %02d [expr { $stdDelta / 3600 }]]
- set mm [::format %02d [expr { ($stdDelta / 60 ) % 60 }]]
- set ss [::format %02d [expr { $stdDelta % 60 }]]
- set tzname {}
- append tzname < $dispStdSignum $hh $mm > $stdSignum $hh : $mm : $ss
- if { $stdMonth >= 0 } {
- if { $dstDelta <= 0 } {
- set dstSignum +
- set dstDelta [expr { - $dstDelta }]
- set dispDstSignum -
- } else {
- set dstSignum -
- set dispDstSignum +
- }
- set hh [::format %02d [expr { $dstDelta / 3600 }]]
- set mm [::format %02d [expr { ($dstDelta / 60 ) % 60 }]]
- set ss [::format %02d [expr { $dstDelta % 60 }]]
- append tzname < $dispDstSignum $hh $mm > $dstSignum $hh : $mm : $ss
- if { $dstYear == 0 } {
- append tzname ,M $dstMonth . $dstDayOfMonth . $dstDayOfWeek
- } else {
- # I have not been able to find any locale on which Windows
- # converts time zone on a fixed day of the year, hence don't
- # know how to interpret the fields. If someone can inform me,
- # I'd be glad to code it up. For right now, we bail out in
- # such a case.
- return :localtime
- }
- append tzname / [::format %02d $dstHour] \
- : [::format %02d $dstMinute] \
- : [::format %02d $dstSecond]
- if { $stdYear == 0 } {
- append tzname ,M $stdMonth . $stdDayOfMonth . $stdDayOfWeek
- } else {
- # I have not been able to find any locale on which Windows
- # converts time zone on a fixed day of the year, hence don't
- # know how to interpret the fields. If someone can inform me,
- # I'd be glad to code it up. For right now, we bail out in
- # such a case.
- return :localtime
- }
- append tzname / [::format %02d $stdHour] \
- : [::format %02d $stdMinute] \
- : [::format %02d $stdSecond]
- }
- dict set WinZoneInfo $data $tzname
- }
- return [dict get $WinZoneInfo $data]
- }
- #----------------------------------------------------------------------
- #
- # LoadTimeZoneFile --
- #
- # Load the data file that specifies the conversion between a
- # given time zone and Greenwich.
- #
- # Parameters:
- # fileName -- Name of the file to load
- #
- # Results:
- # None.
- #
- # Side effects:
- # TZData(:fileName) contains the time zone data
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::LoadTimeZoneFile { fileName } {
- variable DataDir
- variable TZData
- if { [info exists TZData($fileName)] } {
- return
- }
- # Since an unsafe interp uses the [clock] command in the master, this code
- # is security sensitive. Make sure that the path name cannot escape the
- # given directory.
- if { ![regexp {^[[.-.][:alpha:]_]+(?:/[[.-.][:alpha:]_]+)*$} $fileName] } {
- return -code error \
- -errorcode [list CLOCK badTimeZone $:fileName] \
- "time zone \":$fileName\" not valid"
- }
- try {
- source -encoding utf-8 [file join $DataDir $fileName]
- } on error {} {
- return -code error \
- -errorcode [list CLOCK badTimeZone :$fileName] \
- "time zone \":$fileName\" not found"
- }
- return
- }
- #----------------------------------------------------------------------
- #
- # LoadZoneinfoFile --
- #
- # Loads a binary time zone information file in Olson format.
- #
- # Parameters:
- # fileName - Relative path name of the file to load.
- #
- # Results:
- # Returns an empty result normally; returns an error if no Olson file
- # was found or the file was malformed in some way.
- #
- # Side effects:
- # TZData(:fileName) contains the time zone data
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::LoadZoneinfoFile { fileName } {
- variable ZoneinfoPaths
- # Since an unsafe interp uses the [clock] command in the master, this code
- # is security sensitive. Make sure that the path name cannot escape the
- # given directory.
- if { ![regexp {^[[.-.][:alpha:]_]+(?:/[[.-.][:alpha:]_]+)*$} $fileName] } {
- return -code error \
- -errorcode [list CLOCK badTimeZone $:fileName] \
- "time zone \":$fileName\" not valid"
- }
- foreach d $ZoneinfoPaths {
- set fname [file join $d $fileName]
- if { [file readable $fname] && [file isfile $fname] } {
- break
- }
- unset fname
- }
- ReadZoneinfoFile $fileName $fname
- }
- #----------------------------------------------------------------------
- #
- # ReadZoneinfoFile --
- #
- # Loads a binary time zone information file in Olson format.
- #
- # Parameters:
- # fileName - Name of the time zone (relative path name of the
- # file).
- # fname - Absolute path name of the file.
- #
- # Results:
- # Returns an empty result normally; returns an error if no Olson file
- # was found or the file was malformed in some way.
- #
- # Side effects:
- # TZData(:fileName) contains the time zone data
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::ReadZoneinfoFile {fileName fname} {
- variable MINWIDE
- variable TZData
- if { ![file exists $fname] } {
- return -code error "$fileName not found"
- }
- if { [file size $fname] > 262144 } {
- return -code error "$fileName too big"
- }
- # Suck in all the data from the file
- set f [open $fname r]
- fconfigure $f -translation binary
- set d [read $f]
- close $f
- # The file begins with a magic number, sixteen reserved bytes, and then
- # six 4-byte integers giving counts of fileds in the file.
- binary scan $d a4a1x15IIIIII \
- magic version nIsGMT nIsStd nLeap nTime nType nChar
- set seek 44
- set ilen 4
- set iformat I
- if { $magic != {TZif} } {
- return -code error "$fileName not a time zone information file"
- }
- if { $nType > 255 } {
- return -code error "$fileName contains too many time types"
- }
- # Accept only Posix-style zoneinfo. Sorry, 'leaps' bigots.
- if { $nLeap != 0 } {
- return -code error "$fileName contains leap seconds"
- }
- # In a version 2 file, we use the second part of the file, which contains
- # 64-bit transition times.
- if {$version eq "2"} {
- set seek [expr {
- 44
- + 5 * $nTime
- + 6 * $nType
- + 4 * $nLeap
- + $nIsStd
- + $nIsGMT
- + $nChar
- }]
- binary scan $d @${seek}a4a1x15IIIIII \
- magic version nIsGMT nIsStd nLeap nTime nType nChar
- if {$magic ne {TZif}} {
- return -code error "seek address $seek miscomputed, magic = $magic"
- }
- set iformat W
- set ilen 8
- incr seek 44
- }
- # Next come ${nTime} transition times, followed by ${nTime} time type
- # codes. The type codes are unsigned 1-byte quantities. We insert an
- # arbitrary start time in front of the transitions.
- binary scan $d @${seek}${iformat}${nTime}c${nTime} times tempCodes
- incr seek [expr { ($ilen + 1) * $nTime }]
- set times [linsert $times 0 $MINWIDE]
- set codes {}
- foreach c $tempCodes {
- lappend codes [expr { $c & 0xff }]
- }
- set codes [linsert $codes 0 0]
- # Next come ${nType} time type descriptions, each of which has an offset
- # (seconds east of GMT), a DST indicator, and an index into the
- # abbreviation text.
- for { set i 0 } { $i < $nType } { incr i } {
- binary scan $d @${seek}Icc gmtOff isDst abbrInd
- lappend types [list $gmtOff $isDst $abbrInd]
- incr seek 6
- }
- # Next come $nChar characters of time zone name abbreviations, which are
- # null-terminated.
- # We build them up into a dictionary indexed by character index, because
- # that's what's in the indices above.
- binary scan $d @${seek}a${nChar} abbrs
- incr seek ${nChar}
- set abbrList [split $abbrs \0]
- set i 0
- set abbrevs {}
- foreach a $abbrList {
- for {set j 0} {$j <= [string length $a]} {incr j} {
- dict set abbrevs $i [string range $a $j end]
- incr i
- }
- }
- # Package up a list of tuples, each of which contains transition time,
- # seconds east of Greenwich, DST flag and time zone abbreviation.
- set r {}
- set lastTime $MINWIDE
- foreach t $times c $codes {
- if { $t < $lastTime } {
- return -code error "$fileName has times out of order"
- }
- set lastTime $t
- lassign [lindex $types $c] gmtoff isDst abbrInd
- set abbrev [dict get $abbrevs $abbrInd]
- lappend r [list $t $gmtoff $isDst $abbrev]
- }
- # In a version 2 file, there is also a POSIX-style time zone description
- # at the very end of the file. To get to it, skip over nLeap leap second
- # values (8 bytes each),
- # nIsStd standard/DST indicators and nIsGMT UTC/local indicators.
- if {$version eq {2}} {
- set seek [expr {$seek + 8 * $nLeap + $nIsStd + $nIsGMT + 1}]
- set last [string first \n $d $seek]
- set posix [string range $d $seek [expr {$last-1}]]
- if {[llength $posix] > 0} {
- set posixFields [ParsePosixTimeZone $posix]
- foreach tuple [ProcessPosixTimeZone $posixFields] {
- lassign $tuple t gmtoff isDst abbrev
- if {$t > $lastTime} {
- lappend r $tuple
- }
- }
- }
- }
- set TZData(:$fileName) $r
- return
- }
- #----------------------------------------------------------------------
- #
- # ParsePosixTimeZone --
- #
- # Parses the TZ environment variable in Posix form
- #
- # Parameters:
- # tz Time zone specifier to be interpreted
- #
- # Results:
- # Returns a dictionary whose values contain the various pieces of the
- # time zone specification.
- #
- # Side effects:
- # None.
- #
- # Errors:
- # Throws an error if the syntax of the time zone is incorrect.
- #
- # The following keys are present in the dictionary:
- # stdName - Name of the time zone when Daylight Saving Time
- # is not in effect.
- # stdSignum - Sign (+, -, or empty) of the offset from Greenwich
- # to the given (non-DST) time zone. + and the empty
- # string denote zones west of Greenwich, - denotes east
- # of Greenwich; this is contrary to the ISO convention
- # but follows Posix.
- # stdHours - Hours part of the offset from Greenwich to the given
- # (non-DST) time zone.
- # stdMinutes - Minutes part of the offset from Greenwich to the
- # given (non-DST) time zone. Empty denotes zero.
- # stdSeconds - Seconds part of the offset from Greenwich to the
- # given (non-DST) time zone. Empty denotes zero.
- # dstName - Name of the time zone when DST is in effect, or the
- # empty string if the time zone does not observe Daylight
- # Saving Time.
- # dstSignum, dstHours, dstMinutes, dstSeconds -
- # Fields corresponding to stdSignum, stdHours, stdMinutes,
- # stdSeconds for the Daylight Saving Time version of the
- # time zone. If dstHours is empty, it is presumed to be 1.
- # startDayOfYear - The ordinal number of the day of the year on which
- # Daylight Saving Time begins. If this field is
- # empty, then DST begins on a given month-week-day,
- # as below.
- # startJ - The letter J, or an empty string. If a J is present in
- # this field, then startDayOfYear does not count February 29
- # even in leap years.
- # startMonth - The number of the month in which Daylight Saving Time
- # begins, supplied if startDayOfYear is empty. If both
- # startDayOfYear and startMonth are empty, then US rules
- # are presumed.
- # startWeekOfMonth - The number of the week in the month in which
- # Daylight Saving Time begins, in the range 1-5.
- # 5 denotes the last week of the month even in a
- # 4-week month.
- # startDayOfWeek - The number of the day of the week (Sunday=0,
- # Saturday=6) on which Daylight Saving Time begins.
- # startHours - The hours part of the time of day at which Daylight
- # Saving Time begins. An empty string is presumed to be 2.
- # startMinutes - The minutes part of the time of day at which DST begins.
- # An empty string is presumed zero.
- # startSeconds - The seconds part of the time of day at which DST begins.
- # An empty string is presumed zero.
- # endDayOfYear, endJ, endMonth, endWeekOfMonth, endDayOfWeek,
- # endHours, endMinutes, endSeconds -
- # Specify the end of DST in the same way that the start* fields
- # specify the beginning of DST.
- #
- # This procedure serves only to break the time specifier into fields. No
- # attempt is made to canonicalize the fields or supply default values.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::ParsePosixTimeZone { tz } {
- if {[regexp -expanded -nocase -- {
- ^
- # 1 - Standard time zone name
- ([[:alpha:]]+ | <[-+[:alnum:]]+>)
- # 2 - Standard time zone offset, signum
- ([-+]?)
- # 3 - Standard time zone offset, hours
- ([[:digit:]]{1,2})
- (?:
- # 4 - Standard time zone offset, minutes
- : ([[:digit:]]{1,2})
- (?:
- # 5 - Standard time zone offset, seconds
- : ([[:digit:]]{1,2} )
- )?
- )?
- (?:
- # 6 - DST time zone name
- ([[:alpha:]]+ | <[-+[:alnum:]]+>)
- (?:
- (?:
- # 7 - DST time zone offset, signum
- ([-+]?)
- # 8 - DST time zone offset, hours
- ([[:digit:]]{1,2})
- (?:
- # 9 - DST time zone offset, minutes
- : ([[:digit:]]{1,2})
- (?:
- # 10 - DST time zone offset, seconds
- : ([[:digit:]]{1,2})
- )?
- )?
- )?
- (?:
- ,
- (?:
- # 11 - Optional J in n and Jn form 12 - Day of year
- ( J ? ) ( [[:digit:]]+ )
- | M
- # 13 - Month number 14 - Week of month 15 - Day of week
- ( [[:digit:]] + )
- [.] ( [[:digit:]] + )
- [.] ( [[:digit:]] + )
- )
- (?:
- # 16 - Start time of DST - hours
- / ( [[:digit:]]{1,2} )
- (?:
- # 17 - Start time of DST - minutes
- : ( [[:digit:]]{1,2} )
- (?:
- # 18 - Start time of DST - seconds
- : ( [[:digit:]]{1,2} )
- )?
- )?
- )?
- ,
- (?:
- # 19 - Optional J in n and Jn form 20 - Day of year
- ( J ? ) ( [[:digit:]]+ )
- | M
- # 21 - Month number 22 - Week of month 23 - Day of week
- ( [[:digit:]] + )
- [.] ( [[:digit:]] + )
- [.] ( [[:digit:]] + )
- )
- (?:
- # 24 - End time of DST - hours
- / ( [[:digit:]]{1,2} )
- (?:
- # 25 - End time of DST - minutes
- : ( [[:digit:]]{1,2} )
- (?:
- # 26 - End time of DST - seconds
- : ( [[:digit:]]{1,2} )
- )?
- )?
- )?
- )?
- )?
- )?
- $
- } $tz -> x(stdName) x(stdSignum) x(stdHours) x(stdMinutes) x(stdSeconds) \
- x(dstName) x(dstSignum) x(dstHours) x(dstMinutes) x(dstSeconds) \
- x(startJ) x(startDayOfYear) \
- x(startMonth) x(startWeekOfMonth) x(startDayOfWeek) \
- x(startHours) x(startMinutes) x(startSeconds) \
- x(endJ) x(endDayOfYear) \
- x(endMonth) x(endWeekOfMonth) x(endDayOfWeek) \
- x(endHours) x(endMinutes) x(endSeconds)] } {
- # it's a good timezone
- return [array get x]
- }
- return -code error\
- -errorcode [list CLOCK badTimeZone $tz] \
- "unable to parse time zone specification \"$tz\""
- }
- #----------------------------------------------------------------------
- #
- # ProcessPosixTimeZone --
- #
- # Handle a Posix time zone after it's been broken out into fields.
- #
- # Parameters:
- # z - Dictionary returned from 'ParsePosixTimeZone'
- #
- # Results:
- # Returns time zone information for the 'TZData' array.
- #
- # Side effects:
- # None.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::ProcessPosixTimeZone { z } {
- variable MINWIDE
- variable TZData
- # Determine the standard time zone name and seconds east of Greenwich
- set stdName [dict get $z stdName]
- if { [string index $stdName 0] eq {<} } {
- set stdName [string range $stdName 1 end-1]
- }
- if { [dict get $z stdSignum] eq {-} } {
- set stdSignum +1
- } else {
- set stdSignum -1
- }
- set stdHours [lindex [::scan [dict get $z stdHours] %d] 0]
- if { [dict get $z stdMinutes] ne {} } {
- set stdMinutes [lindex [::scan [dict get $z stdMinutes] %d] 0]
- } else {
- set stdMinutes 0
- }
- if { [dict get $z stdSeconds] ne {} } {
- set stdSeconds [lindex [::scan [dict get $z stdSeconds] %d] 0]
- } else {
- set stdSeconds 0
- }
- set stdOffset [expr {
- (($stdHours * 60 + $stdMinutes) * 60 + $stdSeconds) * $stdSignum
- }]
- set data [list [list $MINWIDE $stdOffset 0 $stdName]]
- # If there's no daylight zone, we're done
- set dstName [dict get $z dstName]
- if { $dstName eq {} } {
- return $data
- }
- if { [string index $dstName 0] eq {<} } {
- set dstName [string range $dstName 1 end-1]
- }
- # Determine the daylight name
- if { [dict get $z dstSignum] eq {-} } {
- set dstSignum +1
- } else {
- set dstSignum -1
- }
- if { [dict get $z dstHours] eq {} } {
- set dstOffset [expr { 3600 + $stdOffset }]
- } else {
- set dstHours [lindex [::scan [dict get $z dstHours] %d] 0]
- if { [dict get $z dstMinutes] ne {} } {
- set dstMinutes [lindex [::scan [dict get $z dstMinutes] %d] 0]
- } else {
- set dstMinutes 0
- }
- if { [dict get $z dstSeconds] ne {} } {
- set dstSeconds [lindex [::scan [dict get $z dstSeconds] %d] 0]
- } else {
- set dstSeconds 0
- }
- set dstOffset [expr {
- (($dstHours*60 + $dstMinutes) * 60 + $dstSeconds) * $dstSignum
- }]
- }
- # Fill in defaults for European or US DST rules
- # US start time is the second Sunday in March
- # EU start time is the last Sunday in March
- # US end time is the first Sunday in November.
- # EU end time is the last Sunday in October
- if {
- [dict get $z startDayOfYear] eq {}
- && [dict get $z startMonth] eq {}
- } then {
- if {($stdSignum * $stdHours>=0) && ($stdSignum * $stdHours<=12)} {
- # EU
- dict set z startWeekOfMonth 5
- if {$stdHours>2} {
- dict set z startHours 2
- } else {
- dict set z startHours [expr {$stdHours+1}]
- }
- } else {
- # US
- dict set z startWeekOfMonth 2
- dict set z startHours 2
- }
- dict set z startMonth 3
- dict set z startDayOfWeek 0
- dict set z startMinutes 0
- dict set z startSeconds 0
- }
- if {
- [dict get $z endDayOfYear] eq {}
- && [dict get $z endMonth] eq {}
- } then {
- if {($stdSignum * $stdHours>=0) && ($stdSignum * $stdHours<=12)} {
- # EU
- dict set z endMonth 10
- dict set z endWeekOfMonth 5
- if {$stdHours>2} {
- dict set z endHours 3
- } else {
- dict set z endHours [expr {$stdHours+2}]
- }
- } else {
- # US
- dict set z endMonth 11
- dict set z endWeekOfMonth 1
- dict set z endHours 2
- }
- dict set z endDayOfWeek 0
- dict set z endMinutes 0
- dict set z endSeconds 0
- }
- # Put DST in effect in all years from 1916 to 2099.
- for { set y 1916 } { $y < 2100 } { incr y } {
- set startTime [DeterminePosixDSTTime $z start $y]
- incr startTime [expr { - wide($stdOffset) }]
- set endTime [DeterminePosixDSTTime $z end $y]
- incr endTime [expr { - wide($dstOffset) }]
- if { $startTime < $endTime } {
- lappend data \
- [list $startTime $dstOffset 1 $dstName] \
- [list $endTime $stdOffset 0 $stdName]
- } else {
- lappend data \
- [list $endTime $stdOffset 0 $stdName] \
- [list $startTime $dstOffset 1 $dstName]
- }
- }
- return $data
- }
- #----------------------------------------------------------------------
- #
- # DeterminePosixDSTTime --
- #
- # Determines the time that Daylight Saving Time starts or ends from a
- # Posix time zone specification.
- #
- # Parameters:
- # z - Time zone data returned from ParsePosixTimeZone.
- # Missing fields are expected to be filled in with
- # default values.
- # bound - The word 'start' or 'end'
- # y - The year for which the transition time is to be determined.
- #
- # Results:
- # Returns the transition time as a count of seconds from the epoch. The
- # time is relative to the wall clock, not UTC.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::DeterminePosixDSTTime { z bound y } {
- variable FEB_28
- # Determine the start or end day of DST
- set date [dict create era CE year $y]
- set doy [dict get $z ${bound}DayOfYear]
- if { $doy ne {} } {
- # Time was specified as a day of the year
- if { [dict get $z ${bound}J] ne {}
- && [IsGregorianLeapYear $y]
- && ( $doy > $FEB_28 ) } {
- incr doy
- }
- dict set date dayOfYear $doy
- set date [GetJulianDayFromEraYearDay $date[set date {}] 2361222]
- } else {
- # Time was specified as a day of the week within a month
- dict set date month [dict get $z ${bound}Month]
- dict set date dayOfWeek [dict get $z ${bound}DayOfWeek]
- set dowim [dict get $z ${bound}WeekOfMonth]
- if { $dowim >= 5 } {
- set dowim -1
- }
- dict set date dayOfWeekInMonth $dowim
- set date [GetJulianDayFromEraYearMonthWeekDay $date[set date {}] 2361222]
- }
- set jd [dict get $date julianDay]
- set seconds [expr {
- wide($jd) * wide(86400) - wide(210866803200)
- }]
- set h [dict get $z ${bound}Hours]
- if { $h eq {} } {
- set h 2
- } else {
- set h [lindex [::scan $h %d] 0]
- }
- set m [dict get $z ${bound}Minutes]
- if { $m eq {} } {
- set m 0
- } else {
- set m [lindex [::scan $m %d] 0]
- }
- set s [dict get $z ${bound}Seconds]
- if { $s eq {} } {
- set s 0
- } else {
- set s [lindex [::scan $s %d] 0]
- }
- set tod [expr { ( $h * 60 + $m ) * 60 + $s }]
- return [expr { $seconds + $tod }]
- }
- #----------------------------------------------------------------------
- #
- # GetLocaleEra --
- #
- # Given local time expressed in seconds from the Posix epoch,
- # determine localized era and year within the era.
- #
- # Parameters:
- # date - Dictionary that must contain the keys, 'localSeconds',
- # whose value is expressed as the appropriate local time;
- # and 'year', whose value is the Gregorian year.
- # etable - Value of the LOCALE_ERAS key in the message catalogue
- # for the target locale.
- #
- # Results:
- # Returns the dictionary, augmented with the keys, 'localeEra' and
- # 'localeYear'.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::GetLocaleEra { date etable } {
- set index [BSearch $etable [dict get $date localSeconds]]
- if { $index < 0} {
- dict set date localeEra \
- [::format %02d [expr { [dict get $date year] / 100 }]]
- dict set date localeYear [expr {
- [dict get $date year] % 100
- }]
- } else {
- dict set date localeEra [lindex $etable $index 1]
- dict set date localeYear [expr {
- [dict get $date year] - [lindex $etable $index 2]
- }]
- }
- return $date
- }
- #----------------------------------------------------------------------
- #
- # GetJulianDayFromEraYearDay --
- #
- # Given a year, month and day on the Gregorian calendar, determines
- # the Julian Day Number beginning at noon on that date.
- #
- # Parameters:
- # date -- A dictionary in which the 'era', 'year', and
- # 'dayOfYear' slots are populated. The calendar in use
- # is determined by the date itself relative to:
- # changeover -- Julian day on which the Gregorian calendar was
- # adopted in the current locale.
- #
- # Results:
- # Returns the given dictionary augmented with a 'julianDay' key whose
- # value is the desired Julian Day Number, and a 'gregorian' key that
- # specifies whether the calendar is Gregorian (1) or Julian (0).
- #
- # Side effects:
- # None.
- #
- # Bugs:
- # This code needs to be moved to the C layer.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::GetJulianDayFromEraYearDay {date changeover} {
- # Get absolute year number from the civil year
- switch -exact -- [dict get $date era] {
- BCE {
- set year [expr { 1 - [dict get $date year] }]
- }
- CE {
- set year [dict get $date year]
- }
- }
- set ym1 [expr { $year - 1 }]
- # Try the Gregorian calendar first.
- dict set date gregorian 1
- set jd [expr {
- 1721425
- + [dict get $date dayOfYear]
- + ( 365 * $ym1 )
- + ( $ym1 / 4 )
- - ( $ym1 / 100 )
- + ( $ym1 / 400 )
- }]
- # If the date is before the Gregorian change, use the Julian calendar.
- if { $jd < $changeover } {
- dict set date gregorian 0
- set jd [expr {
- 1721423
- + [dict get $date dayOfYear]
- + ( 365 * $ym1 )
- + ( $ym1 / 4 )
- }]
- }
- dict set date julianDay $jd
- return $date
- }
- #----------------------------------------------------------------------
- #
- # GetJulianDayFromEraYearMonthWeekDay --
- #
- # Determines the Julian Day number corresponding to the nth given
- # day-of-the-week in a given month.
- #
- # Parameters:
- # date - Dictionary containing the keys, 'era', 'year', 'month'
- # 'weekOfMonth', 'dayOfWeek', and 'dayOfWeekInMonth'.
- # changeover - Julian Day of adoption of the Gregorian calendar
- #
- # Results:
- # Returns the given dictionary, augmented with a 'julianDay' key.
- #
- # Side effects:
- # None.
- #
- # Bugs:
- # This code needs to be moved to the C layer.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::GetJulianDayFromEraYearMonthWeekDay {date changeover} {
- # Come up with a reference day; either the zeroeth day of the given month
- # (dayOfWeekInMonth >= 0) or the seventh day of the following month
- # (dayOfWeekInMonth < 0)
- set date2 $date
- set week [dict get $date dayOfWeekInMonth]
- if { $week >= 0 } {
- dict set date2 dayOfMonth 0
- } else {
- dict incr date2 month
- dict set date2 dayOfMonth 7
- }
- set date2 [GetJulianDayFromEraYearMonthDay $date2[set date2 {}] \
- $changeover]
- set wd0 [WeekdayOnOrBefore [dict get $date dayOfWeek] \
- [dict get $date2 julianDay]]
- dict set date julianDay [expr { $wd0 + 7 * $week }]
- return $date
- }
- #----------------------------------------------------------------------
- #
- # IsGregorianLeapYear --
- #
- # Determines whether a given date represents a leap year in the
- # Gregorian calendar.
- #
- # Parameters:
- # date -- The date to test. The fields, 'era', 'year' and 'gregorian'
- # must be set.
- #
- # Results:
- # Returns 1 if the year is a leap year, 0 otherwise.
- #
- # Side effects:
- # None.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::IsGregorianLeapYear { date } {
- switch -exact -- [dict get $date era] {
- BCE {
- set year [expr { 1 - [dict get $date year]}]
- }
- CE {
- set year [dict get $date year]
- }
- }
- if { $year % 4 != 0 } {
- return 0
- } elseif { ![dict get $date gregorian] } {
- return 1
- } elseif { $year % 400 == 0 } {
- return 1
- } elseif { $year % 100 == 0 } {
- return 0
- } else {
- return 1
- }
- }
- #----------------------------------------------------------------------
- #
- # WeekdayOnOrBefore --
- #
- # Determine the nearest day of week (given by the 'weekday' parameter,
- # Sunday==0) on or before a given Julian Day.
- #
- # Parameters:
- # weekday -- Day of the week
- # j -- Julian Day number
- #
- # Results:
- # Returns the Julian Day Number of the desired date.
- #
- # Side effects:
- # None.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::WeekdayOnOrBefore { weekday j } {
- set k [expr { ( $weekday + 6 ) % 7 }]
- return [expr { $j - ( $j - $k ) % 7 }]
- }
- #----------------------------------------------------------------------
- #
- # BSearch --
- #
- # Service procedure that does binary search in several places inside the
- # 'clock' command.
- #
- # Parameters:
- # list - List of lists, sorted in ascending order by the
- # first elements
- # key - Value to search for
- #
- # Results:
- # Returns the index of the greatest element in $list that is less than
- # or equal to $key.
- #
- # Side effects:
- # None.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::BSearch { list key } {
- if {[llength $list] == 0} {
- return -1
- }
- if { $key < [lindex $list 0 0] } {
- return -1
- }
- set l 0
- set u [expr { [llength $list] - 1 }]
- while { $l < $u } {
- # At this point, we know that
- # $k >= [lindex $list $l 0]
- # Either $u == [llength $list] or else $k < [lindex $list $u+1 0]
- # We find the midpoint of the interval {l,u} rounded UP, compare
- # against it, and set l or u to maintain the invariant. Note that the
- # interval shrinks at each step, guaranteeing convergence.
- set m [expr { ( $l + $u + 1 ) / 2 }]
- if { $key >= [lindex $list $m 0] } {
- set l $m
- } else {
- set u [expr { $m - 1 }]
- }
- }
- return $l
- }
- #----------------------------------------------------------------------
- #
- # clock add --
- #
- # Adds an offset to a given time.
- #
- # Syntax:
- # clock add clockval ?count unit?... ?-option value?
- #
- # Parameters:
- # clockval -- Starting time value
- # count -- Amount of a unit of time to add
- # unit -- Unit of time to add, must be one of:
- # years year months month weeks week
- # days day hours hour minutes minute
- # seconds second
- #
- # Options:
- # -gmt BOOLEAN
- # (Deprecated) Flag synonymous with '-timezone :GMT'
- # -timezone ZONE
- # Name of the time zone in which calculations are to be done.
- # -locale NAME
- # Name of the locale in which calculations are to be done.
- # Used to determine the Gregorian change date.
- #
- # Results:
- # Returns the given time adjusted by the given offset(s) in
- # order.
- #
- # Notes:
- # It is possible that adding a number of months or years will adjust the
- # day of the month as well. For instance, the time at one month after
- # 31 January is either 28 or 29 February, because February has fewer
- # than 31 days.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::add { clockval args } {
- if { [llength $args] % 2 != 0 } {
- set cmdName "clock add"
- return -code error \
- -errorcode [list CLOCK wrongNumArgs] \
- "wrong \# args: should be\
- \"$cmdName clockval ?number units?...\
- ?-gmt boolean? ?-locale LOCALE? ?-timezone ZONE?\""
- }
- if { [catch { expr {wide($clockval)} } result] } {
- return -code error $result
- }
- set offsets {}
- set gmt 0
- set locale c
- set timezone [GetSystemTimeZone]
- foreach { a b } $args {
- if { [string is integer -strict $a] } {
- lappend offsets $a $b
- } else {
- switch -exact -- $a {
- -g - -gm - -gmt {
- set gmt $b
- }
- -l - -lo - -loc - -loca - -local - -locale {
- set locale [string tolower $b]
- }
- -t - -ti - -tim - -time - -timez - -timezo - -timezon -
- -timezone {
- set timezone $b
- }
- default {
- throw [list CLOCK badOption $a] \
- "bad option \"$a\",\
- must be -gmt, -locale or -timezone"
- }
- }
- }
- }
- # Check options for validity
- if { [info exists saw(-gmt)] && [info exists saw(-timezone)] } {
- return -code error \
- -errorcode [list CLOCK gmtWithTimezone] \
- "cannot use -gmt and -timezone in same call"
- }
- if { [catch { expr { wide($clockval) } } result] } {
- return -code error "expected integer but got \"$clockval\""
- }
- if { ![string is boolean -strict $gmt] } {
- return -code error "expected boolean value but got \"$gmt\""
- } elseif { $gmt } {
- set timezone :GMT
- }
- EnterLocale $locale
- set changeover [mc GREGORIAN_CHANGE_DATE]
- if {[catch {SetupTimeZone $timezone} retval opts]} {
- dict unset opts -errorinfo
- return -options $opts $retval
- }
- try {
- foreach { quantity unit } $offsets {
- switch -exact -- $unit {
- years - year {
- set clockval [AddMonths [expr { 12 * $quantity }] \
- $clockval $timezone $changeover]
- }
- months - month {
- set clockval [AddMonths $quantity $clockval $timezone \
- $changeover]
- }
- weeks - week {
- set clockval [AddDays [expr { 7 * $quantity }] \
- $clockval $timezone $changeover]
- }
- days - day {
- set clockval [AddDays $quantity $clockval $timezone \
- $changeover]
- }
- hours - hour {
- set clockval [expr { 3600 * $quantity + $clockval }]
- }
- minutes - minute {
- set clockval [expr { 60 * $quantity + $clockval }]
- }
- seconds - second {
- set clockval [expr { $quantity + $clockval }]
- }
- default {
- throw [list CLOCK badUnit $unit] \
- "unknown unit \"$unit\", must be \
- years, months, weeks, days, hours, minutes or seconds"
- }
- }
- }
- return $clockval
- } trap CLOCK {result opts} {
- # Conceal the innards of [clock] when it's an expected error
- dict unset opts -errorinfo
- return -options $opts $result
- }
- }
- #----------------------------------------------------------------------
- #
- # AddMonths --
- #
- # Add a given number of months to a given clock value in a given
- # time zone.
- #
- # Parameters:
- # months - Number of months to add (may be negative)
- # clockval - Seconds since the epoch before the operation
- # timezone - Time zone in which the operation is to be performed
- #
- # Results:
- # Returns the new clock value as a number of seconds since
- # the epoch.
- #
- # Side effects:
- # None.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::AddMonths { months clockval timezone changeover } {
- variable DaysInRomanMonthInCommonYear
- variable DaysInRomanMonthInLeapYear
- variable TZData
- # Convert the time to year, month, day, and fraction of day.
- set date [GetDateFields $clockval $TZData($timezone) $changeover]
- dict set date secondOfDay [expr {
- [dict get $date localSeconds] % 86400
- }]
- dict set date tzName $timezone
- # Add the requisite number of months
- set m [dict get $date month]
- incr m $months
- incr m -1
- set delta [expr { $m / 12 }]
- set mm [expr { $m % 12 }]
- dict set date month [expr { $mm + 1 }]
- dict incr date year $delta
- # If the date doesn't exist in the current month, repair it
- if { [IsGregorianLeapYear $date] } {
- set hath [lindex $DaysInRomanMonthInLeapYear $mm]
- } else {
- set hath [lindex $DaysInRomanMonthInCommonYear $mm]
- }
- if { [dict get $date dayOfMonth] > $hath } {
- dict set date dayOfMonth $hath
- }
- # Reconvert to a number of seconds
- set date [GetJulianDayFromEraYearMonthDay \
- $date[set date {}]\
- $changeover]
- dict set date localSeconds [expr {
- -210866803200
- + ( 86400 * wide([dict get $date julianDay]) )
- + [dict get $date secondOfDay]
- }]
- set date [ConvertLocalToUTC $date[set date {}] $TZData($timezone) \
- $changeover]
- return [dict get $date seconds]
- }
- #----------------------------------------------------------------------
- #
- # AddDays --
- #
- # Add a given number of days to a given clock value in a given time
- # zone.
- #
- # Parameters:
- # days - Number of days to add (may be negative)
- # clockval - Seconds since the epoch before the operation
- # timezone - Time zone in which the operation is to be performed
- # changeover - Julian Day on which the Gregorian calendar was adopted
- # in the target locale.
- #
- # Results:
- # Returns the new clock value as a number of seconds since the epoch.
- #
- # Side effects:
- # None.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::AddDays { days clockval timezone changeover } {
- variable TZData
- # Convert the time to Julian Day
- set date [GetDateFields $clockval $TZData($timezone) $changeover]
- dict set date secondOfDay [expr {
- [dict get $date localSeconds] % 86400
- }]
- dict set date tzName $timezone
- # Add the requisite number of days
- dict incr date julianDay $days
- # Reconvert to a number of seconds
- dict set date localSeconds [expr {
- -210866803200
- + ( 86400 * wide([dict get $date julianDay]) )
- + [dict get $date secondOfDay]
- }]
- set date [ConvertLocalToUTC $date[set date {}] $TZData($timezone) \
- $changeover]
- return [dict get $date seconds]
- }
- #----------------------------------------------------------------------
- #
- # ChangeCurrentLocale --
- #
- # The global locale was changed within msgcat.
- # Clears the buffered parse functions of the current locale.
- #
- # Parameters:
- # loclist (ignored)
- #
- # Results:
- # None.
- #
- # Side effects:
- # Buffered parse functions are cleared.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::ChangeCurrentLocale {args} {
- variable FormatProc
- variable LocaleNumeralCache
- variable CachedSystemTimeZone
- variable TimeZoneBad
- foreach p [info procs [namespace current]::scanproc'*'current] {
- rename $p {}
- }
- foreach p [info procs [namespace current]::formatproc'*'current] {
- rename $p {}
- }
- catch {array unset FormatProc *'current}
- set LocaleNumeralCache {}
- }
- #----------------------------------------------------------------------
- #
- # ClearCaches --
- #
- # Clears all caches to reclaim the memory used in [clock]
- #
- # Parameters:
- # None.
- #
- # Results:
- # None.
- #
- # Side effects:
- # Caches are cleared.
- #
- #----------------------------------------------------------------------
- proc ::tcl::clock::ClearCaches {} {
- variable FormatProc
- variable LocaleNumeralCache
- variable CachedSystemTimeZone
- variable TimeZoneBad
- foreach p [info procs [namespace current]::scanproc'*] {
- rename $p {}
- }
- foreach p [info procs [namespace current]::formatproc'*] {
- rename $p {}
- }
- catch {unset FormatProc}
- set LocaleNumeralCache {}
- catch {unset CachedSystemTimeZone}
- set TimeZoneBad {}
- InitTZData
- }
|