Skip to content

Kruutteri1/java_time_utils

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

java_time_utils

A Ballerina wrapper library around java.time — providing LocalDate, LocalDateTime, LocalTime, and Period for working with dates and times without time zones.

📚 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.0.5"  # check central.ballerina.io for the latest version
import kruutteri1/java_time_utils as jt;

Table of Contents


Quick Start

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

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

    io:println(date.toString());
    io:println(time.toString());
    io:println(dateTime.toString());
}

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 intyears * 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/LocalTimepanic 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

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors