SNMP EventTime in Human Readable format in Java

人走茶凉 提交于 2020-07-16 05:42:07

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!