datetimegmt

What does NNN mean in date format <YYMMDDhhmmssNNN><C|D|G|H>?


hi I has date format and I want converted to correct GMT date :

<YYMMDDhhmmssNNN><C|D|G|H>

Sample value on that date: 210204215026000C

I get this explanation for part NNN :

NNN     If flag is C or D then NNN is the number of hours relativeto GMT,
        if flag is G or H, NNN is the number of quarter hours relative to GMT
C|D|G|H C and G = Ahead of GMT, D and H = Behind GMT

but I did not get how number of hours relative to GMT can present on 3 digits ? it should be in 2 digit as i knew the offset for hours related to GMT is from 0 to 23 , and also what quarter hours relative to GMT mean ?

I want to use Scala or Java.


Solution

  • I don’t know why they set 3 digits aside for the offset. I agree with you that 2 digits suffice for all cases. Maybe they just wanted to be very sure they would never run of out space, and maybe they even overdid this a bit. 3 digits is not a problem as long as the actual values are within the range that java.time.ZoneOffset can handle, +/-18 hours. In your example NNN is 000, so 0 hours from GMT, which certainly is OK and trivial to handle.

    A quarter hour is a quarter of an hour. As Salman A mentioned in a comment, 22 quarter hours ahead of Greenwich means an offset of +05:30, currently used in Sri Lanka and India. If the producer of the string wants to use this option, they can give numbers up to 72 (still comfortably within 2 digits). 18 * 4 = 72, so 18 hours equals 72 quarter hours. To imagine a situation where 2 digits would be too little, think an offset of 25 hours. I wouldn’t think it realistic, on the other hand no one can guarantee that it will never happen.

    Java solution: how to parse and convert to GMT time

    I am using these constants:

    private static final Pattern DATE_PATTERN
            = Pattern.compile("(\\d{12})(\\d{3})(\\w)");
    private static final DateTimeFormatter FORMATTER
            = DateTimeFormatter.ofPattern("uuMMddHHmmss");
    private static final int SECONDS_IN_A_QUARTER_HOUR
            = Math.toIntExact(Duration.ofHours(1).dividedBy(4).getSeconds());
    

    Parse and convert like this:

        String sampleValue = "210204215026000C";
        Matcher matcher = DATE_PATTERN.matcher(sampleValue);
        if (matcher.matches()) {
            LocalDateTime ldt = LocalDateTime.parse(matcher.group(1), FORMATTER);
            int offsetAmount = Integer.parseInt(matcher.group(2));
            char flag = matcher.group(3).charAt(0);
    
            // offset amount denotes either hours or quarter hours
            boolean quarterHours = flag == 'G' || flag == 'H';
            boolean negative = flag == 'D' || flag == 'H';
            if (negative) {
                offsetAmount = -offsetAmount;
            }
            ZoneOffset offset = quarterHours
                    ? ZoneOffset.ofTotalSeconds(offsetAmount * SECONDS_IN_A_QUARTER_HOUR)
                    : ZoneOffset.ofHours(offsetAmount);
    
            OffsetDateTime dateTime = ldt.atOffset(offset);
            OffsetDateTime gmtDateTime = dateTime.withOffsetSameInstant(ZoneOffset.UTC);
    
            System.out.println("GMT time: " + gmtDateTime);
        }
        else {
            System.out.println("Invalid value: " + sampleValue);
        }
    

    Output is:

    GMT time: 2021-02-04T21:50:26Z

    I think my code covers all valid cases. You will probably want to validate that the flag is indeed C, D, G or H, and also handle the potential DateTimeException and NumberFormatException from the parsing and creating the ZoneOffset (NumberFormatException should not happen).