3

Given an amount of days, say 25, convert it into a duration text such as "3 Weeks, 4 Days"

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

Edit: The solution should expand past weeks to include months and years. Bonus points for including centuries and so on. Extra bonus if it is somewhat configurable, meaning you can tell the method to exclude the normalization of weeks.

Bob
  • 97,670
  • 29
  • 122
  • 130

4 Answers4

5
String.Format("{0} Weeks, {1} days", days / 7, days % 7);
Gavin Miller
  • 43,168
  • 21
  • 122
  • 188
  • Maybe some basic logic can be added. E.g. just count it as days if days < 6, not adding an s if not using a plural (or replace s with '(s)'). – Brian Nov 17 '08 at 20:47
2

This is a recursive solution. Note that a duration only really makes sense measured against a particular point in time on a given calendar because month and year lengths vary. But here is a simple solution assuming 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 []
simonuk
  • 391
  • 1
  • 5
0
    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());
        }
    }
Bob
  • 97,670
  • 29
  • 122
  • 130
0

Here's an F# version based on the previously posted C# version. The main difference is it's applicative rather than imperative (no 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)    
Brian
  • 117,631
  • 17
  • 236
  • 300