Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accepting a date in Phoenix JSON API

In Phoenix I have this model

schema "events" do
  field :start_time, Ecto.DateTime
  field :end_time, Ecto.DateTime
  belongs_to :calendar, Weozo.Calendar

  timestamps
end

@required_fields ~w(calendar_id start_time end_time)
@optional_fields ~w()

def changeset(model, params \\ :empty) do
  model
  |> cast(params, @required_fields, @optional_fields)
end

and I have a JSON API controller generated by the scaffold (piped through :api) where the create function looks like this

  def create(conn, %{"event" => event_params}) do
    changeset = Event.changeset(%Event{}, event_params)

    case Repo.insert(changeset) do
      {:ok, event} ->
        conn
        |> put_status(:created)
        |> put_resp_header("location", event_path(conn, :show, event))
        |> render("show.json", event: event)
      {:error, changeset} ->
        conn
        |> put_status(:unprocessable_entity)
        |> render(Weozo.ChangesetView, "error.json", changeset: changeset)
    end
  end

This is just the default boiler plate. Now, I want to create an Event with a Javascript client, using jQuery I do this:

$.post('/api/events', 
  {"event": {"calendar_id": 1, "start_time": Date.now(), "end_time": Date.now()}}
).always(function(r){console.log(r.responseText)})

and it returns this

{"errors":{"start_time":["is invalid"],"end_time":["is invalid"]}}

So by default, Javascript posts the date as an integer (Epoch format). I've tried a bunch of variations in curl as well like this:

Just a normal date

curl -X POST http://localhost:4000/api/events \
  -H "Content-Type: application/json" \
  -d '{"event": {"calendar_id": 1, "start_time": "2015-10-29", "end_time": "2015-10-29"}}'

RFC 1123 formatted date

curl -X POST http://localhost:4000/api/events \
  -H "Content-Type: application/json"
  -d '{"event":{"calendar_id":"1","start_time":"Thu, 29 Oct 2015 20:11:54 GMT","end_time":"Thu, 29 Oct 2015 20:11:54 GMT"}}'

ISO 8601 formatted date

curl -X POST http://localhost:4000/api/events \
  -H "Content-Type: application/json"
  -d '{"event":{"calendar_id":"1","start_time":"2015-10-29T20:12:30+0000","end_time":"2015-10-29T20:12:30+0000"}}'

They all receive the same "is invalid" error message.

So Phoenix doesn't accept Epoch, RFC 1123 or ISO 8601. How should I format my date for Phoenix to accept it?

like image 480
Fredrik Avatar asked Oct 29 '15 20:10

Fredrik


2 Answers

The changeset will cast the datetime fields using Ecto.DateTime.cast/1 from the docs:

• a binary in the "YYYY-MM-DD HH:MM:DD" format (may be separated by T and/or followed by "Z", as in 2014-04-17T14:00:00Z)

• a binary in the "YYYY-MM-DD HH:MM:DD.USEC" format (may be separated by T and/or followed by "Z", as in 2014-04-17T14:00:00.030Z)

Times should be in the ISO8601 format:

2015-10-29T20:12:30Z

You can generate such a string in JavaScript with:

new Date().toISOString()
like image 145
Gazler Avatar answered Oct 17 '22 03:10

Gazler


Reading the ISO 8601 standard a bit closer, it says

YYYY-MM-DDTHH:mm:ss.sssZ

Z is the time zone offset specified as “Z” (for UTC) or either “+” or “-” followed by a time expression HH:mm

So it seems that my ISO 8601 formatting above was incorrect, the following curl works.

curl -X POST http://localhost:4000/api/events \
  -H "Content-Type: application/json" \
  -d '{"event": {"calendar_id": 1, "start_time": "2015-10-29T00:00:00Z", "end_time": "2015-10-29T00:00:00Z"}}'

But this one, that I feel should still work, doesn't:

curl -X POST http://localhost:4000/api/events \
  -H "Content-Type: application/json" \
  -d '{"event": {"calendar_id": 1, "start_time": "2015-10-29T00:00:00+01:00", "end_time": "2015-10-29T00:00:00+01:00"}}'

Regardless, the way to successfully create an event with Javascript would be to use the toJSON() function or toISOString() on a Date.

$.post('/api/events', 
  {"event": {"calendar_id": 1, "start_time": (new Date()).toJSON(), "end_time": (new Date()).toJSON()}}
)
like image 36
Fredrik Avatar answered Oct 17 '22 04:10

Fredrik