deval.techie
deval.techie

Reputation: 63

Database DateTime milli and nano seconds are truncated by default if they are 0s, while using it in Java 11 using ZonedDateTime

I am fetching datetime from an Oracle database and parsing in Java 11 using ZonedDateTime as below:

  1. Oracle --> 1/19/2020 06:09:46.038631 PM

    Java ZonedDateTime output --> 2020-01-19T18:09:46.038631Z[UTC]

  2. Oracle --> 1/19/2011 4:00:00.000000 AM

    Java ZonedDateTime output --> 2011-01-19T04:00Z[UTC] (So, here the 0s are truncated by default. However, my requirement is to have consistent fixed length output like #1.)

Expected Java ZonedDateTime output --> 2011-01-19T04:00:00.000000Z[UTC]

However, I didn’t find any date API methods to achieve above expected output. Instead of manipulating a string, is there a way to preserve the trailing 0s with fixed length?

We have consistent ZonedDateTime types in the application, so we do not prefer to change that.

Upvotes: 2

Views: 1223

Answers (2)

ELinda
ELinda

Reputation: 2821

Small thing to add to other answers: The fraction-of-second (S) and nanos (n) are similar but nanos will output more places/digits if more are available. The docs didn't seem to elaborate this well. If only up to 6 nano digits are available, then there shouldn't be a difference.

import java.time.*;
import java.util.*;
import java.time.format.*;
 
public class TestFormats {
 
    public static void main(String[] args)
    {
        LocalDateTime dt = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.SSSSSS'Z'");
        System.out.println("Current time: "
                           + formatter.format(dt));

        System.out.println("New LocalDateTime with 0 nano: "
                           + formatter.format(dt.withNano(0)));
        System.out.println("New LocalDateTime with specific nano: "
                           + formatter.format(dt.withNano(80888)));
        System.out.println("New LocalDateTime with specific bigger nano: "
                           + formatter.format(dt.withNano(8088888)));
        
        DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.nnnnnn'Z'");
        System.out.println("Format2 Current time: "
                           + formatter2.format(dt));

        System.out.println("Format2 New LocalDateTime with 0 nano: "
                           + formatter2.format(dt.withNano(0)));
        System.out.println("Format2 New LocalDateTime with specific nano: "
                           + formatter2.format(dt.withNano(80888)));
        System.out.println("Format2 New LocalDateTime with specific bigger nano: "
                           + formatter2.format(dt.withNano(8088888)));
                           
    }
}

Result:

Current time: 2023-07-12 15:55:30.321719Z
New LocalDateTime with 0 nano: 2023-07-12 15:55:30.000000Z
New LocalDateTime with specific nano: 2023-07-12 15:55:30.000080Z
New LocalDateTime with specific bigger nano: 2023-07-12 15:55:30.008088Z
Format2 Current time: 2023-07-12T15:55:30.321719992Z
Format2 New LocalDateTime with 0 nano: 2023-07-12T15:55:30.000000Z
Format2 New LocalDateTime with specific nano: 2023-07-12T15:55:30.080888Z
Format2 New LocalDateTime with specific bigger nano: 2023-07-12T15:55:30.8088888Z

Upvotes: 0

Arvind Kumar Avinash
Arvind Kumar Avinash

Reputation: 79075

We have consistent ZonedDateTime type in application, so we do not prefer to change that.

Why do you think 2011-01-19T04:00Z[UTC] is inconsistent? A date-time object is supposed to hold (and provide methods/functions to operate with) only the date, time, and time-zone information. It is not supposed to store any formatting information; otherwise, it will violate the Single-responsibility principle. The formatting should be handled by a formating class e.g. DateTimeFormatter (for modern date-time API), DateFormat (for legacy java.util date-time API) etc.

Every class is supposed to override the toString() function; otherwise, Object#toString will be returned when its object will be printed. A ZonedDateTime has date, time and time-zone information. Given below is how its toString() for time-part has been implemented:

@Override
public String toString() {
    StringBuilder buf = new StringBuilder(18);
    int hourValue = hour;
    int minuteValue = minute;
    int secondValue = second;
    int nanoValue = nano;
    buf.append(hourValue < 10 ? "0" : "").append(hourValue)
        .append(minuteValue < 10 ? ":0" : ":").append(minuteValue);
    if (secondValue > 0 || nanoValue > 0) {
        buf.append(secondValue < 10 ? ":0" : ":").append(secondValue);
        if (nanoValue > 0) {
            buf.append('.');
            if (nanoValue % 1000_000 == 0) {
                buf.append(Integer.toString((nanoValue / 1000_000) + 1000).substring(1));
            } else if (nanoValue % 1000 == 0) {
                buf.append(Integer.toString((nanoValue / 1000) + 1000_000).substring(1));
            } else {
                buf.append(Integer.toString((nanoValue) + 1000_000_000).substring(1));
            }
        }
    }
    return buf.toString();
}

As you can see, the second and nano parts are included in the returned string only when they are greater than 0. It means that you need to use a formatting class if you want these (second and nano) zeros in the output string. Given below is an example:

    import java.time.LocalDateTime;
    import java.time.ZoneOffset;
    import java.time.ZonedDateTime;
    import java.time.format.DateTimeFormatter;
    import java.time.format.DateTimeFormatterBuilder;
    import java.util.Locale;

    public class Main {
        public static void main(String[] args) {
            String input = "1/19/2011 4:00:00.000000 AM";

            // Formatter for input string
            DateTimeFormatter inputFormatter = new DateTimeFormatterBuilder()
                                                .parseCaseInsensitive()
                                                .appendPattern("M/d/u H:m:s.n a")
                                                .toFormatter(Locale.ENGLISH);

            ZonedDateTime zdt = LocalDateTime.parse(input, inputFormatter).atZone(ZoneOffset.UTC);

            // Print `zdt` in default format i.e. the string returned by `zdt.toString()`
            System.out.println(zdt);

            // Formatter for input string
            DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.nnnnnnz");
            String output = zdt.format(outputFormatter);
            System.out.println(output);
        }
    }

Output:

2011-01-19T04:00Z
2011-01-19T04:00:00.000000Z

Food for thought:

public class Main {
    public static void main(String[] args) {
        double d = 5.0000;
        System.out.println(d);
    }
}

What output do you expect from the code given above? Does 5.0 represent a value different from 5.0000? How will you print 5.0000? [Hint: Check String#format, NumberFormat, BigDecimal etc.]

Upvotes: 2

Related Questions