Skip to content

Commit

Permalink
Merge pull request #136 from aai/asn1-utctime
Browse files Browse the repository at this point in the history
Added ASN.1 utcTime parsing/formatting support
  • Loading branch information
bitwalker committed Mar 19, 2016
2 parents 76b3e11 + f7131b9 commit ff4757b
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 10 deletions.
12 changes: 12 additions & 0 deletions lib/format/datetime/formatter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,18 @@ defmodule Timex.Format.DateTime.Formatter do
wday = format_token(locale, :wdshort, date, modifiers, flags, width_spec(-1, nil))
"#{wday} #{month} #{day} #{hour}:#{min}:#{sec} #{year}"
end
def format_token(locale, :asn1_utc_time, %DateTime{} = date, modifiers, _flags, _width) do
# `130305232519Z`
date = Timezone.convert(date, "UTC")
flags = [padding: :zeroes]
year = format_token(locale, :year2, date, modifiers, flags, width_spec(2..2))
month = format_token(locale, :month, date, modifiers, flags, width_spec(2..2))
day = format_token(locale, :day, date, modifiers, flags, width_spec(2..2))
hour = format_token(locale, :hour24, date, modifiers, flags, width_spec(2..2))
min = format_token(locale, :min, date, modifiers, flags, width_spec(2..2))
sec = format_token(locale, :sec, date, modifiers, flags, width_spec(2..2))
"#{year}#{month}#{day}#{hour}#{min}#{sec}Z"
end
def format_token(locale, :kitchen, %DateTime{} = date, modifiers, _flags, _width) do
# `3:25PM`
hour = format_token(locale, :hour12, date, modifiers, [], width_spec(2..2))
Expand Down
22 changes: 13 additions & 9 deletions lib/format/datetime/formatters/default.ex
Original file line number Diff line number Diff line change
Expand Up @@ -101,17 +101,21 @@ defmodule Timex.Format.DateTime.Formatters.Default do
* `{ISOord}` - `YYYY-DDD`. That is, year number, followed by the ordinal
day number (e.g. `2007-113`)
* `{ASN1:UTCtime}` - `YYMMDD<time>Z`. Full 2-digit year date and time in UTC without
separators (e.g. `070813134801Z`)
These directives provide support for miscellaneous common formats:
* `{RFC822}` - e.g. `Mon, 05 Jun 14 23:20:59 UT`
* `{RFC822z}` - e.g. `Mon, 05 Jun 14 23:20:59 Z`
* `{RFC1123}` - e.g. `Tue, 05 Mar 2013 23:25:19 GMT`
* `{RFC1123z}` - e.g. `Tue, 05 Mar 2013 23:25:19 +0200`
* `{RFC3339}` - e.g. `2013-03-05T23:25:19+02:00`
* `{RFC3339z}` - e.g. `2013-03-05T23:25:19Z`
* `{ANSIC}` - e.g. `Tue Mar 5 23:25:19 2013`
* `{UNIX}` - e.g. `Tue Mar 5 23:25:19 PST 2013`
* `{kitchen}` - e.g. `3:25PM`
* `{RFC822}` - e.g. `Mon, 05 Jun 14 23:20:59 UT`
* `{RFC822z}` - e.g. `Mon, 05 Jun 14 23:20:59 Z`
* `{RFC1123}` - e.g. `Tue, 05 Mar 2013 23:25:19 GMT`
* `{RFC1123z}` - e.g. `Tue, 05 Mar 2013 23:25:19 +0200`
* `{RFC3339}` - e.g. `2013-03-05T23:25:19+02:00`
* `{RFC3339z}` - e.g. `2013-03-05T23:25:19Z`
* `{ANSIC}` - e.g. `Tue Mar 5 23:25:19 2013`
* `{UNIX}` - e.g. `Tue Mar 5 23:25:19 PST 2013`
* `{ASN1:UTCtime}` - e.g. `130305232519Z`
* `{kitchen}` - e.g. `3:25PM`
"""
use Timex.Format.DateTime.Formatter
Expand Down
25 changes: 25 additions & 0 deletions lib/parse/datetime/parsers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,31 @@ defmodule Timex.Parse.DateTime.Parsers do
])
end
@doc """
ASN.1 UTCTime standard date/time format.
Example: `130305232519Z`
"""
def asn1_utc_time(_) do
parts = [
sequence([
year2([padding: :zeroes, min: 2, max: 2]),
month2([padding: :zeroes, min: 2, max: 2]),
day_of_month([padding: :zeroes, min: 2, max: 2])
]),
choice([
sequence([
hour24([padding: :zeroes, min: 2, max: 2]),
minute([padding: :zeroes, min: 2, max: 2]),
second([padding: :zeroes, min: 2, max: 2])
]),
sequence([
hour24([padding: :zeroes, min: 2, max: 2]),
minute([padding: :zeroes, min: 2, max: 2]),
])
])
]
sequence(parts ++ [literal(char("Z"))])
end
@doc """
Kitchen clock time format.
Example: `3:25PM`
"""
Expand Down
3 changes: 2 additions & 1 deletion lib/parse/datetime/tokenizers/default.ex
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ defmodule Timex.Parse.DateTime.Tokenizers.Default do
"ISOord", "ISOweek-day", "ISOweek", "ISOdate", "ISOtime", "ISOz", "ISO",
"ISO:Extended", "ISO:Extended:Z", "ISO:Basic", "ISO:Basic:Z", "RFC822z",
"RFC822", "RFC1123z", "RFC1123", "RFC3339z", "RFC3339", "ANSIC", "UNIX",
"kitchen"
"ASN1:UTCtime", "kitchen"
])],
&coalesce_token/1
)
Expand Down Expand Up @@ -140,6 +140,7 @@ defmodule Timex.Parse.DateTime.Tokenizers.Default do
"RFC3339z" -> Directive.get(:rfc_3339z, directive, opts)
"ANSIC" -> Directive.get(:ansic, directive, opts)
"UNIX" -> Directive.get(:unix, directive, opts)
"ASN1:UTCtime" -> Directive.get(:asn1_utc_time, directive, opts)
"kitchen" -> Directive.get(:kitchen, directive, opts)
end
end
Expand Down
2 changes: 2 additions & 0 deletions lib/parse/datetime/tokenizers/directive.ex
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ defmodule Timex.Parse.DateTime.Tokenizers.Directive do
do: %Directive{type: :unix, value: directive, flags: flags, modifiers: mods, width: width, parser: Parsers.unix(flags)}
def get(:kitchen, directive, flags, mods, width),
do: %Directive{type: :kitchen, value: directive, flags: flags, modifiers: mods, width: width, parser: Parsers.kitchen(flags)}
def get(:asn1_utc_time, directive, flags, mods, width),
do: %Directive{type: :asn1_utc_time, value: directive, flags: flags, modifiers: mods, width: width, parser: Parsers.asn1_utc_time(flags)}
def get(:slashed, directive, flags, mods, width),
do: %Directive{type: :slashed, value: directive, flags: flags, modifiers: mods, width: width, parser: Parsers.slashed(flags)}
def get(:strftime_iso_date, directive, flags, mods, width),
Expand Down
7 changes: 7 additions & 0 deletions test/format_default_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,13 @@ defmodule DateFormatTest.FormatDefault do
assert { :ok, "Tue Mar 5 23:25:19 2013" } = format(date, "{ANSIC}")
end

test "format ASN1 UTC Time" do
local = {{2013,3,5},{23,25,19}}
date = Timex.datetime(local, :utc)

assert { :ok, "130305232519Z" } = format(date, "{ASN1:UTCtime}")
end

test "format UNIX" do
local = {{2013,3,5},{23,25,19}}
date = Timex.datetime(local, :utc)
Expand Down
10 changes: 10 additions & 0 deletions test/parse_default_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,16 @@ defmodule DateFormatTest.ParseDefault do
assert { :ok, ^date } = parse("Mon Nov 16 22:23:48 UTC 2015", "{UNIX}")
end


test "parse ASN1:UTCtime" do
# * `{ASN1:UTCtime}` - e.g. `Tue Mar 5 23:25:19 2013`
date = Timex.datetime({{2009, 3, 5}, {23, 25, 19}})
assert { :ok, ^date } = parse("090305232519Z", "{ASN1:UTCtime}")

date = Timex.datetime({{2015, 11, 16}, {22, 23, 00}}, "UTC")
assert { :ok, ^date } = parse("1511162223Z", "{ASN1:UTCtime}")
end

test "parse kitchen" do
# * `{kitchen}` - e.g. `3:25PM`
date = DateTime.now |> Timex.set(hour: 15, minute: 25, second: 0, millisecond: 0)
Expand Down

0 comments on commit ff4757b

Please sign in to comment.