【问题标题】:Optimizing ISO String Formatting in Elixir在 Elixir 中优化 ISO 字符串格式
【发布时间】:2019-04-30 04:34:53
【问题描述】:

我有两个函数用于将 ISO 日期和 ISO 时间字符串格式化为常规字符串,例如“1999-10-12” -> “12/10/1999” & “18:00:00” -> “6:00pm”,它们涉及到很多转换函数...

我想知道是否有更高效的方式来格式化 ISO 字符串输入:

  # ISO Date String to DD/MM/YYYY
  def date_to_dd_mm_yyyy(iso_date) do
    {_, date} = Date.from_iso8601(iso_date)
    {year, month, day} = Date.to_erl(date)

    Integer.to_string(day) <> "/" <> Integer.to_string(month) <> "/" <> Integer.to_string(year)
  end

  # ISO Time String to 12 hour time
  def time_to_12hour(iso_time) do
    {:ok, new_time} = Time.from_iso8601(iso_time)
    {hour, minute, _second} = Time.to_erl(new_time)

    minute_string =
      cond do
        minute < 10 ->
          "0" <> Integer.to_string(minute)
        true ->
          Integer.to_string(minute)
      end

    hour_string =
      cond do
        hour === 0 ->
          "12"
        hour > 12 ->
          Integer.to_string(hour - 12)
        hour <= 12 ->
          Integer.to_string(hour)
      end

    meridiem =
      cond do
        hour >= 12 ->
          "pm"
        hour < 12 ->
          "am"
      end

    hour_string <> ":" <> minute_string <> meridiem
  end

【问题讨论】:

    标签: performance date time elixir


    【解决方案1】:

    使用模式匹配:

    defmodule IsoToMine do
      # "1999-01-01"
      def convert(<<y::binary-size(4), "-",
                    m::binary-size(2), "-",
                    d::binary-size(2)>> = iso_date),
      # add {:ok, date} = Date.from_iso8601(iso_date)
      # here to blow up on wrong input
        do: Enum.join([y, d, m], "/")
    
      # "18:00:00"
      def convert(<<h::binary-size(2), ":",
                    m::binary-size(2), ":",
                    _::binary-size(2)>>) do
        {h, apm} =
          case String.to_integer(h) do
            0 -> {12, "am"}
            12 -> {12, "pm"}
            am when am < 12 -> {am, "am"}
            pm when pm > 12 -> {rem(pm, 12), "pm"}
          end
    
        to_string(h) <> ":" <> m <> apm
      end
    end
    

    现在IsoToMine.convert/1 是杂食性的:

    iex|1 ▶ IsoToMine.convert "1999-01-01"
    #⇒ "1999/01/01"
    iex|2 ▶ IsoToMine.convert "12:03:01"  
    #⇒ "12:03pm"
    iex|3 ▶ IsoToMine.convert "11:03:01"
    #⇒ "11:03am"
    iex|4 ▶ IsoToMine.convert "18:03:01"
    #⇒ "6:03pm"
    

    请注意,上面的函数不会验证输入,并且会在输入错误时出现MatchError / NoFunctionClause 异常,但我只是复制了 初始 行为,就像您的代码一样(保存你的在格式良好但不可能的日期爆炸,而我的快乐运行。)

    在错误的输入上添加检查和/或优雅的回退是一项非常简单的任务,我留给你作为练习。

    【讨论】:

    • 感谢 Aleksei,这种二进制大小的模式匹配方法对我来说是新的。很好!
    【解决方案2】:

    不确定它是否更高效,但有一个名为Timex 的现成库可以为您做到这一点:

    iex> "1999-10-12" |> Timex.parse!("{ISOdate}") |> Timex.format!("{D}/{M}/{YYYY}")
    "12/10/1999"
    iex> "18:00:00" |> Timex.parse!("{ISOtime}") |> Timex.format!("{kitchen}")
    "6:00PM"
    

    正如他们所说,premature optimization is the root of all evil,所以我建议测量速度,并仅在日期解析和格式化成为瓶颈时尝试创建此代码的更优化版本。

    【讨论】:

    • 感谢您的回复——从 Elixir 1.8 开始,原生 Elixir DateTime/Date/Time 模块具有 Timex 的许多功能,因此我不再使用 Timex(它是一个相当重的依赖),而不是只使用除了 tzdata 包之外的核心模块
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-24
    • 1970-01-01
    • 2023-03-16
    • 1970-01-01
    相关资源
    最近更新 更多