I've been trying to read the binary file with Java, and the binary file is written in C#. And some of those data is contain a DateTime data.
When DateTime data will be written into the file (in binary), it using DateTime.ToBinary(); on C#.
For reading the DateTime data, it will convert first from bytes into long data, using BitConverter.ToInt64(byte[], 0), and then convert it again from long into DateTime data using DateTime.FromBinary(long). (All of those are written in C#).
Let's say the long data after converting from bytes is = -8586803256090942249, and when convert it into DateTime, it will return = 3/17/2018 5:07:56 PM
Now, I'm trying to read that binary file with Java. And for converting the bytes data into long data, I'm using this code : ByteBuffer.wrap(byte[]).order(ByteOrder.LITTLE_ENDIAN).getLong().
It will return the exact long data value as C# did. But when I try to convert it from long data into DateTime in Java, using Date date = new Date(long), it will return = Sun May 06 19:04:17 WIB 272097407 instead.
Can you help me what is the correct solution for this ? Is there any equivalent for DateTime.FromBinary() from C# in Java ? Or is my code wrong ? All of yours answers is really appreciated.
In Java:
long fromBytes = -8586803256090942249L;
// Mask out kind and ticks
int kind = Math.toIntExact((fromBytes >> 62) & 0x3);
long ticks = fromBytes & 0x3FFF_FFFF_FFFF_FFFFL;
LocalDateTime cSharpEpoch = LocalDate.of(1, Month.JANUARY, 1).atStartOfDay();
// 100 nanosecond units or 10^-7 seconds
final int unitsPerSecond = 10_000_000;
long seconds = ticks / unitsPerSecond;
long nanos = (ticks % unitsPerSecond) * 100;
LocalDateTime ldt = cSharpEpoch.plusSeconds(seconds).plusNanos(nanos);
switch (kind) {
case 0: // Unspecified
case 2: // Local time
System.out.println("Result LocalDateTime: " + ldt);
break;
case 1: // UTC
OffsetDateTime utcDateTime = ldt.atOffset(ZoneOffset.UTC);
System.out.println("Result OffsetDateTime in UTC: " + utcDateTime);
break;
default:
System.out.println("Not a valid DateTimeKind: " + kind);
break;
}
Output:
Result LocalDateTime: 2018-03-17T10:07:56.383355900
Edit: The number is
A 64-bit signed integer that encodes the Kind property in a 2-bit
field and the Ticks property in a 62-bit field.
Tetsuya Yamamoto was correct so far as the ticks property denotes number of 100-nanosecond intervals elapsed since 0001/01/01 at start of day (midnight). The kind is either 0 for unspecified, 1 for UTC or 2 for local time. So I am masking the kind and the ticks out separately.
Even though the kind is 2 in your case, which should be for local time, it seems that the time is indeed in UTC. It’s the only way that the time printed could agree with your expected 5:07:56 PM Western Indonesian Time. Maybe the number was generated on a computer with its time zone set to UTC.
To get the time in your time zone:
ZoneId targetZone = ZoneId.of("Asia/Jakarta");
ZonedDateTime zdt = ldt.atZone(ZoneOffset.UTC).withZoneSameInstant(targetZone);
System.out.println("Converted to target time zone: " + zdt);
Converted to target time zone: 2018-03-17T17:07:56.383355900+07:00[Asia/Jakarta]
This agrees with what you said you got on the C# side.
PS Avoid the Date class in Java if you can, it is long outdated and poorly designed and was replaced many years ago now by java.time, the modern Java date and time API (which I am of course using in the above). If you do need a Date for a legacy API that you cannot change or don’t want to change just now, as you already noted in a comment, the conversion is like this:
Instant inst = ldt.atOffset(ZoneOffset.UTC).toInstant();
Date date = Date.from(inst);
System.out.println(date);
Output on a JVM with default time zone Asia/Jakarta:
Sat Mar 17 17:07:56 WIB 2018
Acknowledgement: Andreas in an answer (link below) explained the structure of the 64 bits number and gave the link to the documentation. I have taken them from there.
Links
DateTime.FromBinary(Int64) Method from the .NET documentation
Andreas’ answer to a duplicate question
Related
I have a Calendar object that corresponds to 2021-07-05T18:00:00.000-04:00 (Eastern Daylight Time). Yet Calendar.get(DST_OFFSET) and Calendar.getTimeZone().getDSTSavings() both give 0. It should be 1 hour. What am I missing or what am I going wrong? All the other methods I play with are returning the expected values.
I am creating the Calendar using setTimeInMillis() and TimeZone using offsets. Is that the reason it is not working? The displayed civil times are always right...
As much as I would like to use the new Java time I am using Java for Android. Last I checked only the most recent versions of Android support the new Java time. They may eventually add support to their older versions.
One problem is that the input defines an offset from UTC, but not a real time zone with specific rules (like if DST is applied at all and if it is, when will DST be applied).
Calendar is clearly not capable of handling those rules, the class (and probably the entire API) was not designed to be.
That's one of the reasons for java.time having been introduced in Java 8.
Here's some example use of java.time in a situation like yours:
public static void main(String[] args) {
// example String in ISO format
String dateString = "2021-07-05T18:00:00.000-04:00";
// define your time zone
ZoneId americaNewYork = ZoneId.of("America/New_York");
// parse the (zone-less) String and add the time zone
ZonedDateTime odt = OffsetDateTime.parse(dateString)
.atZoneSameInstant(americaNewYork);
// then get the rules of that zone
long hours = americaNewYork.getRules()
// then get the daylight savings of the datetime
.getDaylightSavings(odt.toInstant())
// and get the full hours of the dst offset
.toHoursPart();
// use a formatter to format the output (nearly) as desired
System.out.println(odt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME)
+ " has a daylight saving offset of "
+ hours);
}
This prints
2021-07-05T18:00:00-04:00[America/New_York] has a daylight saving offset of 1
EDIT:
Your comment made me provide a similar version that uses a long as input:
public static void main(String[] args) {
// example String in ISO format
long input = 1625522400000L;
// create an Instant from the input
Instant instant = Instant.ofEpochMilli(input);
// define your time zone
ZoneId americaNewYork = ZoneId.of("America/New_York");
// then get the rules of that zone
long hours = americaNewYork.getRules()
// then get the daylight savings of the Instant
.getDaylightSavings(instant)
// and get the full hours of the dst offset
.toHoursPart();
// use a formatter to format the output (nearly) as desired
System.out.println(ZonedDateTime.ofInstant(instant, americaNewYork)
.format(DateTimeFormatter.ISO_ZONED_DATE_TIME)
+ " has a daylight saving offset of "
+ hours);
}
The output is just the same as in the above example.
in a java bug tracker you will find your problem.
During the "fall-back" period, Calendar doesn't support disambiguation and the given local time is interpreted as standard time.
To avoid the unexpected DST to standard time change, call add() to reset the value.
you can resolve it by replacing set() with
cal.add(Calendar.MINUTE, -cal.get(Calendar.MINUTE));
I need to convert what I think is a Julian timestamp to a regular time stamp with Java.
The application that generates this timestamp is a proprietary payment system (Base24-EPS from ACI). I need to be able to pull and parse the value from the database with a Java application.
A sample timestamp value in decimal is 18 digits:
137955731472778910
With DALCI (internal tool provided by Base24-EPS), I can see this is equivalent of:
3: convert 137955731472778910 to datetime(yyyy/mm/dd hh:mm:ss);
2019/12/14 12:39:07
I found an answer here which seems to be related. But 137955731472778910 is smaller than 210866803200000000, which is the Julian timestamp for 01-JAN-1970 (epoch for unix time).
All the other Julian timestamp online converter I see, for example http://www.onlineconversion.com/julian_date.htm, have Julian date format as double 2458806.52903.
18 digits seem too long.
Do you know how can I parse this timestamp format with Java?
Many thanks.
Assuming you are in the UTC timezone (you probably aren't, but you haven't told me what timezone you are in), I have a formula:
long timestampFromAci = ...;
long timestampJava = (timestamp - 122192460002790000L) / 10000;
Instant.ofEpochMilli(timestampJava);
new Date(timestampJava); // Old, deprecated - use java.time classes
This assumes that the conversion is linear.
Your product timestamp has 10000 units per millisecond, since there are 2145052000 milliseconds between 2019/11/19 16:48:15 and 2019/12/14 12:39:07, and the difference in your product's timestamp is 21450514084700.
If you divide these two, that's almost exactly 10000 - the difference is because your tool doesn't display fractional seconds.
Extrapolating from that, I can derive that value that your product timestamp would have for the Unix epoch op 1/1/1970 - 122192460002790000.
However, as I said, I made the assumption that you are in the UTC timezone. For every hour that your timezone is off from UTC, you need to adjust that number by 3600 seconds times 10,000,000 units product timestamp units per second.
I have written below code which is running, and giving output. But I'm not sure It's a right one.
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date date = new Date();
sdf.setTimeZone(TimeZone.getTimeZone("GMT-7"));
String value = sdf.format(date);
System.out.println(value);
Date date2 = sdf.parse(value);
long result = date2.getTime();
System.out.println(result);
return result;
The above code what I'm trying is, I just need to get the current time of GMT time zone and convert it as epoch format which is gonna used in Oracle db.
Can someone tell me that format, and the above code is right?
First, you should not store time since the epoch as a timestamp in your database. Look into the date-time datatypes that your DMBS offers. In Oracle I think that a date column will be OK. For most other DBMS you would need a datetime column. timestamp and timestamp with timezone may be other and possibly even sounder options depending on your exact requirements.
However, taking your word for it: Getting the number of milliseconds since the epoch is simple when you know how:
long millisecondsSinceEpoch = System.currentTimeMillis();
System.out.println(millisecondsSinceEpoch);
This just printed:
1533458641714
The epoch is defined in UTC, so in this case we need to concern ourselves with no other time zones.
If you needed seconds rather than milliseconds, it’s tempting to divide by 1000. However, doing your own time conversions is a bad habit since the libraries already offers them, and using the appropriate library methods gives clearer, more explanatory and less error-prone code:
long secondsSinceEpoch = Instant.now().getEpochSecond();
System.out.println(secondsSinceEpoch);
1533458641
You said:
I just need to get the current time of GMT time zone…
Again taking your word:
OffsetDateTime currentTimeInUtc = OffsetDateTime.now(ZoneOffset.UTC);
System.out.println(currentTimeInUtc);
long millisecondsSinceEpoch = currentTimeInUtc.toInstant().toEpochMilli();
System.out.println(millisecondsSinceEpoch);
2018-08-05T08:44:01.719265Z
1533458641719
I know that GMT and UTC are not exactly the same, but for most applications they can be (and are) used interchangeably.
Can someone tell me (if) the above code is right?
When I ran your code just now, its output agreed with mine except the milliseconds were rounded down to whole thousands (whole seconds):
1533458641000
Your code has some issues, though:
You are using the old, long out-dated and poorly designed classes SimpleDateFormat, Date and TimeZone. The first in particular has a reputation for being troublesome. Instead we should use java.time, the modern Java date and time API.
Bug: In your format pattern string you are using lowercase hh for hour of day. hh is for hour within AM or PM, from 1 through 12, so will give you incorrect results at least half of the day. Uppercase HH is for hour of day.
Don’t use GMT-7 as a time zone. Use for example America/Los_Angeles. Of course select the time zone that makes sense for your situation. Edit: You said:
I just want to specify the timezone for sanjose. GMT-7 is refer to
sanjose current time.
I believe many places are called San Jose. If you mean San Jose, California, USA, you are going to modify your program to use GMT-8 every time California goes back to standard time and opposite when summer time (DST) begins?? Miserable idea. Use America/Los_Angeles and your program will work all year.
Since you ask for time in the GMT time zone, what are you using GMT-7 for at all?
There is no point that I can see in formatting your Date into a string and parsing it back. Even if you did it correctly, the only result you would get would be to lose your milliseconds since there are no milliseconds in your format (it only has second precision; this also explained the rounding down I observed).
Links
Oracle tutorial: Date Time explaining how to use java.time, the modern Java date and time API.
San Jose, California on Wikipedia
Why not use Calendar class?
public long getEpochTime(){
return Calendar.getInstance(TimeZone.getTimeZone("GMT-7")).getTime().getTime()/1000; //( milliseconds to seconds)
}
It'll return the current Date's Epoch/Unix Timestamp.
Based on Harald's Comment:
public static long getEpochTime(){
return Clock.system(TimeZone.getTimeZone("GMT-7").toZoneId() ).millis()/1000;
}
Here is a solution using the java.time API
ZonedDateTime zdt = LocalDateTime.now().atZone(ZoneId.of("GMT-7"));
long millis = zdt.toInstant().toEpochMilli();
I have a number format in a file that Matlab converts to a date string. The format is structured, for example, as 732161.8561226851.
When I perform datestr(732161.8561226851) in Matlab, I get:
ans =
'02-Aug-2004 20:32:49'
I wish to achieve the same output but as a Java function. I haven't seen this format before, however, and am unsure of the conversion process taken.
What is the process to convert this into a date string so I can create a Java function?
// Matlab epoch is January 0, 0000, so subtract 1 day from January 1:
Instant matlabEpoch = Instant.parse("0000-01-01T00:00:00Z").minus(1, ChronoUnit.DAYS);
Instant inst = matlabEpoch.plusMillis(
Math.round(732161.8561226851 * TimeUnit.DAYS.toMillis(1)));
System.out.println(inst);
This prints:
2004-08-02T20:32:49Z
I trust that #Riley Jacob’s answer is correct and see that it uses the long outdated Date class. So I wanted to give a modern version of the same answer. Other than avoiding the old-fashioned class it also has the advantage of leaving most of the calculation to library methods, which in turn gives clearer code.
I find it worth noting that we hit the same result as Matlab with millisecond precision. If the result had been one millisecond off, it would have been printed as either 2004-08-02T20:32:48.999Z or 2004-08-02T20:32:49.001Z.
Link: Oracle tutorial: Date Time explaining how to use java.time.
The format is MATLAB's DateNumber: the number of days elasped since January 0, 0000. You can use Java's toUTCString with some modifying:
var d = new Date(dateMATLAB*86400000-62167305600000);
var n = d.toUTCString();
Where dateMATLAB is MATLAB's date output. The value 62167305600000 is the number of milliseconds which elapsed between year 0 and 1970. Using your example,
var d = new Date(732161.8561226851*86400000-62167305600000);
var n = d.toUTCString();
Will output
Mon, 02 Aug 2004 20:32:48 GMT
I am getting current time from Ruby on Rails webservice in Unix Timestamp format (ie. in seconds from 1 Jan 1970), the timezone on server is UTC.
In Java I am trying to convert local current time to UTC time. But every time it is giving 6+ minutes ahead time. I want to get the difference of UTC current time and the time returned from service. My Java code is -
SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
Date utc_current = new Date(System.currentTimeMillis());
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
long serverTime = 1424936268000L;
long resTime = sdf.getCalendar().getTimeInMillis() - serverTime;
System.out.println("Time Diff : " + resTime);
Where serverTime is the time I am getting from webservice. And the value for resTime shows negative value which is approx 6+ minutes.
So my question is why UTC timezone giving ahead time for System.currentTimeMillis?
In contrast to the assumption in a comment of of #JB Nizet the expressions sdf.getCalendar().getTimeInMillis() and System.currentTimeMillis() are not equivalent. Proof:
SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
System.out.println("date via System.currentTimeMillis()=" + f.format(utc_current));
System.out.println("date via sdf.getCalendar()=" + f.format(new Date(resTime)));
Output:
date via System.currentTimeMillis()=2015-02-26T12:19:09
date via sdf.getCalendar()=1889-12-31T04:41:21
If you carefully study the source code of SimpleDateFormat and DateFormat you will find within the initialization part code like:
private void initializeDefaultCentury() {
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.add( Calendar.YEAR, -80 );
parseAmbiguousDatesAsAfter(calendar.getTime());
}
The conclusion is to strictly avoid the method getCalendar() on your DateFormat-object. It is only used as intermediate mutable object for internal format and parse processing. It is hard to say what you will really get as time this way. Instead use directly System.currentTimeMillis() to compare your local time with server time.
Another problem is the pattern you use. "dd-MM-yyyy hh:mm:ss" is probably not correct because it uses the clock hour of half day in range 1-12 but the information for am/pm is missing. Use better the pattern symbol HH. Check the documentation of webservice for the right format.
Make sure the the clock on the server and on the client machine are synchronized. The 6 minutes could simply be an offset between the two.