Help us shape the future of Go! Take the 2026 Go Developer Survey

utils

package
v0.18.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 6, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package utils contains some common utilities used by all other packages.

Index

Constants

View Source
const (
	PrimaryKeySeparator = "-#-" // used to hash a composite primary key

	// MaxTableNameLength is the maximum table name length in MySQL.
	MaxTableNameLength = 64
)
View Source
const (
	// NameFormatTimestamp is the time.Format layout used in the timestamped
	// _<table>_old_<timestamp> name when SkipDropAfterCutover is set.
	NameFormatTimestamp = "20060102_150405"
)

Variables

This section is empty.

Functions

func Abs added in v0.18.0

func Abs[T Signed](n T) T

Abs returns the absolute value of n. The standard library has no integer abs (math.Abs is float64-only), so this avoids a float round trip.

Like any two's-complement abs, it overflows for the most negative value of T: Abs(math.MinInt64) is math.MinInt64.

func AuxTableName added in v0.13.0

func AuxTableName(tableName, suffix string) string

AuxTableName builds a deterministic auxiliary table name for the given original table name and suffix (e.g. "_chkpnt", "_new", "_old"). The returned name is `_<table><suffix>`, with the table-name portion deterministically truncated when needed and the result hard-capped at MySQL's 64-character identifier limit (a defensive guard against an abusively long suffix; in practice callers pass small suffixes).

Truncation is deterministic: the same (tableName, suffix) input always produces the same output. Two distinct table names that share a long common prefix can collide; callers must record the original table name out-of-band (e.g. in the checkpoint) so collisions can be detected.

func CanonicalInteger added in v0.18.0

func CanonicalInteger(s string) (string, bool)

CanonicalInteger returns the decimal integer literal s in the form MySQL renders it as text: no '+' sign, no leading zeros, and "0" for negative zero. So "+007" is "7", "-0" is "0", and "-00123" is "-123". It accepts an optional sign followed by one or more ASCII digits, of any length, and returns false for anything else (a decimal point, an exponent, spaces).

func CheckpointTableName added in v0.13.0

func CheckpointTableName(tableName string) string

CheckpointTableName returns the auxiliary checkpoint table name for the given original table.

func CloseAndLog added in v0.10.2

func CloseAndLog(closer Closer)

CloseAndLog closes a resource and logs any error. This is useful for defer statements where the error cannot be meaningfully handled except by logging. Example: defer utils.CloseAndLog(db)

func CloseAndLogWithContext added in v0.10.2

func CloseAndLogWithContext(ctx context.Context, closer ContextCloser)

CloseAndLogWithContext closes a resource that requires context and logs any error. This is useful for defer statements on resources like table locks. Example: defer utils.CloseAndLogWithContext(ctx, lock)

func DBLevelGrantCoversSchema added in v0.15.0

func DBLevelGrantCoversSchema(grant, schemaName string) bool

DBLevelGrantCoversSchema reports whether a single SHOW GRANTS line is a database-level grant that confers the privileges spirit needs on schemaName. Unlike a literal substring match, it expands MySQL wildcard patterns in the granted database name (see MySQLLikeMatch), so a grant on `strata_%`.* is recognized as covering strata_boardgames_sharded_n80. The previous literal match handled only exact and escaped-underscore database names.

func DBLevelGrantHasAny added in v0.18.0

func DBLevelGrantHasAny(grant, schemaName string, privs ...string) bool

DBLevelGrantHasAny reports whether a single SHOW GRANTS line is a database-level grant whose database name pattern matches schemaName (see MySQLLikeMatch) and that confers ALL PRIVILEGES or any privilege in privs.

func DBLevelGrantName added in v0.18.0

func DBLevelGrantName(grant, schemaName string) (string, bool)

DBLevelGrantName returns the database name of a single SHOW GRANTS line if it is a database-level grant whose name pattern matches schemaName (see MySQLLikeMatch), with the doubled backquotes SHOW GRANTS writes undone. The name is returned as granted, so a pattern keeps its wildcards and escapes.

SHOW GRANTS prints one line per mysql.db row, and MySQL applies only one row to a schema, not the union of every row whose name matches it: an exact-name row can shadow a pattern row, depending on the order the grants were created. Callers that need a privilege on the schema can group the matching lines by this name and require the privilege on every name.

func EstimateRenderedChunkSize added in v0.18.0

func EstimateRenderedChunkSize(rows [][]any) uint64

EstimateRenderedChunkSize estimates the size of a chunk's rows, for the memory-based dynamic chunker: the sum of EstimateRenderedRowSize over every row. It is not exact Go heap accounting (it is the payload plus quotes and separators), but it is good enough to servo chunk row-count toward a byte budget: the servo cares about the relative size of chunks, and any consistent measure converges. It is cheap because the rows are already in hand.

Every row counts as at least 2 bytes (its parentheses), even one whose values are all empty. This keeps the sum non-zero for any chunk that has rows, so a zero-byte total unambiguously means an empty (gap) chunk (see dynamicChunkSizer.feedbackBytes).

func EstimateRenderedRowSize added in v0.18.0

func EstimateRenderedRowSize(values []any) int

EstimateRenderedRowSize estimates the size in bytes of a row's values as they will be rendered into a VALUES clause. It does not need to be precise: the statement budget it feeds (applier.MaxStatementSizeBytes, 1 MiB) sits ~64x below a typical max_allowed_packet, so the estimate only has to be the right order of magnitude to keep a statement well clear of the wire limit.

The applier uses it to cut chunklets, the copier (via EstimateRenderedChunkSize) to size chunks, and callers that batch rows before handing them to Apply to bound a batch by the same measure — the checksum's chunk repair does this.

It does need to be cheap. It runs on every value of every copied row, once per row on top of the rendering writeChunklet does anyway, so it is pure overhead on the hottest client-side path. The previous implementation measured len(fmt.Sprintf("%v", value)), which was neither cheap nor accurate: a text-protocol Scan into *any hands back []byte for string, temporal and DECIMAL columns, and %v renders a []byte as "[49 50 51 …]" — roughly four characters per byte. That cost ~2.2us and ~12 allocations per row and over-estimated by ~2.7x, so chunklets were being cut well short of the budget they were supposed to fill. A type switch is ~290x cheaper, allocates nothing, and lands much closer to what datum.String() actually emits.

func FormatMySQLDouble added in v0.18.0

func FormatMySQLDouble(f float64) string

FormatMySQLDouble renders a double the way MySQL does when it converts one to text with no fixed number of decimals (my_gcvt in strings/dtoa.cc), which is the form SHOW CREATE TABLE reports a float or double column's DEFAULT in.

The digits are the shortest decimal that round-trips to the same double, at most 17 of them. They are laid out in fixed notation unless the exponent makes that longer than MySQL allows: with decpt the position of the decimal point (value = 0.d1d2… × 10^decpt), fixed notation is used while -14 <= decpt <= 15, or when decpt > 15 but the digits already run past the decimal point. Otherwise it is exponent notation in MySQL's own spelling: no '+' on a positive exponent and no padding, with the '.' omitted when there is a single digit. Negative zero is "0". Verified against MySQL 8.0.43:

1e2                   -> 100
1.50                  -> 1.5
0.0000001 (1e-7)      -> 0.0000001
1e-15                 -> 0.000000000000001
1e-16                 -> 1e-16
0.0000000000000001234 -> 1.234e-16
100000000000000 (1e14)-> 100000000000000
1e15                  -> 1e15
1234567890123456      -> 1.234567890123456e15
1234567890123456.7    -> 1234567890123456.8
123456789012345678    -> 1.2345678901234568e17
1.5e300               -> 1.5e300
-0.0                  -> 0

func FormatMySQLFloat added in v0.18.0

func FormatMySQLFloat(f float32) string

FormatMySQLFloat renders a single-precision float the way MySQL does: like FormatMySQLDouble, but with at most 6 significant digits (FLT_DIG), correctly rounded from the float's exact value, trailing zeros dropped. Verified against MySQL 8.0.43:

0.1          -> 0.1
1.23456789   -> 1.23457
1234567      -> 1234570
16777217     -> 16777200
1e-45        -> 1.4013e-45
3.4e38       -> 3.4e38

func FormatScaledDecimal added in v0.18.0

func FormatScaledDecimal(value *big.Int, scale int) string

FormatScaledDecimal renders value × 10^-scale with exactly scale fractional digits, the way MySQL reports a DECIMAL(M,D) value: 124 at scale 2 is "1.24", 5 at scale 2 is "0.05", -124 at scale 2 is "-1.24", and any value at scale 0 is its plain digits.

func GlobalGrantHasAny added in v0.18.0

func GlobalGrantHasAny(grant string, privs ...string) bool

GlobalGrantHasAny reports whether a single SHOW GRANTS line is a global (*.*) grant of ALL PRIVILEGES or of any privilege in privs. ALL PRIVILEGES counts because it includes every static privilege, so privs should contain at least one static privilege for it to be meaningful.

func GlobalGrantNamesAny added in v0.18.0

func GlobalGrantNamesAny(grant string, privs ...string) bool

GlobalGrantNamesAny reports whether a single SHOW GRANTS line is a global (*.*) grant that names any privilege in privs explicitly. Unlike GlobalGrantHasAny, ALL PRIVILEGES does not count. Use it for dynamic privileges such as SHOW_ROUTINE: GRANT ALL includes a dynamic privilege only if it was registered when the grant was issued, so a global ALL grant made before an upgrade can lack it, and SHOW GRANTS still prints ALL PRIVILEGES.

func HashKey

func HashKey(key []any) string

HashKey converts a composite key into a string so that it can be used as a map key. The result is an opaque map key only — it is never reversed back into SQL (the applier builds DELETE literals from the original typed values, not from this string). Each component is escaped before joining so that values containing the separator cannot collide with key boundaries: without escaping, ("a-#-b", "c") and ("a", "b-#-c") would hash to the same string, causing one row's buffered change to silently overwrite another's.

func IsDecimalNumber added in v0.18.0

func IsDecimalNumber(text string) bool

IsDecimalNumber reports whether text is a decimal number in the form MySQL reads from a literal or a string: an optional sign, digits with an optional fraction, and an optional exponent. At least one digit is required, so "-" and "." are not numbers. No surrounding whitespace is accepted; callers that want MySQL's string conversion trim it first.

func IsEnumOrSetType added in v0.18.0

func IsEnumOrSetType(mysqlType string) bool

IsEnumOrSetType reports whether a MySQL column type string is an ENUM or SET.

func IsEnumType added in v0.18.0

func IsEnumType(mysqlType string) bool

IsEnumType reports whether a MySQL column type string (as reported by information_schema.COLUMNS.column_type) is an ENUM, e.g. "enum('a','b')".

func IsSetType added in v0.18.0

func IsSetType(mysqlType string) bool

IsSetType reports whether a MySQL column type string is a SET, e.g. "set('a','b')".

func JSONEqual added in v0.18.0

func JSONEqual(a, b string) (equal, valid bool)

JSONEqual reports whether a and b are the same JSON document, and whether both are valid JSON at all. Object keys compare in any order, whitespace is ignored, and a number compares by the exact value MySQL stores for it in a JSON value: an integer literal that fits in an int64 or a uint64 is that integer, every other number is the nearest double (an integer literal too large for a uint64 included: 12345678901234567890123 is stored as 1.2345678901234568e22). So 9007199254740992 and 9007199254740993 are different documents, 1e2 and 100.0 are the same, as are 1 and 1.0, and the integer 9007199254740993 differs from the double 9007199254740992.0 — and from 9007199254740993.0, which is that same double. Decoding everything to float64 (encoding/json's default), or comparing an integer against a double as float64, folds integers past 2^53 together, which hides a change MySQL stores and reports.

func MySQLLikeMatch added in v0.15.0

func MySQLLikeMatch(pattern, name string) bool

MySQLLikeMatch reports whether name matches the given MySQL LIKE-style pattern. As in MySQL pattern matching, '%' matches any sequence of characters (including the empty string), '_' matches any single character, and a backslash escapes the following character so that it is treated as a literal (so `\%` and `\_` match a literal '%' and '_').

This mirrors how MySQL evaluates the database-name portion of a database-level GRANT. A grant on `strata_%`.* applies to a database named strata_boardgames, even though SHOW GRANTS reports the pattern verbatim. Privilege checks therefore cannot compare the granted database name to the target schema with a plain string match; they must expand the pattern.

func NewTableName added in v0.13.0

func NewTableName(tableName string) string

NewTableName returns the auxiliary _new table name for the given original table.

func OldTableName added in v0.13.0

func OldTableName(tableName string) string

OldTableName returns the auxiliary _old table name for the given original table.

func OldTableNameWithTimestamp added in v0.13.0

func OldTableNameWithTimestamp(tableName, timestamp string) string

OldTableNameWithTimestamp returns the auxiliary _old_<timestamp> table name for the given original table and timestamp string. Used when SkipDropAfterCutover is set so the renamed-away table is preserved with a unique name across multiple migrations.

func ParseEnumSetElements added in v0.18.0

func ParseEnumSetElements(mysqlType string) ([]string, error)

ParseEnumSetElements extracts the element values from a MySQL type string like "enum('a','b','c')" or "set('x','y','z')", as information_schema reports it in COLUMNS.COLUMN_TYPE.

It uses a small state machine to correctly handle values containing commas, the characters MySQL escapes in that text, and the hex literals it writes for members that are not valid utf8mb3 (see QuoteEnumSetMember).

It is fail-closed: any unexpected characters or malformed structure returns an error so that callers (safety checks, the binlog ENUM/SET decoder) are never silently bypassed.

func ParseRoleNames added in v0.18.0

func ParseRoleNames(grant string) []string

ParseRoleNames extracts role names from a SHOW GRANTS line that grants roles. e.g. "GRANT `rds_superuser_role`@`%`,`other_role`@`%` TO `user`@`%`" returns ["rds_superuser_role", "other_role"]

func ParseSizeNumber added in v0.18.0

func ParseSizeNumber(s string) (uint64, error)

ParseSizeNumber parses a MySQL size_number: an unsigned integer with an optional K, M or G suffix (case-insensitive), the form AUTOEXTEND_SIZE and the tablespace sizes accept. It returns the value in bytes, so "4M" is 4194304. Any other suffix is an error, as it is in MySQL.

func Percentile added in v0.18.0

func Percentile[T cmp.Ordered](values []T, p int) T

Percentile returns the p-th percentile (0 < p <= 100) of values, which may be in any order. It uses the nearest-rank method: the smallest value with at least p% of the samples at or below it, i.e. sorted[ceil(n*p/100) - 1]. It returns the zero value for an empty slice. A p outside (0, 100] is clamped: p <= 0 returns the smallest value and p > 100 the largest, so a bad p cannot panic.

values is not modified: Percentile sorts a copy, so callers can pass a live history (such as a ring buffer) without it being reordered. The rank is computed in integer arithmetic so float rounding of n*p can never move it by one.

func ProcedureGrantHasAny added in v0.18.0

func ProcedureGrantHasAny(grant, schemaName, procName string, privs ...string) bool

ProcedureGrantHasAny reports whether a single SHOW GRANTS line is a procedure-level grant on schemaName.procName that confers any privilege in privs. Stored routine names are not case sensitive, so neither name is compared case sensitively.

func QuoteEnumSetMember added in v0.18.0

func QuoteEnumSetMember(member string) string

QuoteEnumSetMember renders an ENUM or SET member the way MySQL writes it in information_schema.COLUMNS.COLUMN_TYPE (and SHOW CREATE TABLE).

A member that is valid utf8mb3 is written in single quotes, with a single quote doubled and a backslash, newline, carriage return and NUL backslash-escaped. No other character is escaped: a tab, Ctrl-Z, backspace or double quote is written as itself. This is MySQL's append_unescaped().

Any other member is written as a hex literal of its bytes, e.g. x'815c': invalid UTF-8, and a character outside utf8mb3 (a 4-byte UTF-8 character). Only a binary-charset column can hold such bytes, and MySQL writes its members this way. (A column of any other charset holds a 4-byte character only in utf8mb4, and MySQL reports that character as '?'. Spirit writes the member instead, so that it reads back unchanged.)

parseSQLQuotedList decodes exactly these forms.

func RoundDecimal added in v0.18.0

func RoundDecimal(text string, scale int) (value *big.Int, exact, ok bool)

RoundDecimal reads a decimal number (see IsDecimalNumber) exactly and rounds it to scale fractional digits, half away from zero, as MySQL rounds a decimal literal or a string into a DECIMAL or integer column. It returns the rounded value scaled by 10^scale as an integer (so 1.235 at scale 2 is 124), whether the rounding was exact, and false for a text that is not a decimal number or whose exponent is beyond any MySQL type's range. A result of zero is never negative, which is also how MySQL stores -0.001 at scale 2.

func StringContainsAll added in v0.18.0

func StringContainsAll(s string, substrings ...string) bool

StringContainsAll returns true if `s` contains all non empty given `substrings` The function returns `false` if no non-empty arguments are given.

func TopologicalOrder added in v0.18.0

func TopologicalOrder[T comparable](nodes []T, dependsOn map[T][]T) []T

TopologicalOrder returns nodes reordered so that every node comes after the nodes it depends on. dependsOn maps a node to the nodes that must precede it; a dependency that is not itself in nodes, or is the node itself, is ignored.

The order is deterministic: among the nodes whose dependencies are all placed, the earliest in nodes goes next, so nodes with no dependency between them keep their input order. A dependency cycle cannot be satisfied; when every remaining node still waits on another, the earliest remaining node that another remaining node waits on is placed anyway, so the broken edge is one of the cycle's and no node is placed ahead of a dependency it could have followed. nodes must not contain duplicates.

func TruncateTableName added in v0.13.0

func TruncateTableName(tableName string, suffixLength int) string

TruncateTableName truncates a table name to fit within MySQL's 64-character limit, reserving space for a suffix of the given length. If the table name already fits, it is returned unchanged.

func UnsupportedIdentifierError added in v0.18.0

func UnsupportedIdentifierError(kind, name string) error

UnsupportedIdentifierError returns an error when name, a schema or table name, contains a character Spirit refuses in those identifiers, and nil otherwise. kind describes the identifier in the error message, e.g. "table name" or "schema name".

Two characters are refused:

  • '.': the replication client keys each table's subscription by joining its schema and table name with a '.', so `a`.`b.c` and `a.b`.`c` map to the same key and a row change on one table can be applied to the other.
  • '`': every statement Spirit builds that names the table has to escape the backtick, and a single missed escape produces broken or wrong SQL. Refusing the name removes that class of bug rather than chasing each statement.

func ValidUTF8MB3 added in v0.18.0

func ValidUTF8MB3(s string) bool

ValidUTF8MB3 reports whether s is valid in MySQL's utf8mb3 charset: valid UTF-8 with no character outside the Basic Multilingual Plane, since utf8mb3 stores at most 3 bytes per character. A 4-byte character such as an emoji is valid utf8mb4 but not utf8mb3.

It is the test MySQL 8.0.33+ applies when SHOW CREATE TABLE converts a binary, varbinary, or utf8mb4 char or varchar column's DEFAULT to the system charset: a default that fails it is reported as a hex literal instead of a string.

func WithCancelGrace added in v0.18.0

func WithCancelGrace(parent context.Context, grace time.Duration) (context.Context, context.CancelFunc)

WithCancelGrace returns a context that keeps parent's values and deadline but outlives parent's cancellation by grace: it is canceled grace after parent is canceled, at parent's deadline, or when the returned cancel is called, whichever comes first.

Use it for a statement that must not be abandoned mid-flight on cancel. The MySQL driver's cancellation only closes the socket, so a statement the server already received can still commit after the caller has moved on.

Types

type Closer added in v0.10.2

type Closer interface {
	Close() error
}

Closer is an interface for types that have a Close() method. This is compatible with io.Closer and many other types in the codebase.

type ContextCloser added in v0.10.2

type ContextCloser interface {
	Close(context.Context) error
}

ContextCloser is an interface for types that have a Close(context.Context) method. This is used for resources like table locks that need context for cleanup.

type Signed added in v0.18.0

type Signed interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64
}

Signed is the set of signed integer types Abs accepts.

type Temporal added in v0.18.0

type Temporal struct {
	Negative                    bool // TIME only
	Year, Month, Day            int  // DATETIME and DATE; zero for TIME
	Hour, Minute, Second, Micro int  // Hour runs to 838 for TIME
	// contains filtered or unexported fields
}

Temporal is a MySQL DATETIME, DATE or TIME value as MySQL reads it from a literal: its fields, its microseconds, and the digit MySQL keeps past the microseconds to round them with. Build one with ParseDateTimeString, ParseDateTimeNumber, ParseTimeString or ParseTimeNumber; round it to the column's fractional-seconds precision with Temporal.RoundDateTime or Temporal.RoundTime, or truncate it with Temporal.Truncate; render it with Temporal.DateTimeString, Temporal.DateString or Temporal.TimeString.

The parsers accept the spellings MySQL 8.0 accepts and reads unambiguously, and reject (return false for) everything else, including the spellings MySQL rejects. A caller canonicalizing a literal can therefore leave any rejected text as written: it is either invalid, which MySQL reports the same way in any spelling, or a form rare enough not to be worth modelling.

func ParseDateTimeNumber added in v0.18.0

func ParseDateTimeNumber(text string) (Temporal, bool)

ParseDateTimeNumber reads a DATETIME, TIMESTAMP or DATE numeric literal the way MySQL does (number_to_datetime): the integer part is YYMMDD, YYYYMMDD, YYMMDDHHMMSS or YYYYMMDDHHMMSS by its magnitude, so 10101 is 2001-01-01 and 9991231 is 0999-12-31; a two-digit year resolves to 1970-2069. A fraction is read only after the fourteen-digit form. Zero, which MySQL rejects as a default, is false, and so is a thirteen-digit number (a year 100-999 datetime), which is left to the caller.

func ParseDateTimeString added in v0.18.0

func ParseDateTimeString(text string) (Temporal, bool)

ParseDateTimeString reads a DATETIME, TIMESTAMP or DATE string literal: 'YYYY-MM-DD[ HH:MM[:SS[.fraction]]]' with any punctuation between the date fields and between the time fields, spaces, a 'T' or punctuation between the date and the time, fields of any width, a trailing separator, and a two-digit year resolved to 1970-2069; or the compact 'YYMMDD', 'YYYYMMDD', 'YYMMDDHHMMSS', 'YYYYMMDDHHMMSS' and 'YYMMDDTHHMMSS' / 'YYYYMMDDTHHMMSS', the last four with an optional fraction. Whitespace around the value is skipped. The fields are range-checked (month, day for the month and year, hour, minute, second), so a date MySQL rejects is false. The time fields are zero when absent; the caller decides whether to keep them.

A time-zone suffix ('2020-01-01 10:00:00+00:00') is false: MySQL converts the value to the session time zone, so no fixed text reproduces it.

func ParseTimeNumber added in v0.18.0

func ParseTimeNumber(text string) (Temporal, bool)

ParseTimeNumber reads a TIME numeric literal the way MySQL does (number_to_time): the integer part is HHMMSS read right to left, up to 838:59:59, and the fraction is seconds.

func ParseTimeString added in v0.18.0

func ParseTimeString(text string) (Temporal, bool)

ParseTimeString reads a TIME string literal: '[-][D ]HH:MM[:SS][.fraction]' with fields of any width and the day part separated by spaces or tabs, or '[-]HHMMSS[.fraction]' read right to left as seconds, minutes and hours. Whitespace around the value is skipped. Hours (days × 24 + hours) run to 838 and minutes and seconds to 59; the maximum 838:59:59 admits no fraction. A fraction past six digits is rounded from its last digit, which is how MySQL's TIME reader (unlike its DATETIME reader) carries the extra digits.

MySQL first tries a string of twelve or more characters as a DATETIME and keeps its time part ('2020-01-01 10:00:00' is '10:00:00'); such strings are false here unless they cannot be a date, so the caller leaves them alone.

func (Temporal) DateString added in v0.18.0

func (t Temporal) DateString() string

DateString renders 'YYYY-MM-DD'.

func (Temporal) DateTimeString added in v0.18.0

func (t Temporal) DateTimeString(fsp int) string

DateTimeString renders 'YYYY-MM-DD HH:MM:SS' with fsp fraction digits.

func (Temporal) RoundDateTime added in v0.18.0

func (t Temporal) RoundDateTime(fsp int) (Temporal, bool)

RoundDateTime rounds the value to fsp fractional digits the way MySQL stores it under its default sql_mode: the digit past the microseconds rounds them first, then the microseconds round half up to fsp, each carrying into the seconds and on through the date. It returns false when the carry runs past 9999-12-31, and when it happens in year 0000, where MySQL does not carry the date but stores the zero date ('0000-12-09 23:59:59.5' is '0000-00-00 00:00:00', '0000-06-15 10:00:00.5' is '0000-00-00 10:00:01'), a value this reader does not produce.

func (Temporal) RoundTime added in v0.18.0

func (t Temporal) RoundTime(fsp int) (Temporal, bool)

RoundTime is Temporal.RoundDateTime for a TIME value: the carry runs into the hours, and the result is false past 838:59:59.

func (Temporal) TimeString added in v0.18.0

func (t Temporal) TimeString(fsp int) string

TimeString renders '[-]HH:MM:SS' with fsp fraction digits; hours take as many digits as they need, and a zero value carries no sign.

func (Temporal) Truncate added in v0.18.0

func (t Temporal) Truncate(fsp int) Temporal

Truncate drops the fraction past fsp digits the way MySQL stores the value with TIME_TRUNCATE_FRACTIONAL in its sql_mode, for a DATETIME, DATE or TIME alike: nothing rounds and nothing carries (a negative TIME truncates toward zero), so the result is always valid.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL