如何实现产品插入日期/范围查询?解决:utc_datetime转换报错
问题:产品插入日期筛选功能报错,无法将日期字符串转为
:utc_datetime 需要实现按产品插入日期过滤的功能,支持单日期(起始/结束)或日期范围筛选,但修改查询语句后一直报错,不确定代码是否正确,求改进建议和解决方案。
表单代码
<%= form_for @conn, Routes.products_path(@conn, :index), [method: :get, as: :search, class: "ml-2 row", page_size: @page.page_size, page: @page.page_number], fn f -> %> <div class="col-12 align-items-end row"> <label class="form-label col-1"> <%= search_input f, :start_date, class: "form-control", placeholder: "From" %> </label> <label class="form-label col-1"> <%= search_input f, :end_date, class: "form-control", placeholder: "To" %> </label> <label class="form-label col-1"> <%= submit "Search", class: "btn btn-primary" %> </label> </div> <% end %>
控制器index函数代码
products = ProductsRepo.get_products_by_insertion_date(start_date, end_date) page = products |> ProductsRepo.paginate() render(conn, "index.html", products: page.entries, page: page)
查询函数代码
def get_products_by_insertion_date(nil, nil) do Products end def get_products_by_insertion_date(start_date, nil) do from(p in Products, where: p.inserted_at >= ^start_date) end def get_products_by_insertion_date(nil, end_date) do from(p in Products, where: p.inserted_at <= ^end_date) end def get_products_by_insertion_date(start_date, end_date) do from(p in Products, where: p.inserted_at >= ^start_date and p.inserted_at <= ^end_date ) end
当前报错信息
值
"2023-01-13"在where条件中无法转换为类型:utc_datetime
解决方案
报错核心原因:表单提交的是纯字符串格式日期(如"2023-01-13"),但数据库inserted_at字段是:utc_datetime类型,Ecto无法直接将无时间信息的日期字符串转换为带UTC时间的日期类型。
步骤1:在控制器中转换日期参数
需要把表单传来的日期字符串解析为Ecto可识别的DateTime类型,同时处理空值和日期范围的边界问题(比如结束日期要包含当天的最后一秒):
def index(conn, %{"search" => search_params}) do start_date = parse_date(search_params["start_date"], false) end_date = parse_date(search_params["end_date"], true) products = ProductsRepo.get_products_by_insertion_date(start_date, end_date) page = products |> ProductsRepo.paginate() render(conn, "index.html", products: page.entries, page: page) end # 辅助解析函数:处理日期字符串转UTC DateTime defp parse_date(nil, _), do: nil defp parse_date("", _), do: nil defp parse_date(date_str, false) do # 起始日期:转为当天00:00:00的UTC时间 case NaiveDateTime.from_iso8601("#{date_str}T00:00:00") do {:ok, naive_dt} -> DateTime.from_naive!(naive_dt, "Etc/UTC") _ -> nil end end defp parse_date(date_str, true) do # 结束日期:转为当天23:59:59的UTC时间 case NaiveDateTime.from_iso8601("#{date_str}T23:59:59") do {:ok, naive_dt} -> DateTime.from_naive!(naive_dt, "Etc/UTC") _ -> nil end end
步骤2:优化查询函数(可选)
可以将多子句查询简化为单函数,通过条件拼接减少代码重复:
def get_products_by_insertion_date(start_date, end_date) do query = from p in Products query = if start_date, do: from p in query, where: p.inserted_at >= ^start_date, else: query query = if end_date, do: from p in query, where: p.inserted_at <= ^end_date, else: query query end
额外优化建议
- 表单中使用
date_input替代search_input,浏览器会提供标准日期选择器,确保提交的日期格式为YYYY-MM-DD,减少格式错误:<%= date_input f, :start_date, class: "form-control", placeholder: "From" %> <%= date_input f, :end_date, class: "form-control", placeholder: "To" %> - 增加参数验证逻辑,若日期格式无效,返回前端友好提示,避免无效查询。
内容的提问来源于stack exchange,提问作者Kacper Wielki
相关产品推荐
相关产品推荐

