Skip to content

Commit

Permalink
Re-implement custom format for DateTime & various improvements
Browse files Browse the repository at this point in the history
- Split replacements for `DateTime` and `DateTimeOffset` in 2 functions
- Make compilation fails if calling `DateTime` constructor with microseconds
  • Loading branch information
MangelMaxime committed Feb 29, 2024
1 parent ef6d50a commit 8e80705
Show file tree
Hide file tree
Showing 5 changed files with 781 additions and 58 deletions.
13 changes: 9 additions & 4 deletions src/Fable.Cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

#### All
* [GH-3769](https://github.com/fable-compiler/Fable/pull/3769) [All] Local plugin build does not run indefinably. (by @nojaf)
* [GH-3769](https://github.com/fable-compiler/Fable/pull/3769) [JS/TS] Types hidden by signature files should not be exported. (by @nojaf)
* [GH-3772](https://github.com/fable-compiler/Fable/pull/3772) [JS/TS] Re-implement `DateTime.ToString` custom format handling (by @MangelMaxime)

* [GH-3769](https://github.com/fable-compiler/Fable/pull/3769) Local plugin build does not run indefinably. (by @nojaf)
It now supports all custom format specifiers, and behave as if `CultureInfo.InvariantCulture` was used (Fable does not support Globalization).
* [GH-3772](https://github.com/fable-compiler/Fable/pull/3772) [JS/TS] Make compilation fails if calling `DateTime` constructor with microseconds (by @MangelMaxime)

#### JavaScript
JavaScript `Date` does not support microseconds, we need to wait for `Temporal` to be widely supported before reconsidering this.

### Changed

* [GH-3769](https://github.com/fable-compiler/Fable/pull/3769) Types hidden by signature files should not be exported. (by @nojaf)
* [GH-3772](https://github.com/fable-compiler/Fable/pull/3772) [JS/TS] Split replacement for `DateTime` and `DateTimeOffset` (by @MangelMaxime)

## 4.13.0 - 2024-02-13

Expand Down
94 changes: 67 additions & 27 deletions src/Fable.Transforms/Replacements.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2840,15 +2840,8 @@ let ignoreFormatProvider meth args =
| "TryParse", input :: _culture :: defVal :: _ -> [ input; defVal ]
| _ -> args

let dates (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
let getTime (e: Expr) =
Helper.InstanceCall(e, "getTime", t, [])

let moduleName =
if i.DeclaringEntityFullName = Types.datetime then
"Date"
else
"DateOffset"
let dateTime (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
let moduleName = "Date"

match i.CompiledName with
| ".ctor" ->
Expand All @@ -2857,9 +2850,6 @@ let dates (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr optio
| ExprType(Number(Int64, _)) :: _ ->
Helper.LibCall(com, moduleName, "fromTicks", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| ExprType(DeclaredType(e, [])) :: _ when e.FullName = Types.datetime ->
Helper.LibCall(com, "DateOffset", "fromDate", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| _ ->
let last = List.last args

Expand All @@ -2870,33 +2860,82 @@ let dates (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr optio
let argTypes = (List.take 6 i.SignatureArgTypes) @ [ Int32.Number; last.Type ]

Helper.LibCall(com, "Date", "create", t, args, argTypes, ?loc = r) |> Some

// JavaScript Date doesn't support microseconds precision
| 8, Number(Int32, NumberInfo.Empty) ->
"JavaScript Date with doesn't support microseconds precision"
|> addError com ctx.InlinePath r

None
// | 8, Number(_, NumberInfo.IsEnum ent) when ent.FullName = "System.DateTimeKind" ->
// "JavaScript Date with doesn't support microseconds precision" |> addError com ctx.InlinePath r
// None

| _ ->
Helper.LibCall(com, moduleName, "create", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| "ToString" ->
Helper.LibCall(com, "Date", "toString", t, args, i.SignatureArgTypes, ?thisArg = thisArg, ?loc = r)
|> Some
| "get_Kind"
| "get_Offset" as meth ->
let moduleName =
if meth = "get_Kind" then
"Date"
else
"DateOffset"
// | "get_Kind" ->
// Helper.LibCall(com, moduleName, "kind", t, [ thisArg.Value ], [ thisArg.Value.Type ], ?loc = r)
// |> Some
// let y = DateTime.Now.UtcTicks
// let x = DateTimeOffset.Now.UtcTicks
// failwith "Not implemented"

| "get_Ticks" ->
Helper.LibCall(com, "Date", "getTicks", t, [ thisArg.Value ], [ thisArg.Value.Type ], ?loc = r)

Check warning

Code scanning / Ionide.Analyzers.Cli

Replace unsafe option unwrapping with graceful handling of each case. Warning

Replace unsafe option unwrapping with graceful handling of each case.

Check warning

Code scanning / Ionide.Analyzers.Cli

Replace unsafe option unwrapping with graceful handling of each case. Warning

Replace unsafe option unwrapping with graceful handling of each case.
|> Some
| meth ->
let args = ignoreFormatProvider meth args
let meth = Naming.removeGetSetPrefix meth |> Naming.lowerFirst

Helper.LibCall(com, moduleName, meth, t, [ thisArg.Value ], [ thisArg.Value.Type ], ?loc = r)
Helper.LibCall(com, moduleName, meth, t, args, i.SignatureArgTypes, ?thisArg = thisArg, ?loc = r)
|> Some


let dateTimeOffset (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
let moduleName = "DateOffset"

match i.CompiledName with
| ".ctor" ->
match args with
| [] -> Helper.LibCall(com, moduleName, "minValue", t, [], [], ?loc = r) |> Some
| ExprType(Number(Int64, _)) :: _ ->
Helper.LibCall(com, moduleName, "fromTicks", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| ExprType(DeclaredType(e, [])) :: _ when e.FullName = Types.datetime ->
Helper.LibCall(com, moduleName, "fromDate", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| _ ->
let last = List.last args

match args.Length, last.Type with
| 7, Number(_, NumberInfo.IsEnum ent) when ent.FullName = "System.DateTimeKind" ->
let args = (List.take 6 args) @ [ makeIntConst 0; last ]

let argTypes = (List.take 6 i.SignatureArgTypes) @ [ Int32.Number; last.Type ]

Helper.LibCall(com, "Date", "create", t, args, argTypes, ?loc = r) |> Some

| _ ->
Helper.LibCall(com, moduleName, "create", t, args, i.SignatureArgTypes, ?loc = r)
|> Some
| "ToString" ->
Helper.LibCall(com, "Date", "toString", t, args, i.SignatureArgTypes, ?thisArg = thisArg, ?loc = r)
|> Some
| "get_Offset" ->
Helper.LibCall(com, moduleName, "offset", t, [ thisArg.Value ], [ thisArg.Value.Type ], ?loc = r)

Check warning

Code scanning / Ionide.Analyzers.Cli

Replace unsafe option unwrapping with graceful handling of each case. Warning

Replace unsafe option unwrapping with graceful handling of each case.

Check warning

Code scanning / Ionide.Analyzers.Cli

Replace unsafe option unwrapping with graceful handling of each case. Warning

Replace unsafe option unwrapping with graceful handling of each case.
|> Some
// DateTimeOffset
| "get_LocalDateTime" ->
Helper.LibCall(com, "DateOffset", "toLocalTime", t, [ thisArg.Value ], [ thisArg.Value.Type ], ?loc = r)
|> Some
| "get_UtcDateTime" ->
Helper.LibCall(com, "DateOffset", "toUniversalTime", t, [ thisArg.Value ], [ thisArg.Value.Type ], ?loc = r)
|> Some
| "get_DateTime" ->
let kind = System.DateTimeKind.Unspecified |> int |> makeIntConst
let kind = DateTimeKind.Unspecified |> int |> makeIntConst

Helper.LibCall(
com,
Expand All @@ -2921,6 +2960,7 @@ let dates (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr optio
Helper.LibCall(com, moduleName, meth, t, args, i.SignatureArgTypes, ?thisArg = thisArg, ?loc = r)
|> Some


let dateOnly (com: ICompiler) (ctx: Context) r t (i: CallInfo) (thisArg: Expr option) (args: Expr list) =
match i.CompiledName with
| ".ctor" when args.Length = 4 ->
Expand Down Expand Up @@ -3878,8 +3918,8 @@ let private replacedModules =
"System.Console", console
"System.Diagnostics.Debug", debug
"System.Diagnostics.Debugger", debug
Types.datetime, dates
Types.datetimeOffset, dates
Types.datetime, dateTime
Types.datetimeOffset, dateTimeOffset
Types.dateOnly, dateOnly
Types.timeOnly, timeOnly
Types.timespan, timeSpans
Expand Down Expand Up @@ -4054,11 +4094,11 @@ let tryType typ =
| Builtin kind ->
match kind with
| BclGuid -> Some(Types.guid, guids, [])
| BclDateTime -> Some(Types.datetime, dates, [])
| BclDateTimeOffset -> Some(Types.datetimeOffset, dates, [])
| BclDateTime -> Some(Types.datetime, dateTime, [])
| BclDateTimeOffset -> Some(Types.datetimeOffset, dateTimeOffset, [])
| BclDateOnly -> Some(Types.dateOnly, dateOnly, [])
| BclTimeOnly -> Some(Types.timeOnly, timeOnly, [])
| BclTimer -> Some("System.Timers.Timer", timers, [])
| BclTimer -> Some(Types.timer, timers, [])
| BclTimeSpan -> Some(Types.timespan, timeSpans, [])
| BclHashSet genArg -> Some(Types.hashset, hashSets, [ genArg ])
| BclDictionary(key, value) -> Some(Types.dictionary, dictionaries, [ key; value ])
Expand Down
3 changes: 3 additions & 0 deletions src/Fable.Transforms/Transforms.Util.fs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,9 @@ module Types =
[<Literal>]
let timeOnly = "System.TimeOnly"

[<Literal>]
let timer = "System.Timers.Timer"

[<Literal>]
let int8 = "System.SByte"

Expand Down
Loading

0 comments on commit 8e80705

Please sign in to comment.