how to get a java date in "packed byte" format? - java

I'm trying to manually enter a forum user into an smf database but can't seem to figure out how to get the current date in the proper format, which I found out is something called "packed byte".
can anyone point me to some info to help out?

If I interpreted the spec you linked in comments correctly - you can use a combination of String formatting and parsing to get what you need. I chose to use String formatting because although the output expected is a base-16 number, it appears to encode values as base-10 values within the base-16 number.
Calendar toPack = Calendar.getInstance();
int century = (toPack.get(Calendar.YEAR) - 1900) / 100;
int year = toPack.get(Calendar.YEAR) % 100;
int dayOfYear = toPack.get(Calendar.DAY_OF_YEAR);
String packedDate = String.format("%02d%02d%03dC", century, year, dayOfYear);
int packed = Integer.parseInt(packedDate, 16);
System.out.printf("0x%x%n", packed);
Output:
0x114238c

According IBM's SFM Wiki the 'byte packed date format' is defined as follows:
0x[0C][YY][DDD][+]
where:
C = centuries since 1900 (e.g. 1 for the 21st century)
YY = year
DDD = day (1 for Jan. 1 366 for Dec. 31)
+ = 0xC (Hardcoded)
Example: Aug. 31. 2014 = 0x01 14 244 C
In Java you could use the java.util.Calendar to create a hex string containing all required values and use Long.valueOf(...,16) to get a number out of it.

Related

Java int formating?

I want the user to input hours and minutes for a Local.Time from 00 to 23 and from 00 to 59, I scanned this as an int. It works but for values from 00 to 09 the int ignores the 0 and places then as a 0,1,2...9 instead of 00,01,02,03...09; this breaks the Local.Time since, for example "10:3"; is not a valid format for time.
I have read I can format this as a String, but I don't think that helps me since I need an int value to build the LocalTime and subsequent opperations with it.
There is a way of formatting this while kepping the variable as an int??
Can I code this differently to bypass this??
Am I wrong about how these classes work??
I am pretty new to these concepts
Here is the code I am using
int hours;
int minutes;
System.out.println("Input a number for the hours (00-23): ");
hours = scan.nextInt();
System.out.println("Input a number for the minutes (00-59): ");
minutes = scan.nextInt();
LocalTime result = LocalTime.parse(hours + ":" + minutes);
I tried using the NumberFormat class but it returns an error when trying to declare its variables (something like it is an abstract variable and cannot be instanced)
I also tried using the String format but I don't really know what to do with that string after that, it asks me for a int and not a string to build this method
First: an int doesn't differentiate between 09 and 9. They are the same value: the number nine.
Next: if you already have numbers, then going back to a string to produce a date is an anti-pattern: you are losing type checking by this. So instead of using LocalTime.parse and constructing the input from int values, you should simply use LocalTime.of(hours, minutes):
LocalTime result = LocalTime.of(hours, minutes);
tl;dr Use LocalTime.of(hours, minutes), it's most straight-forward
Alternative: Parse with a suitable DateTimeFormatter:
public static void main(String[] args) {
// single-digit example values
int hours = 9;
int minutes = 1;
// define a formatter that parses single-digit hours and minutes
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("H:m");
// use it as second argument in LocalTime.parse
LocalTime result = LocalTime.parse(hours + ":" + minutes, dtf);
// see the result
System.out.println(result);
}
Output:
09:01
Fix
use the proper DateTimeFormatter
LocalTime.parse(hours + ":" + minutes, DateTimeFormatter.ofPattern("H:m"));
Build the expected default format
LocalTime.parse(String.format("%02d:%02d", hours, minutes));
Improve
Use a more appropriate method
LocalTime.of(hours, minutes);

CodenameOne - fixing Calendar get(Calendar.DAY_OF_WEEK) bigger value than expected

I am creating a workaround to fix the SimpleDateFormat "clone" class of CN1 in my app.
I cannot use other classes from pure Java.
I used this instruction in my CN1 app
int dayNumber=c.get(Calendar.DAY_OF_WEEK);
for handling the u letter in the format string.
At the time of the creation of this post it's Wednesday
dayNumber happens to have value of 4.
So I replaced that instruction with
int dayNumber=c.get(Calendar.DAY_OF_WEEK)-1;
because I found in Oracle documentation
Day number of week (1 = Monday, ..., 7 = Sunday)
I would like to know if it is correct
so that I have the 7 days of the week covered so it is just as
(1 = Monday, ..., 7 = Sunday)
and I can have the right u value for Java and Android compatibility.
I understand that java.time, the modern Java date and time API, is not yet part of CodeName One, and that therefore you cannot use it in your case. Apart from special situations like yours no one should use Calendar since it is poorly designed and long outdated. Everyone should use java.time.
To get (1 = Monday, ..., 7 = Sunday) from Calendar (the numbers that you would get by default from java.time):
int dayNumber = (c.get(Calendar.DAY_OF_WEEK) + 5) % 7 + 1;
Seen under modulo 7 I am first adding 5, then 1, so 6 in total, which is the same as subtracting 1. I am using this trickery to make sure I get a number in the 1 through 7 interval (which we don’t always by simply subtracting 1 from the return value from Calendar).
I am demonstrating the edge cases and using java.time for it:
LocalDate ld = LocalDate.of(2021, Month.SEPTEMBER, 5);
ZonedDateTime startOfDay = ld.atStartOfDay(ZoneId.of("Etc/UTC"));
Calendar c = GregorianCalendar.from(startOfDay);
int dayNumber = (c.get(Calendar.DAY_OF_WEEK) + 5) % 7 + 1;
System.out.format("%s: %s = %d%n", ld, ld.getDayOfWeek(), dayNumber);
ld = ld.plusDays(1);
startOfDay = ld.atStartOfDay(ZoneId.of("Etc/UTC"));
c = GregorianCalendar.from(startOfDay);
dayNumber = (c.get(Calendar.DAY_OF_WEEK) + 5) % 7 + 1;
System.out.format("%s: %s = %d%n", ld, ld.getDayOfWeek(), dayNumber);
Output is:
2021-09-05: SUNDAY = 7
2021-09-06: MONDAY = 1
Disclaimer: I don’t know the CodeName One Calendar in particular. I strongly expect it to behave exactly the same as the original java.util.Calendar, and my answer is based on the assumption that it does.

Compare date Range by Month (Integer) and Year (Integer)

I'm having a problem comparing the date range. I have to validate dates that are within a certain month and year. The month and year are integer values.
NOTE: I´m using OUTSYSTEMS aggregates using Oracle DataBase
Example for two results of a query:
Start Date End Date
1 2020-08-16 2020-10-14
2 2019-11-01 2020-08-15
Case 1
Input:
Month = 9
Year = 2020
Expected Result:
Start Date End Date
1 2020-08-16 2020-10-14
Case 2
Input:
Month = 8
Year = 2020
Expected Result:
Start Date End Date
1 2020-08-16 2020-10-14
2 2019-11-01 2020-08-15
Case 3
Input:
Month = 3
Year = 2020
Expected Result:
Start Date End Date
2 2019-11-01 2020-08-15
Case 4
Input:
Month = 10
Year = 2019
Expected Result: No Row
The selection is in Java Way. I´m using a system function like Month() and Year() to convert the rows to the integers.
Like this
((Month(StartDate) <= Month and Month(EndDate) = Month)
and
(Year(StartDate) <= Year and Year(EndDate) = Year))
or
((Month(StartDate) <= Month and Month(EndDate) = Month)
and
(Year(StartDate) <= Year and Year(EndDate) = Year))
The code above won't work. I try many combinations without success. I have no special comparison functions. For my analysis, I have four scenarios to create to bring the dates that are included in the month and year that I am researching. But I'm not getting the code to work. Someone can light the way for me
A simple approach uses arithmetics:
where year * 100 + month
between year(startdate) * 100 + month(startdate)
and year(enddate) * 100 + month(enddate)
However this probably isn't the most efficient method. In general, you want to avoid applying functions on the column you filter on. A better alternative woul be to convert the year/month parameter to a date - unfortunately you did not tag your database, and date functions are highly vendor-specific, so it is not really possible to suggest.
If you don't want between:
where year * 100 + month >= year(startdate) * 100 + month(startdate)
and year * 100 + month <= year(enddate) * 100 + month(enddate)
Does this work? Considering your inputs m for month and y for year:
StartDate <= AddDays(AddMonths(NewDate(Year(y), Month(m), 1),1)-1)
and
EndDate >= NewDate(Year(y), Month(m), 1))
The thinking is like: filter by all start dates that are lower than the last day of input month and all the end dates that are greater than the first day of input month.
Regarding performance, with this approach you don't have to do any logic/filter on the columns you're filtering on.
The vendor-independent solution
The answer by GMB is nice, I might go with it if it were me. As GMB says, it is vendor specific because the date functions are. If you want a solution that works across database vendors, do the date math in Java so you only need simple date comparisons in the database.
int month = 8;
int year = 2020;
YearMonth ym = YearMonth.of(year, month);
LocalDate monthStart = ym.atDay(1);
LocalDate monthEnd = ym.atEndOfMonth();
When you pass these dates to your query, your search condition may be put simply:
where startDate <= monthEnd and endDate >= monthStart

Period between dates using Java/Joda

I have 2 joda dates as follows:
org.joda.time.DateTime a;
org.joda.time.DateTime b;
I want the difference between the 2 dates in terms of Years, months and days (eg. 0 years, 2 months, 5 days).
I can use the org.joda.time.Days.daysBetween(a, b) or monthsBetween(a, b) or yearsBetween(a, b) to get the whole values of each respectively.
Since a month does have number of fixed number of days, how can I calculate this?
Eg. If I get monthsbetween = 2 and daysbetween = 65, how can I write this as "2 months and x days"
Is there any other way to get this?
Try this:
Calendar ca = Calendar.getInstance();
ca.setTime(a);
Calendar cb = Calendar.getInstance();
cb.setTime(b);
System.out.printf("%d months and %d days"
, ca.get(Calendar.MONTH) - cb.get(Calendar.MONTH)
, ca.get(Calendar.DAY_OF_MONTH) - cb.get(Calendar.DAY_OF_MONTH));
Im not too familiar with Joda, but it looks like your best option is to divide the days left by 30.5, and then round it up back to a whole integer. Like so:
double daysDivided = daysbetween / 30.5;
int daysbetweenFixed = (int) daysDivided;
System.out.printf("%d months and %d days", monthsbetween, daysbetweenFixed);
//optional output ^
I'm sure you would know i chose 30.5 because it seems like the a good average month length, excluding February. Because there is no set length of a month this is the best we can do with only these integers.

TOD clock time to java.util.Date or milliseconds

I have a database table, which is filled with data from a mainframe via ETL.
One column of that table is called "TOD" as in Time-Of-Day.
This columns stores values such as :
"CAE7631DC43DC686"
"CAE7631C4AC6DC0B"
"CAE6216DF2BC0D04"
"CAE621D8F9916E8E"
all these values are around Feb 10th 2013 and Feb 11th 2013.
now, on mainframe, this is a time-date representation (TOD clock).
it represents the time past from 01.01.1900 in macroseconds (1/1 000 000 of a second).
What I need is a java library / method / algorithm implementation that could convert these strings to java.util.Date's.
Found these sites on the web :
http://paul.saers.com/Tod_howto.html
http://www.longpelaexpertise.com.au/toolsTOD.php
This page explains how to calculate it, but it's a little too much for me.
I'm sure I'd do some errors somewhere.
So, my question is; do you know about a library (Joda Time ?) that I could use ?
I need to convert these value to a java.util.Date and a Date object to a string representation, (like "CAE621D8F9916E8E").
Thanks in advance.
In my use case I have a getter method that directly reads the 8 bytes TOD as byte array and translates it into a long, but here to adhere to the poster:
BigInteger bi = new BigInteger ("CAE7631DC43DC686", 16); // no strip off of 686
long tod = bi2.longValue();
I used the following to avoid the BigDecimal calculation overhead:
tod = tod >>> 12; // remove rightmost 3 bytes and replace with zeros
tod = tod - 2208988800000000l; // substract 1970
tod = tod/1000; // make millis out of micros
// timeformatter and dateformatter without Joda
SimpleDateFormat timeFormatter = new SimpleDateFormat("HH:mm:ss.SS z Z", Locale.getDefault());
SimpleDateFormat dateFormatter = new SimpleDateFormat("dd.MM.yyyy", Locale.getDefault());
// Display
System.out.println(timeFormatter.format(new Date(tod)));
System.out.println(dateFormatter.format(new Date(tod)));
The output will be:
22:59:46.420 CET +0100
10.02.2013
Step by step, using Joda:
Data used in the calculation can be found on the website you referred to The other reference you gave states that TOD is expressed in UTC
// we start with your string minus the three last digits
// which are some internal z/Series cruft
BigInteger bi = new BigInteger ("CAE7631DC43DC", 16); // 686 stripped off
// then, from tables the website we get the TOD value for start of epoch
// here also, minus the three last digits
BigInteger startOfEpoch70 = new BigInteger ("7D91048BCA000", 16); // 000 stripped off
// using that we calculate the offset in microseconds in epoch
BigInteger microsinepoch = bi.subtract(startOfEpoch70);
// and reduce to millis
BigInteger millisinepoch = microsinepoch.divide(new BigInteger("1000"));
// which we convert to a long to feed to Joda
long millisinepochLong = millisinepoch.longValue();
// Et voila, the result in UTC
DateTime result = new DateTime(millisinepochLong).withZone(DateTimeZone.UTC);
// Now, if you want a result in some other timezone, that's equally easy
// with Joda:
DateTime result2 = result.toDateTime(DateTimeZone.forID("EET"));
System.out.println("The result is " + result + " or represented in timezone EET "
+ result2);
Which gives this output:
The result is 2013-02-10T21:59:46.420Z or represented in timezone
EET 2013-02-10T23:59:46.420+02:00
The "cruft" I refer to is explained as follows:
We skip the last 12 bits (normally,some of these bits are used by MVS to tell what processor was used to read the TOD clock and what LPAR was active).
Of course, instead of brutally snipping these bytes off the string, one could also do
bi = bi.divide(new BigInteger("1000", 16));
as dividing by hex 1000 will also get rid of the last 12 bits.
EDIT: as Mehmet pointed out in the comments, TOD is in UTC and this means that the resulting DateTime should be told so. For convenience I also showed how to transpose that DateTime to another time zone (using EET as an example)
Parse your hex date using BigInteger:
new BigInteger("CAE7631DC43DC686", 16);
Then do the necessary conversions to the Unix epoch using the various methods offered by BigInteger (multiply, ...).

Categories

Resources