Could some one explain why this past date getting increased by one hour , when I convert it to Moscow Timezone ?
I'm using JDK 1.6.0_12 version. .
2011-04-02T11:39:46+0300 --> Sat Apr 02 12:39:46 MSK 2011 // 11:39 --> 12:39
My current system time-zone is "Europe/Moscow" UTC+3 .
Also please note that this past date is in DST(Daylight Saving ) time-zone period UTC+4 , earlier used in Russia.
There was a legislative change of Russian time-zone definitions in October 2014 . Since then Russia uses UTC+3 all through out a year .
I already checked
this old post of 2014 . But I think this issue looks different.
Our developers expect that every past date (like "2011-04-02T11:39:46+0300" and which is in DST period ), should contain current time zone offset value i.e +0300 , not +0400 . And they think JRE is converting it incorrectly to UTC+4 , though "Default Time Zone Offset" shows +3 here . Is this way of handling time-zone offset value for past dates correct?
Same output is given on JRE 1.8 , which I think is an updated version ,there shouldn't be any issue in TZ definition in JRE 1.8.
Thanks in Advance !
Java Code:
import java.text.SimpleDateFormat;
import java.util.TimeZone;
import java.util.Date;
public class HelloWorld{
public static void main(String []args)
{
String dateInString = "2011-04-02T11:39:46+0300";
System.out.println(dateInString);
try {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
Date date = dateFormat.parse(dateInString);
System.out.println(date);
} catch (Exception e) {
System.out.println(e);
}
final TimeZone tzone = TimeZone.getDefault();
System.out.println("Default Time Zone ID - " + tzone.getID());
System.out.println("Default Time Zone Offset - (" + (tzone.getRawOffset() / 60 / 60 / 1000) + ") hour.");
}
}
Output :
2011-04-02T11:39:46+0300
Sat Apr 02 12:39:46 MSK 2011
Default Time Zone ID - Europe/Moscow
Default Time Zone Offset - (3) hour.
12:39 is the correct time
You are getting the correct result. In your string, 2011-04-02T11:39:46+0300, the trailing +0300 is an offset from UTC. So the point in time is the same as 2011-04-02T08:39:46+00:00 (UTC). As you say yourself, Moscow was at UTC offset +04:00 from 27 March 2011 to 26 October 2014. So to get the correct time for Moscow Java needs to add 1 hour to the hour in the string. Or 4 hours to the UTC hour of 08:39:46. In any case the time in Moscow was 12:39:46 at this point in time.
Or to answer your question:
… why this past date getting increased by one hour , when I convert it
to Moscow Timezone ?
Because Moscow on that date was 1 hour ahead of the time in the string.
java.time
That said I agree with those who recommend java.time, the modern Java date and time API, for the job. SimpleDateFormat is a notorious troublemaker of a class, and Date and TimeZone are poorly and confusingly designed too. All are long outdated. The modern API is so much nicer to work with.
For example:
ZoneId zone = ZoneId.of("Europe/Moscow");
ZonedDateTime zdt = ZonedDateTime.of(2011, 4, 2, 11, 39, 46, 0, zone);
System.out.println(zdt);
Output:
2011-04-02T11:39:46+04:00[Europe/Moscow]
You can also see from the output that Java knows that Moscow was at offset +04:00 back then.
Your question very well illustrates why java.time (opposite the old TimeZone class) makes the distinction between a time zone and an offset. A time zone includes all historic, the present and all known future offsets from UTC. This is what you need to represent historic times in Moscow correctly. In java.time a time zone is identified by a ZoneId object and obeys a ZoneRules object (most often we need not concern ourselves with the latter and can just trust Java to make the right conversions). A UTC offset is represented by a ZoneOffset object.
Question: how could I use java.time with Java 1.6?
This is your lucky day. java.time exactly requires at least Java 6.
In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in.
In non-Android Java 6 and 7 get the ThreeTen Backport, the backport of the modern classes (ThreeTen for JSR 310; see the links at the bottom).
On older Android either use desugaring or the Android edition of ThreeTen Backport. It’s called ThreeTenABP. In the latter case make sure you import the date and time classes from org.threeten.bp with subpackages.
Links
Time Changes in Moscow Over the Years
Oracle tutorial: Date Time explaining how to use java.time.
Java Specification Request (JSR) 310, where java.time was first described.
ThreeTen Backport project, the backport of java.time to Java 6 and 7 (ThreeTen for JSR-310).
Java 8+ APIs available through desugaring
ThreeTenABP, Android edition of ThreeTen Backport
Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.
Both modern java date/time api and legacy one (that is used in jdk1.6) rely on system unix time and the tzdata file bundled with the JRE. Looks like the developers are right and your java is using a very old one version of tzdata and your developers are right.
Also, the tzdata keeps information about legal changes and if you are trying to convert date/time in the past, it will apply conversion rules that were relevant at that time.
Regarding JDK 1.8: there was an update to Russian timezone information in 8u101, so you should use at least 8u101 for a better timezone conversion.
The best decision for you would be to use modern java or update your JREs tzdata manually if you really need to use an old one.
You need to set time-zone to SimpleDateFormat as shown below:
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
public class Main {
public static void main(String[] args) throws ParseException {
String dateInString = "2011-04-02T11:39:46+0300";
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
dateFormat.setTimeZone(TimeZone.getTimeZone("Europe/Moscow"));// Set time-zone
Date date = dateFormat.parse(dateInString);
System.out.println(dateFormat.format(date));
}
}
Output:
2011-04-02T12:39:46+0400
Note that java.util.Date does not have time-zone information. It's simply the number of milliseconds from the standard Java epoch of 1970-01-01T00:00:00Z where Z stands for UTC (0 hour offset), also known as Zulu time-zone. At any given moment, you will get the same number of milliseconds on the JVMs sitting in any part of the word. When you try to print an object of java.util.Date, the date-time string for the JVM's time-zone is calculated from this milliseconds value and the same is displayed. If you want to get the date-time String in a specific time-zone, you need to set it explicitly to the SimpleDateFormat and use the same to format the java.util.Date.
Related
There is a Daylight Savings Time discrepancy between the IANA database and the Java tzdb.dat 2019c
database in the Africa/Cassablanca time zone that has me very confused. There may be others but I found this one. From what I can tell the IANA time zone database clearly shows that DST is supported in Morocco (Africa/Casablanca) unfortunately the Java time zone database tzdb.dat in the 2019c release does not agree. This has and will cause me infinite grief. What am I missing here, or have others seen this kind of thing
The IANA table 2019c for Africa/Casablanca
Note: the part of the table below shows that the normal time is UTC+1 and Day light savings time is UTC a slash (/) separates standard and daylight abbreviations
Zone NAME STDOFF RULES FORMAT [UNTIL]
Zone Africa/Casablanca -0:30:20 - LMT 1913 Oct 26
0:00 Morocco +00/+01 1984 Mar 16
1:00 - +01 1986
0:00 Morocco +00/+01 2018 Oct 28 3:00
1:00 Morocco +01/+00
From Oct 28 2018 till present offset is +1 for standard and +0 for daylight savings (a slash (/) separates standard and daylight abbreviations.)
STDOFF 1:00 so The amount of time to add to UT to get standard time, without any adjustment for daylight saving this corresponds to UTC +1 that is the current Morocco time.
So we take Zone Africa/Casablanca which is UTC and add the offsets +01/+00 depending on Ramadan.'
The test
I wrote a simple Java class to check the 2019c TZDB. This class (shown below) shows that something is wrong with the latest Java time zone data file tzdb.dat file. This test was run using IBM SR5FP40 with the time zone 2019c data file. I had the same results with OpenJDK using a 2019c datafile.
First line from tzdb.dat shows 2019c
TZDB 2019cX Africa/Abidjan Africa/Accra Africa/Addis_Ababa Africa/Algiers
Test that shows the problem
Time Zone = Africa/Casablanca
Supports Day light Savings time = false
Date Mon May 20 00:00:00 WET 2019 is currently in DST false
Time zone name Western European Time
Time zone ID Africa/Casablanca
A baseline test was also run to show if DST is supported the code will show it.
Time Zone = Europe/Rome
Supports Day light Savings time = true
Date Mon May 20 00:00:00 CEST 2019 is currently in DST true
Time zone name Central European Time
Time zone ID Europe/Rome
The code is here for reference
import java.util.*;
import java.text.SimpleDateFormat;
import java.text.ParseException;
public class checkdaylight{
public static void main(String[] args)
{
// Create TimeZone object
//Europe/Rome
//Pacific/Pago_Pago
//Africa/Casablanca
String TimezoneToTest = "Africa/Casablanca";
System.out.println("Time Zone = " + TimezoneToTest);
TimeZone obj = TimeZone.getTimeZone(TimezoneToTest);
TimeZone.setDefault(TimeZone.getTimeZone(TimezoneToTest)); //to avoid confusion
// Checking day light time
// and displaying the result
System.out.println("Supports Day light Savings time = "
+ obj.useDaylightTime());
String pattern = "yyyy-MM-dd";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern);
try{
simpleDateFormat.setTimeZone(TimeZone.getTimeZone(TimezoneToTest));
Date checkdate = simpleDateFormat.parse("2019-05-20");
System.out.println("Date " + checkdate.toString()+ " is currently in DST "+ obj.inDaylightTime(checkdate));
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println("Time zone name " +obj.getDisplayName());
System.out.println("Time zone ID " + obj.getID());
}
}
Morocco now on DST permanently
As of October 26, 2018, Morocco switched to DST permanently†.
The Morocco government gave only 2 days notice(!), in Decree 2.18.855. Apparently the government intends to stop all clock-shifting, with no more changes for DST nor for Ramadan. This decree means an offset of +01:00 year-round.
This decree also means Morocco is no longer “on DST”. The new-normal is an offset one hour ahead of UTC, whereas in the old days the offset was normally at UTC (an offset of zero hours-minutes-seconds). So notice in the code below that calling ZoneRules::isDaylightSavings returns false nowadays (early 2020).
See Wikipedia for details: Daylight saving time in Morocco.
†When I say “permanently”, take that with a grain of salt. Politicians around the world have shown a penchant for frequently changing the offset of their respective time zone(s). This "permanently on DST" is only the latest fad to have caught the fancy of politicians. Always expect further changes.
Avoid legacy date-time classes
You are using terrible date-time classes that were supplanted years ago by the modern java.time classes defined in JSR 310. There is no longer any reason to use those awful legacy classes.
java.time
Specify your intended time zone.
ZoneId zCasablanca = ZoneId.of( "Africa/Casablanca" ) ;
Fetch the zone rules.
ZoneRules rulesCasablanca = zCasablanca.getRules() ;
Interrogate the rules that apply for a specific moment in that zone.
ZoneOffset offset = rulesCasablanca.getOffset( Instant.now() ) ;
boolean isDst = rules.isDaylightSavings( instant ) ;
Or collapse that to a single line.
ZoneOffset offset = ZoneId.of( "Africa/Casablanca" ).getRules().getOffset( Instant.now() ) ;
Verify your version of Java.
System.out.println( "Java vendor and version:" ) ;
System.out.println( " " + System.getProperty("java.vendor") ) ;
System.out.println( " " + Runtime.version() ) ;
String tzdataVersion =
java.time.zone.ZoneRulesProvider
.getVersions("UTC")
.lastEntry()
.getKey()
;
System.out.println( "tzdata: " + tzdataVersion ) ;
System.out.println( "" ) ;
See this code run live at IdeOne.com.
Java vendor and version:
Oracle Corporation
12.0.1+12
tzdata: 2018g
offset.toString(): +01:00
isDst: false
Specific date
Let's try your specific date.
LocalDate localDate = LocalDate.parse( "2019-05-20" ) ;
ZonedDateTime zdt = localDate.atStartOfDay( z ) ;
System.out.println( "zdt.toString(): " + zdt ) ;
System.out.println(
"offset: " + rules.getOffset( zdt.toInstant() ) +
" | is in DST: " + rules.isDaylightSavings( zdt.toInstant() )
);
zdt.toString(): 2019-05-20T00:00+01:00[Africa/Casablanca]
offset: +01:00 | is in DST: false
tzdata
Oracle lists the tzdata files built into Java runtimes.
That list shows that the Morocco switch to permanent DST was accounted for in tzdata2018g. That tzdata file was bundled with Java versions 11.0.2, 8u201, and 7u211. At least Oracle bundled it, while I assume the OpenJDK project did so as well (I did not verify).
Morocco switches to permanent +01 on 2018-10-27.
Morocco switches from +00/+01 to permanent +01 effective 2018-10-27, so its clocks will not fall back on 2018-10-28 as previously scheduled.
To get the version of the tzdata data file in use by your JVM, see this Answer on the Question, Java - find tzdata version in use regardless of JRE version.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes.
Where to obtain the java.time classes?
Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and Java SE 7
Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
Later versions of Android bundle implementations of the java.time classes.
For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.
When I print date that I get from server, it shows Mon Jun 24 16:15:31 GMT+09:00 2019
val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
val date: Date? = formatter.parse(checkedDate) // date from server
val transformedDate = ("${String.format("%02d", date!!.month + 1)}.${String.format("%02d", date!!.date)}.${date.year + 1900}")
val title: String? = ("$transformedDate")
val longGmtTime = date.time
val mZone = TimeZone.getDefault()
val offset = mZone.getOffset(longGmtTime)
val longLocalTime = longGmtTime + offset - (9 * HOUR)
val localDate = Date() // local date
localDate.time = longLocalTime
val localFormatTime = formatter.format(localDate)
val transformedLocalDate = ("${String.format("%02d", localDate!!.month + 1)}.${String.format("%02d", localDate!!.date)}.${localDate.year + 1900}")
And it gives me server time: 2019-06-24 16:15:31 -> 06.24.2019, local time(Asia/Seoul)-> 2019-06-25 01:15:30 ->06.25.2019 for the result.
The server time and local time must be the same. But the local time shows somewhere else.
What's the problem?
What's the problem?
The gross problem list includes:
You are using the poorly designed and long outdated Java date and time classes Date, TimeZone and SimpleDateFormat.
You are using the deprecated methods getMonth, getDate and getYear of the Date class. These methods work unreliably across time zone, which is the main reason why they were deprecated.
You are doing the time zone conversion manually using addition, subtraction and multiplication. Date and time math is error-prone, and you should always leave it to proven library methods.
The millisecond count you get from Date.getTime is since the epoch of 1970-01-01T00:00:00 UTC. This is a unique moment in time and independent of time zone, so adding to and subtracting from the millisecond count for time zone conversion makes no sense.
I can reproduce your result when I set my JVM’s default time zone to Asia/Seoul and assume that HOUR is 0 (or some value in the range from 0 through 111). I assume that you had wanted HOUR to denote the number of milliseconds in an hour, 3 600 000 (at least usually, exceptions exist).
You were formatting your date by concatenating the results of calls to Strirg.format. It’s better to leave formatting to a specialized date formatter.
The fix: java.time
ZoneId serverTimeZone = ZoneId.of("Asia/Seoul");
DateTimeFormatter serverFormatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");
ZoneId clientTimeZone = ZoneId.systemDefault();
String checkedDate = "2019-06-24 16:15:31";
ZonedDateTime serverDateTime = LocalDateTime.parse(checkedDate, serverFormatter)
.atZone(serverTimeZone);
ZonedDateTime clientDateTime = serverDateTime.withZoneSameInstant(clientTimeZone);
System.out.println("clientDateTime: " + clientDateTime);
Sorry that I can write and run only Java code, I trust you to translate. With my JVM’s time zone still set to Asia/Seoul I get:
clientDateTime: 2019-06-24T16:15:31+09:00[Asia/Seoul]
The server time and the client time are the same, as you requested. If instead I keep my own time zone, I get:
clientDateTime: 2019-06-24T09:15:31+02:00[Europe/Copenhagen]
So there is a conversion taking place.
To format the date:
DateTimeFormatter displayFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)
.withLocale(Locale.forLanguageTag("ko-KR"));
String transformedLocalDate = clientDateTime.format(displayFormatter);
System.out.println("transformedLocalDate: " + transformedLocalDate);
transformedLocalDate: 2019. 6. 24.
Or if you insist on month.date.year:
DateTimeFormatter displayFormatter = DateTimeFormatter.ofPattern("MM.dd.u");
transformedLocalDate: 06.24.2019
A further recommendation would be to have your server deliver a date-time string in UTC in ISO 8601 format. That would go like 2019-06-24T07:15:31Z for the moment used in the examples.
Question: Can I use java.time with minSdk API level 23 on Android?
Yes, java.time works nicely on older and newer Android devices. It just requires at least Java 6.
In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in.
In Java 6 and 7 get the ThreeTen Backport, the backport of the modern classes (ThreeTen for JSR 310; see the links at the bottom).
On (older) Android use the Android edition of ThreeTen Backport. It’s called ThreeTenABP. And make sure you import the date and time classes from org.threeten.bp with subpackages.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Java Specification Request (JSR) 310, where java.time was first described.
ThreeTen Backport project, the backport of java.time to Java 6 and 7 (ThreeTen for JSR-310).
ThreeTenABP, Android edition of ThreeTen Backport
Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.
Wikipedia article: ISO 8601
You should specify setup server timezone instead of you device (which is default)
Im using SimpleDateFormat to get the current date and hour, the Date is working well but for unknown reasons the hour value is 3 hours backwards.
for example if I will run the code on 19:40 the time value will be 16:40 and I don't know why.
Would really appreciate some help.
String timeStamp = new SimpleDateFormat("M/d/yyyy&HH:mm").format(Calendar.getInstance().getTime());
java.time and ThreeTenABP
java.time, the modern Java date and time API, gives a pretty natural way to control the time zone. For example:
ZoneId zone = ZoneId.of("Asia/Istanbul");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("M/d/uuuu&HH:mm");
String timeStamp = ZonedDateTime.now(zone).format(formatter );
System.out.println(timeStamp);
When I ran this snippet just now, the output was:
6/6/2019&19:53
I don’t know what your time zone is and trust you to specify the correct one. It matters.
Question: Can I use java.time on Android?
Yes, java.time works nicely on older and newer Android devices. It just requires at least Java 6.
In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in.
In Java 6 and 7 get the ThreeTen Backport, the backport of the modern classes (ThreeTen for JSR 310; see the links at the bottom).
On (older) Android use the Android edition of ThreeTen Backport. It’s called ThreeTenABP. And make sure you import the date and time classes from org.threeten.bp with subpackages.
What went wrong in your code?
As others have said, your issue is a time zone issue. Apparently your SimpleDateFormat had its time zone set to UTC. When you’re not specifying time zone, it picks up its time zone from the JVM, which in turn usually picks it up from the operating system if not specified otherwise. That your JVM is using UTC is a standard practice, so not that much of a surprise.
Anyway, the datetime classes you were using, SimpleDateFormat and Calendar, are poorly designed, the former in particular notoriously troublesome. Fortunately both are long outdated. Instead use java.time, the modern Java date and time API.
I've just noticed that the phone hour is 3 hours backwards as well. Is
there a way to change it?
Most likely the phone system clock is correct (maybe even updated automatically from a time server), but its time zone is set to UTC. There should be a way to set it to your time zone in the phone settings (then it will likely also be used by your JVM).
ISO 8601
As an aside your timestamp format is peculiar. I recommend that you use a standard format, best ISO 8601, and also that you include offset in the string so that even if the time zone was wrong, the time would still be unambiguous and correct. Two examples. First you may use UTC consciously:
String timeStamp = Instant.now().toString();
2019-06-06T16:57:19.493599Z
The trailing Z means UTC.
If you want your own time zone:
String timeStamp = OffsetDateTime.now(zone).toString();
2019-06-06T19:58:29.788376+03:00
Links
Oracle tutorial: Date Time explaining how to use java.time.
Java Specification Request (JSR) 310, where java.time was first described.
ThreeTen Backport project, the backport of java.time to Java 6 and 7 (ThreeTen for JSR-310).
ThreeTenABP, Android edition of ThreeTen Backport
Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.
Wikipedia article: ISO 8601
A Date does not have any timezone information; it's just a wrapper around a long that is assumed to be UTC epoch milliseconds. It's no coincidence that your timezone is 3 hours ahead of UTC and the formatted time is 3 hours out.
Although you can specify the timezone that SimpleDateFormat renders a Date in:
SimpleDateFormat format = new SimpleDateFormat("M/d/yyyy&HH:mm");
format.setTimeZone(TimeZone.getTimeZone("Europe/Moscow"));
Due to their many problems, Date and Calendar are largely deprecated.
Use LocalDateTime and DateTimeFormatter instead:
String timeStamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("M/d/yyyy&HH:mm"));
I'm managing devices that report their system clock as seconds since midnight 01-01-1900.
I need to convert this into a timestamp.
So far, I'm doing this as follows:
import java.text.SimpleDateFormat;
import java.util.Calendar;
public class TestTime
{
// Pass seconds since 01-01-1900 00:00:00 on the command line
public static void main(String[] args)
{
// ---------------------
// Create time formatter
// ---------------------
SimpleDateFormat format;
format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// ---------------------------
// Compose 01-01-1900 00:00:00
// ---------------------------
Calendar cal;
cal = Calendar.getInstance();
cal.set(Calendar.YEAR, 1900);
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.set(Calendar.DAY_OF_MONTH, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
// -------------------
// Show what we've got
// -------------------
System.out.println(format.format(cal.getTime()));
// ---------------------------------------------
// Add the seconds as passed on the command line
// ---------------------------------------------
long secs = Long.parseLong(args[0]);
while (secs > Integer.MAX_VALUE)
{
cal.add(Calendar.SECOND, Integer.MAX_VALUE);
secs -= Integer.MAX_VALUE;
}
cal.add(Calendar.SECOND, (int)secs);
// -------------------
// Show what we've got
// -------------------
System.out.println(args[0] + " corresponds to " + format.format(cal.getTime()));
} // main
} // class TestTime
When running this on my local PC (Italy, Windows 7), I get the following:
java -cp . TestTime 3752388800
1900-01-01 00:00:00
3752388800 corresponds to 2018-11-28 10:13:20
This is perfectly correct.
I get the same results when running this on a Linux machine (still in Italy).
However, running the very same program on a Linux machine in Brazil, I get different results:
java -cp . TestTime 3752388800
1900-01-01 00:00:00
3752388800 corresponds to 2018-11-28 11:19:48
Whatever value I pass on the commandline, the difference is always 01:06:28.
Any idea where this difference is coming from?
BTW, I'm not concerned about the timezone. I just need a timestamp
Update 1:
The very same thing happens also when using Java 6 (which is the actual version used within our production environment in Brazil).
So, the problem does not depend on the java version
Update 2:
The problem does not occur when entering a number of seconds below 441763200 (which corresponds to 01-01-1914 00:00:00)
The question remains why we get a difference for Brazil?
java.time
A solution is to make sure you do your conversion in UTC:
Instant base = LocalDate.of(1900, Month.JANUARY, 1)
.atStartOfDay(ZoneOffset.UTC)
.toInstant();
String secsSince1900String = "3752388800";
long secsSince1900 = Long.parseLong(secsSince1900String);
Instant target = base.plusSeconds(secsSince1900);
System.out.println(target);
Output (independent of JVM time zone):
2018-11-28T10:13:20Z
The trailing Z in the output means UTC. I have tested while setting my JVM’s default time zone to America/Sao_Paulo, it made no difference. If you want, you can formate the date and time to your liking, for example:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formattedDateTime = target.atOffset(ZoneOffset.UTC).format(formatter);
System.out.println(formattedDateTime);
2018-11-28 10:13:20
What went wrong when running in Brazil?
There are a number of time zones across Brazil. I took São Paulo as an example and reproduced your output readily. At the turn of the century in 1900, São Paulo was at offset -03:06:28 from GMT. Your Calendar uses the JVM’s default time zone, so you really set its time of day to 03:06:28 GMT, which explains the difference.
That said, the date-time classes you were using — SimpleDateFormat and Calendar — have design problems and have fortunately been replaced by java.time, the modern Java date and time API, with Java 8 nearly 5 years ago. One trait of the modern API is we more naturally make time zone explicit, which makes it easier to avoid issues like yours, and also to fix them if we run into them anyway.
Question: Our production Java version is Java 6. Can I use java.time?
Yes, java.time works nicely on Java 6. It has been backported.
In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in.
In Java 6 and 7 get the ThreeTen Backport, the backport of the new classes (ThreeTen for JSR 310; see the links at the bottom).
On (older) Android use the Android edition of ThreeTen Backport. It’s called ThreeTenABP. And make sure you import the date and time classes from org.threeten.bp with subpackages.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Java Specification Request (JSR) 310, where java.time was first described.
ThreeTen Backport project, the backport of java.time to Java 6 and 7 (ThreeTen for JSR-310).
ThreeTenABP, Android edition of ThreeTen Backport
Question: How to use ThreeTenABP in Android Project, with a very thorough explanation.
Have a look at this site: https://www.timeanddate.com/time/zone/brazil/sao-paulo and navigate to Time zone changes for: 1900-1924. There you can see an offset of -03:06:28 to UTC before 01-01-1914. It is exactly the same reason as in Why is subtracting these two times (in 1927) giving a strange result?
On Jan 1st, 1914 Brazil changed the time and added 6 minutes and 28 seconds to their time moving from LMT to BRT (see Time Changes in São Paulo Over the Years, 1900-1924).
The additional one hour of difference is probably that Brazil spans 3 time zones (UTC-4, UTC-3, UTC-2) and you didn't set the time zone in your code, depending on the JVM system time zone.
This is my program snippet
import java.lang.Math;
import java.util.Date;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
public class Main
{
public static void main(String[] args)
{
String dateTime = "2017-03-12 02:46:00";
// convert string to java.util.Date
try {
SimpleDateFormat e = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = e.parse(dateTime);
System.out.println(d);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
This is the output of that program
Sun Mar 12 03:46:00 PDT 2017
Expected Output
Sun Mar 12 02:46:00 PDT/PST 2017
Apparently, it is adding daylight saving time which occurs on PST at 2017-03-12 02:00:00
Few things I am bounded.
I cannot change server default timezone or anything specific to JVM
I must return back java.util.Date as final value.
Edit:
To some comment pointing me out how java.util.Date only stores long timestamp. Can you please give me a way where this function works
java.util.Date convertStringToDate(String str) {
// code to convert String to Date
}
convertStringToDate("2017-03-12 02:46:00");
should give me 2017-03-12 02:46:00 value in Date class? I don't care about what timezone it provides. It should have that value, whatever timezone it is while printing. Again my JVM is in PST.
Use java.time, not legacy date-time classes
You are using troublesome old date-time classes such as java.util.Date that are now legacy, supplanted by the java.time classes.
LocalDateTime
2016-03-12 02:46:00 value …I don't care about what timezone it provides. It should have that value, whatever timezone it is…
If you truly want to represent that date and time-of-day without regard for time zone, use the LocalDateTime class. This class purposely ignores time zone.
To parse, adjust your input string to comply with the ISO 8601 standard formats used by the java.time classes for parsing/generating strings.
String input = "2016-03-12 02:46:00".replace( " " , "T" );
LocalDateTime ldt = LocalDateTime.parse( input );
But beware: By ignoring time zone you lose the meaning of this date+time. Without the context of a time zone, we do not know if you mean the 2 AM in Auckland NZ, or 2 AM in Kolkata India (some hours later), or 2 AM in Paris France (more hours later), or 2 AM in Montréal Québec (still more hours later). A LocalDateTime is a rough idea about possible moments, but is not actually a point on the timeline.
ZonedDateTime
This is the output of that program Sun Mar 12 03:46:00 PDT 2017
Expected Output Sun Mar 12 02:46:00 PDT/PST 2017
Now you contradict yourself.
By including the PDT or PST with your expected output, you mean a specific moment on the timeline perceived through the lens of a particular region’s wall-clock time. This contradicts your statement that you want "2016-03-12 02:46:00" regardless of time zone. It is crucial that you understand this distinction to properly handle date-time work.
If indeed the intent of the string 2016-03-12 02:46:00 is to represent a moment in the wall-clock time of the left coast of north America (as I guess you meant by PDT), then we must parse that string firstly as a LocalDateTime as it lacks any indicator of time zone, but then immediately adjust it into a time zone to get a ZonedDateTime object.
Specify a proper time zone name in the format of continent/region, such as America/Montreal, Africa/Casablanca, or Pacific/Auckland. Never use the 3-4 letter abbreviation such as EST or IST or PDT or PST as they are not true time zones, not standardized, and not even unique(!).
Here I arbitrarily chose America/Los_Angeles as the time zone, as your Question does not mention a specific time zone, only “PDT”.
String input = "2017-03-12 02:46:00".replace( " " , "T" );
LocalDateTime ldt = LocalDateTime.parse( input );
ZoneId z = ZoneId.of( "America/Los_Angeles" );
ZonedDateTime zdt = ldt.atZone( z );
But it just so happens that March 12 of 2017 has an anomaly. That is the day when the craziness known as Daylight Saving Time (DST) kicks in. The clocks in much of the left coast of north America at 2 AM jump to 3 AM. There is no two o’clock hour. The day is 23 hours long rather than the usual 24 hours. So your request for 2:46 is asking for a nonexistent moment, an invalid value. The design choice in java.time to resolve this conundrum is to jump forward, following the "Spring Forward" of DST. The result is in the 3 AM hour, 03:46.
See this code run live in IdeOne.com.
input: 2017-03-12T02:46:00
ldt.toString(): 2017-03-12T02:46
zdt.toString(): 2017-03-12T03:46-07:00[America/Los_Angeles]
Note the 2 AM hour becomes the 3 AM hour in that output.
A reasonable person could make arguments for a different design choice in handling this anomaly, such as throwing an Exception. But this is how java.time works. Study the class doc and be sure you understand the behavior on this important topic.
If you want to detect such an anomaly, call toLocalDateTime on the ZonedDateTime object, and compare to the first LocalDateTime. With no anomaly, the pair of LocalDateTime objects will be equal; with an anomaly they will not be equal.
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Where to obtain the java.time classes?
Java SE 8 and SE 9 and later
Built-in.
Part of the standard Java API with a bundled implementation.
Java 9 adds some minor features and fixes.
Java SE 6 and SE 7
Much of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
Android
The ThreeTenABP project adapts ThreeTen-Backport (mentioned above) for Android specifically.
See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.