diff --git a/timedesc/Timedesc/Interval/index.html b/timedesc/Timedesc/Interval/index.html index 9f762779..7d2bc924 100644 --- a/timedesc/Timedesc/Interval/index.html +++ b/timedesc/Timedesc/Interval/index.html @@ -8,7 +8,7 @@ unit
Pretty printing for interval.
Default format string:
[{syear} {smon:Xxx} {sday:0X} {shour:0X}:{smin:0X}:{ssec:0X}{ssec-frac:.} \ {stzoff-sign}{stzoff-hour:0X}:{stzoff-min:0X}:{stzoff-sec:0X}, {eyear} \ {emon:Xxx} {eday:0X} {ehour:0X}:{emin:0X}:{esec:0X}{esec-frac:.} \ -{etzoff-sign}{etzoff-hour:0X}:{etzoff-min:0X}:{etzoff-sec:0X})
Follows same format string rules as Date_time.to_string
, but tags are prefixed with 's' for "start time", and 'e' for "end exc time", e.g. for interval (x, y)
{syear}
gives year of the x
{ehour:cX}
gives hour of the y
val to_string : ?display_using_tz:Time_zone.t -> ?format:string -> t -> string
val pp_seq :
+{etzoff-sign}{etzoff-hour:0X}:{etzoff-min:0X}:{etzoff-sec:0X})Follows same format string rules as pp
, but tags are prefixed with 's' for "start time", and 'e' for "end exc time", e.g. for interval (x, y)
{syear}
gives year of the x
{ehour:cX}
gives hour of the y
val to_string : ?display_using_tz:Time_zone.t -> ?format:string -> t -> string
val pp_seq :
?display_using_tz:Time_zone.t ->
?format:string ->
?sep:(Stdlib.Format.formatter -> unit -> unit) ->
diff --git a/timedesc/Timedesc/Utils/index.html b/timedesc/Timedesc/Utils/index.html
index a62d7f3b..995f0b9d 100644
--- a/timedesc/Timedesc/Utils/index.html
+++ b/timedesc/Timedesc/Utils/index.html
@@ -1,3 +1,2 @@
-Utils (timedesc.Timedesc.Utils) Module Timedesc.Utils
val ptime_span_of_span : Span.t -> Ptime.span option
val ptime_of_timestamp : timestamp -> Ptime.t option
val span_of_ptime_span : Ptime.span -> Span.t
Warning: Subnanosecond information is lost in this conversion
val timestamp_of_ptime : Ptime.t -> timestamp
Warning: Subnanosecond information is lost in this conversion
Month utils
val human_int_of_month : month -> int
This yields the usual human readable numbering of 1 to 12 inclusive
val index_of_month : month -> int
This yields the index based numbering of 0 to 11 inclusive
val month_of_human_int : int -> month option
val month_of_index : int -> month option
val weekday_of_tm_int : int -> weekday option
val tm_int_of_weekday : weekday -> int
val weekday_of_iso_int : int -> weekday option
val iso_int_of_weekday : weekday -> int
val get_local_tz_for_arg : unit -> Time_zone.t
val abbr_string_of_weekday : weekday -> string
Warning: Following functions are direct applications of the relevant formulas with little to no error checking.
You are advised to read the source code of the following functions in
date_time_utils.ml} if you intend to use them
- to ensure they behave as you expect.
val jd_of_date : Date.t -> int
val jd_span_of_unix_epoch : Span.t
val jd_of_iso_week_date : year:int -> week:int -> weekday:weekday -> int
val weekday_of_jd : int -> weekday
val iso_week_date_of_jd : int -> int * int * weekday
+Utils (timedesc.Timedesc.Utils) Module Timedesc.Utils
val ptime_span_of_span : Span.t -> Ptime.span option
val ptime_of_timestamp : timestamp -> Ptime.t option
val span_of_ptime_span : Ptime.span -> Span.t
Warning: Subnanosecond information is lost in this conversion
val timestamp_of_ptime : Ptime.t -> timestamp
Warning: Subnanosecond information is lost in this conversion
Month utils
val human_int_of_month : month -> int
This yields the usual human readable numbering of 1 to 12 inclusive
val index_of_month : month -> int
This yields the index based numbering of 0 to 11 inclusive
val month_of_human_int : int -> month option
val month_of_index : int -> month option
val weekday_of_tm_int : int -> weekday option
val tm_int_of_weekday : weekday -> int
val weekday_of_iso_int : int -> weekday option
val iso_int_of_weekday : weekday -> int
val get_local_tz_for_arg : unit -> Time_zone.t
val abbr_string_of_weekday : weekday -> string
Warning: Following functions are direct applications of the relevant formulas with little to no error checking.
You are advised to read the source code of the following functions in date_time_utils.ml
if you intend to use them to ensure they behave as you expect.
val jd_of_date : Date.t -> int
val jd_span_of_unix_epoch : Span.t
val jd_of_iso_week_date : year:int -> week:int -> weekday:weekday -> int
val weekday_of_jd : int -> weekday
val iso_week_date_of_jd : int -> int * int * weekday
diff --git a/timedesc/Timedesc/index.html b/timedesc/Timedesc/index.html
index b4738403..2b4b6697 100644
--- a/timedesc/Timedesc/index.html
+++ b/timedesc/Timedesc/index.html
@@ -1,5 +1,5 @@
-Timedesc (timedesc.Timedesc) Module Timedesc
Time description and manipulations
Timedesc provides utilities to describe points of time, and properly handle calendar and time zone information.
Tutorial
Getting started
Suppose we want to get the time right now, we can simply do Timedesc.now ()
. But what if we want to get the time right now in a different time zone? Say New York? Then we can simply do:
Timedesc.now ~tz_of_date_time:(Timedesc.Time_zone.make_exn "America/New_York") ()
.
And if we want to construct a date time from scratch, we can use constructors such as make
, with similar time zone specification:
Timedesc.make ~tz:(Timedesc.Time_zone.make_exn "Australia/Sydney") ~year:2021 ~month:5 ~day:30 ~hour:14 ~minute:10 ~second:0 ()
.
Since we deal with timestamps quite frequently, lets have a look at how Timedesc also makes working with them easier. Suppose we receive a timestamp similar to the result returned by Unix.gettimeofday
, i.e. seconds since unix epoch in float
, we can digest it in myriad ways. If we just want to construct a date time out of it, then we can use of_timestamp_float_s
. If we want to get it into the representation used in Timedesc, say to perform arithmetic operations over it etc, then we can use Timestamp.of_float_s
. But in either case, we can always swap back and forth via to_timestamp
and of_timestamp
.
In general it is better to use Timestamp as much as possible, unless you require a precision higher than nanosecond. This is because floating point is a lossy representation - if you convert a date time to floating point and back, you may not get the same date time back (i.e. it may not round trip). Also, performing arithmetic operations over floating points can introduce more and more errors, and it is advisable to use the arithmetic functions provided in Span
or Timestamp
.
To access the values of date time, we can use the constructors such as year
, month
, day
, hour
.
Time zone
By now, one nicety should be obvious: you don't have to worry about what is the time zone offset at when and where - Timedesc takes care of that for you properly! All you have to do is to make a time zone following the *nix naming convention. However, even though we follow the same naming convention, we don't actually rely on the OS time zone database, and our code will run fine on any platform.
To see what time zones Timedesc supports during run time, we can refer to Time_zone.available_time_zones
. Alternatively, for a text file containing all the supported time zones by default, refer to available-time-zones.txt
in the repository.
If you are aware of DST: Yes, Timedesc takes care of that for you properly as well - Timedesc does not allow you to construct a date time that does not exist for the particular time zone, and any ambiguity is made explicit as return type via local_dt_result
.
This does mean Timedesc does not "resolve" the result into one of the possibilities arbitrarily, and you need to resolve the ambiguity yourself. If such a coercion is desirable, however, then you can use either min_of_local_dt_result
or max_of_local_dt_result
.
Span/duration
Timedesc offers both machine-friendly and human-friendly ways of dealing with spans.
For the machine-friendly side, functions in the top level of Span
provide efficient constructions and arithmetic operations.
For the human-friendly side, Span.For_human
provides functions which work at a level closer to human language. For instance, we say things like "2 hours and 15 minutes" quite frequently, to represent this as Span.t
, we can do:
Timedesc.Span.For_human.make_exn ~hours:2 ~minutes:15 ()
And in the case of fractional descriptions, such as "1.5 hours", we can do:
Timedesc.Span.For_human.make_frac_exn ~hours:1.5 ()
Finally, to access the human friendly "view", we can use Span.For_human.view
.
Using both Ptime and Timedesc
Ptime is a (very) commonly used package in projects due to being very portable, and robust. However, it lacks certain features which Timedesc provides, such as first class support for time zones, support for different date systems. As such one may wish to use both Ptime and Timedesc, especially if Ptime is already being used for a particular project.
To facilitate such use of both Ptime and Timedesc, utilities for converting to and from Ptime types are available as:
Note that Timedesc only supports nanosecond precision, while Ptime supports picosecond precision. If subnanosecond precision is a concern for you, then the above functions are not suitable.
Advanced usage
Unambiguous date time
Occasionally, we receive date times which carry both the time zone and the exact offset from UTC. Naturally we can discard the time zone since the offset alone suffices in deducing the precise timestamp. However, we can actually ask Timedesc to digest both via make_unambiguous
, which checks the offset against the time zone record to make sure it is actually a possible offset.
Other calendar systems
Other than Gregorian calendar, Timedesc also supports ISO week date and ISO ordinal date.
To construct date time in the alternative systems, we can use constructors such as ISO_week_date_time.make
and ISO_ord_date_time.make
.
Then to access the representation in the alternative date systems, we can use accessors such as iso_week
, and day_of_year
.
Using date by itself
Sometimes we are only interested in the date component rather than both date and time. We can use Date
module in this case.
To construct a Gregorian calendar date, we can use Date.Ymd_date.make
. To construct ISO week date and ISO ordinal date, we can use Date.ISO_week_date.make
and Date.ISO_ord
respectively.
We have similar set of accessors for accessing values of Date.t
, such as Date.year
, Date.iso_week
, Date.day_of_year
.
To obtain a "view" (in a manner similar to the human-friendly "view" from Span.For_human
), we can use Date.ISO_week_date.view
and Date.ISO_ord.view
.
Further reading
Misconceptions
- Time zone offsets are always in hours
- What we typically consider a time zone, e.g. "Europe/Paris", always has a constant offset
- With a time zone and a specific date time, we can always obtain a unique "unix timestamp" (time since unix epoch)
- We can always calculate time zone offset at some date time, and apply it universally for any other date time in the same time zone
- Many more on various online resources...
Time zone, time zone offset, and date time
It is tempting to think that a time zone maps cleanly to a constant offset, and indeed we may define time zone as such, e.g. UTC+1, UTC-10, but this is far from what we mean in everyday context.
Very often, what we consider to be time zone actually represents a table which records what offset to use in which period, which we index/refer to by geographical names like "Europe/Paris", "Australia/Sydney". These tables are defined by governmental bodies, and attributes of the table, such as offset of any particular period, start and end of any particular period, may not show any observable pattern.
Thus it is not uncommon to see date time errors arising from attempts of applying some formulas universally, which might work well for a lot of cases in contemporary time periods, but fail for some combinations.
We make explicit of above explanation by considering "Europe/Paris" as an example, which observes a common form of transition called Daylight Saving Time (DST).
When DST starts (usually in March), the clocks "jump forward" by 1 hour, usually jumping from 2am to 3am, leading 2am to 3am (exclusive) to become non-existent.
Indeed we can observe the lack of continuity of Europe/Paris timeline below (UTC timeline is always continuous):
Mar
+Timedesc (timedesc.Timedesc) Module Timedesc
Time description and manipulations
Timedesc provides utilities to describe points of time, and properly handle calendar and time zone information.
Tutorial
Getting started
Suppose we want to get the time right now, we can simply do Timedesc.now ()
. But what if we want to get the time right now in a different time zone? Say New York? Then we can simply do:
Timedesc.now ~tz_of_date_time:(Timedesc.Time_zone.make_exn "America/New_York") ()
.
And if we want to construct a date time from scratch, we can use constructors such as make
, with similar time zone specification:
Timedesc.make ~tz:(Timedesc.Time_zone.make_exn "Australia/Sydney") ~year:2021 ~month:5 ~day:30 ~hour:14 ~minute:10 ~second:0 ()
.
Since we deal with timestamps quite frequently, lets have a look at how Timedesc also makes working with them easier. Suppose we receive a timestamp similar to the result returned by Unix.gettimeofday
, i.e. seconds since unix epoch in float
, we can digest it in myriad ways. If we just want to construct a date time out of it, then we can use of_timestamp_float_s
. If we want to get it into the representation used in Timedesc, say to perform arithmetic operations over it etc, then we can use Timestamp.of_float_s
. But in either case, we can always swap back and forth via to_timestamp
and of_timestamp
.
In general it is better to use timestamp
as much as possible, unless you require a precision higher than nanosecond. This is because floating point is a lossy representation - if you convert a date time to floating point and back, you may not get the same date time back (i.e. it may not round trip). Also, performing arithmetic operations over floating points can introduce more and more errors, and it is advisable to use the arithmetic functions provided in Span
or Timestamp
.
To access the values of date time, we can use the constructors such as year
, month
, day
, hour
.
Time zone
By now, one nicety should be obvious: you don't have to worry about what is the time zone offset at when and where - Timedesc takes care of that for you properly! All you have to do is to make a time zone following the *nix naming convention. However, even though we follow the same naming convention, we don't actually rely on the OS time zone database, and our code will run fine on any platform.
To see what time zones Timedesc supports during run time, we can refer to Time_zone.available_time_zones
. Alternatively, for a text file containing all the supported time zones by default, refer to available-time-zones.txt
in the repository.
If you are aware of DST: Yes, Timedesc takes care of that for you properly as well - Timedesc does not allow you to construct a date time that does not exist for the particular time zone, and any ambiguity is made explicit as return type via local_dt_result
.
This does mean Timedesc does not "resolve" the result into one of the possibilities arbitrarily, and you need to resolve the ambiguity yourself. If such a coercion is desirable, however, then you can use either min_of_local_dt_result
or max_of_local_dt_result
.
Span/duration
Timedesc offers both machine-friendly and human-friendly ways of dealing with spans.
For the machine-friendly side, functions in the top level of Span
provide efficient constructions and arithmetic operations.
For the human-friendly side, Span.For_human
provides functions which work at a level closer to human language. For instance, we say things like "2 hours and 15 minutes" quite frequently, to represent this as Span.t
, we can do:
Timedesc.Span.For_human.make_exn ~hours:2 ~minutes:15 ()
And in the case of fractional descriptions, such as "1.5 hours", we can do:
Timedesc.Span.For_human.make_frac_exn ~hours:1.5 ()
Finally, to access the human friendly "view", we can use Span.For_human.view
.
Using both Ptime and Timedesc
Ptime is a (very) commonly used package in projects due to being very portable, and robust. However, it lacks certain features which Timedesc provides, such as first class support for time zones, support for different date systems. As such one may wish to use both Ptime and Timedesc, especially if Ptime is already being used for a particular project.
To facilitate such use of both Ptime and Timedesc, utilities for converting to and from Ptime types are available as:
Note that Timedesc only supports nanosecond precision, while Ptime supports picosecond precision. If subnanosecond precision is a concern for you, then the above functions are not suitable.
Advanced usage
Unambiguous date time
Occasionally, we receive date times which carry both the time zone and the exact offset from UTC. Naturally we can discard the time zone since the offset alone suffices in deducing the precise timestamp. However, we can actually ask Timedesc to digest both via make_unambiguous
, which checks the offset against the time zone record to make sure it is actually a possible offset.
Other calendar systems
Other than Gregorian calendar, Timedesc also supports ISO week date and ISO ordinal date.
To construct date time in the alternative systems, we can use constructors such as ISO_week_date_time.make
and ISO_ord_date_time.make
.
Then to access the representation in the alternative date systems, we can use accessors such as iso_week
, and day_of_year
.
Using date by itself
Sometimes we are only interested in the date component rather than both date and time. We can use Date
module in this case.
To construct a Gregorian calendar date, we can use Date.Ymd.make
. To construct ISO week date and ISO ordinal date, we can use Date.ISO_week_date.make
and Date.ISO_ord.make
respectively.
We have similar set of accessors for accessing values of Date.t
, such as Date.year
, Date.iso_week
, Date.day_of_year
.
To obtain a "view" (in a manner similar to the human-friendly "view" from Span.For_human
), we can use Date.ISO_week_date.view
and Date.ISO_ord.view
.
Further reading
Misconceptions
- Time zone offsets are always in hours
- What we typically consider a time zone, e.g. "Europe/Paris", always has a constant offset
- With a time zone and a specific date time, we can always obtain a unique "unix timestamp" (time since unix epoch)
- We can always calculate time zone offset at some date time, and apply it universally for any other date time in the same time zone
- Many more on various online resources...
Time zone, time zone offset, and date time
It is tempting to think that a time zone maps cleanly to a constant offset, and indeed we may define time zone as such, e.g. UTC+1, UTC-10, but this is far from what we mean in everyday context.
Very often, what we consider to be time zone actually represents a table which records what offset to use in which period, which we index/refer to by geographical names like "Europe/Paris", "Australia/Sydney". These tables are defined by governmental bodies, and attributes of the table, such as offset of any particular period, start and end of any particular period, may not show any observable pattern.
Thus it is not uncommon to see date time errors arising from attempts of applying some formulas universally, which might work well for a lot of cases in contemporary time periods, but fail for some combinations.
We make explicit of above explanation by considering "Europe/Paris" as an example, which observes a common form of transition called Daylight Saving Time (DST).
When DST starts (usually in March), the clocks "jump forward" by 1 hour, usually jumping from 2am to 3am, leading 2am to 3am (exclusive) to become non-existent.
Indeed we can observe the lack of continuity of Europe/Paris timeline below (UTC timeline is always continuous):
Mar
UTC -------------|-------------
1am
@@ -35,7 +35,7 @@
minute:int ->
second:int ->
unit ->
- (t, error) Stdlib.result
Constructs a date time providing only a time zone (defaults to local time zone).
A precise offset is inferred if possible.
Note that this may yield a ambiguous date time if the time zone has varying offsets, causing a local date time to appear twice, e.g. countries with DST.
See make_unambiguous
for the more precise construction.
See Date.Ymd_date.make
for error handling of date specification.
See Time.make
for error handling of time of day specification.
Constructs a date time providing only a time zone (defaults to local time zone).
A precise offset is inferred if possible.
Note that this may yield a ambiguous date time if the time zone has varying offsets, causing a local date time to appear twice, e.g. countries with DST.
See make_unambiguous
for the more precise construction.
See Date.Ymd.make
for error handling of date specification.
See Time.make
for error handling of time of day specification.
val make_exn :
?tz:Time_zone.t ->
?ns:int ->
?s_frac:float ->
diff --git a/timere/Timere/index.html b/timere/Timere/index.html
index 667458a3..5e0df490 100644
--- a/timere/Timere/index.html
+++ b/timere/Timere/index.html
@@ -24,14 +24,7 @@
&& (hour of dt is in p.hours or p.hour_ranges)
&& (minute of dt is in p.minutes or p.minute_ranges)
&& (second of dt is in p.seconds or p.second_ranges)
-&& (ns of dt is in p.ns or p.ns_ranges)Empty pattern levels are treated as wildcard, e.g. if p.years
and p.year_ranges
are both empty, then (dt.year is in p.years or p.year_ranges)
is true
.
val iso_week_pattern :
- ?years:int list ->
- ?year_ranges:int range list ->
- ?weeks:int list ->
- ?week_ranges:int range list ->
- unit ->
- t
Pattern matches over ISO week date times.
A pattern p
matches ISO week date time dt
if
(year of dt is in p.years or p.year_ranges)
-&& (week of dt is in p.weeks or p.week_ranges)
Empty pattern levels are treated as wildcard, e.g. if p.years
and p.year_ranges
are both empty, then (dt.year is in p.years or p.year_ranges)
is true
.
val years : int list -> t
years l
is a shorthand for pattern ~years:l ()
val iso_years : int list -> t
iso_years l
is a shorthand for iso_week_pattern ~years:l ()
iso_year_ranges l
is a shorthand for iso_week_pattern ~year_ranges:l ()
val months : int list -> t
months l
is a shorthand for pattern ~months:l ()
val iso_weeks : int list -> t
iso_weeks l
is a shorthand for iso_week_pattern ~weeks:l ()
iso_week_ranges l
is a shorthand for iso_week_pattern ~week_ranges:l ()
val days : int list -> t
days l
is a shorthand for pattern ~month_days:l ()
val weekdays : Timedesc.weekday list -> t
weekdays l
is a shorthand for pattern ~weekdays:l ()
val weekday_ranges : Timedesc.weekday range list -> t
weekday_ranges l
is a shorthand for pattern ~weekday_ranges:l ()
val hours : int list -> t
hours l
is a shorthand for pattern ~hours:l ()
val minutes : int list -> t
minutes l
is a shorthand for pattern ~minutes:l ()
minute_ranges l
is a shorthand for pattern ~minute_ranges:l ()
val seconds : int list -> t
seconds l
is a shorthand for pattern ~seconds:l ()
second_ranges l
is a shorthand for pattern ~second_ranges:l ()
val ns : int list -> t
ns l
is a shorthand for pattern ~ns:l ()
val nth_weekday_of_month : int -> Timedesc.weekday -> t
nth_weekday_of_month n wday
picks the nth weekday of all months, where 1 <= n && n <= 5
Intervals
Explicit intervals
val intervals : ?skip_invalid:bool -> Timedesc.Interval.t list -> t
intervals l
skip_invalid
defaults to false
val interval_seq : ?skip_invalid:bool -> Timedesc.Interval.t Stdlib.Seq.t -> t
interval_seq s
skip_invalid
defaults to false
val sorted_intervals : ?skip_invalid:bool -> Timedesc.Interval.t list -> t
sorted_intervals l
skip_invalid
defaults to false
val years : int list -> t
years l
is a shorthand for pattern ~years:l ()
val months : int list -> t
months l
is a shorthand for pattern ~months:l ()
val days : int list -> t
days l
is a shorthand for pattern ~month_days:l ()
val weekdays : Timedesc.weekday list -> t
weekdays l
is a shorthand for pattern ~weekdays:l ()
val weekday_ranges : Timedesc.weekday range list -> t
weekday_ranges l
is a shorthand for pattern ~weekday_ranges:l ()
val hours : int list -> t
hours l
is a shorthand for pattern ~hours:l ()
val minutes : int list -> t
minutes l
is a shorthand for pattern ~minutes:l ()
minute_ranges l
is a shorthand for pattern ~minute_ranges:l ()
val seconds : int list -> t
seconds l
is a shorthand for pattern ~seconds:l ()
second_ranges l
is a shorthand for pattern ~second_ranges:l ()
val ns : int list -> t
ns l
is a shorthand for pattern ~ns:l ()
val nth_weekday_of_month : int -> Timedesc.weekday -> t
nth_weekday_of_month n wday
picks the nth weekday of all months, where 1 <= n && n <= 5
Intervals
Explicit intervals
val intervals : ?skip_invalid:bool -> Timedesc.Interval.t list -> t
intervals l
skip_invalid
defaults to false
val interval_seq : ?skip_invalid:bool -> Timedesc.Interval.t Stdlib.Seq.t -> t
interval_seq s
skip_invalid
defaults to false
val sorted_intervals : ?skip_invalid:bool -> Timedesc.Interval.t list -> t
sorted_intervals l
skip_invalid
defaults to false
val sorted_interval_seq :
?skip_invalid:bool ->
Timedesc.Interval.t Stdlib.Seq.t ->
t
sorted_interval_seq s
skip_invalid
defaults to false
Pattern matching intervals
Pattern matching intervals are designed to handle intervals where start and end points follow some pattern, but cannot be captured by pattern
efficiently, e.g. you cannot represent "5:30pm to 6:11pm" via a single pattern
module Points : sig ... end
type points = Points.t