The date-time group (DTG) is a compact date/time format commonly used in militaries around the world. A date-time group includes the time, time zone and date, and therefore represents a fixed and unambiguous point in time.
This post contains a summary of the date-time group format and sample code to implement it. All of the information contained here was sourced from publicly available, non-authoritative sources and is likely to be incomplete. Those with access to an official specification of the format should use that instead of relying on anything written here.
The basic date-time group format is: ddhhmmZMMMyy
, where Z
is a letter representing the time zone. Judicious use of spaces can greatly aid human comprehension.
Examples:
25 July 2016 18:41 UTC+10 = 25 1841K JUL 16
23 February 2016 09:45 UTC = 23 0945Z FEB 16
The time zone letters represent the UTC offset of the timezone in use, and are as follows:
Z UTC+00
A UTC+01
B UTC+02
C UTC+03
D UTC+04
E UTC+05
F UTC+06
G UTC+07
H UTC+08
I UTC+09
K UTC+10
L UTC+11
M UTC+12
N UTC-01
O UTC-02
P UTC-03
Q UTC-04
R UTC-05
S UTC-06
T UTC-07
U UTC-08
V UTC-09
W UTC-10
X UTC-11
Y UTC-12
Notes:
Months are abbreviated as follows:
January JAN
February FEB
March MAR
April APR
May MAY
June JUN
July JUL
August AUG
September SEP
October OCT
November NOV
December DEC
The following code to produce date-time groups is written in swift (2.2) and implemented as an extension on NSDate
. It needs the version of Foundation
supplied with Xcode, not the open source Foundation
that is part of the swift project.
I have chosen to give a two letter code for a half hour offset, a two letter plus asterisk code for a quarter hour offset, and the code M+
for timezones more than 12 hours ahead of UTC. The two letter code intuitively suggests the correct time zone, and the presence of a symbol for quarter hour offsets and offsets greater than 12 hours will hopefully encourage the user to investigate their time zone further.
import Foundation
extension NSDate {
private static let dtgTimeZoneLetters: [Character] = ["Y", "X", "W", "V", "U",
"T", "S", "R", "Q", "P",
"O", "N", "Z", "A", "B",
"C", "D", "E", "F", "G",
"H", "I", "K", "L", "M"]
private static let dtgMonthAbbreviations = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN",
"JUL", "AUG", "SEP", "OCT", "NOV", "DEC"]
func dateTimeGroup(timeZone timeZone: NSTimeZone) -> String? {
let utcOffset = timeZone.secondsFromGMT
// make sure we aren't more than 12 hours behind UTC
guard utcOffset >= -12 * 3600 else { return nil }
// make sure we aren't more than 14 hours ahead of UTC
guard utcOffset <= 14 * 3600 else { return nil }
// make sure we can get the gregorian calendar
guard let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian) else { return nil }
calendar.timeZone = timeZone
let timeZoneString: String
switch utcOffset {
case let utcOffset where utcOffset > 12 * 3600:
// more than 12 hour offset
timeZoneString = "M+"
case let utcOffset where utcOffset % 3600 == 0:
// whole hour offset
timeZoneString = String(NSDate.dtgTimeZoneLetters[utcOffset / 3600 + 12])
default:
// half hour or quarter hour offset
let index = Int(floor(Double(utcOffset) / 3600.0)) + 12
let firstLetter = String(NSDate.dtgTimeZoneLetters[index])
let secondLetter = String(NSDate.dtgTimeZoneLetters[index + 1])
let thirdLetter = utcOffset % 1800 == 0 ? "" : "*"
timeZoneString = firstLetter + secondLetter + thirdLetter
}
let requiredCalendarUnits: NSCalendarUnit = [.Year, .Month, .Day, .Hour, .Minute]
let dateComponents = calendar.components(requiredCalendarUnits, fromDate: self)
let monthString = NSDate.dtgMonthAbbreviations[dateComponents.month - 1]
return String(format: "%02d %02d%02d%@ %@ %02d",
dateComponents.day, dateComponents.hour, dateComponents.minute,
timeZoneString, monthString, dateComponents.year % 1000)
}
func dateTimeGroupLocal() -> String? {
return self.dateTimeGroup(timeZone: NSTimeZone.localTimeZone())
}
func dateTimeGroupZulu() -> String? {
return self.dateTimeGroup(timeZone: NSTimeZone(forSecondsFromGMT: 0))
}
}
Usage:
let date = NSDate()
date.dateTimeGroupZulu()
date.dateTimeGroupLocal()
Here’s another implementation in Python (Python 3).
from datetime import datetime, timezone, timedelta
import math
DTG_TZ_LETTERS = 'YXWVUTSRQPONZABCDEFGHIKLM'
DTG_MONTHS = ('JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN',
'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC')
def dtg(dt, tz=timezone.utc):
"""
Outputs a date time group in timezone tz for datetime dt.
dt must be a localized datetime.
"""
utc_offset = int(tz.utcoffset(dt).total_seconds())
if utc_offset < -12 * 3600 or utc_offset > 14 * 3600:
return None
tz_string = None
if utc_offset > 12 * 3600:
tz_string = "M+"
elif utc_offset % 3600 == 0:
index = utc_offset // 3600 + 12
tz_string = DTG_TZ_LETTERS[index: index + 1]
else:
index = int(math.floor(utc_offset / 3600.0)) + 12
tz_string = DTG_TZ_LETTERS[index: index + 2]
if utc_offset % 1800:
tz_string += '*'
dt_adjusted = dt.astimezone(tz)
return '{:02d} {:02d}{:02d}{} {} {:02d}'.format(
dt_adjusted.day,
dt_adjusted.hour,
dt_adjusted.minute,
tz_string,
DTG_MONTHS[dt_adjusted.month - 1],
dt_adjusted.year % 1000)
if __name__ == '__main__':
dt = datetime.now(tz=timezone.utc)
print(dtg(dt))
print(dtg(dt, timezone(timedelta(hours=10))))
print(dtg(dt, timezone(timedelta(hours=5, minutes=45))))
print(dtg(dt, timezone(timedelta(hours=14))))
print(dtg(dt, timezone(timedelta(hours=-9))))