0

I currently have a Date e.g. "2015-10-10T14:34:22Z". I need the year from the Date object for my new LocalDateTime object as this object will be set to that Date object year and have a specific month, day and time set (yyyy-06-15T17:00:00Z).

Taking the getYear() from Date has the 1900 issue.

  • I get the date via LocalDate date = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate()
  • Create another object to set the desired month and day
  • Create LocalDateTime object to set the time

I feel I am doing it a very long convuluted way and would like to ask if there are any other shorter and better alternatives.

EDIT:

Are there are any other shorter and better alternatives?

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
  • 1
    As an aside, are you *certain* you want to use the system default time zone? Everywhere you've specified values in the question, you've used a "Z" at the end suggesting you're thinking about it in terms of UTC. – Jon Skeet Dec 01 '20 at 17:50
  • 1
    Also, as a note, `LocalDateTime` is an "unusual" type to use, because you've discarded some pretty important information at that point (notably, the timezone). Normally you'd be using `Instant` or `ZonedDateTime`. Which brings up the point that the given time isn't guaranteed to "exist" for any given year. That said... why not just create the full set of years? That's the only thing that seems to vary... – Clockwork-Muse Dec 01 '20 at 17:57
  • @Clockwork-Muse I am currently new to Java 8 date libraries and from online, LocalDateTime is the one that is most often used. Sorry, would you be able to explain further the latter option? It would be good to be able to output yyyy-06-15T17:00:00Z in UTC –  Dec 01 '20 at 18:22
  • @Gintoki - It's the difference between arranging a meeting with your neighbor ("Let's talk at 2PM" - although note that the time zone is implicit, not unpresent) and your grandmother on the other side of the world ("I'll call you at 2PM your time"). When most people use `LocalDateTime`, they're not considering the fact that they probably need to communicate the value to somebody in a different time zone, or that if the date is sufficiently in the future the rules may change. – Clockwork-Muse Dec 01 '20 at 18:46
  • Note that we should also ask you, "what does this piece of information represent"? Because that changes answers. – Clockwork-Muse Dec 01 '20 at 18:48
  • If `LocalDateTime` is the most often used class from java.time, then it is most unfortunate. You should very seldom use it. The most often used ones ought to `Instant`, `ZonedDateTime` and `LocalDate`. – Ole V.V. Dec 01 '20 at 22:32

4 Answers4

1

Since your date-time string has timezone offset information. So, you can parse it to an OffsetDateTime object and then get the year from it.

import java.time.LocalDateTime;
import java.time.OffsetDateTime;

public class Main {
    public static void main(String[] args) {
        String strDateTime = "2015-10-10T14:34:22Z";
        OffsetDateTime odt = OffsetDateTime.parse(strDateTime);
        System.out.println(odt);
        System.out.println(odt.getYear());

        // If you want to get LocalDateTime from OffsetDateTime
        LocalDateTime ldt = odt.toLocalDateTime();
        System.out.println(ldt);
    }
}

Output:

2015-10-10T14:34:22Z
2015
2015-10-10T14:34:22

Note that Z in the date-time string stands for Zulu date-time and specifies a timezone offset of +00:00 hours or date-time at UTC.

Taking the getYear() from Date has the 1900 issue.

The date-time API of java.util and their formatting API, SimpleDateFormat are outdated and error-prone. I suggest you should stop using them completely and switch to the modern date-time API. Learn more about the modern date-time API at Trail: Date Time.

If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.

Converting from legacy API to the modern API:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.Date;

public class Main {
    public static void main(String[] args) throws ParseException {
        String strDateTime = "2015-10-10T14:34:22Z";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
        Date date = sdf.parse(strDateTime);
        Instant instant = date.toInstant();

        OffsetDateTime odt = instant.atOffset(ZoneOffset.UTC);
        System.out.println(odt);
        System.out.println(odt.getYear());

        // If you want to get LocalDateTime from OffsetDateTime
        LocalDateTime ldt = odt.toLocalDateTime();
        System.out.println(ldt);
    }
}

Output:

2015-10-10T14:34:22Z
2015
2015-10-10T14:34:22

Note: If you want to convert the Instant into ZonedDateTime at UTC, you can do it as follows:

ZonedDateTime zdt = instant.atZone(ZoneOffset.UTC);

or the following:

ZonedDateTime zdt = instant.atZone(ZoneId.of("Etc/UTC"));

Note that the three-letter name for a ZoneId is error-prone i.e. avoid using something like ZoneId.of("UTC").

What is wrong with your code:

You are using .atZone(ZoneId.systemDefault()) which is converting the object of Instant to an object of ZonedDateTime with your JVM's timezone. You have to use .atOffset(ZoneOffset.UTC) as shown above to keep the date-time with the same timezone offset (i.e. +00:00 hours or date-time at UTC) which is there in the date-time string.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
  • ... I'm relatively certain that OP has a `java.util.Date`, not a formatted string. – Clockwork-Muse Dec 01 '20 at 18:24
  • @Clockwork-Muse - The first line of the question is: `I currently have a Date e.g. "2015-10-10T14:34:22Z"` which is a `String`. The `java.util.Date` does not have timezone information as it simply represents the no. of milliseconds from `January 1, 1970, 00:00:00 GMT`. – Arvind Kumar Avinash Dec 01 '20 at 18:24
  • Sure, but that's more than likely for explanation/as an example, especially as the code they do have starts out as "input.toInstant()`, which is a method on `java.util.Date`. – Clockwork-Muse Dec 01 '20 at 18:44
  • Well...the OP has not mentioned why [s]he is still using the outdated `java.util.Date` and `SimpleDateFormat` and therefore the first part of my answer explains how to solve it purely using the modern API. However, I felt (as you have also mentioned) later that if for any reason the OP is forced to use the legacy API, [s]he should know how to covert `java.util.Date` to modern API correctly and therefore I posted the second part talking about this conversion. This should be enough for him/her to progress. However, I can update the answer if [s]he comment about any issue/doubt. – Arvind Kumar Avinash Dec 01 '20 at 19:08
  • @ArvindKumarAvinash Thank you for the reply and providing a detailed explanation. So if I were to use .atZone(ZoneId.of("UTC")).toLocalDate();, would this be the same as .atOffset(ZoneOffset.UTC)? –  Dec 01 '20 at 21:58
  • @Gintoki - You are most welcome. You should avoid using the three-letter string e.g `UTC`, `IST` etc. to create a `ZoneId`. For `UTC`, you should use the string, `Etc/UTC`. Similarly, for `IST` (India Standard Time), you should use `Asia/Calcutta`. I've just added this update in my answer. – Arvind Kumar Avinash Dec 01 '20 at 22:29
0

try this :

SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssX");
try {
    String s = "2015-10-10T14:34:22+02";
    s = s.replaceAll("T", " ");
    Date d = df.parse(s);
    Calendar cl = Calendar.getInstance();
    cl.setTime(d);
    System.out.println(cl.getTime());
    System.out.println("year : " + cl.get(Calendar.YEAR));
} catch (ParseException e) {
    System.err.println(e);
}

output

Sat Oct 10 13:34:22 GMT+01:00 2015
year : 2015
aziz k'h
  • 775
  • 6
  • 11
0

Maybe this approach could help:

import java.text.ParseException;
import java.time.*;
import java.util.Date;

public class ConvertDate {
    public static void main(String[] args) throws ParseException {
        Date date = new Date();
        LocalDateTime localDateTime = date.toInstant().atOffset(ZoneOffset.UTC).toLocalDateTime();
        System.out.println(localDateTime);
        System.out.println(localDateTime.getYear());
    }
}
Woodchuck
  • 3,869
  • 2
  • 39
  • 70
0

Time zone is crucial

You need to decide in which time zone you want the year. New Year doesn’t happen at one point in time across the globe, but over a span of about 26 hours. So if your date string is within a day or so of New Year, your result could be off by a year if you don’t pick the correct time zone. For example:

    ZoneId zone = ZoneId.of("America/Louisville");
    // The year in the following object does not matter
    ZonedDateTime fixedTimeOfYear = ZonedDateTime.of(2020, 6, 15, 17, 0, 0, 0, zone);
    
    String inputString = "2015-01-01T01:02:03Z";
    OffsetDateTime input = OffsetDateTime.parse(inputString);
    int year = input.atZoneSameInstant(zone).getYear();
    System.out.format("Year in %s is %d%n", zone, year);
    ZonedDateTime desiredTime = fixedTimeOfYear.withYear(year);
    System.out.println("Result: " + desiredTime);

Output from this snippet is:

Year in America/Louisville is 2014
Result: 2014-06-15T17:00-04:00[America/Louisville]

You notice that even though the year in the string is 2015, it is still only 2014 in the time zone that I chose for the demonstration, so the resulting date and time are in 2014. The example was picked to demonstrate my point.

Don’t use LocalDateTime

The frequent use of LocalDateTime that you mention in a comment is a misunderstanding. For a date and time in a known time zone in 2015, for example, LocalDateTime is the wrong class to use. Use ZonedDateTime or at least OffsetDateTime so we know what we are talking about. These classes have the advantages that they keep track of time zone or offset themselves, and that they define an unambiguous point in time. LocalDateTime does nothing of this.

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161