# `Feel.Value.Format`
[🔗](https://github.com/FeeJai/BDM/blob/feel-v0.1.0/apps/feel/lib/feel/value/format.ex#L1)

The text of a value, as `string(...)` writes it (DMN 1.6, section 10.3.4.1; D-56).

The specification gives one example, `string(1.1)` is `"1.1"`, and says a date, time and duration are
written in ISO 8601 (notes 06, AE-16); the TCK (model `0079-feel-string-function`, and the many
`string(...)` of the others) has the rest.

| Value | Text |
| ----- | ---- |
| a string | itself |
| a number | its digits, with its scale and no exponent: `123.45` |
| a boolean | `true`, `false` |
| a date | `2018-12-10`; a year has at least four digits (`0044`, `99999`), and `-` for a year before 0 |
| a time | `10:30:00.0001`, and its zone: `Z` for an offset of 0, `+05:00` and `-02:45:55` for another (the seconds only when it has some), `@Europe/Paris` for a zone id |
| a date and time | the date, `T`, and the time: `2018-12-10T10:30:00@Etc/UTC` |
| a days and time duration | `P1DT2H3M4.1234S`, with the parts that are not zero; `PT0S` for none |
| a years and months duration | `P2Y1M`, with the parts that are not zero; `P0M` for none |
| a list | `[1, 2, 3, "foo"]`: its elements, written as FEEL literals |
| a context | `{a: "foo", b: {bar: "baz"}}`: its entries in order (D-135), a key that is not a plain name in quotes |
| a range | `[18..21)`: a range literal (grammar rule 66), endpoints written as FEEL literals; an end that is not there is left out, with an open bracket: `[2..)`, `(..2]` (D-73) |

An element of a list or a context is written as a FEEL literal, so that the text could be read
back: a string is in quotes (with `\` and `\"` escaped), `null` is `null`, and a date, a time, a date
and time or a duration is an `@"..."` literal. A function has no text yet.

    iex> Feel.Value.Format.string(Decimal.new("123.45"))
    "123.45"
    iex> Feel.Value.Format.string([Decimal.new(1), "foo", true, nil])
    ~s([1, "foo", true, null])
    iex> Feel.Value.Format.string(%{"a" => "foo"})
    ~s({a: "foo"})
    iex> Feel.Value.Format.string(Feel.Value.Context.new([{"b", Decimal.new(1)}, {"a", "foo"}]))
    ~s({b: 1, a: "foo"})
    iex> {:ok, time} = Feel.Value.Time.parse("10:30:00.0001+05:00:01")
    iex> Feel.Value.Format.string(time)
    "10:30:00.0001+05:00:01"

# `date`

```elixir
@spec date(Date.t()) :: String.t()
```

A date: `2018-12-10`. The year has at least four digits, and a year before 0 has a `-`.

    iex> Feel.Value.Format.date(~D[2018-12-10])
    "2018-12-10"
    iex> Feel.Value.Format.date(Date.new!(-999_999_999, 12, 31))
    "-999999999-12-31"
    iex> Feel.Value.Format.date(Date.new!(44, 3, 15))
    "0044-03-15"

# `date_time`

```elixir
@spec date_time(Feel.Value.DateTime.t()) :: String.t()
```

A date and time: the date, `T`, and the time.

    iex> {:ok, date_time} = Feel.Value.DateTime.parse("2011-12-31T10:15:30@Europe/Paris")
    iex> Feel.Value.Format.date_time(date_time)
    "2011-12-31T10:15:30@Europe/Paris"

# `days_and_time`

```elixir
@spec days_and_time(Decimal.t()) :: String.t()
```

A days and time duration, from its seconds: the days, hours, minutes and seconds that are not zero, and
`PT0S` for none. The seconds keep their fraction, without its zeros at the end.

    iex> Feel.Value.Format.days_and_time(Decimal.new(93_600))
    "P1DT2H"
    iex> Feel.Value.Format.days_and_time(Decimal.new("93784.1234"))
    "P1DT2H3M4.1234S"
    iex> Feel.Value.Format.days_and_time(Decimal.new(0))
    "PT0S"

# `string`

```elixir
@spec string(Feel.Value.t()) :: String.t()
```

The text of a value of FEEL, as `string(...)` writes it. `null` has none (`string(null)` is `null`).

A function has no text, and raises `RuntimeError`: `string(...)` does not ask for one (D-82), and a list or a
context that holds a function has none either.

# `string_within`

```elixir
@spec string_within(Feel.Value.t(), non_neg_integer() | :infinity) ::
  {:ok, String.t()} | {:over, pos_integer()}
```

`string/1`, unless the text is longer than `max` bytes (`:infinity` for no cap): then `{:over, size}`, with the bytes
counted when the text passed `max`, and the rest of it is not written.

A list can hold the same string a hundred thousand times, and a context the same list: the text of such a value is
as long as the product of their sizes, and a cap that looks at the finished text sees it after it is made. The text
is written as a list of pieces and added up as it is, and the walk ends at the first piece over `max`.

    iex> Feel.Value.Format.string_within([Decimal.new(1), "foo"], 100)
    {:ok, ~s([1, "foo"])}
    iex> Feel.Value.Format.string_within([Decimal.new(1), "foo"], 8)
    {:over, 10}
    iex> Feel.Value.Format.string_within("foo", 1)
    {:ok, "foo"}

# `time`

```elixir
@spec time(Feel.Value.Time.t()) :: String.t()
```

A time: the time of day, and the zone if it has one.

    iex> {:ok, time} = Feel.Value.Time.parse("23:59:00Z")
    iex> Feel.Value.Format.time(time)
    "23:59:00Z"
    iex> {:ok, time} = Feel.Value.Time.parse("11:59:45-02:45:55")
    iex> Feel.Value.Format.time(time)
    "11:59:45-02:45:55"

# `years_and_months`

```elixir
@spec years_and_months(integer()) :: String.t()
```

A years and months duration, from its months: the years and the months that are not zero, and `P0M` for none.

    iex> Feel.Value.Format.years_and_months(25)
    "P2Y1M"
    iex> Feel.Value.Format.years_and_months(-12)
    "-P1Y"
    iex> Feel.Value.Format.years_and_months(0)
    "P0M"

---

*Consult [api-reference.md](api-reference.md) for complete listing*
