Documenting the available .NET standard formats of .ToString(fmt) with a small F# code snippet


Inspired by ploeh’s stringf

Listing all standard formats of .ToString for DateTime with

1: 
let dateFormats = docuPrint DateTime.Now

that results in the following, where you can pick your favorite format easily.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
('M', "08 Mai")
('m', "08 Mai")
('d', "08.05.2015")
('g', "08.05.2015 19:03")
('G', "08.05.2015 19:03:24")
('t', "19:03")
('T', "19:03:24")
('u', "2015-05-08 19:03:24Z")
('s', "2015-05-08T19:03:24")
('O', "2015-05-08T19:03:24.8516093+02:00")
('o', "2015-05-08T19:03:24.8516093+02:00")
('D', "Freitag, 8. Mai 2015")
('U', "Freitag, 8. Mai 2015 17:03:24")
('f', "Freitag, 8. Mai 2015 19:03")
('F', "Freitag, 8. Mai 2015 19:03:24")
('R', "Fri, 08 May 2015 19:03:24 GMT")
('r', "Fri, 08 May 2015 19:03:24 GMT")
('Y', "Mai 2015")
('y', "Mai 2015")

The main logic is this simple, we try all single char formats on the given type.
Yes it’s a generic function, because of the inline.

1: 
2: 
3: 
let inline getAllFormatSpecifier someType =
    ['A' .. 'Z'] @ ['a' .. 'z']  
    |> List.choose (exToOption (tupleTee (string >> flip stringf someType))) 

It’s signature look like this

1: 
2: 
3: 
val inline getAllFormatSpecifier :
  someType: ^a -> (char * string) list
    when  ^a : (member ToString :  ^a * string -> string)

Here is how to read this nice function composition from left to right:

  1. List.choose keeps only Some‘s
  2. The Some comes from exToOption that converts exceptions to None and successful results to Some
  3. tupleTee keeps the input of the function togehter with the result of the function as a tuple
  4. string converts a character from the concatinated lists ['A' .. 'Z'] @ ['a' .. 'z'] to a string to fit the stringf
  5. and finally the parameter order needs to be exchanged with flip and go into stringf with the given someType

The full code

  1: 
  2: 
  3: 
  4: 
  5: 
  6: 
  7: 
  8: 
  9: 
 10: 
 11: 
 12: 
 13: 
 14: 
 15: 
 16: 
 17: 
 18: 
 19: 
 20: 
 21: 
 22: 
 23: 
 24: 
 25: 
 26: 
 27: 
 28: 
 29: 
 30: 
 31: 
 32: 
 33: 
 34: 
 35: 
 36: 
 37: 
 38: 
 39: 
 40: 
 41: 
 42: 
 43: 
 44: 
 45: 
 46: 
 47: 
 48: 
 49: 
 50: 
 51: 
 52: 
 53: 
 54: 
 55: 
 56: 
 57: 
 58: 
 59: 
 60: 
 61: 
 62: 
 63: 
 64: 
 65: 
 66: 
 67: 
 68: 
 69: 
 70: 
 71: 
 72: 
 73: 
 74: 
 75: 
 76: 
 77: 
 78: 
 79: 
 80: 
 81: 
 82: 
 83: 
 84: 
 85: 
 86: 
 87: 
 88: 
 89: 
 90: 
 91: 
 92: 
 93: 
 94: 
 95: 
 96: 
 97: 
 98: 
 99: 
100: 
101: 
102: 
103: 
104: 
105: 
106: 
107: 
108: 
109: 
110: 
111: 
112: 
113: 
114: 
115: 
116: 
117: 
118: 
119: 
120: 
121: 
122: 
123: 
124: 
125: 
126: 
127: 
128: 
129: 
130: 
131: 
132: 
133: 
open System

// http://blog.ploeh.dk/2015/05/08/stringf/
let inline stringf format (x : ^a) = 
    (^a : (member ToString : string -> string) (x, format))

let flip f a b = f b a
let tupleTee f x = x, f x
let exToOption f x = try Some(f x) with _ -> None

// function composition in F# is awesome !
let inline getAllFormatSpecifier someType =
    ['A' .. 'Z'] @ ['a' .. 'z']  
    > List.choose (exToOption (tupleTee (string >> flip stringf someType))) 

// print it nice to choose one
let inline docuPrint x =
    x
    |> getAllFormatSpecifier
    |> List.sortBy snd              
    |> List.iter (printfn "%A")

let dateFormats = docuPrint DateTime.Now
(*
('M', "08 Mai")
('m', "08 Mai")
('d', "08.05.2015")
('g', "08.05.2015 19:03")
('G', "08.05.2015 19:03:24")
('t', "19:03")
('T', "19:03:24")
('u', "2015-05-08 19:03:24Z")
('s', "2015-05-08T19:03:24")
('O', "2015-05-08T19:03:24.8516093+02:00")
('o', "2015-05-08T19:03:24.8516093+02:00")
('D', "Freitag, 8. Mai 2015")
('U', "Freitag, 8. Mai 2015 17:03:24")
('f', "Freitag, 8. Mai 2015 19:03")
('F', "Freitag, 8. Mai 2015 19:03:24")
('R', "Fri, 08 May 2015 19:03:24 GMT")
('r', "Fri, 08 May 2015 19:03:24 GMT")
('Y', "Mai 2015")
('y', "Mai 2015")
*)

let timespanFormats = docuPrint <| TimeSpan(1, 2, 3)
(*
('T', "01:02:03")
('c', "01:02:03")
('t', "01:02:03")
('G', "0:01:02:03.0000000")
('g', "1:02:03")
*)

// be caereful, it rounds
let floatFormats = docuPrint 0.425      
(*
('G', "0.425")
('R', "0.425")
('g', "0.425")
('r', "0.425")
('F', "0.43")
('N', "0.43")
('f', "0.43")
('n', "0.43")
('E', "4.250000E-001")
('e', "4.250000e-001")
('P', "42.50%")
('p', "42.50%")
('C', "Fr. 0.43")
('c', "Fr. 0.43")
*)

// be caereful, it rounds
let decimalFormats = docuPrint 0.425m
(*
('G', "0.425")
('g', "0.425")
('F', "0.43")
('N', "0.43")
('f', "0.43")
('n', "0.43")
('E', "4.250000E-001")
('e', "4.250000e-001")
('P', "42.50%")
('p', "42.50%")
('C', "Fr. 0.43")
('c', "Fr. 0.43")
*)


let intFormats = docuPrint 42   
(*
('X', "2A")
('x', "2a")
('P', "4'200.00%")
('p', "4'200.00%")
('E', "4.200000E+001")
('e', "4.200000e+001")
('D', "42")
('G', "42")
('d', "42")
('g', "42")
('F', "42.00")
('N', "42.00")
('f', "42.00")
('n', "42.00")
('C', "Fr. 42.00")
('c', "Fr. 42.00")
*)


let bigIntFormats = docuPrint 42I
(*
('X', "2A")
('x', "2a")
('P', "4'200.00%")
('p', "4'200.00%")
('E', "4.200000E+001")
('e', "4.200000e+001")
('D', "42")
('G', "42")
('R', "42")
('d', "42")
('g', "42")
('r', "42")
('F', "42.00")
('N', "42.00")
('f', "42.00")
('n', "42.00")
('C', "Fr. 42.00")
('c', "Fr. 42.00")
*)
val dateFormats : obj

Full name: DocumentingToString.dateFormats

val getAllFormatSpecifier : someType:'a -> 'b list

Full name: DocumentingToString.getAllFormatSpecifier

val someType : 'a
Multiple items
module List

from Microsoft.FSharp.Collections

——————–
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
  interface IEnumerable
  interface IEnumerable<'T>
  member Head : 'T
  member IsEmpty : bool
  member Item : index:int -> 'T with get
  member Length : int
  member Tail : 'T list
  static member Cons : head:'T * tail:'T list -> 'T list
  static member Empty : 'T list

Full name: Microsoft.FSharp.Collections.List<_>

val choose : chooser:('T -> 'U option) -> list:'T list -> 'U list

Full name: Microsoft.FSharp.Collections.List.choose

Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

——————–
type string = System.String

Full name: Microsoft.FSharp.Core.string

Multiple items
val char : value:'T -> char (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.char

——————–
type char = System.Char

Full name: Microsoft.FSharp.Core.char

type 'T list = List<'T>

Full name: Microsoft.FSharp.Collections.list<_>

namespace System
union case Option.Some: Value: 'T -> Option<'T>
union case Option.None: Option<'T>
val sortBy : projection:('T -> 'Key) -> list:'T list -> 'T list (requires comparison)

Full name: Microsoft.FSharp.Collections.List.sortBy

val snd : tuple:('T1 * 'T2) -> 'T2

Full name: Microsoft.FSharp.Core.Operators.snd

val iter : action:('T -> unit) -> list:'T list -> unit

Full name: Microsoft.FSharp.Collections.List.iter

val printfn : format:Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn