123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997open!ImportopenStd_internalletarch_sixtyfour=Sys.word_size_in_bits=64moduleSpan=Span_nsmoduleOfday=Ofday_nstypet=Span.t(* since the Unix epoch (1970-01-01 00:00:00 UTC) *)[@@derivingbin_io,compare,hash,typerep]moduleReplace_polymorphic_compare_efficient=Span.Replace_polymorphic_comparemoduleReplace_polymorphic_compare=Replace_polymorphic_compare_efficientincludeReplace_polymorphic_compare_efficientinclude(Span:Quickcheck.S_rangewithtypet:=t)letnow=Span.since_unix_epochletequal=Span.equalletmin_value_for_1us_rounding=Span.min_value_for_1us_roundingletmax_value_for_1us_rounding=Span.max_value_for_1us_roundingletepoch=Span.zeroletadd=Span.(+)letsub=Span.(-)letdiff=Span.(-)letabs_difftu=Span.abs(difftu)letmax=Span.maxletmin=Span.minletnext=Span.nextletprev=Span.prevletto_span_since_epocht=tletof_span_since_epochs=sletto_int63_ns_since_epocht:Int63.t=Span.to_int63_ns(to_span_since_epocht)letof_int63_ns_since_epochi=of_span_since_epoch(Span.of_int63_nsi)let[@cold]overflow()=raise_s[%message"Time_ns: overflow"]letis_earliert1~than:t2=t1<t2letis_latert1~than:t2=t1>t2letadd_overflowedxy~sum=ifSpan.(>)ySpan.zerothenSpan.(<)sumxelseSpan.(>)sumx;;letsub_overflowedxy~diff=ifSpan.(>)ySpan.zerothenSpan.(>)diffxelseSpan.(<)diffx;;letadd_exnxy=letsum=addxyinifadd_overflowedxy~sumthenoverflow()elsesum;;letsub_exnxy=letdiff=subxyinifsub_overflowedxy~diffthenoverflow()elsediff;;letadd_saturatingxy=letsum=addxyinifadd_overflowedxy~sumthenifSpan.(y>zero)thenSpan.max_value_representableelseSpan.min_value_representableelsesum;;letsub_saturatingxy=letdiff=subxyinifsub_overflowedxy~diffthenifSpan.(y>zero)thenSpan.min_value_representableelseSpan.max_value_representableelsediff;;letto_int_ns_since_epoch=ifarch_sixtyfourthenfunt->Int63.to_int_exn(to_int63_ns_since_epocht)elsefun_->failwith"Time_ns.to_int_ns_since_epoch: unsupported on 32bit machines";;letof_int_ns_since_epochi=of_int63_ns_since_epoch(Int63.of_inti)letto_time_float_round_nearestt=Time_float.of_span_since_epoch(Span.to_span_float_round_nearest(to_span_since_epocht));;letto_time_float_round_nearest_microsecondt=Time_float.of_span_since_epoch(Span.to_span_float_round_nearest_microsecond(to_span_since_epocht));;letmin_time_value_for_1us_rounding=to_time_float_round_nearestmin_value_for_1us_rounding;;letmax_time_value_for_1us_rounding=to_time_float_round_nearestmax_value_for_1us_rounding;;letcheck_before_conversion_for_1us_roundingtime=ifTime_float.(<)timemin_time_value_for_1us_rounding||Time_float.(>)timemax_time_value_for_1us_roundingthenfailwiths~here:[%here]"Time_ns does not support this time"time[%sexp_of:Time_float.Stable.With_utc_sexp.V2.t];;letof_time_float_round_nearesttime=of_span_since_epoch(Span.of_span_float_round_nearest(Time_float.to_span_since_epochtime));;letof_time_float_round_nearest_microsecondtime=check_before_conversion_for_1us_roundingtime;of_span_since_epoch(Span.of_span_float_round_nearest_microsecond(Time_float.to_span_since_epochtime));;let[@cold]raise_next_multiple_got_nonpositive_intervalinterval=failwiths~here:[%here]"Time_ns.next_multiple got nonpositive interval"interval[%sexp_of:Span.t];;letnext_multiple_internal~can_equal_after~base~after~interval=ifSpan.(<=)intervalSpan.zerothenraise_next_multiple_got_nonpositive_intervalinterval;letbase_to_after=diffafterbaseinifSpan.(<)base_to_afterSpan.zerothenbase(* [after < base], choose [k = 0]. *)else(letnext=addbase(Span.scale_int63interval(Span.divbase_to_afterinterval))inifnext>after||(can_equal_after&&next=after)thennextelseaddnextinterval);;letnext_multiple?(can_equal_after=false)~base~after~interval()=next_multiple_internal~can_equal_after~base~after~interval;;letprev_multiple?(can_equal_before=false)~base~before~interval()=next_multiple_internal~can_equal_after:(notcan_equal_before)~base~after:(subbeforeinterval)~interval;;letrandom?state()=Span.random?state()moduleUtc:sigvalto_date_and_span_since_start_of_day:t->Date0.t*Span.tvalof_date_and_span_since_start_of_day:Date0.t->Span.t->tend=struct(* a recreation of the system call gmtime specialized to the fields we need that also
doesn't rely on Unix. *)letto_date_and_span_since_start_of_dayt=letopenInt63.Oinlet(!<)i=Int63.of_int_exniinlet(!>)t=Int63.to_int_exntinletns_since_epoch=to_int63_ns_since_epochtinletns_per_day=!<86_400*!<1_000_000_000inletapprox_days_from_epoch=ns_since_epoch/ns_per_dayinletdays_from_epoch=ifns_since_epoch<!<0&&approx_days_from_epoch*ns_per_day<>ns_since_epochthenapprox_days_from_epoch-!<1elseapprox_days_from_epochinletns_since_start_of_day=ns_since_epoch-(ns_per_day*days_from_epoch)inletdate=Date0.Days.add_daysDate0.Days.unix_epoch!>days_from_epoch|>Date0.Days.to_dateinletspan_since_start_of_day=Span.of_int63_nsns_since_start_of_dayindate,span_since_start_of_day;;letof_date_and_span_since_start_of_daydatespan_since_start_of_day=assert(Span.(>=)span_since_start_of_daySpan.zero&&Span.(<)span_since_start_of_daySpan.day);letdays_from_epoch=Date0.Days.diff(Date0.Days.of_datedate)Date0.Days.unix_epochinletspan_in_days_since_epoch=Span.scale_intSpan.daydays_from_epochinletspan_since_epoch=Span.(+)span_in_days_since_epochspan_since_start_of_dayinof_span_since_epochspan_since_epoch;;endmoduleAlternate_sexp=structmoduleT=structtypenonrect=t[@@derivingcompare,hash]moduleOfday_as_span=structopenInt.Oletseconds_to_stringseconds_span=letseconds=Span.to_int_secseconds_spaninleth=seconds/3600inletm=seconds/60%60inlets=seconds%60insprintf"%02d:%02d:%02d"hms;;lettwo_digit_of_stringstring=assert(String.lengthstring=2&&String.for_allstring~f:Char.is_digit);Int.of_stringstring;;letseconds_of_stringseconds_string=matchString.splitseconds_string~on:':'with|[h_string;m_string;s_string]->leth=two_digit_of_stringh_stringinletm=two_digit_of_stringm_stringinlets=two_digit_of_strings_stringinSpan.of_int_sec((((h*60)+m)*60)+s)|_->assertfalse;;letns_of_100_ms=100_000_000letns_of_10_ms=10_000_000letns_of_1_ms=1_000_000letns_of_100_us=100_000letns_of_10_us=10_000letns_of_1_us=1_000letns_of_100_ns=100letns_of_10_ns=10letns_of_1_ns=1letsub_second_to_stringsub_second_span=letopenInt.Oinletns=Span.to_int63_nssub_second_span|>Int63.to_int_exninifns=0then""elseifns%ns_of_100_ms=0thensprintf".%01d"(ns/ns_of_100_ms)elseifns%ns_of_10_ms=0thensprintf".%02d"(ns/ns_of_10_ms)elseifns%ns_of_1_ms=0thensprintf".%03d"(ns/ns_of_1_ms)elseifns%ns_of_100_us=0thensprintf".%04d"(ns/ns_of_100_us)elseifns%ns_of_10_us=0thensprintf".%05d"(ns/ns_of_10_us)elseifns%ns_of_1_us=0thensprintf".%06d"(ns/ns_of_1_us)elseifns%ns_of_100_ns=0thensprintf".%07d"(ns/ns_of_100_ns)elseifns%ns_of_10_ns=0thensprintf".%08d"(ns/ns_of_10_ns)elsesprintf".%09d"ns;;letsub_second_of_stringstring=ifString.is_emptystringthenSpan.zeroelse(letdigits=String.chop_prefix_exnstring~prefix:"."inassert(String.for_alldigits~f:Char.is_digit);letmultiplier=matchString.lengthdigitswith|1->ns_of_100_ms|2->ns_of_10_ms|3->ns_of_1_ms|4->ns_of_100_us|5->ns_of_10_us|6->ns_of_1_us|7->ns_of_100_ns|8->ns_of_10_ns|9->ns_of_1_ns|_->assertfalseinSpan.of_int63_ns(Int63.of_int(Int.of_stringdigits*multiplier)));;letto_stringspan=assert(Span.(>=)spanSpan.zero&&Span.(<)spanSpan.day);letseconds_span=span|>Span.to_int_sec|>Span.of_int_secinletsub_second_span=Span.(-)spanseconds_spaninseconds_to_stringseconds_span^sub_second_to_stringsub_second_span;;letof_stringstring=letlen=String.lengthstringinletprefix_len=8in(* "HH:MM:DD" *)letsuffix_len=len-prefix_leninletseconds_string=String.substring~pos:0~len:prefix_leninletsub_second_string=String.substring~pos:prefix_len~len:suffix_leninletseconds_span=seconds_of_stringseconds_stringinletsub_second_span=sub_second_of_stringsub_second_stringinSpan.(+)seconds_spansub_second_span;;endletto_stringt=letdate,span_since_start_of_day=Utc.to_date_and_span_since_start_of_daytinDate0.to_stringdate^" "^Ofday_as_span.to_stringspan_since_start_of_day^"Z";;letof_stringstring=letdate_string,ofday_string_with_zone=String.lsplit2_exnstring~on:' 'inletofday_string=String.chop_suffix_exnofday_string_with_zone~suffix:"Z"inletdate=Date0.of_stringdate_stringinletofday=Ofday_as_span.of_stringofday_stringinUtc.of_date_and_span_since_start_of_daydateofday;;lett_sexp_grammar=Sexplib.Sexp_grammar.coerceString.t_sexp_grammarincludeSexpable.Of_stringable(structtypenonrect=tletto_string=to_stringletof_string=of_stringend)endincludeTincludeComparable.Make(T)includeReplace_polymorphic_compare_efficientmoduleStable=structmoduleV1=structmoduleT=struct(* see tests in lib/core/test/src/test_time_ns that ensure stability of this
representation *)typenonrect=t[@@derivingbin_io,compare,hash,sexp,sexp_grammar]typenonreccomparator_witness=comparator_witnessletcomparator=comparatorendincludeTincludeComparable.Stable.V1.Make(T)endendendmoduleStable=structmoduleV1=structendmoduleOption=structendmoduleAlternate_sexp=Alternate_sexp.StablemoduleSpan=Span.StablemoduleOfday=Ofday.Stableend(* this code is directly duplicated from Time.ml functor, converted enough to get Time_ns
to/of_string working *)moduleTo_and_of_string:sigvalof_date_ofday:zone:Zone.t->Date.t->Ofday.t->tvalof_date_ofday_precise:Date.t->Ofday.t->zone:Zone.t->[`Onceoft|`Twiceoft*t|`Neveroft]valto_date_ofday:t->zone:Zone.t->Date.t*Ofday.tvalto_date_ofday_precise:t->zone:Zone.t->Date.t*Ofday.t*[`Only|`Also_atoft|`Also_skippedofDate.t*Ofday.t]valto_date:t->zone:Zone.t->Date.tvalto_ofday:t->zone:Zone.t->Ofday.tvalconvert:from_tz:Zone.t->to_tz:Zone.t->Date.t->Ofday.t->Date.t*Ofday.tvalreset_date_cache:unit->unitvalutc_offset:t->zone:Zone.t->Span.tvalof_string:string->t[@@deprecated"[since 2021-04] Use [of_string_with_utc_offset]"]valof_string_with_utc_offset:string->tvalto_string:t->string[@@deprecated"[since 2021-04] Use [to_string_utc]"]valto_string_utc:t->stringvalto_filename_string:t->zone:Zone.t->stringvalof_filename_string:string->zone:Zone.t->tvalto_string_trimmed:t->zone:Zone.t->stringvalto_sec_string:t->zone:Zone.t->stringvalto_sec_string_with_zone:t->zone:Zone.t->stringvalof_localized_string:zone:Zone.t->string->tvalof_string_gen:default_zone:(unit->Zone.t)->find_zone:(string->Zone.t)->string->tvalto_string_abs:t->zone:Zone.t->stringvalto_string_abs_trimmed:t->zone:Zone.t->stringvalto_string_abs_parts:t->zone:Zone.t->stringlistvalto_string_iso8601_basic:t->zone:Zone.t->stringvaloccurrence:[`First_after_or_at|`Last_before_or_at]->t->ofday:Ofday.t->zone:Zone.t->tend=struct(* this code is directly duplicated from Time_float0.ml, converted enough to get
Time_ns to/of_string working *)moduleDate_and_ofday=structtypet=Int63.tletto_synthetic_span_since_epocht=Span.of_int63_nstletof_date_ofdaydateofday=letdays=Date0.Days.diff(Date0.Days.of_datedate)Date0.Days.unix_epoch|>Int63.of_intinletopenInt63.Oin(days*Span.to_int63_nsSpan.day)+Span.to_int63_ns(Ofday.to_span_since_start_of_dayofday);;letto_absoluterelative~offset_from_utc=sub_exn(Span.of_int63_nsrelative)offset_from_utc;;letof_absoluteabsolute~offset_from_utc=Span.to_int63_ns(add_exnabsoluteoffset_from_utc);;letns_per_day=Span.to_int63_nsSpan.dayletto_days_from_epocht=(* note Time_ns represents about 146 years, not enough for [Date.create_exn] to ever
raise *)letopenInt63.Oinletdays_from_epoch_approx=t/ns_per_dayin(* when [t] is negative the integer division that calculated days_from_epoch_approx
will leave us one day short because it truncates (e.g. -100 / 86_400 = 0 and we
want -1) -- adjust for that here. *)ift<days_from_epoch_approx*ns_per_daythenInt63.preddays_from_epoch_approxelsedays_from_epoch_approx;;letofday_of_days_from_epocht~days_from_epoch=letopenInt63.Oinletdays_from_epoch_in_ns=days_from_epoch*ns_per_dayinletremainder=t-days_from_epoch_in_nsinSpan.of_int63_nsremainder|>Ofday.of_span_since_start_of_day_exn;;letdate_of_days_from_epoch~days_from_epoch=Int63.to_int_exndays_from_epoch|>Date0.Days.add_daysDate0.Days.unix_epoch|>Date0.Days.to_date;;letto_datet=letdays_from_epoch=to_days_from_epochtindate_of_days_from_epoch~days_from_epoch;;letto_ofdayt=letdays_from_epoch=to_days_from_epochtinofday_of_days_from_epocht~days_from_epoch;;endmoduleZone:sig(* This interface is directly duplicated from Time_intf.Zone, converted enough to get
this to work.
The problem is has references to Time0_intf.S, which is the functor input interface
that Time_ns currently does not satisfy. *)typetime=ttypet=Zone.t[@@derivingsexp_of]moduleIndex=Zone.Index(* copied functions reexported from Zone *)valutc:tvalindex_has_prev_clock_shift:t->Index.t->boolvalindex_has_next_clock_shift:t->Index.t->bool(* new functions defined below *)valindex:t->time->Index.tvalindex_offset_from_utc_exn:t->Index.t->timevalindex_prev_clock_shift_time_exn:t->Index.t->timevalindex_next_clock_shift_time_exn:t->Index.t->timevalabsolute_time_of_date_and_ofday:t->Date_and_ofday.t->timevaldate_and_ofday_of_absolute_time:t->time->Date_and_ofday.tvalnext_clock_shift:t->strictly_after:time->(time*Span.t)optionvalprev_clock_shift:t->at_or_before:time->(time*Span.t)optionend=structtypetime=tincludeZoneletof_span_in_secondsspan_in_seconds=(* NB. no actual rounding or exns can occur here *)Time_in_seconds.Span.to_int63_seconds_round_down_exnspan_in_seconds|>Span.of_int63_seconds;;letof_time_in_secondstime_in_seconds=Time_in_seconds.to_span_since_epochtime_in_seconds(* NB. no actual rounding or exns can occur here *)|>Time_in_seconds.Span.to_int63_seconds_round_down_exn|>Span.of_int63_seconds|>of_span_since_epoch;;letto_time_in_seconds_round_down_exntime=to_span_since_epochtime|>Span.to_int63_seconds_round_down_exn|>Time_in_seconds.Span.of_int63_seconds|>Time_in_seconds.of_span_since_epoch;;letto_date_and_ofday_in_seconds_round_down_exnrelative=Date_and_ofday.to_synthetic_span_since_epochrelative|>Span.to_int63_seconds_round_down_exn|>Time_in_seconds.Span.of_int63_seconds|>Time_in_seconds.Date_and_ofday.of_synthetic_span_since_epoch;;letindexttime=indext(to_time_in_seconds_round_down_exntime)letindex_of_date_and_ofdaytrelative=index_of_date_and_ofdayt(to_date_and_ofday_in_seconds_round_down_exnrelative);;letindex_offset_from_utc_exntindex=of_span_in_seconds(index_offset_from_utc_exntindex);;letindex_prev_clock_shift_time_exntindex=of_time_in_seconds(index_prev_clock_shift_time_exntindex);;letindex_next_clock_shift_time_exntindex=of_time_in_seconds(index_next_clock_shift_time_exntindex);;letindex_prev_clock_shift_amount_exntindex=of_span_in_seconds(index_prev_clock_shift_amount_exntindex);;letindex_prev_clock_shifttindex=matchindex_has_prev_clock_shifttindexwith|false->None|true->Some(index_prev_clock_shift_time_exntindex,index_prev_clock_shift_amount_exntindex);;letindex_next_clock_shifttindex=index_prev_clock_shiftt(Index.nextindex)letprev_clock_shiftt~at_or_before:time=index_prev_clock_shiftt(indexttime)letnext_clock_shiftt~strictly_after:time=index_next_clock_shiftt(indexttime)letdate_and_ofday_of_absolute_timettime=letindex=indexttimein(* no exn because [index] always returns a valid index *)letoffset_from_utc=index_offset_from_utc_exntindexinDate_and_ofday.of_absolutetime~offset_from_utc;;letabsolute_time_of_date_and_ofdaytrelative=letindex=index_of_date_and_ofdaytrelativein(* no exn because [index_of_date_and_ofday] always returns a valid index *)letoffset_from_utc=index_offset_from_utc_exntindexinDate_and_ofday.to_absoluterelative~offset_from_utc;;endletof_date_ofday~zonedateofday=letrelative=Date_and_ofday.of_date_ofdaydateofdayinZone.absolute_time_of_date_and_ofdayzonerelative;;letof_date_ofday_precisedateofday~zone=(* We assume that there will be only one zone shift within a given local day. *)letstart_of_day=of_date_ofday~zonedateOfday.start_of_dayinletproposed_time=addstart_of_day(Ofday.to_span_since_start_of_dayofday)inmatchZone.next_clock_shiftzone~strictly_after:start_of_daywith|None->`Onceproposed_time|Some(shift_start,shift_amount)->letshift_backwards=Span.(shift_amount<zero)in(* start and end of the "problematic region" *)lets,e=ifshift_backwardsthenaddshift_startshift_amount,shift_startelseshift_start,addshift_startshift_amountinifproposed_time<sthen`Onceproposed_timeelseifs<=proposed_time&&proposed_time<ethenifshift_backwardsthen`Twice(proposed_time,subproposed_timeshift_amount)else`Nevershift_startelse`Once(subproposed_timeshift_amount);;moduleDate_cache=structtypenonrect={mutablezone:Zone.t;mutablecache_start_incl:t;mutablecache_until_excl:t;mutableeffective_day_start:t;mutabledate:Date0.t}endletdate_cache:Date_cache.t={zone=Zone.utc;cache_start_incl=epoch;cache_until_excl=epoch;effective_day_start=epoch;date=Date0.unix_epoch};;letreset_date_cache()=date_cache.zone<-Zone.utc;date_cache.cache_start_incl<-epoch;date_cache.cache_until_excl<-epoch;date_cache.effective_day_start<-epoch;date_cache.date<-Date0.unix_epoch;;letis_in_cachetime~zone=phys_equalzonedate_cache.zone&&time>=date_cache.cache_start_incl&&time<date_cache.cache_until_excl;;letset_date_cachetime~zone=matchis_in_cachetime~zonewith|true->()|false->letindex=Zone.indexzonetimein(* no exn because [Zone.index] always returns a valid index *)letoffset_from_utc=Zone.index_offset_from_utc_exnzoneindexinletrel=Date_and_ofday.of_absolutetime~offset_from_utcinletdate=Date_and_ofday.to_daterelinletspan=Date_and_ofday.to_ofdayrel|>Ofday.to_span_since_start_of_dayinleteffective_day_start=sub(Date_and_ofday.to_absoluterel~offset_from_utc)spaninleteffective_day_until=addeffective_day_startSpan.dayinletcache_start_incl=matchZone.index_has_prev_clock_shiftzoneindexwith|false->effective_day_start|true->effective_day_start|>max(Zone.index_prev_clock_shift_time_exnzoneindex)inletcache_until_excl=matchZone.index_has_next_clock_shiftzoneindexwith|false->effective_day_until|true->effective_day_until|>min(Zone.index_next_clock_shift_time_exnzoneindex)indate_cache.zone<-zone;date_cache.cache_start_incl<-cache_start_incl;date_cache.cache_until_excl<-cache_until_excl;date_cache.effective_day_start<-effective_day_start;date_cache.date<-date;;letto_datetime~zone=set_date_cachetime~zone;date_cache.date;;letto_ofdaytime~zone=set_date_cachetime~zone;difftimedate_cache.effective_day_start|>Ofday.of_span_since_start_of_day_exn;;letto_date_ofdaytime~zone=to_datetime~zone,to_ofdaytime~zone(* The correctness of this algorithm (interface, even) depends on the fact that
timezone shifts aren't too close together (as in, it can't simultaneously be the
case that a timezone shift of X hours occurred less than X hours ago, *and*
a timezone shift of Y hours will occur in less than Y hours' time) *)letto_date_ofday_precisetime~zone=letdate,ofday=to_date_ofdaytime~zoneinletclock_shift_after=Zone.next_clock_shiftzone~strictly_after:timeinletclock_shift_before_or_at=Zone.prev_clock_shiftzone~at_or_before:timeinletalso_skipped_earlieramount=(* Using [date] and raising on [None] here is OK on the assumption that clock
shifts can't cross date boundaries. This is true in all cases I've ever heard
of (and [of_date_ofday_precise] would need revisiting if it turned out to be
false) *)matchOfday.subofdayamountwith|Someofday->`Also_skipped(date,ofday)|None->raise_s[%message"Time.to_date_ofday_precise"~span_since_epoch:(to_span_since_epochtime:Span.t)(zone:Zone.t)]inletambiguity=(* Edge cases: the instant of transition belongs to the new zone regime. So if the
clock moved by an hour exactly one hour ago, there's no ambiguity, because the
hour-ago time belongs to the same regime as you, and conversely, if the clock
will move by an hour in an hours' time, there *is* ambiguity. Hence [>.] for
the first case and [<=.] for the second. *)matchclock_shift_before_or_at,clock_shift_afterwith|Some(start,amount),_whenaddstart(Span.absamount)>time->(* clock shifted recently *)ifSpan.(amount>zero)then(* clock shifted forward recently: we skipped a time *)also_skipped_earlieramountelse((* clock shifted back recently: this date/ofday already happened *)assert(Span.(amount<zero));`Also_at(subtime(Span.absamount)))|_,Some(start,amount)whensubstart(Span.absamount)<=time->(* clock is about to shift *)ifSpan.(amount>zero)then(* clock about to shift forward: no effect *)`Onlyelse((* clock about to shift back: this date/ofday will be repeated *)assert(Span.(amount<zero));`Also_at(addtime(Span.absamount)))|_->`Onlyindate,ofday,ambiguity;;letconvert~from_tz~to_tzdateofday=letstart_time=of_date_ofday~zone:from_tzdateofdayinto_date_ofday~zone:to_tzstart_time;;letutc_offsett~zone=letutc_epoch=Zone.date_and_ofday_of_absolute_timezonetinSpan.(-)(Date_and_ofday.to_synthetic_span_since_epochutc_epoch)(to_span_since_epocht);;letoffset_stringtime~zone=letutc_offset=utc_offsettime~zoneinletis_utc=Span.(=)utc_offsetSpan.zeroinifis_utcthen"Z"elseString.concat[(ifSpan.(<)utc_offsetSpan.zerothen"-"else"+");Ofday.to_string_trimmed(Ofday.of_span_since_start_of_day_exn(Span.absutc_offset))];;letto_string_abs_parts=letattempttime~zone=letdate,ofday=to_date_ofdaytime~zoneinletoffset_string=offset_stringtime~zonein[Date0.to_stringdate;String.concat~sep:""[Ofday.to_stringofday;offset_string]]infuntime~zone->tryattempttime~zonewith|(_:exn)->(* If we overflow applying the UTC offset, try again with UTC time. *)attempttime~zone:Zone.utc;;letto_string_abs_trimmedtime~zone=letdate,ofday=to_date_ofdaytime~zoneinletoffset_string=offset_stringtime~zoneinString.concat~sep:" "[Date0.to_stringdate;Ofday.to_string_trimmedofday^offset_string];;letto_string_abstime~zone=String.concat~sep:" "(to_string_abs_parts~zonetime)letto_string_utct=to_string_abst~zone:Zone.utcletto_string=to_string_utcletto_string_iso8601_basictime~zone=String.concat~sep:"T"(to_string_abs_parts~zonetime);;letto_string_trimmedt~zone=letdate,sec=to_date_ofday~zonetinDate0.to_stringdate^" "^Ofday.to_string_trimmedsec;;letto_sec_stringt~zone=letdate,sec=to_date_ofday~zonetinDate0.to_stringdate^" "^Ofday.to_sec_stringsec;;letto_sec_string_with_zonet~zone=to_sec_stringt~zone^offset_stringt~zoneletto_filename_stringt~zone=letdate,ofday=to_date_ofday~zonetinDate0.to_stringdate^"_"^String.tr~target:':'~replacement:'-'(String.drop_suffix(Ofday.to_stringofday)3);;letof_filename_strings~zone=trymatchString.lsplit2s~on:'_'with|None->failwith"no space in filename string"|Some(date,ofday)->letdate=Date0.of_stringdateinletofday=String.tr~target:'-'~replacement:':'ofdayinletofday=Ofday.of_stringofdayinof_date_ofdaydateofday~zonewith|exn->invalid_argf"Time.of_filename_string (%s): %s"s(Exn.to_stringexn)();;letof_localized_string~zonestr=trymatchString.lsplit2str~on:' 'with|None->invalid_arg(sprintf"no space in date_ofday string: %s"str)|Some(date,time)->letdate=Date0.of_stringdateinletofday=Ofday.of_stringtimeinof_date_ofday~zonedateofdaywith|e->Exn.reraisee"Time.of_localized_string";;letoccurrencebefore_or_aftert~ofday~zone=letfirst_guess_date=to_datet~zoneinletfirst_guess=of_date_ofday~zonefirst_guess_dateofdayinletcmp,increment=matchbefore_or_afterwith|`Last_before_or_at->(<=),-1|`First_after_or_at->(>=),1inifcmpfirst_guesstthenfirst_guesselseof_date_ofday~zone(Date0.add_daysfirst_guess_dateincrement)ofday;;letensure_colon_in_offsetoffset=letoffset_length=String.lengthoffsetinifInt.(<=)offset_length2&&Char.is_digitoffset.[0]&&Char.is_digitoffset.[offset_length-1]thenoffset^":00"elseifChar.(=)offset.[1]':'||Char.(=)offset.[2]':'thenoffsetelseifInt.(<)offset_length3||Int.(>)offset_length4thenfailwithf"invalid offset %s"offset()elseString.concat[String.sliceoffset0(offset_length-2);":";String.sliceoffset(offset_length-2)offset_length];;exceptionTime_ns_of_stringofstring*Exn.t[@@derivingsexp]letof_string_gen~default_zone~find_zones=tryletdate,ofday,tz=matchString.splits~on:' 'with|[day;month;year;ofday]->String.concat[day;" ";month;" ";year],ofday,None|[date;ofday;tz]->date,ofday,Sometz|[date;ofday]->date,ofday,None|[s]->(matchString.rsplit2~on:'T'swith|Some(date,ofday)->date,ofday,None|None->failwith"no spaces or T found")|_->failwith"too many spaces"inletofday_to_secod=Span.to_sec(Ofday.to_span_since_start_of_dayod)inletofday,utc_offset=matchtzwith|Some_->ofday,None|None->ifChar.(=)ofday.[String.lengthofday-1]'Z'thenString.subofday~pos:0~len:(String.lengthofday-1),Some0.else(matchString.lsplit2~on:'+'ofdaywith|Some(l,r)->l,Some(ofday_to_sec(Ofday.of_string(ensure_colon_in_offsetr)))|None->(matchString.lsplit2~on:'-'ofdaywith|Some(l,r)->l,Some(-1.*.ofday_to_sec(Ofday.of_string(ensure_colon_in_offsetr)))|None->ofday,None))inletdate=Date0.of_stringdateinletofday=Ofday.of_stringofdayinmatchtzwith|Sometz->of_date_ofday~zone:(find_zonetz)dateofday|None->(matchutc_offsetwith|None->letzone=default_zone()inof_date_ofday~zonedateofday|Someutc_offset->letutc_t=of_date_ofday~zone:Zone.utcdateofdayinsubutc_t(Span.of_secutc_offset))with|e->raise(Time_ns_of_string(s,e));;letof_string_with_utc_offsets=letdefault_zone()=raise_s[%message"time has no time zone or UTC offset"s]inletfind_zonezone_name=failwithf"unable to lookup Zone %s. Try using Core.Time.of_string"zone_name()inof_string_gen~default_zone~find_zones;;letof_string=of_string_with_utc_offsetendincludeTo_and_of_stringletmin_value_representable=of_span_since_epochSpan.min_value_representableletmax_value_representable=of_span_since_epochSpan.max_value_representable(* Legacy definitions based on rounding to the nearest microsecond. *)letmin_value=min_value_for_1us_roundingletmax_value=max_value_for_1us_roundingletto_time=to_time_float_round_nearest_microsecondletof_time=of_time_float_round_nearest_microsecondmodule_=structopenPpx_module_timer_runtimelet()=Duration.format:=(modulestructletduration_of_spans=s|>Span.to_int63_ns|>Duration.of_nanosecondsletspan_of_durationd=d|>Duration.to_nanoseconds|>Span.of_int63_nsletof_stringstring=string|>Span.of_string|>duration_of_spanletto_string_with_same_unitdurations=letspans=durations|>List.map~f:span_of_durationinletunit_of_time=spans|>List.max_elt~compare:Span.compare|>Option.value_map~f:Span.to_unit_of_time~default:Unit_of_time.Nanosecondinspans|>List.map~f:(Span.to_string_hum~unit_of_time~align_decimal:true);;end);;endmoduleHash_queue=structendmoduleHash_set=structendmoduleMap=structendmoduleOption=structendmoduleSet=structendmoduleTable=structendmoduleZone=structendletarg_type=`Use_Time_ns_unixletcomparator=`Use_Time_ns_unixletget_sexp_zone=`Use_Time_ns_unixletinterruptible_pause=`Use_Time_ns_unixletof_date_ofday_zoned=`Use_Time_ns_unixletof_string_abs=`Use_Time_ns_unixletof_string_fix_proto=`Use_Time_ns_unixletpause=`Use_Time_ns_unixletpause_forever=`Use_Time_ns_unixletpp=`Use_Time_ns_unixletset_sexp_zone=`Use_Time_ns_unixletsexp_of_t=`Use_Time_ns_unixletsexp_of_t_abs=`Use_Time_ns_unixlett_of_sexp=`Use_Time_ns_unixlett_of_sexp_abs=`Use_Time_ns_unixletto_date_ofday_zoned=`Use_Time_ns_unixletto_ofday_zoned=`Use_Time_ns_unixletto_string_fix_proto=`Use_Time_ns_unixletvalidate_bound=`Use_Time_ns_unixletvalidate_lbound=`Use_Time_ns_unixletvalidate_ubound=`Use_Time_ns_unix