Class SharedFunctionHelpers
-
Method Summary
Modifier and TypeMethodDescriptionstatic ObjectatDeclaredPrecision(Object value, int precision) A temporal value truncated to a declared fractional precision — the digits past it are dropped, never rounded — for the TIME and TIMESTAMP flavours alike.static byte[]binaryArgBytes(Object value, String functionName) The byte payload of a BINARY-typed argument.static StringcanonicalDateUnit(Object raw) static longdatePart(String unitRaw, LocalDateTime dt) static longdatePart(String unitRaw, LocalDateTime dt, Object source, String functionName) datePart(String, LocalDateTime, String)given the ORIGINAL value as well as its wall clock, which two families of component need: the EPOCH ones read the instant, and the TIMEZONE ones read the offset itself.static longdatePart(String unitRaw, LocalDateTime dt, String functionName) A component of a timestamp, reported forfunctionNamewhen the word names none.static longA component of a TIME.static byte[]static BigDecimaldivideWithSnowflakeScale(BigDecimal dividend, BigDecimal divisor) Divide with Snowflake's result scale:max(S1, min(S1 + 6, 12))where S1 is the dividend's scale (docs: "Arithmetic operators — division"), rounding half away from zero.static LocalDateTimeepochAtScaleToLocalDateTime(long epoch, int scale) static LocalDateTimeepochDecimalToLocalDateTime(BigDecimal seconds) Interpret a numeric TO_TIMESTAMP argument at an explicit scale: the value counts 10-scale-second units since the Unix epoch (scale 0 = seconds — Snowflake's default for a NUMERIC argument, however large — 3 = milliseconds, 9 = nanoseconds).static LocalDateTimeepochToLocalDateTime(long epoch) Interpret a value as a Unix-epoch instant, picking the unit by magnitude the way Snowflake does for a STRING argument containing an integer: |v| < 31,536,000,000 → seconds, else milliseconds, else microseconds, else nanoseconds.static StringfloatText(double d) Snowflake's FLOAT-to-VARCHAR rendering.static longindexOfBytes(byte[] haystack, byte[] needle, int fromIndex) The 1-based position ofneedleinhaystackat or after the 0-basedfromIndex, or 0 when absent — the byte-wise counterpart ofString.indexOfused by POSITION / CHARINDEX over BINARY values.static RuntimeExceptioninvalidDatePart(String unitRaw, String functionName) The bad-component refusal EXTRACT and DATE_PART share, each naming itself as the parameter.static booleanWhether a word names a date COMPONENT that no interval can be measured in.static booleanisExplicitFormat(String format) Whether a TO_DATE / TO_TIME / TO_TIMESTAMP format argument names an explicit model: anything but the word AUTO, in any case — the empty string included, which is a model that matches nothing, so every input is refused under it (live-verified:TO_DATE('2020-01-15', '')is "Can't parse '2020-01-15' as date with format ''").static booleanstatic RuntimeExceptionnotAComponentOfType(Object raw, String functionName, String typeName) The refusal for a unit that is not a component of the VALUE's type — a day or larger asked of a TIME, which carries no date to truncate.static RuntimeExceptionnotADateTimeComponent(Object raw, String functionName) The refusal for a word that is not a date/time component of the function that was given it.static StringoffsetText(ZoneOffset offset) Zat zero, else±HHMM— Snowflake's offset spelling.static StringA zone-carrying timestamp's default output text: the wall clock followed by its OFFSET.static LocalDateparseDateWithFormat(Object value, String format) Parse a value to aLocalDatefor TO_DATE: a numeric value is a Unix epoch (its date part); a string is read against an explicit Snowflake format when one is supplied (a mismatch is "Can't parse '<input>' as date with format '<format>'"), else viatoLocalDate(java.lang.Object).static DayOfWeekparseDayOfWeek(String name) static LocalDateTimeparseTimestampWithFormat(Object value, String format) Parse a value to aLocalDateTimefor TO_TIMESTAMP: a numeric value is a Unix epoch in SECONDS (magnitude-based unit detection applies only to a string of digits — live: TO_TIMESTAMP_NTZ(1631711999000) is year 53676 while TO_TIMESTAMP_NTZ('1631711999000') is 2021-09-15); a string is read against an explicit Snowflake format when one is supplied — a mismatch is the account's own refusal, "Can't parse '<input>' as timestamp with format '<format>'" — under AUTO a string of digits is a magnitude-detected epoch (epochToLocalDateTime(long)), and any other string is read flexibly viatoLocalDateTime(java.lang.Object).static LocalDateTimeparseTimestampWithFormatOrScale(Object value, Object formatOrScale) static ObjectsameTimestampFlavour(Object source, LocalDateTime computed) A computed timestamp given back in the FLAVOUR of the value it was computed from: an LTZ input yields an LTZ result, soDATEADD(day, 1, tl)keeps its offset instead of decaying to a TIMESTAMP_NTZ, and a TZ input keeps the offset it was written with.static StringstripPluralS(String upper) unit.toUpperCase()with one trailingSremoved — the plural strip without a per-call regex compile.static StringtemporalSqlLiteral(Object value) A temporal value as a type-preserving SQL literal ('2026-01-03'::DATE), so a value inlined into regenerated SQL re-enters the engine as a temporal rather than a VARCHAR.static StringA value rendered for a STRING context (::VARCHAR, TO_VARCHAR/TO_CHAR without a format,||, embedding into a VARIANT): temporals use Snowflake's default output forms above, everything else its ordinary text.static StringSnowflake's default TIMESTAMP output text —YYYY-MM-DD HH24:MI:SS.FF3: a SPACE separator and EXACTLY three fractional digits (…:38.604,…:28.000).static StringSnowflake's default TIME output text —HH24:MI:SS, no fractional part.static StringtoHex(byte[] bytes) static LocalDatestatic LocalDateTimestatic LocalDateTimetoLocalDateTime(Object v, boolean targetCarriesZone) The same reader, told whether the target CARRIES a zone.static LocalTimestatic OffsetDateTimeA TIMESTAMP_LTZ value: the INSTANT the input names, expressed at the SESSION zone's offset for that instant.static ObjecttoTemporalValue(String baseTypeUpper, Object value) static byte[]The byte payload a function should hash, digest or encode: a BINARY value contributes its OWN bytes (never its hex rendering —BinaryValue.toString()is the display form, sotoString().getBytes()would silently encode the ASCII hex digits and double the length), and everything else contributes the UTF-8 encoding of its Snowflake output text.static ZonedDateTimeA TIMESTAMP_TZ value: the instant the input names, kept at the offset it was WRITTEN with.static StringvariantDateText(LocalDate date) A DATE's text inside a VARIANT:textOf(java.lang.Object)'s, except that a year before the first keeps its proleptic number, unpadded — live's VARIANT of the second year BC reads"-1-01-01"where its ::VARCHAR reads0002-01-01, and one past 9999 reads"20201-01-15"(live-verified).static StringA TIMESTAMP_TZ's output text: the wall clock it was WRITTEN with, followed by the offset it was written with.static StringyearText(int prolepticYear) A YEAR as the account prints it in every text of a date or a timestamp: the year of the era, four digits at least and never a sign —20201for a year past 9999, where java.time's own rendering is+20201, and0002for the second year before the first, where it is-0001(live-verified over ::VARCHAR, TO_VARCHAR,||and the driver's text).
-
Method Details
-
divideWithSnowflakeScale
Divide with Snowflake's result scale:max(S1, min(S1 + 6, 12))where S1 is the dividend's scale (docs: "Arithmetic operators — division"), rounding half away from zero. Integer / integer therefore yields six fractional digits —1/3 = 0.333333— matching Snowflake output. Runtime values carry no declared column scale, so S1 is the value's carried scale, UNSTRIPPED —10/2keeps its six digits (5.000000) exactly as Snowflake renders it. -
digest
-
toHex
-
temporalSqlLiteral
A temporal value as a type-preserving SQL literal ('2026-01-03'::DATE), so a value inlined into regenerated SQL re-enters the engine as a temporal rather than a VARCHAR. Returns null for non-temporal values. -
toUtf8
The byte payload a function should hash, digest or encode: a BINARY value contributes its OWN bytes (never its hex rendering —BinaryValue.toString()is the display form, sotoString().getBytes()would silently encode the ASCII hex digits and double the length), and everything else contributes the UTF-8 encoding of its Snowflake output text.The text is
textOf(java.lang.Object)rather thantoString()so a TIMESTAMP contributes2024-01-02 03:04:05.000— what Snowflake hashes — instead of java.time's2024-01-02T03:04:05. -
indexOfBytes
public static long indexOfBytes(byte[] haystack, byte[] needle, int fromIndex) The 1-based position ofneedleinhaystackat or after the 0-basedfromIndex, or 0 when absent — the byte-wise counterpart ofString.indexOfused by POSITION / CHARINDEX over BINARY values. An empty needle matches atfromIndex. -
binaryArgBytes
The byte payload of a BINARY-typed argument. Snowflake rejects a VARCHAR where BINARY is expected (no implicit text-to-binary coercion for these functions), so anything that is not an actual binary runtime value is an argument-type error. -
isTruthy
-
timestampText
Snowflake's default TIMESTAMP output text —YYYY-MM-DD HH24:MI:SS.FF3: a SPACE separator and EXACTLY three fractional digits (…:38.604,…:28.000).LocalDateTime.toStringinstead used aTand dropped trailing zeros, so every text rendering of a timestamp (::VARCHAR, TO_VARCHAR, ||, a value embedded in a VARIANT) differed from Snowflake's. -
yearText
A YEAR as the account prints it in every text of a date or a timestamp: the year of the era, four digits at least and never a sign —20201for a year past 9999, where java.time's own rendering is+20201, and0002for the second year before the first, where it is-0001(live-verified over ::VARCHAR, TO_VARCHAR,||and the driver's text).- Parameters:
prolepticYear- the ISO year, 0 being the first year before the first- Returns:
- the year's text
-
variantDateText
A DATE's text inside a VARIANT:textOf(java.lang.Object)'s, except that a year before the first keeps its proleptic number, unpadded — live's VARIANT of the second year BC reads"-1-01-01"where its ::VARCHAR reads0002-01-01, and one past 9999 reads"20201-01-15"(live-verified).- Parameters:
date- the date- Returns:
- its text as a VARIANT string holds it
-
offsetTimestampText
A zone-carrying timestamp's default output text: the wall clock followed by its OFFSET. A zero offset is spelledZ, never+0000— live prints2020-01-01 18:00:00.000 Zfor a TIMESTAMP_LTZ read under UTC, and the sameZfor a literal written with one. -
writtenOffsetTimestampText
A TIMESTAMP_TZ's output text: the wall clock it was WRITTEN with, followed by the offset it was written with. UnlikeoffsetTimestampText(java.time.OffsetDateTime)this never re-expresses — that is the whole difference between the two zoned types. Live: a value written10:00:00 +0300reads back2020-01-01 10:00:00.000 +0300under America/Los_Angeles and, unchanged, under UTC, while an LTZ holding the same instant follows the session.- Parameters:
dt- the value, whose zone is the fixed offset it carries- Returns:
- the rendering, a zero offset spelled
Z
-
offsetText
Zat zero, else±HHMM— Snowflake's offset spelling. -
timeText
Snowflake's default TIME output text —HH24:MI:SS, no fractional part. -
textOf
A value rendered for a STRING context (::VARCHAR, TO_VARCHAR/TO_CHAR without a format,||, embedding into a VARIANT): temporals use Snowflake's default output forms above, everything else its ordinary text. A DATE isYYYY-MM-DDwith its era year — seeyearText(int). -
floatText
Snowflake's FLOAT-to-VARCHAR rendering. Java's shortest-round-tripDouble.toStringprinted0.8999999761581421where Snowflake prints0.8999999762, so every SHA2/checksum over a stringified float diverged.The WIDTH is not one number. Live-verified across a magnitude ladder, the significant digits are ten below 10 and then one more per decade, capped at fourteen from 1e4 up:
1.414213562 10 sig (and every smaller magnitude, down to 1.414213562e-16) 14.142135624 11 141.421356237 12 1414.213562373 13 14142.135623731 14 10686474581524.5 15 250000000000000 15 …
THE CAP IS FIFTEEN, AND A LADDER BUILT ON SQRT(2) CANNOT SEE IT: that value's fifteenth significant digit is a zero, so its fourteen- and fifteen-digit renderings are the same text once trailing zeros are stripped. EXP(30) is what separates them —
10686474581524.5is fifteen significant digits where fourteen would have dropped the.5.The notation follows from the width for free:
%gturns scientific once the exponent reaches the precision, and with fifteen digits from 1e5 up that lands the plain range on an exponent of −4 through 14 —2.5::FLOAT * 1e14stays250000000000000and 1e15 is the first to leave, both live-verified. -
toLocalDate
-
toLocalDateTime
-
toLocalDateTime
The same reader, told whether the target CARRIES a zone. A TIMESTAMP_TZ or _LTZ moves the instant by the offset the text gives; a TIMESTAMP_NTZ or a DATE keeps the wall clock and drops it, which is what live does —'2020-01-01 10:00:00 +0300'::TIMESTAMP_NTZis ten o'clock there, and the same text as a TIMESTAMP_TZ is seven o'clock UTC.Frostlake used to do neither:
+0300would not parse at all, and+03:00parsed with the offset silently DISCARDED — so the value read back as a different instant, which is worse than the refusal beside it.The offset has nowhere to be KEPT while these are held as a naive LocalDateTime, so a TIMESTAMP_TZ is normalised to UTC rather than remembering that it was written
+0300. Rendering it back the way live does is a separate piece of work. -
toSessionOffsetDateTime
A TIMESTAMP_LTZ value: the INSTANT the input names, expressed at the SESSION zone's offset for that instant. Live proves it is an instant and not a wall clock, because the same stored value re-renders when the session zone changes —2020-01-01 10:00:00 -0800under America/Los_Angeles is2020-01-01 18:00:00 Zunder UTC, digits and all.Text WITHOUT an offset names a wall clock in the session's zone; text WITH one already fixes the instant and is merely re-expressed. The offset is the zone's AT THAT INSTANT, so a January value and a June value in the same zone carry different ones.
-
toWrittenOffsetTimestamp
A TIMESTAMP_TZ value: the instant the input names, kept at the offset it was WRITTEN with.THIS IS WHAT SEPARATES THE TWO ZONED TYPES. An LTZ is an instant that re-renders in whatever zone the session currently has; a TZ remembers how it was spelled and never moves. Both agree on the instant underneath —
10:00 +0300really is07:00UTC, and comparisons, ordering and EPOCH all read it that way — so the offset is presentation, and only presentation.Text carrying NO offset takes the session zone's offset AT THAT INSTANT, and then that offset is fixed: the same literal cast under America/Los_Angeles reads
-0800and under UTC readsZ, each staying put afterwards. The value is held as aZonedDateTimewhose zone IS that offset, which is what distinguishes it from theOffsetDateTimean LTZ carries.- Parameters:
v- the text, or an already-temporal value to re-flavour- Returns:
- the TZ value, or null for null input
-
sameTimestampFlavour
A computed timestamp given back in the FLAVOUR of the value it was computed from: an LTZ input yields an LTZ result, soDATEADD(day, 1, tl)keeps its offset instead of decaying to a TIMESTAMP_NTZ, and a TZ input keeps the offset it was written with. The wall clock is what the caller computed; only the flavour is restored. -
toLocalTime
-
atDeclaredPrecision
A temporal value truncated to a declared fractional precision — the digits past it are dropped, never rounded — for the TIME and TIMESTAMP flavours alike. A DATE, a precision of nine (or none) and any non-temporal value pass through untouched.- Parameters:
value- the temporal value, in any of the Java carriersprecision- the declared fractional-second digits, 0 to 9- Returns:
- the value at that precision
-
toTemporalValue
-
epochToLocalDateTime
Interpret a value as a Unix-epoch instant, picking the unit by magnitude the way Snowflake does for a STRING argument containing an integer: |v| < 31,536,000,000 → seconds, else milliseconds, else microseconds, else nanoseconds. Returns the wall-clock time at UTC. A NUMERIC TO_TIMESTAMP argument must NOT go through this detection — it is always seconds (seeparseTimestampWithFormatOrScale(java.lang.Object, java.lang.Object)). -
epochDecimalToLocalDateTime
Interpret a numeric TO_TIMESTAMP argument at an explicit scale: the value counts 10-scale-second units since the Unix epoch (scale 0 = seconds — Snowflake's default for a NUMERIC argument, however large — 3 = milliseconds, 9 = nanoseconds). Returns the wall-clock time at UTC. -
epochAtScaleToLocalDateTime
-
isExplicitFormat
Whether a TO_DATE / TO_TIME / TO_TIMESTAMP format argument names an explicit model: anything but the word AUTO, in any case — the empty string included, which is a model that matches nothing, so every input is refused under it (live-verified:TO_DATE('2020-01-15', '')is "Can't parse '2020-01-15' as date with format ''"). -
parseTimestampWithFormatOrScale
-
parseTimestampWithFormat
Parse a value to aLocalDateTimefor TO_TIMESTAMP: a numeric value is a Unix epoch in SECONDS (magnitude-based unit detection applies only to a string of digits — live: TO_TIMESTAMP_NTZ(1631711999000) is year 53676 while TO_TIMESTAMP_NTZ('1631711999000') is 2021-09-15); a string is read against an explicit Snowflake format when one is supplied — a mismatch is the account's own refusal, "Can't parse '<input>' as timestamp with format '<format>'" — under AUTO a string of digits is a magnitude-detected epoch (epochToLocalDateTime(long)), and any other string is read flexibly viatoLocalDateTime(java.lang.Object). An offset the model reads is dropped: this is the NTZ wall clock. -
parseDateWithFormat
Parse a value to aLocalDatefor TO_DATE: a numeric value is a Unix epoch (its date part); a string is read against an explicit Snowflake format when one is supplied (a mismatch is "Can't parse '<input>' as date with format '<format>'"), else viatoLocalDate(java.lang.Object). -
datePart
A component of a TIME. A time of day carries no date, so only the clock components are readable; the day-or-larger units are refused before this is reached (they cannot be decided from a value, because a refusal raised here would never fire over zero rows).- Parameters:
unitRaw- the component as writtentime- the time of day- Returns:
- the component's value
-
datePart
-
datePart
datePart(String, LocalDateTime, String)given the ORIGINAL value as well as its wall clock, which two families of component need: the EPOCH ones read the instant, and the TIMEZONE ones read the offset itself.- Parameters:
unitRaw- the component as writtendt- the wall clocksource- the value the wall clock came from, or null when there is none to consultfunctionName- the function to name in a refusal- Returns:
- the component's value
-
datePart
A component of a timestamp, reported forfunctionNamewhen the word names none.EXTRACT and DATE_PART share ONE vocabulary — measured word by word down the whole abbreviation ladder, and identical at every rung, which is not what either function's own history suggested. What differs is only the sentence: each names ITSELF as the parameter, and the word is quoted exactly as it arrived. DATE_PART's bareword is upper-cased before it gets here (its argument is a unit SLOT), so DATE_PART(ss, …) reports [SS] while DATE_PART('ss', …) and EXTRACT(ss FROM …) both report [ss].
-
parseDayOfWeek
-
stripPluralS
unit.toUpperCase()with one trailingSremoved — the plural strip without a per-call regex compile. -
invalidDatePart
The bad-component refusal EXTRACT and DATE_PART share, each naming itself as the parameter. -
notADateTimeComponent
The refusal for a word that is not a date/time component of the function that was given it. The account quotes the word as the CALL wrote it — a bareword arrives already upper-cased, a quoted literal keeps its own case — so the text is passed through rather than folded here.- Parameters:
raw- the unit word as it reached the functionfunctionName- the function reporting it- Returns:
- the refusal to throw
-
notAComponentOfType
public static RuntimeException notAComponentOfType(Object raw, String functionName, String typeName) The refusal for a unit that is not a component of the VALUE's type — a day or larger asked of a TIME, which carries no date to truncate. The account spells this one differently from the unit-not-known refusal beside it: the word is bracketed WITHOUT quotes, upper-cased whatever the call wrote, and the sentence names the type as well as the function.- Parameters:
raw- the unit word as writtenfunctionName- the function reporting ittypeName- the argument's type- Returns:
- the refusal to throw
-
isComponentOnlyUnit
Whether a word names a date COMPONENT that no interval can be measured in.woyandweekofyearare units for INTERVAL and readable by DATE_PART, but DATEADD, DATEDIFF and DATE_TRUNC all refuse them — the one place the two vocabularies disagree.- Parameters:
raw- the unit word as written- Returns:
- true when an interval function must refuse it
-
canonicalDateUnit
-