@@ -355,6 +355,81 @@ defmodule Duration do
355355 end
356356 end
357357
358+ @ doc """
359+ Converts the given `duration` to a human readable representation.
360+
361+ ## Examples
362+
363+ iex> Duration.to_string(Duration.new!(year: 3))
364+ "3 years"
365+ iex> Duration.to_string(Duration.new!(day: 40, hour: 12, minute: 42, second: 12))
366+ "40 days, 12 hours, 42 minutes, 12 seconds"
367+ iex> Duration.to_string(Duration.new!(second: 30))
368+ "30 seconds"
369+
370+ iex> Duration.to_string(Duration.new!([]))
371+ "0 seconds"
372+
373+ iex> Duration.to_string(Duration.new!(second: 1, microsecond: {2_200, 3}))
374+ "1.002 seconds"
375+ iex> Duration.to_string(Duration.new!(second: 1, microsecond: {-1_200_000, 4}))
376+ "-0.2000 seconds"
377+
378+ """
379+ @ doc since: "1.18.0"
380+ def to_string ( % Duration { } = duration ) do
381+ case to_string_year ( duration , [ ] ) do
382+ [ ] ->
383+ "0 seconds"
384+
385+ [ part ] ->
386+ IO . iodata_to_binary ( part )
387+
388+ parts ->
389+ parts |> Enum . reduce ( & [ & 1 , ", " | & 2 ] ) |> IO . iodata_to_binary ( )
390+ end
391+ end
392+
393+ defp to_string_part ( 0 , _singular , _plural , acc ) , do: acc
394+ defp to_string_part ( 1 , singular , _plural , acc ) , do: [ [ "1" | singular ] | acc ]
395+ defp to_string_part ( x , _singular , plural , acc ) , do: [ [ Integer . to_string ( x ) | plural ] | acc ]
396+
397+ defp to_string_year ( % { year: year } = duration , acc ) do
398+ to_string_month ( duration , to_string_part ( year , " year" , " years" , acc ) )
399+ end
400+
401+ defp to_string_month ( % { month: month } = duration , acc ) do
402+ to_string_week ( duration , to_string_part ( month , " month" , " months" , acc ) )
403+ end
404+
405+ defp to_string_week ( % { week: week } = duration , acc ) do
406+ to_string_day ( duration , to_string_part ( week , " week" , " weeks" , acc ) )
407+ end
408+
409+ defp to_string_day ( % { day: day } = duration , acc ) do
410+ to_string_hour ( duration , to_string_part ( day , " day" , " days" , acc ) )
411+ end
412+
413+ defp to_string_hour ( % { hour: hour } = duration , acc ) do
414+ to_string_minute ( duration , to_string_part ( hour , " hour" , " hours" , acc ) )
415+ end
416+
417+ defp to_string_minute ( % { minute: minute } = duration , acc ) do
418+ to_string_second ( duration , to_string_part ( minute , " minute" , " minutes" , acc ) )
419+ end
420+
421+ defp to_string_second ( % { second: 1 , microsecond: { 0 , _ } } , acc ) do
422+ [ "1 second" | acc ]
423+ end
424+
425+ defp to_string_second ( % { second: 0 , microsecond: { 0 , _ } } , acc ) do
426+ acc
427+ end
428+
429+ defp to_string_second ( % { second: s , microsecond: { ms , p } } , acc ) do
430+ [ [ second_component ( s , ms , p ) | " seconds" ] | acc ]
431+ end
432+
358433 @ doc """
359434 Converts the given `duration` to an [ISO 8601-2:2019](https://en.wikipedia.org/wiki/ISO_8601) formatted string.
360435
@@ -407,15 +482,15 @@ defmodule Duration do
407482 [ ]
408483 end
409484
410- defp second_component ( % { second: 0 , microsecond: { _ , 0 } } ) do
411- ~c " 0S "
485+ defp second_component ( % { second: second , microsecond: { ms , p } } ) do
486+ [ second_component ( second , ms , p ) , ?S ]
412487 end
413488
414- defp second_component ( % { second: second , microsecond: { _ , 0 } } ) do
415- [ Integer . to_string ( second ) , ?S ]
489+ defp second_component ( second , _ms , 0 ) do
490+ Integer . to_string ( second )
416491 end
417492
418- defp second_component ( % { second: second , microsecond: { ms , p } } ) do
493+ defp second_component ( second , ms , p ) do
419494 total_ms = second * @ microseconds_per_second + ms
420495 second = total_ms |> div ( @ microseconds_per_second ) |> abs ( )
421496 ms = total_ms |> rem ( @ microseconds_per_second ) |> abs ( )
@@ -425,8 +500,7 @@ defmodule Duration do
425500 sign ,
426501 Integer . to_string ( second ) ,
427502 ?. ,
428- ms |> Integer . to_string ( ) |> String . pad_leading ( 6 , "0" ) |> binary_part ( 0 , p ) ,
429- ?S
503+ ms |> Integer . to_string ( ) |> String . pad_leading ( 6 , "0" ) |> binary_part ( 0 , p )
430504 ]
431505 end
432506
0 commit comments