Feel.Type.Moment (feel v0.1.0)

Copy Markdown View Source

The numbers that stand for a time and for a date and time in a set (D-109).

A date and a duration are ordered as a number is, and the number of a value is its image (Feel.Type.Image). A time and a date and time are not: the order of two of them depends on their zones (Feel.Runtime.Temporal.compare/3). When either has no zone their local clocks are compared, and when both have a zone (an offset or a zone id) their instants are, at the second (D-54: 10:30:00.0001 and 10:30:00.0002 are equal). A value with no zone has one number to compare, its clock; one with a zone has two, its instant and its clock, and which one a comparison needs depends on the other side.

So the image of a value is the whole second it falls in, and a set has three lines of them, far apart on the number line (each within 3.2E+16 of its centre, the centres 1E+17 apart):

LineOfIts numbersWritten
the clockthe values with no zonethe seconds of the local clock since 0000-01-01T00:00:00; for a time the seconds of the day, 0 to 86,3992021-01-31T10:30:00
the instantsthe values with a zonethe seconds of the instant since 0000-01-01T00:00:00Z; for a time the seconds of the day less the offset, as the run orders times (not wrapped: 00:30:00+01:00 is -1,800)2021-01-31T09:30:00Z
the zoned clockthe values with a zonethe seconds of the local clock, as the firstzoned clock 2021-01-31T10:30:00

A fourth line holds the fraction of the second (D-118): the images are whole seconds, and a value has a fraction of one past it, .5 for 10:00:00.5. It is the same for the values of every zone, so the set has the one line of them: the seconds [0..1) that a value can have, which are all of them when nothing is known (a range of allowed values has any), {0} for a value of whole seconds, and so on. A comparison does not look at it (the run compares at the second); the sum with a fraction, the difference, the second and the value do (Feel.Analysis.MomentArithmetic, Feel.Analysis.MomentZones, Feel.Analysis.MomentProperties). It is on the number line at 3E+17, and a set that has none has every fraction.

A set holds the values of the first line, and those of a zone that have their instant in the second and their clock in the third: a set of three lines is a box, and a value of a zone is in it when both its numbers are. The clock of an instant is the instant and an offset, and an offset is within 18 hours (a fixed offset is within 14, and the zones of the time zone database had a local mean time of up to 15 hours 56 minutes, Asia/Manila before 1844), so each of the two lines is within that of the other, and normalize/2 cuts each to that. A set with a line of the instants and none of the zoned clock has no value with a zone, and neither is kept.

iex> {:ok, date_time} = Feel.Value.DateTime.parse("2021-01-01T00:00:00")
iex> {:ok, [image]} = Feel.Type.Moment.images(date_time)
iex> Feel.Type.Moment.format_value(:date_and_time, image)
"2021-01-01T00:00:00"
iex> {:ok, date_time} = Feel.Value.DateTime.parse("2021-01-01T00:00:00+01:00")
iex> {:ok, [instant, clock]} = Feel.Type.Moment.images(date_time)
iex> {Feel.Type.Moment.format_value(:date_and_time, instant), Feel.Type.Moment.format_value(:date_and_time, clock)}
{"2020-12-31T23:00:00Z", "2021-01-01T00:00:00"}

Summary

Types

A kind whose sets are sets of moments.

The three lines of a set, each as the seconds it holds (split/1).

Functions

The first and the last second of the clock of a kind: of the calendar for a date and time, of a day for a time.

The values of a kind that a set leaves out (D-152), as a set: every one of them, and no other where a set of lines can say it.

Whether a value is one of a set: its seconds are on the lines, and its fraction is one of the fractions.

A set with every number within some seconds of one of its own, ends included: what the clock of a value can be when its instant is in the set and its offset is within that many seconds, and the other way round.

The images of the values of a kind with no zone: the first line.

Writes the set of a kind of moments after the name of the kind: = 2021-01-31T10:30:00+01:00 for the one value of a zone, which is written with its offset, and in and the lines of it otherwise: those with no zone as they are, the instants of the values with a zone with a Z, and their clocks as zoned clock (left out when the instants say as much, and when the clocks are those of the values with no zone, any zone takes their place).

Writes the value that an image stands for: a clock as 2021-01-31T10:30:00, an instant in UTC as 2021-01-31T09:30:00Z; a time as 10:30:00 and 09:30:00Z. The clock of a zoned value is written as a clock. A time on the line of the instants is not wrapped, so one before midnight UTC is written with a minus (-00:30:00Z) and one after it past 24 hours (24:30:00Z). A number that is none of these is written as it is.

The fraction of a second of a value, exactly: 0.5 for 10:00:00.5.

The fractions of a second that any value can have: [0..1).

The fractions of a second that the values of a set can have, as numbers in [0..1). A set that has no line of them has every fraction.

The images of a time or a date and time: {:ok, [clock]} for one with no zone, and {:ok, [instant, zoned_clock]} for one with a zone. It is :error for a value that is not one of FEEL (a zone that is not in the database, a year out of range).

The set of three lines, as split/1 gives them: the inverse.

The kinds whose sets are sets of moments: :time and :date_and_time.

The largest offset of a zone from UTC, in seconds (18 hours).

The set cut to the values of the kind there are, with its lines of a zone made to agree: the instants within the largest offset of the zoned clocks and the zoned clocks within it of the instants, and neither if the other is none.

The most that the instant of a date and time, as the run reads it, can be below the instant it was moved to, for instants from the one given: 2 hours from 2023-03-09, and 25 hours before it.

The set of the one value of a time or a date and time: its lines and its fraction. It is :error for a value that is not one of FEEL.

A set moved by a number of seconds: every end of every interval.

A set moved by whole seconds with no rounding: a fraction of a second is as long as a second can be, and the arithmetic of Decimal rounds to 28 digits.

The three lines of a set, each as the seconds it holds, with the numbers of the second and third taken back to the seconds they are: {clock, instants, zoned_clock}.

The union of two sets of one kind, line by line: a line of more than 16 intervals is made its own hull, and not the hull of the lines together, which a Feel.Type.NumberSet of that many would be.

The images of every value of a kind, on the three lines.

The three lines of the universe of a kind, as the seconds they hold.

The set with the fractions of a second given (a set of numbers in [0..1)), in place of the ones it had. An empty set of fractions leaves the set with every fraction.

Types

kind()

@type kind() :: :time | :date_and_time

A kind whose sets are sets of moments.

lines()

The three lines of a set, each as the seconds it holds (split/1).

Functions

clock_range(atom)

@spec clock_range(kind()) :: {integer(), integer()}

The first and the last second of the clock of a kind: of the calendar for a date and time, of a day for a time.

iex> Feel.Type.Moment.clock_range(:time)
{0, 86399}

complement(set, kind)

@spec complement(Feel.Type.NumberSet.t(), kind()) :: Feel.Type.NumberSet.t()

The values of a kind that a set leaves out (D-152), as a set: every one of them, and no other where a set of lines can say it.

A value with no zone is left out when the set has not its clock. A value with a zone is in the set when its instant is on the second line and its clock on the third, so it is left out when its instant is not, or its clock: two boxes, and a set is one. The set that holds both has the instants that the set leaves out and those within the largest offset of the clocks that it leaves out, and the clocks likewise. It holds a value that the set holds as well only if an instant and a clock that it leaves out are both there and near each other, so the answer is exact when the set holds a line whole, when it holds no value with a zone, and when its instants are what its clocks give within the largest offset (which is how a bound on a time of any zone is made: time >= 10:00:00). Otherwise it holds more.

The fractions of a second are one line for the values of every zone, and a set of some of them leaves values out on every other line, which is no box. For that, and for a set that holds no value, the answer is every value of the kind.

iex> alias Feel.Type.{Moment, NumberSet, Union}
iex> morning = Moment.join({NumberSet.interval(Decimal.new(0), Decimal.new(36_000)), [], []})
iex> Union.format([{:time, Moment.complement(morning, :time)}])
"time in (10:00:00..23:59:59], instants [-18:00:00Z..41:59:59Z]"
iex> Moment.complement(Moment.universe(:time), :time)
[]
iex> {:ok, time} = Feel.Value.Time.parse("10:00:00.5")
iex> {:ok, point} = Moment.point(time)
iex> Moment.complement(point, :time) == Moment.universe(:time)
true

contains?(set, value)

Whether a value is one of a set: its seconds are on the lines, and its fraction is one of the fractions.

dilate(set, seconds)

A set with every number within some seconds of one of its own, ends included: what the clock of a value can be when its instant is in the set and its offset is within that many seconds, and the other way round.

iex> set = Feel.Type.NumberSet.point(Decimal.new(100))
iex> Feel.Type.NumberSet.format(Feel.Type.Moment.dilate(set, 10))
"[90..110]"

floating(kind)

@spec floating(kind()) :: Feel.Type.NumberSet.t()

The images of the values of a kind with no zone: the first line.

format_member(kind, set)

@spec format_member(kind(), Feel.Type.NumberSet.t()) :: String.t()

Writes the set of a kind of moments after the name of the kind: = 2021-01-31T10:30:00+01:00 for the one value of a zone, which is written with its offset, and in and the lines of it otherwise: those with no zone as they are, the instants of the values with a zone with a Z, and their clocks as zoned clock (left out when the instants say as much, and when the clocks are those of the values with no zone, any zone takes their place).

iex> {:ok, date_time} = Feel.Value.DateTime.parse("2021-01-31T10:30:00+01:00")
iex> Feel.Type.Union.format(Feel.Type.Union.of_value(date_time))
"date and time = 2021-01-31T10:30:00+01:00"

format_value(kind, number)

@spec format_value(kind(), Decimal.t()) :: String.t()

Writes the value that an image stands for: a clock as 2021-01-31T10:30:00, an instant in UTC as 2021-01-31T09:30:00Z; a time as 10:30:00 and 09:30:00Z. The clock of a zoned value is written as a clock. A time on the line of the instants is not wrapped, so one before midnight UTC is written with a minus (-00:30:00Z) and one after it past 24 hours (24:30:00Z). A number that is none of these is written as it is.

iex> Feel.Type.Moment.format_value(:time, Decimal.new(37_800))
"10:30:00"
iex> Feel.Type.Moment.format_value(:time, Decimal.new(100_000_000_000_000_000 - 1800))
"-00:30:00Z"

fraction(arg1)

The fraction of a second of a value, exactly: 0.5 for 10:00:00.5.

iex> {:ok, time} = Feel.Value.Time.parse("10:00:00.5")
iex> Feel.Type.Moment.fraction(time)
Decimal.new("0.5")

fraction_universe()

@spec fraction_universe() :: Feel.Type.NumberSet.t()

The fractions of a second that any value can have: [0..1).

fractions(set)

The fractions of a second that the values of a set can have, as numbers in [0..1). A set that has no line of them has every fraction.

iex> alias Feel.Type.{Moment, NumberSet}
iex> {:ok, time} = Feel.Value.Time.parse("10:00:00.5")
iex> {:ok, set} = Moment.point(time)
iex> NumberSet.format(Moment.fractions(set))
"0.5"
iex> NumberSet.format(Moment.fractions(NumberSet.point(Decimal.new(36_000))))
"[0..1)"

images(date_time)

@spec images(Feel.Value.Time.t() | Feel.Value.DateTime.t()) ::
  {:ok, [Decimal.t(), ...]} | :error

The images of a time or a date and time: {:ok, [clock]} for one with no zone, and {:ok, [instant, zoned_clock]} for one with a zone. It is :error for a value that is not one of FEEL (a zone that is not in the database, a year out of range).

iex> {:ok, time} = Feel.Value.Time.parse("10:30:00.5")
iex> Feel.Type.Moment.images(time)
{:ok, [Decimal.new(37_800)]}
iex> {:ok, time} = Feel.Value.Time.parse("00:30:00+01:00")
iex> {:ok, [instant, clock]} = Feel.Type.Moment.images(time)
iex> {Decimal.sub(instant, Decimal.new(100_000_000_000_000_000)), Decimal.sub(clock, Decimal.new(200_000_000_000_000_000))}
{Decimal.new(-1800), Decimal.new(1800)}

join(arg)

@spec join(lines()) :: Feel.Type.NumberSet.t()

The set of three lines, as split/1 gives them: the inverse.

kinds()

@spec kinds() :: [kind(), ...]

The kinds whose sets are sets of moments: :time and :date_and_time.

max_offset()

@spec max_offset() :: pos_integer()

The largest offset of a zone from UTC, in seconds (18 hours).

normalize(set, kind)

@spec normalize(Feel.Type.NumberSet.t(), kind()) :: Feel.Type.NumberSet.t()

The set cut to the values of the kind there are, with its lines of a zone made to agree: the instants within the largest offset of the zoned clocks and the zoned clocks within it of the instants, and neither if the other is none.

iex> alias Feel.Type.{Moment, NumberSet}
iex> {clock, instants, _} = Moment.universe_lines(:time)
iex> set = Moment.join({[], instants, NumberSet.interval(Decimal.new(0), Decimal.new(3600))})
iex> {_, instants, zoned_clock} = set |> Moment.normalize(:time) |> Moment.split()
iex> {NumberSet.format(instants), NumberSet.format(zoned_clock)}
{"[-64800..68400]", "[0..3600]"}
iex> set |> Moment.join_without_clocks() |> Moment.normalize(:time) |> Moment.split()
{[], [], []}
iex> NumberSet.format(clock)
"[0..86399]"

overlap(from)

@spec overlap(integer()) :: pos_integer()

The most that the instant of a date and time, as the run reads it, can be below the instant it was moved to, for instants from the one given: 2 hours from 2023-03-09, and 25 hours before it.

iex> Feel.Type.Moment.overlap(63_845_596_800)
7200
iex> Feel.Type.Moment.overlap(0)
90000

point(value)

@spec point(Feel.Value.Time.t() | Feel.Value.DateTime.t()) ::
  {:ok, Feel.Type.NumberSet.t()} | :error

The set of the one value of a time or a date and time: its lines and its fraction. It is :error for a value that is not one of FEEL.

iex> {:ok, time} = Feel.Value.Time.parse("10:00:00.5")
iex> {:ok, set} = Feel.Type.Moment.point(time)
iex> Feel.Type.Union.format([{:time, set}])
"time = 10:00:00.5"

shift(set, seconds)

A set moved by a number of seconds: every end of every interval.

shift_exact(set, delta)

@spec shift_exact(Feel.Type.NumberSet.t(), integer()) :: Feel.Type.NumberSet.t()

A set moved by whole seconds with no rounding: a fraction of a second is as long as a second can be, and the arithmetic of Decimal rounds to 28 digits.

iex> alias Feel.Type.{Moment, NumberSet}
iex> set = NumberSet.point(Decimal.new("0.123456789012345678901234567890"))
iex> NumberSet.format(Moment.shift_exact(set, 5))
"5.12345678901234567890123456789"

split(set)

@spec split(Feel.Type.NumberSet.t()) :: lines()

The three lines of a set, each as the seconds it holds, with the numbers of the second and third taken back to the seconds they are: {clock, instants, zoned_clock}.

iex> alias Feel.Type.{Moment, NumberSet}
iex> set = NumberSet.new([{{Decimal.new(5), :closed}, {Decimal.new(5), :closed}}, {{Decimal.new(100_000_000_000_003_600), :closed}, {Decimal.new(100_000_000_000_003_600), :closed}}])
iex> {clock, instants, zoned_clock} = Moment.split(set)
iex> {NumberSet.format(clock), NumberSet.format(instants), zoned_clock}
{"5", "3600", []}

union(a, b)

The union of two sets of one kind, line by line: a line of more than 16 intervals is made its own hull, and not the hull of the lines together, which a Feel.Type.NumberSet of that many would be.

universe(kind)

@spec universe(kind()) :: Feel.Type.NumberSet.t()

The images of every value of a kind, on the three lines.

iex> Feel.Type.NumberSet.format(Feel.Type.Moment.floating(:time))
"[0..86399]"

universe_lines(kind)

@spec universe_lines(kind()) :: lines()

The three lines of the universe of a kind, as the seconds they hold.

with_fractions(set, fractions)

The set with the fractions of a second given (a set of numbers in [0..1)), in place of the ones it had. An empty set of fractions leaves the set with every fraction.