问题
I have a stand alone java application that receives SNMP messages via an SNMP trap. I am using SNMP4J library in my application. In the SNMP message received, I need to convert the event time field, which is in hexadecimal format, into a human readable format. The event time field received normally looks as follows for example:
eventTime*SNMPv2-SMI::enterprises.193.183.4.1.4.5.1.7.0 = Hex-STRING:
07 DC 03 0C 12 15 2C 1F 2B 01 00
Can anyone tell me how I can convert the text '07 DC 03 0C 12 15 2C 1F 2B 01 00' into a human readable datetime value with or without the help of SNMP4J library? Thanks.
回答1:
you can use Integer.parseInt("07dc", 16) and out 2012 pops, so this should give a hint of the year, the rest i'm sure you will figure out by yourself if this is indeed the year.
回答2:
Maybe it's a bit late (you posted your question 6 years ago) but I recently got the same problem and found a general solution that takes into account:
1. The SNMP-reply may or may not report the offset from GMT
2. If the time zone is reported, it may be different from our local time zone
/**********************************************************************************************************************
* Import definitions
*********************************************************************************************************************/
import java.text.SimpleDateFormat;
import java.util.*;
/**********************************************************************************************************************
* Class converting an SNMP DateAndTime into something readable.
*********************************************************************************************************************/
public class ConvertDateAndTime
{
/********************************************************************************************************************
* This method converts the specified octet string into an array of bytes.
* <br>The string should be a number of 2-char hexadecimal bytes values separated by any non-hexadecimal character.
*
* @param value_ipar The value returned by the equipment.
* @return The value as an array of bytes.
* @throws Exception Thrown in case of an error
*******************************************************************************************************************/
public static int[] octetStringToBytes(String value_ipar)
{
// ---------------------------
// Split string into its parts
// ---------------------------
String[] bytes;
bytes = value_ipar.split("[^0-9A-Fa-f]");
// -----------------
// Initialize result
// -----------------
int[] result;
result = new int[bytes.length];
// -------------
// Convert bytes
// -------------
int counter;
for (counter = 0; counter < bytes.length; counter++)
result[counter] = Integer.parseInt(bytes[counter], 16);
// ----
// Done
// ----
return (result);
} // octetStringToBytes
/********************************************************************************************************************
* This method converts the 'DateAndTime' value as returned by the device into internal format.
* <br>It returns <code>null</code> in case the reported year equals 0.
* <br>It throws an exception in case of an error.
*******************************************************************************************************************/
public static Date octetStringToDate(String value_ipar)
throws Exception
{
// ---------------------------
// Convert into array of bytes
// ---------------------------
int[] bytes;
bytes = octetStringToBytes(value_ipar);
// -----------------------
// Maybe nothing specified
// -----------------------
if (bytes[0] == 0)
return (null);
// ------------------
// Extract parameters
// ------------------
int year;
int month;
int day;
int hour;
int minute;
int second;
int deci_sec = 0;
int offset = 0;
year = (bytes[0] * 256) + bytes[1];
month = bytes[2];
day = bytes[3];
hour = bytes[4];
minute = bytes[5];
second = bytes[6];
if (bytes.length >= 8)
deci_sec = bytes[7];
if (bytes.length >= 10)
{
offset = bytes[9] * 60;
if (bytes.length >= 11)
offset += bytes[10];
if (bytes[8] == '-')
offset = -offset;
offset *= 60 * 1000;
}
// ------------------------------------
// Get current DST and time zone offset
// ------------------------------------
Calendar calendar;
int my_dst;
int my_zone;
calendar = Calendar.getInstance();
my_dst = calendar.get(Calendar.DST_OFFSET);
my_zone = calendar.get(Calendar.ZONE_OFFSET);
// ----------------------------------
// Compose result
// Month to be converted into 0-based
// ----------------------------------
calendar.clear();
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month - 1);
calendar.set(Calendar.DAY_OF_MONTH, day);
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minute);
calendar.set(Calendar.SECOND, second);
calendar.set(Calendar.MILLISECOND, deci_sec * 100);
// ---------
// Reset DST
// ---------
calendar.add(Calendar.MILLISECOND, my_dst);
// -----------------------------------------------------------------------------------
// If the offset is set, we have to convert the time using the offset of our time zone
// -----------------------------------------------------------------------------------
if (offset != 0)
{
int delta;
delta = my_zone - offset;
calendar.add(Calendar.MILLISECOND, delta);
}
// -------------
// Return result
// -------------
return (calendar.getTime());
} // octetStringToDate
/********************************************************************************************************************
* M A I N
*******************************************************************************************************************/
public static void main(String[] args)
{
try
{
SimpleDateFormat format;
format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss zzz");
Date result;
result = octetStringToDate("07 E2 02 02 12 0C 27 00"); // 18:12 in local time zone
System.out.println(format.format(result)); // "2018-02-02 18:12:39 CET"
result = octetStringToDate("07 E2 02 02 12 0C 27 00 2B 08 00"); // 18:12+08:00
System.out.println(format.format(result)); // "2018-02-02 11:12:39 CET"
result = octetStringToDate("07 E2 02 02 12 0C 27 00 2D 04 00"); // 18:12-04:00
System.out.println(format.format(result)); // "2018-02-02 23:12:39 CET"
}
catch (Exception exception_ipar)
{
exception_ipar.printStackTrace();
}
} // main
} // class ConvertDateAndTime
回答3:
I am providing the modern answer using java.time, the modern Java date and time API.
The hex string consists of the following fields:
Field Octets Contents Range
------------------------------------------------------
1 1-2 year 0..65536
2 3 month 1..12
3 4 day 1..31
4 5 hour 0..23
5 6 minutes 0..59
6 7 seconds (use 60 for leap-second) 0..60
7 8 deci-seconds 0..9
8 9 direction from UTC '+' / '-'
9 10 hours from UTC 0..13
10 11 minutes from UTC 0..59
I wrote this answer on the occasion of a duplicate question in which the example SNMP event time string was 07e4070e04032b
. So I am assuming a hex string with no spaces between the bytes. It seems both from the answer by Robert Koch and from that duplicate question that not all 11 bytes need to be present (the example string is 7 bytes long). So my conversion takes lengths 6, 7, 8, 10 and 11 into account.
public static Temporal decodeSnmpEventTime(String snmpEventTimeString) {
if (snmpEventTimeString.length() % 2 != 0) {
throw new IllegalArgumentException("Not a valid byte string, must have even length");
}
if (snmpEventTimeString.startsWith("00")
|| snmpEventTimeString.charAt(0) > '7') {
throw new IllegalArgumentException(
"This simple implementation cannot handle years before year 256 nor after 32767;"
+ " we need a different conversion to bytes");
}
byte[] bytes = new BigInteger(snmpEventTimeString, 16).toByteArray();
int year = (bytes[0] & 0xFF) * 0x100 + (bytes[1] & 0xFF);
int month = bytes[2] & 0xFF;
checkRange(month, 1, 12);
int dayOfMonth = bytes[3] & 0xFF;
checkRange(dayOfMonth, 1, 31);
int hour = bytes[4] & 0xFF;
checkRange(hour, 0, 23);
int minute = bytes[5] & 0xFF;
checkRange(minute, 0, 59);
int second = 0;
int deciseconds = 0;
if (bytes.length >= 7) {
second = bytes[6] & 0xFF;
checkRange(second, 0, 60); // 60 will cause conversion to fail, though
if (bytes.length >= 8) {
deciseconds = bytes[7] & 0xFF;
checkRange(deciseconds, 0, 9);
}
}
LocalDateTime ldt = LocalDateTime.of(year, month, dayOfMonth,
hour, minute, second, deciseconds * 100_000_000);
if (bytes.length >= 9) { // there’s an offset
char offsetSign = (char) (bytes[8] & 0xFF);
int offsetHours = bytes[9] & 0xFF;
checkRange(offsetHours, 0, 13); // allow 14 for all modern offsets
int offsetMinutes = 0;
if (bytes.length >= 11) {
offsetMinutes = bytes[10] & 0xFF;
checkRange(offsetMinutes, 0, 59);
}
ZoneOffset offset;
if (offsetSign == '+') {
offset = ZoneOffset.ofHoursMinutes(offsetHours, offsetMinutes);
} else if (offsetSign == '-') {
offset = ZoneOffset.ofHoursMinutes(-offsetHours, -offsetMinutes);
} else {
throw new IllegalArgumentException("Offset sign must be + or -, was " + offsetSign);
}
return ldt.atOffset(offset);
} else {
return ldt;
}
}
private static void checkRange(int value, int min, int max) {
if (value < min || value > max) {
throw new IllegalArgumentException("Value " + value + " out of range " + min + ".." + max);
}
}
Let’s try it out:
String snmpEventTimeString = "07e4070e04032b";
Temporal dateTime = decodeSnmpEventTime(snmpEventTimeString);
System.out.println(dateTime);
Output is:
2020-07-14T04:03:43
Links
- SNMP date and time specification
- Duplicate question: How do i get this Hex string converted to Date in java [duplicate]
- Oracle tutorial: Date Time explaining how to use java.time.
来源:https://stackoverflow.com/questions/9700037/snmp-eventtime-in-human-readable-format-in-java