GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — GitHub - Kruutteri1/java_time_utils: A Ballerina wrapper library around java.time

BranchesTags
Open more actions menu

Latest commit

 

History

30 Commits
30 Commits

Folders and files

NameName
Last commit message
Last commit date
tests
tests
 
 
.devcontainer.json
.devcontainer.json
 
 
Appendable.bal
Appendable.bal
 
 
Ballerina.toml
Ballerina.toml
 
 
ChronoLocalDate.bal
ChronoLocalDate.bal
 
 
ChronoLocalDateTime.bal
ChronoLocalDateTime.bal
 
 
ChronoZonedDateTime.bal
ChronoZonedDateTime.bal
 
 
Chronology.bal
Chronology.bal
 
 
Class.bal
Class.bal
 
 
Comparator.bal
Comparator.bal
 
 
DateTimeFormatter.bal
DateTimeFormatter.bal
 
 
DayOfWeek.bal
DayOfWeek.bal
 
 
DecimalStyle.bal
DecimalStyle.bal
 
 
Duration.bal
Duration.bal
 
 
Enum.bal
Enum.bal
 
 
Format.bal
Format.bal
 
 
FormatStyle.bal
FormatStyle.bal
 
 
Instant.bal
Instant.bal
 
 
InterruptedException.bal
InterruptedException.bal
 
 
IsoChronology.bal
IsoChronology.bal
 
 
List.bal
List.bal
 
 
LocalDate.bal
LocalDate.bal
 
 
LocalDateTime.bal
LocalDateTime.bal
 
 
LocalTime.bal
LocalTime.bal
 
 
Locale.bal
Locale.bal
 
 
Map.bal
Map.bal
 
 
Month.bal
Month.bal
 
 
Object.bal
Object.bal
 
 
OffsetDateTime.bal
OffsetDateTime.bal
 
 
OffsetTime.bal
OffsetTime.bal
 
 
Optional.bal
Optional.bal
 
 
ParsePosition.bal
ParsePosition.bal
 
 
Period.bal
Period.bal
 
 
README.md
README.md
 
 
ResolverStyle.bal
ResolverStyle.bal
 
 
Set.bal
Set.bal
 
 
Stream.bal
Stream.bal
 
 
Temporal.bal
Temporal.bal
 
 
TemporalAccessor.bal
TemporalAccessor.bal
 
 
TemporalAdjuster.bal
TemporalAdjuster.bal
 
 
TemporalAmount.bal
TemporalAmount.bal
 
 
TemporalField.bal
TemporalField.bal
 
 
TemporalQuery.bal
TemporalQuery.bal
 
 
TemporalUnit.bal
TemporalUnit.bal
 
 
TextStyle.bal
TextStyle.bal
 
 
ValueRange.bal
ValueRange.bal
 
 
ZoneId.bal
ZoneId.bal
 
 
ZoneOffset.bal
ZoneOffset.bal
 
 
ZoneOffsetTransition.bal
ZoneOffsetTransition.bal
 
 
ZoneRules.bal
ZoneRules.bal
 
 
ZonedDateTime.bal
ZonedDateTime.bal
 
 

Repository files navigation

java_time_utils

A Ballerina wrapper around java.time — LocalDate, LocalDateTime, LocalTime, ZonedDateTime, Instant, ZoneId, ZoneOffset, ZoneRules, and other core helper and enum classes like Month and DayOfWeek for working with dates, times, time zones, and temporal components.

📚 For a complete overview of all features and the full API reference, please visit:
central.ballerina.io/kruutteri1/java_time_utils/latest — detailed documentation and full functionality can be found there.

Note on scope: This library wraps only the members that are fully implemented. A few Java-side wrapper types (Month, DayOfWeek, IsoEra, Chronology, IsoChronology, Class) are declared as Java bindings internally but have no public members yet, so any method that would return or accept one of those types (e.g. getMonth(), getDayOfWeek(), getEra(), getChronology(), getClass()) is intentionally left out of this documentation and out of the public API for now.


Requirements

  • Ballerina Swan Lake 2201.13.4 or later
  • Java 21 runtime (bundled with the Ballerina distribution — no separate install needed)

Installation

# Ballerina.toml
[[dependency]]
org = "kruutteri1"
name = "java_time_utils"
version = "1.1.0"  # check central.ballerina.io for the latest version
import kruutteri1/java_time_utils as jt;

Table of Contents

  • Quick Start
  • LocalDate
  • LocalDateTime
  • LocalTime
  • Period
  • A Note on Method Naming
  • Error Handling
  • Inherited Java Object Methods

Quick Start

import ballerina/io;
import kruutteri1/java_time_utils as jt;

public function main() returns error? {
    jt:LocalDate date = check jt:ofDate(2026, 7, 15);
    jt:LocalTime time = check jt:ofTimeWithSecond(14, 28, 19);
    jt:LocalDateTime dateTime = check jt:ofLocalDateWithLocalTime(date, time);

    io:println(date.toString());      // 2026-07-15
    io:println(time.toString());      // 14:28:19
    io:println(dateTime.toString());  // 2026-07-15T14:28:19
}

LocalDate

A date without a time-of-day or time zone component, such as 2026-07-15.

Creating a LocalDate

  • getCurrentDate() returns LocalDate — current date from the system clock
  • ofDate(int year, int month, int day) returns LocalDate — panics on invalid input
  • ofMonth(int year, Month month, int day) returns LocalDate — using the Month enum, panics on invalid input
  • ofEpochDay(int epochDay) returns LocalDate
  • ofYearDay(int year, int dayOfYear) returns LocalDate — panics if dayOfYear exceeds the year length
  • getMINDate() returns LocalDate — minimum supported date
  • getMAXDate() returns LocalDate — maximum supported date
  • getEPOCHDate() returns LocalDate — 1970-01-01
jt:LocalDate d = jt:ofDate(2026, 7, 15);

Instance Methods

Output: toString() returns string

Component getters (no args, return int): getYear(), getMonthValue(), getDayOfMonth(), getDayOfYear()

Comparison: isEquals(Object other) returns boolean — returns false rather than panicking on a type mismatch. hashCode() also available (see Inherited Java Object Methods).

Checks: isLeapYear(), lengthOfMonth(), lengthOfYear(), toEpochDay()

Combining with time (produces LocalDateTime):

  • atStartOfDay() — 00:00
  • atTime(int hour, int minute)
  • atTimeDetailed(int hour, int minute, int second)
  • atTimeFull(int hour, int minute, int second, int nano)
  • atLocalTime(LocalTime time) — combines with an already-built LocalTime; never panics

Date ranges:

  • datesUntil(LocalDate endExclusive) returns Stream — every date up to (not including) endExclusive. The Stream type is a low-level binding not documented in depth here.

Arithmetic (immutable, returns new LocalDate):

  • Add: plusYears, plusMonths, plusWeeks, plusDays
  • Subtract: minusYears, minusMonths, minusWeeks, minusDays
  • plusMonths/plusYears/minusMonths/minusYears clamp the day-of-month on overflow (e.g. Jan 31 + 1 month → Feb 28/29) — this does not panic.

Altering fields (with*, panics on invalid value instead of clamping): withYear(int year), withMonth(int month), withDayOfMonth(int dayOfMonth), withDayOfYear(int dayOfYear)

LocalDate — Example

import ballerina/io;
import kruutteri1/java_time_utils as jt;

public function main() {
    jt:LocalDate d = jt:ofDate(2026, 7, 15);
    jt:LocalDate nextWeek = d.plusWeeks(1);
    jt:LocalDate newYearDay = d.withMonth(1).withDayOfMonth(1);

    io:println("Year: ", d.getYear());
    io:println("Leap year: ", d.isLeapYear());
    io:println(d.toString());
    io:println(nextWeek.toString());

    jt:LocalDateTime dt = d.atTime(9, 0);
    io:println(dt.toString());

    jt:LocalDate|error invalid = trap jt:ofDate(2026, 2, 30);
    if invalid is error {
        io:println("Rejected as expected: ", invalid.message());
    }
}

LocalDateTime

A date and time without a time zone, such as 2026-07-15T14:28:19.

Creating a LocalDateTime

  • getCurrentDateTime() returns LocalDateTime
  • ofDateTime(int year, int month, int dayOfMonth, int hour, int minute) returns LocalDateTime
  • ofWithSeconds(int year, int month, int dayOfMonth, int hour, int minute, int second) returns LocalDateTime
  • ofWithNanos(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond) returns LocalDateTime
  • ofMonthFullWithsecond(int year, Month month, int dayOfMonth, int hour, int minute, int second) returns LocalDateTime — using the Month enum
  • ofMonthFullWithNano(int year, Month month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond) returns LocalDateTime — using the Month enum and nanoseconds
  • ofLocalDateWithLocalTime(LocalDate date, LocalTime time) returns LocalDateTime — never panics, both inputs already validated
  • getMINDateTime() returns LocalDateTime
  • getMAXDateTime() returns LocalDateTime
jt:LocalDateTime dt = jt:ofDateTime(2026, 7, 15, 14, 28);

Instance Methods

Output: toString() returns string

Component getters: getYear(), getMonthValue(), getDayOfMonth(), getDayOfYear(), getHour(), getMinute(), getSecond(), getNano()

Comparison: isEquals(Object other) returns boolean. hashCode() also available.

Splitting: toLocalDate() returns LocalDate, toLocalTime() returns LocalTime

Arithmetic (immutable):

  • Add: plusYears, plusMonths, plusWeeks, plusDays, plusHours, plusMinutes, plusSeconds, plusNanos
  • Subtract: minusYears, minusMonths, minusWeeks, minusDays, minusHours, minusMinutes, minusSeconds, minusNanos
  • ⚠️ Rollover past midnight in plusHours/plusMinutes/etc. advances the calendar date, unlike LocalTime which just wraps within the same day.

Altering fields (with*, panics on invalid value): withYear, withMonth, withDayOfMonth, withDayOfYear, withHour, withMinute, withSecond, withNano

LocalDateTime — Example

import ballerina/io;
import kruutteri1/java_time_utils as jt;

public function main() {
    jt:LocalDateTime dt = jt:ofDateTime(2026, 7, 15, 14, 28);
    jt:LocalDateTime tomorrow = dt.plusDays(1);

    jt:LocalDate d = dt.toLocalDate();
    jt:LocalTime t = dt.toLocalTime();
    jt:LocalDateTime combined = jt:ofLocalDateWithLocalTime(d, t);

    io:println(dt.toString());
    io:println(tomorrow.toString());

    // Midnight rollover advances the date
    jt:LocalDateTime nearMidnight = jt:ofWithSeconds(2026, 7, 15, 23, 30, 0);
    jt:LocalDateTime rolledOver = nearMidnight.plusHours(1);
    io:println(rolledOver.toString()); // 2026-07-16T00:30
}

LocalTime

A time of day without a date or time zone, such as 10:15:30.

LocalTime is a value-based type — identity operators (==) are unreliable. Use isEquals, compareTo, isAfter, or isBefore.

Creating a LocalTime

  • getCurrentTime() returns LocalTime
  • ofTime(int hour, int minute) returns LocalTime
  • ofTimeWithSecond(int hour, int minute, int second) returns LocalTime
  • ofTimeWithSecondNano(int hour, int minute, int second, int nanoOfSecond) returns LocalTime
  • ofNanoOfDay(int nanoOfDay) returns LocalTime
  • ofSecondOfDay(int secondOfDay) returns LocalTime
  • getMinTime() returns LocalTime — minimum (00:00)
  • getMAXTime() returns LocalTime — maximum (23:59:59.999999999)
  • getMIDNIGHT() returns LocalTime
  • getNOON() returns LocalTime
jt:LocalTime t = jt:ofTimeWithSecond(10, 15, 30);

Instance Methods

Output: toString() returns string

Component getters: getHour(), getMinute(), getSecond(), getNano()

Comparison — unlike LocalDate/LocalDateTime, these are all public here:

  • isEquals(Object other) returns boolean
  • compareTo(LocalTime other) returns int
  • isAfter(LocalTime other) returns boolean
  • isBefore(LocalTime other) returns boolean
  • hashCode() also available

Combining with a date: atDate(LocalDate date) returns LocalDateTime — never panics

As seconds/nanoseconds of day: toSecondOfDay(), toNanoOfDay()

Arithmetic (immutable, wraps cyclically — never panics):

  • Add: plusHours, plusMinutes, plusSeconds, plusNanos
  • Subtract: minusHours, minusMinutes, minusSeconds, minusNanos
  • e.g. 23:00 + 2 hours = 01:00 — stays within the same day, unlike LocalDateTime.

Altering fields (with*, panics on invalid value instead of wrapping): withHour, withMinute, withSecond, withNano

LocalTime — Example

import ballerina/io;
import kruutteri1/java_time_utils as jt;

public function main() {
    jt:LocalTime t = jt:ofTimeWithSecond(10, 15, 30);
    jt:LocalTime inTwoHours = t.plusHours(2);

    boolean isLater = inTwoHours.isAfter(t); // true

    io:println(t.toString());
    io:println(inTwoHours.toString());

    jt:LocalDate d = jt:ofDate(2026, 7, 15);
    jt:LocalDateTime dt = t.atDate(d);
    io:println(dt.toString());

    // Wraparound near midnight (stays within the same day)
    jt:LocalTime lateNight = jt:ofTimeWithSecond(23, 0, 0);
    jt:LocalTime wrapped = lateNight.plusHours(2);
    io:println(wrapped.toString()); // 01:00:00
}

Period

A date-based amount of time in years, months, and days, such as "1 year, 2 months, and 3 days". Unlike LocalDate/LocalDateTime/LocalTime, a Period doesn't represent a point in time — it represents a quantity of time, and is most often produced by between or added to/subtracted from a LocalDate.

⚠️ Period does not store weeks as a separate field. ofPeriodWeeks(n) is a convenience that internally stores n * 7 as days — getDays() afterward returns the day count, not the week count.

Creating a Period

  • ofPeriod(int years, int months, int days) returns Period
  • ofPeriodDays(int days) returns Period
  • ofPeriodMonths(int months) returns Period
  • ofPeriodYears(int years) returns Period
  • ofPeriodWeeks(int weeks) returns Period — stored internally as weeks * 7 days
  • between(LocalDate startDateInclusive, LocalDate endDateExclusive) returns Period — the years/months/days between two dates; negative if endDateExclusive is before startDateInclusive
  • getZERO() returns Period — a period of zero length
jt:Period p = jt:ofPeriod(1, 2, 3);
jt:Period between = jt:between(jt:ofDate(2026, 1, 1), jt:ofDate(2026, 7, 15));

Instance Methods

Output: toString() returns string — ISO-8601 period format, e.g. "P1Y2M3D"

Component getters (no args, return int): getYears(), getMonths(), getDays()

Comparison: isEquals(Object other) returns boolean — returns false rather than panicking on a type mismatch. hashCode() also available (see Inherited Java Object Methods).

Checks:

  • isZero() returns boolean — true only if years, months, and days are all 0
  • isNegative() returns boolean — true if any of the three fields is negative, even if the others are positive

Arithmetic (immutable, returns new Period) — panics on int32 overflow:

  • Add: plusYears, plusMonths, plusDays
  • Subtract: minusYears, minusMonths, minusDays
  • multipliedBy(int scalar) — scales all three fields; panics on overflow
  • negated() — flips the sign of all three fields

Normalization:

  • normalized() returns Period — carries excess months into years (e.g. 14 months → 1 year, 2 months). Does not touch the days field.

Converting to months:

  • toTotalMonths() returns int — years * 12 + months, ignoring days. Uses long arithmetic internally, so unlike the other arithmetic methods this does not overflow at int32 boundaries.

Altering fields (with*, replaces one field without touching the others): withYears(int years), withMonths(int months), withDays(int days)

Period — Example

import ballerina/io;
import kruutteri1/java_time_utils as jt;

public function main() {
    jt:LocalDate startDate = jt:ofDate(2026, 1, 1);
    jt:LocalDate endDate = jt:ofDate(2026, 7, 15);
    jt:Period p = jt:between(startDate, endDate);

    io:println(p.toString());          // P0Y6M14D
    io:println("Years: ", p.getYears());
    io:println("Months: ", p.getMonths());
    io:println("Days: ", p.getDays());

    jt:Period doubled = p.multipliedBy(2);
    io:println(doubled.toString());

    jt:Period normalized = jt:ofPeriod(0, 14, 0).normalized();
    io:println(normalized.toString()); // P1Y2M

    // Safe arithmetic near int32 boundaries
    jt:Period nearMax = jt:ofPeriodYears(2147483647);
    jt:Period|error overflowed = trap nearMax.plusYears(1);
    if overflowed is error {
        io:println("Overflow rejected as expected: ", overflowed.message());
    }
}

A Note on Method Naming

Ballerina does not support method/function overloading, so this library's function names don't mirror java.time one-to-one:

  • Creation functions carry a suffix indicating arity, since Ballerina can't have multiple functions named of in the same module. LocalDate uses ofDate(...); LocalDateTime uses ofDateTime(...) / ofWithSeconds(...) / ofWithNanos(...); LocalTime uses ofTime(...) / ofTimeWithSecond(...) / ofTimeWithSecondNano(...); Period uses ofPeriod(...) / ofPeriodDays(...) / ofPeriodMonths(...) / ofPeriodYears(...) / ofPeriodWeeks(...).
  • equals is exposed as isEquals. Ballerina reserves equals as a keyword-adjacent identifier; this library uses the plain, unescaped name isEquals(Object other) on all four types instead.
  • wait/wait2/wait3 are exposed as doWait / waitWithTimeout / waitWithTimeoutAndNanos — same reasoning, see Inherited Java Object Methods.
  • compareTo, isAfter, isBefore are only public on LocalTime — on LocalDate, LocalDateTime, and Period these either don't apply or exist internally but aren't part of the current public API.

Error Handling

Functions and methods that validate their input — creation functions like ofDate, and any with* method on LocalDate/LocalDateTime/LocalTime — panic on an out-of-range value (e.g. month 13, or February 30th). They return a plain T, not T|error; invalid input triggers a runtime panic from the underlying Java DateTimeException.

Period's arithmetic methods (plus*, minus*, multipliedBy) panic for a different reason — not invalid calendar values, but int32 overflow in the underlying Java ArithmeticException, since Period has no calendar validation of its own.

Either way, use trap to convert the panic into a catchable error:

import ballerina/io;
import kruutteri1/java_time_utils as jt;

public function main() {
    jt:LocalDate|error d = trap jt:ofDate(2026, 2, 30);

    if d is error {
        io:println("Invalid date: ", d.message());
    } else {
        io:println("Created: ", d.toString());
    }
}

If you're confident the input is always valid (e.g. hardcoded constants), you can skip trap — but any dynamic or user-supplied input should go through this pattern to avoid an unhandled panic crashing your program.

This applies uniformly to LocalDate, LocalDateTime, LocalTime, and Period.


Inherited Java Object Methods

All four types (LocalDate, LocalDateTime, LocalTime, Period) inherit these from java.lang.Object, mainly for low-level thread synchronization — you almost certainly don't need them in ordinary business logic.

Method Parameters Description
notify() — Wakes a single thread waiting on the object's monitor
notifyAll() — Wakes all threads waiting on the object's monitor
doWait() — Waits for a notification (may return InterruptedException)
waitWithTimeout(int timeoutMillis) timeoutMillis Waits with a millisecond timeout
waitWithTimeoutAndNanos(int timeoutMillis, int nanos) timeoutMillis, nanos Waits with a timeout (ms + ns)
hashCode() — Hash code, consistent with isEquals
jt:InterruptedException? err = d.doWait();
if err is jt:InterruptedException {
    // handle interruption
}

Available identically on LocalDate, LocalDateTime, LocalTime, and Period.

About

A Ballerina wrapper library around java.time

central.ballerina.io/kruutteri1/java_time_utils/latest

Topics

ballerinaballerina-libraryjava-interopjava-timeutility

Resources

Readme
Activity

Stars

1 star

Watchers

0 watching

Forks

0 forks
Report repository

Releases

Packages

Contributors

Languages

You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.