Packages
timex
0.13.5
3.7.13
3.7.12
3.7.11
3.7.9
3.7.8
3.7.7
3.7.6
3.7.5
3.7.3
3.7.2
3.7.1
3.7.0
3.6.4
3.6.3
3.6.2
retired
3.6.1
3.6.0
3.5.0
3.4.2
3.4.1
3.3.0
3.2.2
3.2.1
3.2.0
3.1.25
retired
3.1.24
3.1.23
3.1.22
3.1.21
3.1.20
3.1.19
3.1.18
3.1.17
3.1.16
3.1.15
3.1.13
3.1.12
3.1.11
3.1.10
3.1.9
3.1.8
3.1.7
3.1.6
3.1.5
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.8
3.0.7
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
2.2.1
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.0.0
1.0.2
1.0.1
1.0.0
1.0.0-rc4
1.0.0-rc3
1.0.0-rc2
1.0.0-rc1
1.0.0-pre
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.2
0.18.1
0.18.0
0.17.0
0.16.2
0.16.1
0.16.0
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.5
0.13.4
0.13.3
0.13.2
0.13.1
0.13.0
0.12.9
0.12.8
0.12.7
0.12.6
0.12.5
0.12.4
0.12.3
0.12.2
0.12.1
0.12.0
0.11.0
0.10.2
0.10.1
0.10.0
0.9.0
0.8.0
0.7.1
0.6.0
0.5.0
0.4.8
0.4.7
0.4.6
Timex is a rich, comprehensive Date/Time library for Elixir projects, with full timezone support via the :tzdata package. If you need to manipulate dates, times, datetimes, timestamps, etc., then Timex is for you!
Current section
Files
Jump to
Current section
Files
lib/parsers/dateformat/default.ex
defmodule Timex.Parsers.DateFormat.DefaultParser do
@moduledoc """
This module is responsible for parsing date strings using
the default timex formatting syntax.
See `Timex.DateFormat.Formatters.DefaultFormatter` for more info.
"""
use Timex.Parsers.DateFormat.Parser
alias Timex.Parsers.DateFormat.Directive
@doc """
The tokenizer used by this parser.
"""
defdelegate tokenize(format_string), to: Timex.Parsers.DateFormat.Tokenizers.Default
@doc """
Extracts the value for a given directive.
"""
def parse_directive(<<>>, _), do: {:error, @invalid_input}
def parse_directive(date_string, %Directive{token: token} = directive) do
{token, do_parse_directive(date_string, directive)}
end
# Special handling for fractional seconds
defp do_parse_directive(<<?., date_string::binary>>, %Directive{token: :sec_fractional} = dir) do
do_parse_directive(date_string, dir)
end
# If we attempt to parse the next character and it's not a number, return an empty string since
# fractional seconds are optional
defp do_parse_directive(<<c::utf8, _::binary>>=date_string, %Directive{token: :sec_fractional})
when not c in ?0..?9 do
{"", date_string}
end
# Numeric directives
defp do_parse_directive(date_string, %Directive{token: token, type: :numeric, pad: pad} = dir) do
date_chars = date_string |> String.to_char_list
# Drop non-numeric characters
date_chars = date_chars |> Enum.drop_while(fn c -> (c in @numerics) == false end)
# Parse padding first
padding_stripped = date_chars |> strip_padding(pad, dir.pad_type)
# Parse value
case padding_stripped do
{:error, _} = error -> error
[] -> {:error, @invalid_input}
padding_stripped ->
# Extract a numeric value up to the maximum length allowed by dir.len
chars = extract_value(padding_stripped, dir.len, @numerics)
# Convert to numeric value
len = length(chars)
padding_stripped = padding_stripped |> Enum.drop(len)
valid? = chars
|> valid_length?(token, dir.len)
|> valid_value?(token, dir.validate)
|> within_bounds?(token, dir.min, dir.max)
case valid? do
{:error, _} = error -> error
str -> {str, padding_stripped |> List.to_string}
end
end
end
defp do_parse_directive(date_string, %Directive{token: token, type: :word} = dir) do
date_chars = date_string |> String.to_char_list
# Drop leading non-alpha characters.
date_chars = date_chars |> Enum.drop_while(&(Enum.member?(@allowed_chars, &1) == false))
# Extract a word value up to the maximum length allowed by dir.len
chars = extract_value(date_chars, dir.len, @allowed_chars)
len = length(chars)
date_chars = date_chars |> Enum.drop(len)
# Validate that the word value is of the correct length
valid? = chars
|> valid_length?(token, dir.len)
|> valid_value?(token, dir.validate)
case valid? do
{:error, _} = error -> error
str -> {str, date_chars |> List.to_string}
end
end
defp do_parse_directive(date_string, %Directive{token: token, type: :match, match: match} = dir) when match != false do
date_chars = date_string |> String.to_char_list
# Drop leading non-word characters.
date_chars = date_chars |> Enum.drop_while(fn c -> (c in @word_chars) == false end)
# Extract a value up to the maximum length allowed by dir.len
chars = extract_value(date_chars, dir.len, @word_chars)
len = length(chars)
date_chars = date_chars |> Enum.drop(len)
# Validate that the value is of the correct length
valid? = chars
|> valid_length?(token, dir.len)
|> valid_value?(token, match: match)
case valid? do
{:error, _} = error -> error
str -> {str, date_chars |> List.to_string}
end
end
defp do_parse_directive(_date_string, directive) do
{:error, "Unsupported directive: #{directive |> Macro.to_string}"}
end
# Strip the padding from a char list
# If 0 is given as padding, do nothing
defp strip_padding(str, 0, _)
when is_list(str),
do: str
# If we reach the end of the input string before
# we trim all the padding, return an error.
defp strip_padding([], pad, _)
when pad > 0,
do: {:error, "Unexpected end of string!"}
# Start trimming off padding, but pass along the source string as well
defp strip_padding(str, pad, pad_type)
when is_list(str),
do: strip_padding(str, str, pad, pad_type)
# If we hit 0, return the stripped string
defp strip_padding(str, _, 0, _),
do: str
# If we hit the end of the string before the
# we trim all the padding, return an error.
defp strip_padding([], _, _, _),
do: {:error, "Unexpected end of string!"}
# Trim off leading zeros
defp strip_padding([h|rest], str, pad, :zero)
when pad > 0 and h == ?0,
do: strip_padding(rest, str, pad - 1, :zero)
# Trim off leading spaces
defp strip_padding([h|rest], str, pad, :space)
when pad > 0 and h == 32,
do: strip_padding(rest, str, pad - 1, :space)
# If the first character is not padding, and is the same
# as the source string's first character, there is no padding
# to strip.
defp strip_padding([h|_], [h|_] = str, pad, _)
when pad > 0,
do: str
# Parse a value from a char list given a max length, and
# a list of valid characters the value can be composed of
defp extract_value(str, str_len, valid_chars) when is_list(str) do
Stream.transform(str, 0, fn char, chars_taken ->
valid_char? = Enum.member?(valid_chars, char)
case {char, str_len} do
{char, :word} when valid_char? ->
{[char], chars_taken + 1}
{char, str_len} when is_number(str_len) and chars_taken < str_len and valid_char? ->
{[char], chars_taken + 1}
{char, _..hi} when chars_taken < hi and valid_char? ->
{[char], chars_taken + 1}
_ ->
{:halt, chars_taken}
end
end) |> Enum.to_list
end
end