mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 02:21:54 +03:00
Add format to the in-memory Column (#6538)
Add format to the in-memory Column # Important Notes Also updates .format in date types. Some rearrangement of date formatting builtins / Java libraries.
This commit is contained in:
parent
4e7f757f53
commit
4ba8409def
@ -419,6 +419,7 @@
|
||||
`Text.write`.][6459]
|
||||
- [Implemented `create_database_table` allowing saving queries as database
|
||||
tables.][6467]
|
||||
- [Implemented `Column.format` for in-memory `Column`s.][6538]
|
||||
- [Added `at_least_one` flag to `Table.tokenize_to_rows`.][6539]
|
||||
- [Moved `Redshift` connector into a separate `AWS` library.][6550]
|
||||
|
||||
@ -627,6 +628,7 @@
|
||||
[6429]: https://github.com/enso-org/enso/pull/6429
|
||||
[6459]: https://github.com/enso-org/enso/pull/6459
|
||||
[6467]: https://github.com/enso-org/enso/pull/6467
|
||||
[6538]: https://github.com/enso-org/enso/pull/6538
|
||||
[6539]: https://github.com/enso-org/enso/pull/6539
|
||||
[6550]: https://github.com/enso-org/enso/pull/6550
|
||||
|
||||
|
@ -614,6 +614,8 @@ type Date
|
||||
|
||||
Arguments:
|
||||
- pattern: The text specifying the format for formatting the date.
|
||||
- locale: The locale in which the format should be interpreted.
|
||||
(Defaults to Locale.default.)
|
||||
|
||||
? Pattern Syntax
|
||||
A custom pattern string consists of one or more custom date and time
|
||||
@ -622,19 +624,19 @@ type Date
|
||||
for a complete format specification.
|
||||
|
||||
> Example
|
||||
Format "2020-06-02" as "2 June 2020"
|
||||
Format "2020-06-02" as "2 Jun 2020"
|
||||
|
||||
from Standard.Base import Date
|
||||
|
||||
example_format = Date.new 2020 6 2 . format "d MMMM yyyy"
|
||||
|
||||
> Example
|
||||
Format "2020-06-02" as "2 June 20"
|
||||
Format "2020-06-02" as "2 Jun 20"
|
||||
|
||||
example_format = Date.new 2020 6 2 . format "d MMMM yy"
|
||||
|
||||
> Example
|
||||
Format "2020-06-02" as "Tuesday, 02 June 2020"
|
||||
Format "2020-06-02" as "Tuesday, 02 Jun 2020"
|
||||
|
||||
example_format = Date.new 2020 6 2 . format "EEEE, dd MMMM yyyy"
|
||||
|
||||
@ -647,8 +649,15 @@ type Date
|
||||
Format "2020-06-02" as "2020AD"
|
||||
|
||||
example_format = Date.new 2020 6 2 . format "yyyyGG"
|
||||
format : Text -> Text
|
||||
format self pattern = Time_Utils.local_date_format self pattern
|
||||
|
||||
> Example
|
||||
Format "2020-06-21" with French locale as "21. juin 2020"
|
||||
|
||||
example_format = Date.new 2020 6 21 . format "d. MMMM yyyy" (Locale.new "fr")
|
||||
format : Text -> Locale -> Text
|
||||
format self pattern locale=Locale.default = case locale of
|
||||
Nothing -> Time_Utils.local_date_format self pattern
|
||||
_ -> Time_Utils.local_date_format_with_locale self pattern locale.java_locale
|
||||
|
||||
## PRIVATE
|
||||
week_days_between start end =
|
||||
|
@ -613,6 +613,8 @@ type Date_Time
|
||||
|
||||
Arguments:
|
||||
- pattern: The pattern that specifies how to format the time.
|
||||
- locale: The locale in which the format should be interpreted.
|
||||
(Defaults to Locale.default.)
|
||||
|
||||
? Pattern Syntax
|
||||
A custom pattern string consists of one or more custom date and time
|
||||
@ -645,6 +647,16 @@ type Date_Time
|
||||
|
||||
example_format =
|
||||
Date_Time.parse "2020-10-08T16:41:13+03:00[Europe/Moscow]" . format "EEE MMM d (HH:mm)"
|
||||
format : Text -> Text
|
||||
format self pattern = @Builtin_Method "Date_Time.format"
|
||||
|
||||
> Example
|
||||
Format "2020-06-21T16:41:13+03:00" with French locale as "21. juin 2020"
|
||||
|
||||
from Standard.Base import Date_Time
|
||||
import Standard.Base.Data.Locale.Locale
|
||||
|
||||
example_format =
|
||||
Date_Time.parse "2020-06-21T16:41:13+03:00" . format "d. MMMM yyyy" (Locale.new "fr")
|
||||
format : Text -> Locale -> Text
|
||||
format self pattern locale=Locale.default = case locale of
|
||||
Nothing -> Time_Utils.date_time_format self pattern
|
||||
_ -> Time_Utils.date_time_format_with_locale self pattern locale.java_locale
|
||||
|
@ -311,6 +311,8 @@ type Time_Of_Day
|
||||
|
||||
Arguments:
|
||||
- pattern: The pattern specifying how to format the time of day.
|
||||
- locale: The locale in which the format should be interpreted.
|
||||
(Defaults to Locale.default.)
|
||||
|
||||
? Pattern Syntax
|
||||
A custom pattern string consists of one or more custom date and time
|
||||
@ -352,5 +354,7 @@ type Time_Of_Day
|
||||
from Standard.Base import Time_Of_Day
|
||||
|
||||
example_format = Time_Of_Day.new 16 21 10 . format "'hour:'h"
|
||||
format : Text -> Text
|
||||
format self pattern = @Builtin_Method "Time_Of_Day.format"
|
||||
format : Text -> Locale -> Text
|
||||
format self pattern locale=Locale.default = case locale of
|
||||
Nothing -> Time_Utils.time_of_day_format self pattern
|
||||
_ -> Time_Utils.time_of_day_format_with_locale self pattern locale.java_locale
|
||||
|
@ -993,6 +993,12 @@ type Column
|
||||
_ = [type, format, on_problems]
|
||||
Error.throw <| Unsupported_Database_Operation.Error "`Column.parse` is not implemented yet for the Database backends."
|
||||
|
||||
## Formatting values is not supported in database columns.
|
||||
format : Text | Column -> Locale -> Column ! Illegal_Argument
|
||||
format self format=Nothing locale=Locale.default =
|
||||
_ = [format, locale]
|
||||
Error.throw <| Unsupported_Database_Operation.Error "`Column.format` is not implemented yet for the Database backends."
|
||||
|
||||
## PRIVATE
|
||||
UNSTABLE
|
||||
Cast the column to a specific type.
|
||||
|
@ -1,5 +1,6 @@
|
||||
from Standard.Base import all
|
||||
from Standard.Base import all
|
||||
from project.Data.Column_Format import all
|
||||
|
||||
import Standard.Base.Data.Array_Proxy.Array_Proxy
|
||||
import Standard.Base.Errors.Common.Index_Out_Of_Bounds
|
||||
import Standard.Base.Errors.Illegal_Argument.Illegal_Argument
|
||||
@ -21,6 +22,7 @@ import project.Data.Type.Value_Type_Helpers
|
||||
from project.Data.Table import print_table
|
||||
from project.Data.Type.Value_Type import Value_Type, Auto
|
||||
from project.Errors import No_Index_Set_Error, Floating_Point_Equality, Invalid_Value_Type, Inexact_Type_Coercion
|
||||
from project.Internal.Java_Exports import make_string_builder
|
||||
|
||||
polyglot java import org.enso.table.data.column.operation.map.MapOperationProblemBuilder
|
||||
polyglot java import org.enso.table.data.column.storage.Storage as Java_Storage
|
||||
@ -1175,6 +1177,108 @@ type Column
|
||||
output = Column.Value (Java_Column.new self.name new_storage)
|
||||
on_problems.attach_problems_after output problems
|
||||
|
||||
## Format a `Column` using a format string (or `Column` of format strings).
|
||||
|
||||
Arguments:
|
||||
- format: The type-dependent format string to use to format the values.
|
||||
If `format` is `""` or `Nothing`, .to_text is used to format the value.
|
||||
- locale: The locale in which the format should be interpreted.
|
||||
|
||||
! Error Conditions
|
||||
|
||||
- If the format is incorrectly formed, or if some values in the column
|
||||
did not match the expected datatype format, an `Illegal_Argument`
|
||||
error is thrown.
|
||||
|
||||
? Supported Types
|
||||
- `Value_Type.Date`
|
||||
- `Value_Type.Date_Time`
|
||||
- `Value_Type.Time`
|
||||
- `Value_Type.Integer`
|
||||
- `Value_Type.Float`
|
||||
- `Value_Type.Boolean`
|
||||
|
||||
? `Value_Type.Date`, `Value_Type.Date_Time`, `Value_Type.Time` format strings
|
||||
|
||||
A custom pattern string consists of one or more custom date and time
|
||||
format specifiers. For example, "d MMM yyyy" will format "2011-12-03"
|
||||
as "3 Dec 2011". See https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/time/format/DateTimeFormatter.html
|
||||
for a complete format specification.
|
||||
|
||||
Note that the format string can specify decimal point and digit
|
||||
separators, but these characters are interpreted in the context of the
|
||||
Locale used. The format string specifies their location, but the Locale
|
||||
has the final decision about which characters are used.
|
||||
|
||||
? `Value_Type.Integer`, `Value_Type.Float` format strings
|
||||
|
||||
Numeric format strings are specified by the Java DecimalFormat class.
|
||||
See https://docs.oracle.com/javase/8/docs/api/java/text/DecimalFormat.html
|
||||
for a complete format specification.
|
||||
|
||||
? `Value_Type.Boolean` format strings
|
||||
|
||||
Format strings for `Boolean` consist of two values that represent true
|
||||
and false, separated by a `|`.
|
||||
|
||||
> Example
|
||||
Format a `Column` of `Dates` in the format `"yyyyMMdd"`.
|
||||
|
||||
input = Column.from_vector "values" [Date.new 2020 12 21, Date.new 2023 4 25]
|
||||
input.format "yyyyMMdd"
|
||||
# ==> ["20201221", "20230425"]
|
||||
|
||||
> Example
|
||||
Format `Column` of `Dates`, using format strings in a second column.
|
||||
|
||||
input = Column.from_vector "values" [Date.new 2020 12 21, Date.new 2023 4 25]
|
||||
formats = Column.from_vector "formats" ["yyyyMMdd", "dd-MM-yyyy"]
|
||||
input.format formats
|
||||
# ==> ["20201221", "25-04-2023"]
|
||||
|
||||
> Example
|
||||
Format a `Column` of `Integers` in the format `"#,##0.00"`.
|
||||
|
||||
input = Column.from_vector "values" [100000000, 2222, 3]
|
||||
input.format "#,##0.00"
|
||||
# ==> ["100,000,000.00", "2,222.00", "3.00"]
|
||||
|
||||
> Example
|
||||
Format a `Column` of `Booleans` in the format `"t|f"`.
|
||||
|
||||
input = Column.from_vector "values" [True, False]
|
||||
input.format "t|f"
|
||||
# ==> ["t", "f"]
|
||||
|
||||
> Example
|
||||
Format a `Column` of numbers, using both decimal point / digit
|
||||
separators and a Locale.
|
||||
|
||||
input = Column.from_vector "values" ["100000000", "2222", "3"] . parse numeric_type
|
||||
input.format "#,##0.00" locale=(Locale.new "fr")
|
||||
# ==> ["100 000 000,00", "2 222,00", "3,00"]
|
||||
format : Text | Column -> Locale -> Column ! Illegal_Argument
|
||||
format self format=Nothing locale=Locale.default =
|
||||
create_formatter = make_value_formatter_for_value_type self.value_type locale
|
||||
|
||||
new_column = case format of
|
||||
"" ->
|
||||
formatter = .to_text
|
||||
map_over_storage self formatter make_string_builder
|
||||
Nothing ->
|
||||
formatter = .to_text
|
||||
map_over_storage self formatter make_string_builder
|
||||
_ : Text ->
|
||||
formatter = create_formatter
|
||||
formatter.if_not_error <|
|
||||
map_over_storage self (formatter format=format) make_string_builder
|
||||
format_column : Column -> Value_Type.expect_text format_column <|
|
||||
formatter = create_formatter
|
||||
formatter.if_not_error <|
|
||||
map_2_over_storage self format_column formatter make_string_builder
|
||||
_ -> Error.throw <| Illegal_Argument.Error <| "Unsupported format type: " + format.to_text
|
||||
new_column
|
||||
|
||||
## ALIAS Transform Column
|
||||
|
||||
Applies `function` to each item in this column and returns the column
|
||||
|
@ -0,0 +1,114 @@
|
||||
from Standard.Base import all
|
||||
import Standard.Base.Errors.Illegal_Argument.Illegal_Argument
|
||||
import Standard.Base.Errors.Illegal_State.Illegal_State
|
||||
import project.Data.Column.Column
|
||||
import project.Data.Data_Formatter.Data_Formatter
|
||||
import project.Data.Type.Storage
|
||||
import project.Data.Type.Value_Type.Value_Type
|
||||
|
||||
from project.Internal.Java_Exports import make_string_builder
|
||||
|
||||
polyglot java import java.lang.IllegalArgumentException
|
||||
polyglot java import java.time.temporal.UnsupportedTemporalTypeException
|
||||
polyglot java import org.enso.table.data.column.operation.map.MapOperationProblemBuilder
|
||||
polyglot java import org.enso.table.data.column.storage.Storage as Java_Storage
|
||||
polyglot java import org.enso.table.data.table.Column as Java_Column
|
||||
polyglot java import org.enso.table.operations.OrderBuilder
|
||||
|
||||
## PRIVATE
|
||||
Create a formatter for the specified `Value_Type`.
|
||||
make_value_formatter_for_value_type : Value_Type -> Locale -> (Any -> Text)
|
||||
make_value_formatter_for_value_type value_type locale = case value_type of
|
||||
Value_Type.Date -> make_value_formatter locale
|
||||
Value_Type.Date_Time _ -> make_value_formatter locale
|
||||
Value_Type.Time -> make_value_formatter locale
|
||||
Value_Type.Boolean -> make_boolean_formatter
|
||||
Value_Type.Integer _ -> make_value_formatter locale
|
||||
Value_Type.Float _ -> make_value_formatter locale
|
||||
bad_type ->
|
||||
msg = "Cannot format a Column of type " + bad_type.to_text
|
||||
Error.throw (Illegal_Argument.Error msg)
|
||||
|
||||
## PRIVATE
|
||||
Create a formatter for the given format string.
|
||||
The `value` parameter has to have a `format` method that takes a format and
|
||||
locale.
|
||||
make_value_formatter : Locale -> (Any -> Text)
|
||||
make_value_formatter locale = value-> format->
|
||||
handle_illegal_argument_exception format <|
|
||||
if format.is_nothing || format.is_empty then value.to_text else
|
||||
value.format format locale
|
||||
## PRIVATE
|
||||
Create a `Boolean` formatter that takes the format string as the second
|
||||
parameter.
|
||||
make_boolean_formatter : (Boolean -> Text -> Text)
|
||||
make_boolean_formatter = bool-> format->
|
||||
if format.is_nothing || format.is_empty then bool.to_text else
|
||||
data_formatter = Data_Formatter.Value.with_format Value_Type.Boolean format
|
||||
data_formatter.format bool
|
||||
|
||||
## PRIVATE
|
||||
Rethrow a Java IllegalArgumentException as an Illegal_Argument.
|
||||
handle_illegal_argument_exception : Text -> Any -> Any
|
||||
handle_illegal_argument_exception format_string ~action =
|
||||
handler cause =
|
||||
msg = cause.payload.getMessage + ' in \"' + format_string + '\"'
|
||||
Error.throw (Illegal_Argument.Error msg)
|
||||
Panic.catch IllegalArgumentException handler=handler <|
|
||||
Panic.catch UnsupportedTemporalTypeException handler=handler action
|
||||
|
||||
## PRIVATE
|
||||
Iterate over a range, exiting early if the body produces an `Error`.
|
||||
each_propagate : Range -> (Number -> Any) -> Nothing ! Error
|
||||
each_propagate range function =
|
||||
if range.step == 0 then Error.throw (Illegal_State.Error "A range with step = 0 is ill-formed.") else
|
||||
end_condition = if range.step > 0 then (>=) else (<=)
|
||||
go current =
|
||||
if end_condition current range.end then Nothing else
|
||||
result = function current
|
||||
result.if_not_error <|
|
||||
@Tail_Call go current+range.step
|
||||
go range.start
|
||||
|
||||
## PRIVATE
|
||||
Map a text-returning function over the column values, using Storage directly.
|
||||
The output column has the same name as the input.
|
||||
map_over_storage : Column -> (Any -> Text) -> (Integer -> Any) -> Boolean -> Column
|
||||
map_over_storage input_column function builder skip_nothing=True =
|
||||
input_storage = input_column.java_column.getStorage
|
||||
num_input_rows = input_storage.size
|
||||
output_storage_builder = builder num_input_rows
|
||||
ok = each_propagate (0.up_to num_input_rows) i->
|
||||
input_value = input_storage.getItemBoxed i
|
||||
if skip_nothing && input_value.is_nothing then output_storage_builder.append Nothing else
|
||||
output_value = function input_value
|
||||
output_value.if_not_error
|
||||
output_storage_builder.append output_value
|
||||
ok.if_not_error <|
|
||||
output_storage = output_storage_builder.seal
|
||||
Column.from_storage input_column.name output_storage
|
||||
|
||||
## PRIVATE
|
||||
Map a text-returning function over the values of two columns, using Storage
|
||||
directly. The output column has the same name as the first input column.
|
||||
`skip_nothing` applies to the first input to the function, not both inputs.
|
||||
map_2_over_storage : Column -> Column -> (Any -> Any -> Text) -> (Integer -> Any) -> Boolean -> Column
|
||||
map_2_over_storage input_column_0 input_column_1 function builder skip_nothing=True =
|
||||
input_storage_0 = input_column_0.java_column.getStorage
|
||||
input_storage_1 = input_column_1.java_column.getStorage
|
||||
case input_storage_0.size != input_storage_1.size of
|
||||
True ->
|
||||
msg = "Column lengths differ: " + input_storage_0.size.to_text + " != " + input_storage_1.size.to_text
|
||||
Error.throw (Illegal_Argument.Error msg)
|
||||
False ->
|
||||
num_input_rows = input_storage_0.size
|
||||
output_storage_builder = builder num_input_rows
|
||||
ok = each_propagate (0.up_to num_input_rows) i->
|
||||
input_value_0 = input_storage_0.getItemBoxed i
|
||||
input_value_1 = input_storage_1.getItemBoxed i
|
||||
if skip_nothing && input_value_0.is_nothing then output_storage_builder.append Nothing else
|
||||
output_value = function input_value_0 input_value_1
|
||||
output_storage_builder.append output_value
|
||||
ok.if_not_error <|
|
||||
output_storage = output_storage_builder.seal
|
||||
Column.from_storage input_column_0.name output_storage
|
@ -194,13 +194,6 @@ public final class EnsoDateTime implements TruffleObject {
|
||||
return Text.create(DateTimeFormatter.ISO_ZONED_DATE_TIME.format(dateTime));
|
||||
}
|
||||
|
||||
@Builtin.Method(description = "Return this datetime to the datetime in the provided time zone.")
|
||||
@Builtin.Specialize
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
public Text format(String pattern) {
|
||||
return Text.create(DateTimeFormatter.ofPattern(pattern).format(dateTime));
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean isDate() {
|
||||
return true;
|
||||
|
@ -125,13 +125,6 @@ public final class EnsoTimeOfDay implements TruffleObject {
|
||||
return Text.create(DateTimeFormatter.ISO_LOCAL_TIME.format(localTime));
|
||||
}
|
||||
|
||||
@Builtin.Method(description = "Return this datetime to the datetime in the provided time zone.")
|
||||
@Builtin.Specialize
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
public Text format(String pattern) {
|
||||
return Text.create(DateTimeFormatter.ofPattern(pattern).format(localTime));
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean isTime() {
|
||||
return true;
|
||||
|
@ -95,6 +95,26 @@ public class Time_Utils {
|
||||
return DateTimeFormatter.ofPattern(format.toString()).format(date);
|
||||
}
|
||||
|
||||
public static String local_date_format_with_locale(LocalDate date, Object format, Locale locale) {
|
||||
return DateTimeFormatter.ofPattern(format.toString()).withLocale(locale).format(date);
|
||||
}
|
||||
|
||||
public static String date_time_format(ZonedDateTime dateTime, Object format) {
|
||||
return DateTimeFormatter.ofPattern(format.toString()).format(dateTime);
|
||||
}
|
||||
|
||||
public static String date_time_format_with_locale(ZonedDateTime dateTime, Object format, Locale locale) {
|
||||
return DateTimeFormatter.ofPattern(format.toString()).withLocale(locale).format(dateTime);
|
||||
}
|
||||
|
||||
public static String time_of_day_format_with_locale(LocalTime localTime, Object format, Locale locale) {
|
||||
return DateTimeFormatter.ofPattern(format.toString()).withLocale(locale).format(localTime);
|
||||
}
|
||||
|
||||
public static String time_of_day_format(LocalTime localTime, Object format) {
|
||||
return DateTimeFormatter.ofPattern(format.toString()).format(localTime);
|
||||
}
|
||||
|
||||
public static LocalDate date_adjust(LocalDate date, AdjustOp op, Period period) {
|
||||
return switch (op) {
|
||||
case PLUS -> date.plus(period);
|
||||
|
337
test/Table_Tests/src/In_Memory/Column_Format_Spec.enso
Normal file
337
test/Table_Tests/src/In_Memory/Column_Format_Spec.enso
Normal file
@ -0,0 +1,337 @@
|
||||
from Standard.Base import all
|
||||
from Standard.Table.Data.Column_Format import all
|
||||
|
||||
import Standard.Base.Data.Locale.Locale
|
||||
import Standard.Base.Data.Time.Date.Date
|
||||
import Standard.Base.Data.Time.Date_Time.Date_Time
|
||||
import Standard.Base.Data.Time.Time_Of_Day.Time_Of_Day
|
||||
import Standard.Base.Errors.Illegal_Argument.Illegal_Argument
|
||||
import Standard.Table.Data.Type.Value_Type.Bits
|
||||
import Standard.Test.Extensions
|
||||
|
||||
from Standard.Table import Column, Value_Type
|
||||
from Standard.Table.Errors import Invalid_Value_Type
|
||||
from Standard.Test import Test, Test_Suite
|
||||
from project.Util import all
|
||||
|
||||
spec =
|
||||
Test.group "Date Column.format, with format string" <|
|
||||
Test.specify "Date column" <|
|
||||
input = Column.from_vector "values" [Date.new 2020 12 21, Date.new 2023 4 25]
|
||||
expected = Column.from_vector "values" ["20201221", "20230425"]
|
||||
actual = input.format "yyyyMMdd"
|
||||
actual . should_equal expected
|
||||
|
||||
Test.specify "Date with locale" <|
|
||||
input = Column.from_vector "values" [Date.new 2020 6 21, Date.new 2023 4 25]
|
||||
expected_default = Column.from_vector "values" ["21. Jun 2020", "25. Apr 2023"]
|
||||
expected_gb = Column.from_vector "values" ["21. Jun 2020", "25. Apr 2023"]
|
||||
expected_fr = Column.from_vector "values" ["21. juin 2020", "25. avril 2023"]
|
||||
input.format "d. MMMM yyyy" . should_equal expected_default
|
||||
input.format "d. MMMM yyyy" (Locale.default) . should_equal expected_default
|
||||
input.format "d. MMMM yyyy" (Locale.new "gb") . should_equal expected_gb
|
||||
input.format "d. MMMM yyyy" (Locale.new "fr") . should_equal expected_fr
|
||||
|
||||
Test.specify "Empty/Nothing format" <|
|
||||
input = Column.from_vector "values" [Date.new 2020 12 21, Date.new 2023 4 25]
|
||||
expected = Column.from_vector "values" ['2020-12-21', '2023-04-25']
|
||||
input.format . should_equal expected
|
||||
input.format "" . should_equal expected
|
||||
input.format Nothing . should_equal expected
|
||||
|
||||
Test.specify "Bad format" <|
|
||||
input = Column.from_vector "values" [Date.new 2020 6 21, Date.new 2023 4 25]
|
||||
input.format "DDDDD" . should_fail_with Illegal_Argument
|
||||
|
||||
Test.group "Date Column.format, with format Column" <|
|
||||
Test.specify "Date column" <|
|
||||
input = Column.from_vector "values" [Date.new 2020 12 21, Date.new 2023 4 25]
|
||||
formats = Column.from_vector "formats" ["yyyyMMdd", "dd-MM-yyyy"]
|
||||
expected = Column.from_vector "values" ["20201221", "25-04-2023"]
|
||||
actual = input.format formats
|
||||
actual . should_equal expected
|
||||
|
||||
Test.specify "Date with locale" <|
|
||||
input = Column.from_vector "values" [Date.new 2020 6 21, Date.new 2023 4 25]
|
||||
formats = Column.from_vector "formats" ["d. MMMM yyyy", "d-MMMM-yyyy"]
|
||||
expected = Column.from_vector "values" ["21. juin 2020", "25-avril-2023"]
|
||||
input.format formats (Locale.new "fr") . should_equal expected
|
||||
|
||||
Test.specify "Empty/Nothing format, with format Column" <|
|
||||
input = Column.from_vector "values" [Date.new 2020 12 21, Date.new 2023 4 25]
|
||||
formats = Column.from_vector "formats" ["", Nothing]
|
||||
expected = Column.from_vector "values" ["2020-12-21", "2023-04-25"]
|
||||
actual = input.format formats
|
||||
actual . should_equal expected
|
||||
|
||||
Test.specify "Bad format" <|
|
||||
input = Column.from_vector "values" [Date.new 2020 6 21, Date.new 2023 4 25, Date.new 2023 4 26]
|
||||
formats = Column.from_vector "formats" ["yyyyMMdd", "DDDDD", "FFF"]
|
||||
input.format formats . should_fail_with Illegal_Argument
|
||||
|
||||
Test.specify "Bad format column type" <|
|
||||
input = Column.from_vector "values" [Date.new 2020 6 21, Date.new 2023 4 25, Date.new 2023 4 26]
|
||||
formats = Column.from_vector "formats" [3, 4, 5]
|
||||
input.format formats . should_fail_with Invalid_Value_Type
|
||||
|
||||
Test.specify "column length mismatch" <|
|
||||
input = Column.from_vector "values" [Date.new 2020 6 21, Date.new 2023 4 25]
|
||||
formats = Column.from_vector "formats" ["yyyyMMdd", "DDDDD", "w"]
|
||||
input.format formats . should_fail_with Illegal_Argument
|
||||
|
||||
Test.group "Date_Time Column.format, with format string" <|
|
||||
Test.specify "Date_Time column" <|
|
||||
input = Column.from_vector "values" [Date_Time.new 2020 12 21 8 10 20, Date_Time.new 2023 4 25 14 25 2]
|
||||
expected = Column.from_vector "values" ["20201221 08.10.20", "20230425 14.25.02"]
|
||||
actual = input.format "yyyyMMdd HH.mm.ss"
|
||||
actual . should_equal expected
|
||||
|
||||
Test.specify "Date_Time with locale" <|
|
||||
input = Column.from_vector "values" [Date_Time.new 2020 6 21 8 10 20, Date_Time.new 2023 4 25 14 25 2]
|
||||
expected_default = Column.from_vector "values" ["21. Jun 2020 08.10.20", "25. Apr 2023 14.25.02"]
|
||||
expected_gb = Column.from_vector "values" ["21. Jun 2020 08.10.20", "25. Apr 2023 14.25.02"]
|
||||
expected_fr = Column.from_vector "values" ["21. juin 2020 08.10.20", "25. avril 2023 14.25.02"]
|
||||
input.format "d. MMMM yyyy HH.mm.ss" . should_equal expected_default
|
||||
input.format "d. MMMM yyyy HH.mm.ss" (Locale.default) . should_equal expected_default
|
||||
input.format "d. MMMM yyyy HH.mm.ss" (Locale.new "gb") . should_equal expected_gb
|
||||
input.format "d. MMMM yyyy HH.mm.ss" (Locale.new "fr") . should_equal expected_fr
|
||||
|
||||
Test.specify "Empty/Nothing format" <|
|
||||
zone = Time_Zone.parse "US/Hawaii"
|
||||
input = Column.from_vector "values" [Date_Time.new 2020 12 21 8 10 20 zone=zone, Date_Time.new 2023 4 25 14 25 2 zone=zone]
|
||||
expected = Column.from_vector "values" ['2020-12-21T08:10:20-10:00[US/Hawaii]', '2023-04-25T14:25:02-10:00[US/Hawaii]']
|
||||
input.format . should_equal expected
|
||||
input.format "" . should_equal expected
|
||||
input.format Nothing . should_equal expected
|
||||
|
||||
Test.specify "Bad format" <|
|
||||
input = Column.from_vector "values" [Date_Time.new 2020 6 21 8 10 20, Date_Time.new 2023 4 25 14 25 2]
|
||||
input.format "DDDDD" . should_fail_with Illegal_Argument
|
||||
|
||||
Test.group "Date_Time Column.format, with format Column" <|
|
||||
Test.specify "Date_Time column" <|
|
||||
input = Column.from_vector "values" [Date_Time.new 2020 6 21 8 10 20, Date_Time.new 2023 4 25 14 25 2]
|
||||
formats = Column.from_vector "formats" ["yyyyMMdd HH.mm.ss", "dd-MM-yyyy HH.mm.ss"]
|
||||
expected = Column.from_vector "values" ["20200621 08.10.20", "25-04-2023 14.25.02"]
|
||||
actual = input.format formats
|
||||
actual . should_equal expected
|
||||
|
||||
Test.specify "Date_Time with locale" <|
|
||||
input = Column.from_vector "values" [Date_Time.new 2020 6 21 8 10 20, Date_Time.new 2023 4 25 14 25 2]
|
||||
formats = Column.from_vector "formats" ["d. MMMM yyyy HH.mm.ss", "d-MMMM-yyyy HH.mm.ss"]
|
||||
expected = Column.from_vector "values" ["21. juin 2020 08.10.20", "25-avril-2023 14.25.02"]
|
||||
input.format formats (Locale.new "fr") . should_equal expected
|
||||
|
||||
Test.specify "Empty/Nothing format, with format Column" <|
|
||||
zone = Time_Zone.parse "US/Hawaii"
|
||||
input = Column.from_vector "values" [Date_Time.new 2020 6 21 8 10 20 zone=zone, Date_Time.new 2023 4 25 14 25 2 zone=zone]
|
||||
formats = Column.from_vector "formats" ["", Nothing]
|
||||
expected = Column.from_vector "values" ['2020-06-21T08:10:20-10:00[US/Hawaii]', '2023-04-25T14:25:02-10:00[US/Hawaii]']
|
||||
actual = input.format formats
|
||||
actual . should_equal expected
|
||||
|
||||
Test.specify "Bad format" <|
|
||||
input = Column.from_vector "values" [Date_Time.new 2020 6 21 8 10 20, Date_Time.new 2023 4 25 14 25 2, Date_Time.new 2023 4 26 3 4 5]
|
||||
formats = Column.from_vector "formats" ["yyyyMMdd HH.mm.ss", "DDDDD", "FFF"]
|
||||
input.format formats . should_fail_with Illegal_Argument
|
||||
|
||||
Test.specify "Bad format column type" <|
|
||||
input = Column.from_vector "values" [Date_Time.new 2020 6 21 8 10 20, Date_Time.new 2023 4 25 14 25 2]
|
||||
formats = Column.from_vector "formats" [3, 4, 5]
|
||||
input.format formats . should_fail_with Invalid_Value_Type
|
||||
|
||||
Test.specify "column length mismatch" <|
|
||||
input = Column.from_vector "values" [Date_Time.new 2020 6 21 8 10 20, Date_Time.new 2023 4 25 14 25 2]
|
||||
formats = Column.from_vector "formats" ["yyyyMMdd", "DDDDD", "w"]
|
||||
input.format formats . should_fail_with Illegal_Argument
|
||||
|
||||
Test.group "Time_Of_Day Column.format, with format string" <|
|
||||
Test.specify "Time_Of_Day column" <|
|
||||
input = Column.from_vector "values" [Time_Of_Day.new 8 10 20, Time_Of_Day.new 14 25 2]
|
||||
expected = Column.from_vector "values" ["08.10.20", "14.25.02"]
|
||||
actual = input.format "HH.mm.ss"
|
||||
actual . should_equal expected
|
||||
|
||||
Test.specify "Time_Of_Day with locale" <|
|
||||
input = Column.from_vector "values" [Time_Of_Day.new 8 10 20, Time_Of_Day.new 14 25 2]
|
||||
# Note that the results are all the same.
|
||||
expected = Column.from_vector "values" ["08.10.20", "14.25.02"]
|
||||
input.format "HH.mm.ss" . should_equal expected
|
||||
input.format "HH.mm.ss" (Locale.default) . should_equal expected
|
||||
input.format "HH.mm.ss" (Locale.new "gb") . should_equal expected
|
||||
|
||||
Test.specify "Empty/Nothing format" <|
|
||||
input = Column.from_vector "values" [Time_Of_Day.new 8 10 20, Time_Of_Day.new 14 25 2]
|
||||
expected = Column.from_vector "values" ['08:10:20', '14:25:02']
|
||||
input.format . should_equal expected
|
||||
input.format "" . should_equal expected
|
||||
input.format Nothing . should_equal expected
|
||||
|
||||
Test.specify "Bad format" <|
|
||||
input = Column.from_vector "values" [Time_Of_Day.new 8 10 20, Time_Of_Day.new 14 25 2]
|
||||
input.format "DDDDD" . should_fail_with Illegal_Argument
|
||||
|
||||
Test.specify "Format for wrong date type" <|
|
||||
input = Column.from_vector "values" [Time_Of_Day.new 8 10 20, Time_Of_Day.new 14 25 2]
|
||||
input.format "yyyyMMdd HH.mm.ss" . should_fail_with Illegal_Argument
|
||||
|
||||
Test.group "Time_Of_Day Column.format, with format Column" <|
|
||||
Test.specify "Time_Of_Day column" <|
|
||||
input = Column.from_vector "values" [Time_Of_Day.new 8 10 20, Time_Of_Day.new 14 25 2]
|
||||
formats = Column.from_vector "formats" ["HH.mm.ss", "ss mm HH"]
|
||||
expected = Column.from_vector "values" ["08.10.20", "02 25 14"]
|
||||
actual = input.format formats
|
||||
actual . should_equal expected
|
||||
|
||||
Test.specify "Time_Of_Day with locale" <|
|
||||
input = Column.from_vector "values" [Time_Of_Day.new 8 10 20, Time_Of_Day.new 14 25 2]
|
||||
formats = Column.from_vector "formats" ["HH.mm.ss", "ss mm HH"]
|
||||
expected = Column.from_vector "values" ["08.10.20", "02 25 14"]
|
||||
input.format formats (Locale.new "fr") . should_equal expected
|
||||
|
||||
Test.specify "Empty/Nothing format, with format Column" <|
|
||||
input = Column.from_vector "values" [Time_Of_Day.new 8 10 20, Time_Of_Day.new 14 25 2]
|
||||
formats = Column.from_vector "formats" ["", Nothing]
|
||||
expected = Column.from_vector "values" ["08:10:20", "14:25:02"]
|
||||
actual = input.format formats
|
||||
actual . should_equal expected
|
||||
|
||||
Test.specify "Bad format" <|
|
||||
input = Column.from_vector "values" [Time_Of_Day.new 8 10 20, Time_Of_Day.new 14 25 2, Time_Of_Day.new 3 4 5]
|
||||
formats = Column.from_vector "formats" ["HH.mm.ss", "DDDDD", "FFF"]
|
||||
input.format formats . should_fail_with Illegal_Argument
|
||||
|
||||
Test.specify "Bad format column type" <|
|
||||
input = Column.from_vector "values" [Time_Of_Day.new 8 10 20, Time_Of_Day.new 14 25 2]
|
||||
formats = Column.from_vector "formats" [3, 4, 5]
|
||||
input.format formats . should_fail_with Invalid_Value_Type
|
||||
|
||||
Test.specify "column length mismatch" <|
|
||||
input = Column.from_vector "values" [Time_Of_Day.new 8 10 20, Time_Of_Day.new 14 25 2]
|
||||
formats = Column.from_vector "formats" ["yyyyMMdd", "DDDDD", "w"]
|
||||
input.format formats . should_fail_with Illegal_Argument
|
||||
|
||||
Test.group "Boolean Column.format, with format string" <|
|
||||
Test.specify "Boolean column" <|
|
||||
input = Column.from_vector "values" [True, False]
|
||||
expected = Column.from_vector "values" ["t", "f"]
|
||||
actual = input.format "t|f"
|
||||
actual . should_equal expected
|
||||
|
||||
Test.specify "Empty/Nothing format" <|
|
||||
input = Column.from_vector "values" [True, False]
|
||||
expected = Column.from_vector "values" ["True", "False"]
|
||||
input.format . should_equal expected
|
||||
input.format "" . should_equal expected
|
||||
input.format Nothing . should_equal expected
|
||||
|
||||
Test.specify "Bad format" <|
|
||||
input = Column.from_vector "values" [True, False]
|
||||
input.format "x|y|z" . should_fail_with Illegal_Argument
|
||||
|
||||
Test.group "Boolean Column.format, with format Column" <|
|
||||
Test.specify "Time_Of_Day column" <|
|
||||
input = Column.from_vector "values" [True, False, True, False]
|
||||
formats = Column.from_vector "formats" ["True|False", "True|False", "troo|valz", "troo|valz"]
|
||||
expected = Column.from_vector "values" ["True", "False", "troo", "valz"]
|
||||
actual = input.format formats
|
||||
actual . should_equal expected
|
||||
|
||||
Test.specify "Empty/Nothing format, with format Column" <|
|
||||
input = Column.from_vector "values" [True, False]
|
||||
formats = Column.from_vector "formats" ["", Nothing]
|
||||
expected = Column.from_vector "values" ["True", "False"]
|
||||
input.format formats . should_equal expected
|
||||
|
||||
Test.specify "Bad format" <|
|
||||
input = Column.from_vector "values" [True, False]
|
||||
formats = Column.from_vector "formats" ["True|False", "xyzzy"]
|
||||
input.format formats . should_fail_with Illegal_Argument
|
||||
|
||||
spec_with_numeric_type "Integer" (Value_Type.Integer Bits.Bits_64)
|
||||
spec_with_numeric_type "Float" (Value_Type.Float Bits.Bits_64)
|
||||
|
||||
Test.group "Integer" <|
|
||||
Test.specify "Integer Column (constructing the column directly from Integers)" <|
|
||||
input = Column.from_vector "values" [100000000, 2222, 3]
|
||||
expected = Column.from_vector "values" ["100,000,000.00", "2,222.00", "3.00"]
|
||||
input.format "#,##0.00" . should_equal expected
|
||||
|
||||
Test.group "Numeric, empty/Nothing" <|
|
||||
Test.specify "Integer" <|
|
||||
input = Column.from_vector "values" ["100000000", "2222", "3"] . parse (Value_Type.Integer Bits.Bits_64)
|
||||
expected = Column.from_vector "values" ["100000000", "2222", "3"]
|
||||
input.format . should_equal expected
|
||||
input.format "" . should_equal expected
|
||||
input.format Nothing . should_equal expected
|
||||
|
||||
Test.specify "Float" <|
|
||||
input = Column.from_vector "values" ["100000000", "2222", "3"] . parse (Value_Type.Float Bits.Bits_64)
|
||||
expected = Column.from_vector "values" ['1.0E8', '2222.0', '3.0']
|
||||
input.format . should_equal expected
|
||||
input.format "" . should_equal expected
|
||||
input.format Nothing . should_equal expected
|
||||
|
||||
Test.specify "Integer, with format Column" <|
|
||||
input = Column.from_vector "values" ["100000000", "2222", "3"] . parse (Value_Type.Integer Bits.Bits_64)
|
||||
formats = Column.from_vector "formats" ["", Nothing, Nothing]
|
||||
expected = Column.from_vector "values" ["100000000", "2222", "3"]
|
||||
input.format formats . should_equal expected
|
||||
|
||||
Test.specify "Float, with format Column" <|
|
||||
input = Column.from_vector "values" ["100000000", "2222", "3"] . parse (Value_Type.Float Bits.Bits_64)
|
||||
formats = Column.from_vector "formats" ["", Nothing, Nothing]
|
||||
expected = Column.from_vector "values" ['1.0E8', '2222.0', '3.0']
|
||||
input.format formats . should_equal expected
|
||||
|
||||
Test.group "Errors" <|
|
||||
Test.specify "Unsupported column type" <|
|
||||
input = Column.from_vector "values" ["100000000", "hey", "3"]
|
||||
input.format "xyz" . should_fail_with Illegal_Argument
|
||||
|
||||
Test.specify "Format is not text" <|
|
||||
input = Column.from_vector "values" [Date.new 2020 12 21, Date.new 2023 4 25]
|
||||
input.format 73 . should_fail_with Illegal_Argument
|
||||
|
||||
Test.group "Edge cases" <|
|
||||
Test.specify "empty table is ok" <|
|
||||
input = Column.from_vector "values" [Date.new 2020 12 21, Date.new 2023 4 25] . take 0
|
||||
expected = Column.from_vector "values" []
|
||||
actual = input.format "yyyyMMdd"
|
||||
actual . should_equal expected
|
||||
|
||||
spec_with_numeric_type name numeric_type =
|
||||
Test.group name <|
|
||||
Test.specify "Column" <|
|
||||
input = Column.from_vector "values" ["100000000", "2222", "3"] . parse numeric_type
|
||||
expected = Column.from_vector "values" ["100,000,000.00", "2,222.00", "3.00"]
|
||||
input.format "#,##0.00" . should_equal expected
|
||||
|
||||
Test.specify "Column with locale" <|
|
||||
input = Column.from_vector "values" ["100000000", "2222", "3"] . parse numeric_type
|
||||
expected = Column.from_vector "values" ["100 000 000,00", "2 222,00", "3,00"]
|
||||
input.format "#,##0.00" locale=(Locale.new "fr") . should_equal expected
|
||||
|
||||
Test.specify "Bad format" <|
|
||||
input = Column.from_vector "values" ["100000000", "2222", "3"] . parse numeric_type
|
||||
input.format "#.##0,00" . should_fail_with Illegal_Argument
|
||||
|
||||
Test.group name+", with format Column" <|
|
||||
Test.specify "Column" <|
|
||||
input = Column.from_vector "values" ["100000000", "2222", "3"] . parse numeric_type
|
||||
formats = Column.from_vector "formats" ["#,##0.00", "0.00", "0"]
|
||||
expected = Column.from_vector "values" ["100,000,000.00", "2222.00", "3"]
|
||||
input.format formats . should_equal expected
|
||||
|
||||
Test.specify "Column with locale" <|
|
||||
input = Column.from_vector "values" ["100000000", "2222", "3"] . parse numeric_type
|
||||
formats = Column.from_vector "formats" ["#,##0.00", "0.00", "0"]
|
||||
expected = Column.from_vector "values" ["100 000 000,00", "2222,00", "3"]
|
||||
input.format formats locale=(Locale.new "fr") . should_equal expected
|
||||
|
||||
Test.specify "Bad format" <|
|
||||
input = Column.from_vector "values" ["100000000", "2222", "3"] . parse numeric_type
|
||||
formats = Column.from_vector "formats" ["#,##0.00", "#.##0,00", "0"]
|
||||
input.format formats . should_fail_with Illegal_Argument
|
||||
|
||||
main = Test_Suite.run_main spec
|
@ -5,6 +5,7 @@ from Standard.Test import Test_Suite
|
||||
import project.In_Memory.Aggregate_Column_Spec
|
||||
import project.In_Memory.Builders_Spec
|
||||
import project.In_Memory.Column_Spec
|
||||
import project.In_Memory.Column_Format_Spec
|
||||
import project.In_Memory.Common_Spec
|
||||
import project.In_Memory.Join_Performance_Spec
|
||||
import project.In_Memory.Parse_To_Table_Spec
|
||||
@ -17,6 +18,7 @@ import project.In_Memory.Table_Time_Of_Day_Spec
|
||||
spec =
|
||||
Table_Spec.spec
|
||||
Column_Spec.spec
|
||||
Column_Format_Spec.spec
|
||||
Common_Spec.spec
|
||||
Table_Date_Spec.spec
|
||||
Table_Date_Time_Spec.spec
|
||||
|
@ -37,6 +37,11 @@ spec_with name create_new_date parse_date =
|
||||
text = create_new_date 2020 12 21 . format "yyyyMMdd"
|
||||
text . should_equal "20201221"
|
||||
|
||||
Test.specify "should format local date using provided pattern and locale" <|
|
||||
d = create_new_date 2020 6 21
|
||||
d.format "d. MMMM yyyy" (Locale.new "gb") . should_equal "21. Jun 2020"
|
||||
d.format "d. MMMM yyyy" (Locale.new "fr") . should_equal "21. juin 2020
|
||||
|
||||
Test.specify "should format local date using default pattern" <|
|
||||
text = create_new_date 2020 12 21 . to_text
|
||||
text . should_equal "2020-12-21"
|
||||
|
@ -65,6 +65,11 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
|
||||
text = create_new_datetime 1970 (zone = Time_Zone.utc) . format "yyyy-MM-dd'T'HH:mm:ss"
|
||||
text . should_equal "1970-01-01T00:00:00"
|
||||
|
||||
Test.specify "should format using provided pattern and locale" <|
|
||||
d = create_new_datetime 2020 6 21
|
||||
d.format "d. MMMM yyyy" (Locale.new "gb") . should_equal "21. Jun 2020"
|
||||
d.format "d. MMMM yyyy" (Locale.new "fr") . should_equal "21. juin 2020
|
||||
|
||||
Test.specify "should format using default pattern" <|
|
||||
text = create_new_datetime 1970 (zone = Time_Zone.utc) . to_text
|
||||
text . should_equal "1970-01-01T00:00:00Z[UTC]"
|
||||
|
@ -34,6 +34,13 @@ specWith name create_new_time parse_time =
|
||||
text = create_new_time 12 20 44 . format "HHmmss"
|
||||
text . should_equal "122044"
|
||||
|
||||
Test.specify "should format using provided pattern and locale" <|
|
||||
d = create_new_time 12 20 44
|
||||
# Note that the results are all the same.
|
||||
d.format "HH:mm" . should_equal "12:20"
|
||||
d.format "HH:mm" (Locale.new "gb") . should_equal "12:20"
|
||||
d.format "HH:mm" (Locale.new "fr") . should_equal "12:20"
|
||||
|
||||
Test.specify "should format local time using default pattern" <|
|
||||
text = create_new_time 12 20 44 . to_text
|
||||
text . should_equal "12:20:44"
|
||||
|
Loading…
Reference in New Issue
Block a user