123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350(**
Outside of Core Time appears to be a single module with a number of submodules:
- Time
- Span
- Ofday
- Zone
The reality under the covers isn't as simple for a three reasons:
- We want as much Time functionality available to Core_kernel as possible, and
Core_kernel modules shouldn't rely on Unix functions. Some functions in Time
require Unix, which creates one split.
- We want some functionality to be functorized so that code can be shared
between Time and Time_ns.
- Time has internal circular dependencies. For instance, Ofday.now relies on
Time.now, but Time also wants to expose Time.to_date_ofday, which relies on Ofday.
We use a stack of modules to break the cycle.
This leads to the following modules within Core_kernel and Core:
Core_kernel.Span - the core type of span
Core_kernel.Ofday - the core type of ofday, which is really a constrained span
Core_kernel.Date - the core type of date
Core_kernel.Zone - the base functor for creating a Zone type
Core_kernel.Time_float0 - contains the base Time.t type and lays out the basic
relationship between Time, Span, Ofday, and Zone
Core_kernel.Time_float - ties Time, Span, Ofday, Zone, and Date together and provides
the higher level functions for them that don't rely on Unix
Core_kernel.Time - re-exposes Time_float
Core.Zone_cache - implements a caching layer between the Unix filesystem and Zones
Core.Core_date - adds the Unix dependent functions to Date
Core.Core_time - adds the Unix dependent functions to Time
Core - renames the Core_{base} modules to {base} for ease of access in
modules outside of Core
*)open!Importopen!Int.Replace_polymorphic_comparemoduleSys=Core_sysincludeCore_time_intfmoduleMake(Time0:Time.S_kernel_without_zone)(Time:Time.S_kernelwithmoduleTime:=Time0)=structmoduleSpan=structincludeTime.Spanletarg_type=Core_kernel.Command.Arg_type.createof_stringendmoduleZone=structincludeTime.Zoneinclude(Core_zone:Core_zone.Extend_zonewithtypet:=t)letarg_type=Core_kernel.Command.Arg_type.createof_stringendmoduleOfday=structincludeTime.Ofdayletarg_type=Core_kernel.Command.Arg_type.createof_stringletnow~zone=Time.to_ofday~zone(Time.now())moduleZoned=structtypet={ofday:Time.Ofday.t;zone:Zone.t;}[@@derivingbin_io,fields,compare,hash]typesexp_repr=Time.Ofday.t*Zone.t[@@derivingsexp]letsexp_of_tt=[%sexp_of:sexp_repr](t.ofday,t.zone)lett_of_sexpsexp=let(ofday,zone)=[%of_sexp:sexp_repr]sexpin{ofday;zone;};;letto_timetdate=Time.of_date_ofday~zone:(zonet)date(ofdayt)letcreateofdayzone={ofday;zone}letcreate_localofday=createofday(Lazy.forceZone.local)letof_stringstring:t=matchString.splitstring~on:' 'with|[ofday;zone]->{ofday=Time.Ofday.of_stringofday;zone=Zone.of_stringzone;}|_->failwithf"Ofday.Zoned.of_string %s"string();;letto_string(t:t):string=String.concat[Time.Ofday.to_stringt.ofday;" ";Zone.to_stringt.zone];;letarg_type=Core_kernel.Command.Arg_type.createof_stringmoduleWith_nonchronological_compare=structtypenonrect=t[@@derivingbin_io,compare,sexp,hash]endincludePretty_printer.Register(structtypenonrect=tletto_string=to_stringletmodule_name="Core.Time.Ofday.Zoned"end)endendinclude(Time:moduletypeofTimewithmoduleZone:=Time.ZoneandmoduleOfday:=Time.OfdayandmoduleSpan:=Time.Span)letof_tmtm~zone=(* Explicitly ignoring isdst, wday, yday (they are redundant with the other fields
and the [zone] argument) *)let{Core_unix.tm_year;tm_mon;tm_mday;tm_hour;tm_min;tm_sec;tm_isdst=_;tm_wday=_;tm_yday=_}=tminletdate=Date.create_exn~y:(tm_year+1900)~m:(Month.of_int_exn(tm_mon+1))~d:tm_mdayinletofday=Ofday.create~hr:tm_hour~min:tm_min~sec:tm_sec()inof_date_ofday~zonedateofday;;letof_string_fix_protoutcstr=tryletexpect_length=21in(* = 8 + 1 + 12 *)letexpect_dash=8inifChar.(<>)str.[expect_dash]'-'thenfailwithf"no dash in position %d"expect_dash();letzone=matchutcwith|`Utc->Zone.utc|`Local->Lazy.forceZone.localinifInt.(>)(String.lengthstr)expect_lengththenfailwithf"input too long"();of_date_ofday~zone(Date.of_string_iso8601_basicstr~pos:0)(Ofday.of_string_iso8601_extendedstr~pos:(expect_dash+1))withexn->invalid_argf"Time.of_string_fix_proto %s: %s"str(Exn.to_stringexn)();;letto_string_fix_protoutct=letzone=matchutcwith|`Utc->Zone.utc|`Local->Lazy.forceZone.localinletdate,sec=to_date_ofdayt~zonein(Date.to_string_iso8601_basicdate)^"-"^(Ofday.to_millisecond_stringsec);;letformatts~zone=letepoch_time=Zone.date_and_ofday_of_absolute_timezonet|>Date_and_ofday.to_synthetic_span_since_epoch|>Span.to_secinCore_unix.strftime(Unix.gmtimeepoch_time)s;;letparses~fmt~zone=Core_unix.strptime~fmts|>of_tm~zone;;letpause_forspan=lettime_remaining=(* If too large a float is passed in (Span.max_value for instance) then
nanosleep will return immediately, leading to an infinite and expensive
select loop. This is handled by pausing for no longer than 100 days.
*)letspan=Span.minspan(Span.scaleSpan.day100.)inCore_unix.nanosleep(Span.to_secspan)inifFloat.(>)time_remaining0.0then`Remaining(Span.of_sectime_remaining)else`Ok;;(** Pause and don't allow events to interrupt. *)letrecpausespan=matchpause_forspanwith|`Remainingspan->pausespan|`Ok->();;(** Pause but allow events to interrupt. *)letinterruptible_pause=pause_forletrecpause_forever()=pause(Span.of_day1.0);pause_forever();;letto_stringt=to_string_abst~zone:(Lazy.forceZone.local)letensure_colon_in_offsetoffset=ifChar.(=)offset.[1]':'||Char.(=)offset.[2]':'thenoffsetelsebeginletoffset_length=String.lengthoffsetinifInt.(<)offset_length3||Int.(>)offset_length4thenfailwithf"invalid offset %s"offset()elseString.concat[String.sliceoffset0(offset_length-2);":";String.sliceoffset(offset_length-2)offset_length]end;;exceptionTime_string_not_absoluteofstring[@@derivingsexp]letof_string_gen~if_no_timezones=letdefault_zone()=matchif_no_timezonewith|`Fail->raise(Time_string_not_absolutes);|`Local->Lazy.forceZone.local|`Use_this_onezone->zoneinof_string_gen~default_zone~find_zone:Zone.find_exns;;letof_string_abss=of_string_gen~if_no_timezone:`Failsletof_strings=of_string_gen~if_no_timezone:`Localsletarg_type=Core_kernel.Command.Arg_type.createof_string_absincludePretty_printer.Register(structtypenonrect=tletto_string=to_stringletmodule_name="Core.Time"end)letsexp_zone=refZone.localletget_sexp_zone()=(Lazy.force!sexp_zone)letset_sexp_zonezone=sexp_zone:=lazyzonelett_of_sexp_gen~if_no_timezonesexp=trymatchsexpwith|Sexp.List[Sexp.Atomdate;Sexp.Atomofday;Sexp.Atomtz]->of_date_ofday~zone:(Zone.find_exntz)(Date.of_stringdate)(Ofday.of_stringofday)(* This is actually where the output of [sexp_of_t] is handled, since that's e.g.
(2015-07-06 09:09:44.787988+01:00). *)|Sexp.List[Sexp.Atomdate;Sexp.Atomofday_and_possibly_zone]->of_string_gen~if_no_timezone(date^" "^ofday_and_possibly_zone)|Sexp.Atomdatetime->of_string_gen~if_no_timezonedatetime|_->of_sexp_error"Time.t_of_sexp"sexpwith|Of_sexp_error_ase->raisee|e->of_sexp_error(sprintf"Time.t_of_sexp: %s"(Exn.to_stringe))sexp;;lett_of_sexpsexp=t_of_sexp_gensexp~if_no_timezone:(`Use_this_one(Lazy.force!sexp_zone))lett_of_sexp_abssexp=t_of_sexp_gensexp~if_no_timezone:`Failletsexp_of_t_abst~zone=Sexp.List(List.map(Time.to_string_abs_parts~zonet)~f:(funs->Sexp.Atoms));;letsexp_of_tt=sexp_of_t_abs~zone:(Lazy.force!sexp_zone)tmoduletypeC=Comparable.Map_and_set_binablewithtypet:=tandtypecomparator_witness:=comparator_witnessletmake_comparable?(sexp_of_t=sexp_of_t)?(t_of_sexp=t_of_sexp)():(moduleC)=(modulestructmoduleC=structtypenonrect=t[@@derivingbin_io]typenonreccomparator_witness=comparator_witnessletcomparator=comparatorletsexp_of_t=sexp_of_tlett_of_sexp=t_of_sexpendincludeCmoduleMap=Map.Make_binable_using_comparator(C)moduleSet=Set.Make_binable_using_comparator(C)end)(* In 108.06a and earlier, times in sexps of Maps and Sets were raw floats. From
108.07 through 109.13, the output format remained raw as before, but both the raw
and pretty format were accepted as input. From 109.14 on, the output format was
changed from raw to pretty, while continuing to accept both formats. Once we
believe most programs are beyond 109.14, we will switch the input format to no
longer accept raw. *)include(valmake_comparable()~t_of_sexp:(funsexp->matchOption.try_with(fun()->of_span_since_epoch(Span.of_sec(Float.t_of_sexpsexp)))with|Somet->t|None->t_of_sexpsexp))let%test_=Set.equal(Set.of_list[epoch])(Set.t_of_sexp(Sexp.List[Float.sexp_of_t(Span.to_sec(to_span_since_epochepoch))]));;includeHashable.Make_binable(structtypenonrect=t[@@derivingbin_io,compare,hash,sexp]end)moduleExposed_for_tests=structletensure_colon_in_offset=ensure_colon_in_offsetendend