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:
GregoryTravis 2023-05-09 04:47:40 -04:00 committed by GitHub
parent 4e7f757f53
commit 4ba8409def
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 637 additions and 24 deletions

View File

@ -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

View File

@ -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 =

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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);

View 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

View File

@ -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

View File

@ -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"

View File

@ -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]"

View File

@ -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"