0

I would like to set java.util.Date object to java.util.Calendar object to change Date object's year as below:

TimezoneId is equal to "Europe/Istanbul".And year is 2018.

Date object is 1969-12-31 22:00:00.0 (1969-12-31T22:00:00.000Z) and zoneinfo is below:

"sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]"

Europe/Istanbul timezone is UTC+3 but when I run code with this date object new GregorianCalendar changes time of date object as -1 hour.Something is working wrongly and I could not find why this not convert UTC to UTC+3.Returned Date object is equals with Sun Dec 31 21:00:00 UTC 2017.UTC-1 is not correct for Europe/Istanbul.How can I fix this ?

public static Date setYearOfDate(Date date, int year, String timeZoneId)
    {
        Calendar customCalendar = new GregorianCalendar(TimeZone.getTimeZone(timeZoneId));
        customCalendar.setTime(date);
        customCalendar.set(Calendar.YEAR, year);

        return customCalendar.getTime();
    }

Note:customCalendar zoneinfo is below: "sun.util.calendar.ZoneInfo[id="Europe/Istanbul",offset=10800000,dstSavings=0,useDaylight=false,transitions=130,lastRule=null]"

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
volkan
  • 209
  • 4
  • 12
  • 1
    Which Java version are you using? – Rafael Guillen Feb 10 '21 at 09:23
  • @RafaelGuillen version is Java8 – volkan Feb 10 '21 at 11:03
  • 1
    Istanbul may be on UTC+3 *now*, but in the past it has been either on UTC+2 or alternating between UTC+2 and UTC+3 for daylight saving time. In 1969 and early parts of 1970, it was UTC+2. See https://www.timeanddate.com/time/zone/turkey/istanbul?syear=1960&eyear=2030 – Matt Johnson-Pint Feb 10 '21 at 20:54
  • I recommend you don’t use `Date` and `Calendar`. Those classes are poorly designed and long outdated. If you really want a date without time of day, use use `LocalDate` from [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). If you want to include time and time zone, use `ZonedDateTime` from the same API. – Ole V.V. Feb 12 '21 at 14:57
  • The comment by @MattJohnson-Pint implies that the result you got is correct. – Ole V.V. Feb 12 '21 at 15:02
  • @MattJohnson-Pint thank you, you are right these year has been used UTC+2 and when we changed these years it is working. – volkan Feb 15 '21 at 06:11
  • @MattJohnson-Pint - Thanks for the information. However, it's the problem with only legacy date-time API; not the modern date-time API. I have described both of these findings in my answer. – Arvind Kumar Avinash Feb 15 '21 at 06:20
  • @OleV.V. - I didn't see your second comment earlier. As I have already replied to Matt's comment, it's the problem with only legacy date-time API; not the modern date-time API. I have described both of these findings in my answer. – Arvind Kumar Avinash Feb 15 '21 at 07:16
  • @ArvindKumarAvinash It’s not perfectly clear. It seems to me that the OP wanted to change the year in Turkey time zone, which makes the difference here. `Instant.parse("1969-12-31T22:00:00.000Z").atZone(ZoneId.of("Europe/Istanbul")).withYear(2018).toInstant()` yields `2017-12-31T21:00:00Z`, which agrees with the OP’s reported result. As Rafael Guillen also says in the ohther answer. Perhaps the OP just misinterpreted the result? – Ole V.V. Feb 15 '21 at 20:03

2 Answers2

4

java.time

I recommend you do it using java.time API.

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

class Main {
    public static void main(String[] args) {
        String dateTimeString = "1969-12-31T22:00:00.000Z";
        System.out.println(setYearOfDate(dateTimeString, 2018, "Europe/Istanbul"));
    }

    public static String setYearOfDate(String dateTimeString, int year, String zoneId) {
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss.SSSX", Locale.ENGLISH)
                                                    .withZone(ZoneId.of(zoneId));

        ZonedDateTime zdt = ZonedDateTime.parse(dateTimeString);
        zdt = zdt.withYear(year);

        return dtf.format(zdt);
    }
}

Output:

2019-01-01T01:00:00.000+03

The java.util date-time API and their formatting API, SimpleDateFormat are outdated and error-prone. It is recommended to stop using them completely and switch to the modern date-time API.

A good example of the erroneous behaviour of SimpleDateFormat is demonstrated below:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;

class Main {
    public static void main(String[] args) throws ParseException {
        String dateTimeString = "1969-12-31T22:00:00.000Z";
        String timeZoneId = "Europe/Istanbul";
        System.out.println(setYearOfDate(dateTimeString, 2018, timeZoneId));
    }

    public static String setYearOfDate(String dateTimeString, int year, String timeZoneId) throws ParseException {
        Calendar calendar = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", Locale.ENGLISH);
        calendar.setTime(sdf.parse(dateTimeString));
        calendar.set(Calendar.YEAR, year);
        sdf.setTimeZone(TimeZone.getTimeZone(timeZoneId));
        return sdf.format(calendar.getTime());
    }
}

Output:

2019-01-01T02:00:00.000+03

You will observe this behaviour of SimpleDateFormat for the years, 1968 and 1970 as well. However, if you change the year in the input string to some other year, it behaves in the expected manner e.g. the same code will output 2019-01-01T01:00:00.000+03 for the input string, 1979-12-31T22:00:00.000Z.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
  • But as I see the output of hour UTC+1 is not correct for Europe/Istanbul timezone.It should be UTC+3 – volkan Feb 10 '21 at 09:08
  • @volkan - I was busy with a meeting when I posted the answer. I've updated it and it shows the output you are expecting. I'm still busy with a day-long meeting. If you have any question, feel free to comment and I will respond whenever I will get some break, the way I just did. – Arvind Kumar Avinash Feb 10 '21 at 09:35
  • @Live_and_Let_Live thank you for kind response but I would not like to set UTC+3.Only The issue is why Europe/Istanbul (UTC+3) affects hour -1 hour ? Normally I expect that this setYearOfDate method should change as UTC+3, not UTC-1. – volkan Feb 10 '21 at 11:08
  • @volkan - `SimpleDateFormat` is known to be error-prone. I recommend you switch to the modern date-time API. With `SimpleDateFormat`, you will get incorrect output for the years, 1968, 1969, and 1970 while it returns the expected result f you change the year in the input string e.g. the same code will output `2019-01-01T01:00:00.000+03` for the input string, `1979-12-31T22:00:00.000Z`. – Arvind Kumar Avinash Feb 10 '21 at 18:40
  • the years of between 1965 — 1969 has no changes, UTC +2 hours all of the period for Europe/Istanbul timezone.So as we changed 1979 it is working.Thank you. – volkan Feb 15 '21 at 06:15
1

I think @Live_and_Let_Live answer is wright, to print the Date you need to provide the TimeZone to SimpleDateFotmat, actually you don't need to use the SimpleDateFotmat to set the year; using your code and applying the TimeZone correctly you'll get the expected result:

public static void main (String[] args) throws java.text.ParseException
{
    String initialDate = "1969-12-31T22:00:00.000Z";
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");
    
    Date startDate = sdf.parse(initialDate);
            
    System.out.println("Start date: " + startDate);
    System.out.println("Start date formatted: " + sdf.format(startDate));
    
    Date resultDate = setYearOfDate(startDate, 2018, "Europe/Istanbul");
    System.out.println("Result date: " + resultDate);
    System.out.println("Result date formatted: " + sdf.format(resultDate));
        
    SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");
    sdf2.setTimeZone(TimeZone.getTimeZone("Europe/Istanbul"));
    System.out.println("Result date formatted with TimeZone: " + sdf2.format(resultDate));
}

Outputs:

Start date: Wed Dec 31 22:00:00 GMT 1969
Start date formatted: 1969-12-31T22:00:00.000Z
Result date: Sun Dec 31 21:00:00 GMT 2017
Result date formatted: 2017-12-31T21:00:00.000Z
Result date formatted with TimeZone: 2018-01-01T00:00:00.000+03

EDIT: Here is the Java 8 Date & Time API alternative:

public static void main(String[] args) {
    String initialDate = "1969-12-31T22:00:00.000Z";
    ZoneId zone = ZoneId.of("Europe/Istanbul");
    DateTimeFormatter formatter = DateTimeFormatter.ISO_ZONED_DATE_TIME.withZone(zone);
    
    ZonedDateTime startDate = ZonedDateTime.parse(initialDate, formatter);

    System.out.println("Start date: " + startDate);
    System.out.println("Start date formatted with TimeZone: " + formatter.format(startDate));

    ZonedDateTime resultDate = setYearOfDate(startDate, 2018, "Europe/Istanbul");
    System.out.println("Result date: " + resultDate);
    System.out.println("Result date formatted with TimeZone: " + formatter.format(resultDate));
}

static ZonedDateTime setYearOfDate(ZonedDateTime date, int year, String timeZoneId)
{
    GregorianCalendar calendar = GregorianCalendar.from(date);
    calendar.set(GregorianCalendar.YEAR, year); 
    return calendar.toZonedDateTime();
}

Output:

Start date: 1970-01-01T00:00+02:00[Europe/Istanbul]
Start date formatted with TimeZone: 1970-01-01T00:00:00+02:00[Europe/Istanbul]
Result date: 2018-01-01T00:00+03:00[Europe/Istanbul]
Result date formatted with TimeZone: 2018-01-01T00:00:00+03:00[Europe/Istanbul]
Rafael Guillen
  • 1,343
  • 10
  • 25
  • @rafael_guillen thanks but the issue is result date should be : Sun Dec 31 01:00:00 (UTC+3) The issue is why Europe/Istanbul (UTC+3) affects hour UTC-1 hour ? – volkan Feb 10 '21 at 11:17
  • 2
    The starting date (`1969-12-31T22:00:00.000Z`) actually is `1970-01-01T00:00:00.000+02`, so I think the result (`2018-01-01T00:00:00.000+03) is correct. – Rafael Guillen Feb 10 '21 at 11:45