Convert days to human readable text duration

Given the number of days, say 25, convert it to a text of duration, for example "3 weeks, 4 days"

C # and F # solutions would be nice if the F # variation offers any improvement over C #.

Edit: The solution should expand over several weeks, including months and years. Bonus points for centuries and so on. An additional bonus, if it is somewhat customizable, that is, you can say that the method eliminates the normalization of weeks.

+3
source share
4 answers

This is a recursive solution. Please note that the duration is really measured at a specific point in time on this calendar, as the length of the month and year varies. But here is a simple solution involving fixed lengths:

let divmod n m = n / m, n % m    

let units = [
    ("Centuries", TimeSpan.TicksPerDay * 365L * 100L );
    ("Years", TimeSpan.TicksPerDay * 365L);
    ("Weeks", TimeSpan.TicksPerDay * 7L);
    ("Days", TimeSpan.TicksPerDay)
]

let duration days =
    let rec duration' ticks units acc =
        match units with
        | [] -> acc
        | (u::us) ->
            let (wholeUnits, ticksRemaining) = divmod ticks (snd u)
            duration' ticksRemaining us (((fst u), wholeUnits) :: acc)
    duration' (TimeSpan.FromDays(float days).Ticks) units []
+1
source
String.Format("{0} Weeks, {1} days", days / 7, days % 7);
+5
source
    public class UnitOfMeasure {
        public UnitOfMeasure(string name, int value) {
            Name = name;
            Value = value;
        }

        public string Name { get; set; }
        public int Value { get; set; }

        public static UnitOfMeasure[] All = new UnitOfMeasure[] {
            new UnitOfMeasure("Year", 356),
            new UnitOfMeasure("Month", 30),
            new UnitOfMeasure("Week", 7),
            new UnitOfMeasure("Day", 1)
        };

        public static string ConvertToDuration(int days) {
            List<string> results = new List<string>();

            for (int i = 0; i < All.Length; i++) {
                int count = days / All[i].Value;

                if (count >= 1) {
                    results.Add((count + " " + All[i].Name) + (count == 1 ? string.Empty : "s"));
                    days -= count * All[i].Value;
                }
            }

            return string.Join(", ", results.ToArray());
        }
    }
0
source

Here's a version of F # based on a previously published version of C #. The main difference is that it is applicable and not imperative (without mutable variables).

#light
let conversions = [|
    365, "Year", "Years"
    30, "Month", "Months"
    7, "Week", "Weeks"
    1, "Day", "Days" |]
let ToDuration numDays =
    conversions
    |> Array.fold_left (fun (remainDays,results) (n,sing,plur) ->
        let count = remainDays / n
        if count >= 1 then 
            remainDays - (count * n),
              (sprintf "%d %s" count (if count=1 then sing else plur)) :: results
        else
            remainDays, results
    ) (numDays,[])
    |> snd |> (fun rs -> System.String.Join(", ", List.rev rs |> List.to_array))
printfn "%s" (ToDuration 1008)    
0
source

Source: https://habr.com/ru/post/1699680/


All Articles