Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions data/README.zone_spec_csv_file
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
# Generating

https://github.com/fmidev/smartmet-timezones/tree/master/bin provided
a method to generate the content, which uses an unlicence, so it was
copied into this repository for posterity and future use.

./create_date_time_zoneinfo.sh > date_time_zonespec.csv

# Compatibility

The current version is IANA 2025b.

Previous versions of the data file (IANA 2016c) had short and long special
names but only for America/Chicago, America/Denver, America/Los_Angeles,
America/New_York, America/Phoenix, but time zones like America/Detroit,
America/Fort_Wayne, America/Indiana/Petersburg, etc... did not; when
regenerating for IANA 2025b the short and long names became identical
for those previously mentions time zones.

# File Format

The csv file containing the zone_specs used by the
boost::local_time::tz_database is intended to be customized by the
Expand Down
21 changes: 21 additions & 0 deletions data/create_date_time_zoneinfo.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/bin/sh

# Header field
echo '"ID","STD ABBR","STD NAME","DST ABBR","DST NAME","GMT offset","DST adjustment","DST Start Date rule","Start time","DST End date rule","End time"'

# We ignore POSIX and leap-second files, Boost does not use them.

for tz in $(find /usr/share/zoneinfo \
-path /usr/share/zoneinfo/right -prune -o \
-path /usr/share/zoneinfo/posix -prune -o \
-type f \
| grep -v "\.tab" \
| grep -v posix \
| grep -v right \
| grep -v leapseconds \
| grep -v tzdata \
| sed -e 's|/usr/share/zoneinfo/||g' \
| sort);
do
perl tzinfo.pl $tz;
done
552 changes: 240 additions & 312 deletions data/date_time_zonespec.csv

Large diffs are not rendered by default.

257 changes: 257 additions & 0 deletions data/tzinfo.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
#!/usr/bin/perl -w

# created based on tzdump.pl

use strict;

sub timeoffset
{
my $value = shift;
my $seconds = $value % 60;
my $minutes = ($value / 60) % 60;
my $hours = $value / 3600;
sprintf "%+03d:%02d:%02d",$hours,$minutes,$seconds;
}

# mtwtfss mtwtfss

sub timerule
{
my ($time,$isgmt,$offset) = @_;

# print "isgmt=$isgmt time=$time offset=$offset\n";

$time += $offset if(!$isgmt);

my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$foo) = gmtime($time);
++$mon;
my $nweek = int(($mday-1)/7) + 1;
$nweek = -1 if($nweek >= 4);
"$nweek;$wday;$mon";
}

sub dsttime
{
my ($time,$isstd,$offset1,$offset2) = @_;
# print "isstd=$isstd time=$time offset1=$offset1 offset2=$offset2\n";

$time += ($isstd ? $offset1 : $offset2);

my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$foo) = gmtime($time);
sprintf "%+03d:%02d:%02d",$hour,$min,$sec;
}

sub tzinfo
{
my $dir = "/usr/share/zoneinfo";
my $zone = shift;

if (not open (FILE, "$dir/$zone"))
{
warn "open $dir/$zone: $!\n";
return;
}

my $year = 1900 + (gmtime(time))[5];

my $tzfile = {};
my $data;

sysread (FILE, $data, 44) or die;

my ($header, $reserved, $ttisgmtcnt, $ttisstdcnt, $leapcnt, $timecnt,
$typecnt, $charcnt) = unpack "a4 a16 N N N N N N", $data;

if ($header ne "TZif")
{
warn "$dir/$zone: Doesn't look like tzfile data\n";
close FILE;
return;
}

# print "ttisgmtcnt=$ttisgmtcnt, ttisstdcnt=$ttisstdcnt, leapcnt=$leapcnt, timecnt=$timecnt, typecnt=$typecnt, charcnt=$charcnt\n";

my $tzdata;
my $index = 0;

sysread (FILE, $tzdata, 5 * $timecnt + 6 * $typecnt + $charcnt +
8 * $leapcnt + $ttisstdcnt + $ttisgmtcnt);

my $tzchunk = sub
{
my $howbig = shift;
my $chunk = substr ($tzdata, $index, $howbig);
$index += $howbig;
$chunk;
};

my %ttimes;
my @ttimes;
my @ttindexes;

my @dates; # settings for current year
my @indexes;

my $lastindex = -1;

if ($timecnt)
{
$data = &$tzchunk (4 * $timecnt);
@ttimes = unpack ("N" x $timecnt, $data);

$data = &$tzchunk ($timecnt);
@ttindexes = unpack "C" x $timecnt, $data;

for (my $i=0; $i<=$#ttimes; $i++)
{
my $ruleyear = 1900 + (gmtime($ttimes[$i]))[5];

$lastindex = $ttindexes[$i];

if ($ruleyear == $year)
{
push @dates, $ttimes[$i];
push @indexes, $ttindexes[$i];
}
}
}

my @ttinfo;

foreach (0 .. $typecnt - 1)
{
$data = &$tzchunk (6);
my ($gmtoff, $isdst, $abbrind) = unpack ("N c C", $data);
$gmtoff = unpack ("l", pack ("L", $gmtoff));
push (@ttinfo, { gmtoff => $gmtoff, isdst => $isdst, abbrind => $abbrind });
# print "ttinfo[$_]: gmtoff=$gmtoff isdst=$isdst abbrind=$abbrind\n";
}


my $tznames;

if ($charcnt)
{
$tznames = &$tzchunk ($charcnt) or die;
# print "abbrevs: ", join (", ", split /\000/, $tznames), "\n";
}

my @leapsecs;

if ($leapcnt)
{
$data = &$tzchunk (8 * $leapcnt);
# print "leapsecs: ", join (", ", unpack ("NN" x $leapcnt, $data)), "\n";
}

my @stddata;
if ($ttisstdcnt)
{
$data = &$tzchunk ($ttisstdcnt);
@stddata = unpack ("C" x $ttisstdcnt, $data);
# print "isstd: ", join (", ", @stddata), "\n";
}

my @gmtdata;
if ($ttisgmtcnt)
{
$data = &$tzchunk ($ttisgmtcnt);
@gmtdata = unpack ("C" x $ttisgmtcnt, $data);
# print "isgmt: ", join(", ", @gmtdata), "\n";
}

close FILE;

# Print Boost date_time_zoneinfo line

# for(my $i=0; $i<=$#dates; $i++)
# {
# print scalar gmtime ($dates[$i]), " => ", $indexes[$i], " ", $ttimes[$i], "\n";
# }

if ($#dates == 1)
{
# ID, STD ABBR, STD NAME, DST ABBR, DST NAME, GMT offset, DST adjustment, Rule1, Time1, Rule2, Time2
# "Europe/Helsinki","EET","EET","EEST","EEST","+02:00:00","+01:00:00","-1;0;3","+03:00:00","-1;0;10","+04:00:00"
# Rule = week,day,month
# week=1...5 or -1 if last week. We interpret week 4/5 to always imply last week
# day=0...6, 0=Sun

my $isdst1 = $ttinfo[$indexes[0]]{isdst};
my $isdst2 = $ttinfo[$indexes[1]]{isdst};

my $abbrind1 = $ttinfo[$indexes[0]]{abbrind};
my $abbrind2 = $ttinfo[$indexes[1]]{abbrind};

my $isgmt1 = $gmtdata[$indexes[0]];
my $isgmt2 = $gmtdata[$indexes[1]];

my $isstd1 = $stddata[$indexes[0]];
my $isstd2 = $stddata[$indexes[1]];

my $zname1 = substr($tznames,$abbrind1,index($tznames,"\000",$abbrind1)-$abbrind1);
my $zname2 = substr($tznames,$abbrind2,index($tznames,"\000",$abbrind2)-$abbrind2);

if($isdst1)
{
my $tmp = $zname1;
$zname1 = $zname2;
$zname2 = $tmp;
}

my $offset1 = $ttinfo[$indexes[0]]{gmtoff};
my $offset2 = $ttinfo[$indexes[1]]{gmtoff};

my $gmtoffset = ($isdst1 ? timeoffset($offset2) : timeoffset($offset1));
my $dstadjustment = timeoffset($isdst1 ? $offset1-$offset2 : $offset2-$offset1);

my $dststartrule = timerule($isdst1 ? $dates[0] : $dates[1], $isgmt1, $offset1);
my $dststarttime = ($isdst1 ? dsttime($dates[1],$isstd2,$offset2,$offset1) : dsttime($dates[0],$isstd1,$offset1,$offset2));

my $dstendrule = timerule($isdst1 ? $dates[1] : $dates[0], $isgmt2, $offset2);
my $dstendtime = ($isdst1 ? dsttime($dates[0],$isstd1,$offset1,$offset2) : dsttime($dates[1],$isstd2,$offset2,$offset1));

print "\"$zone\",\"$zname1\",\"$zname1\",\"$zname2\",\"$zname2\",\"$gmtoffset\",\"$dstadjustment\",\"$dststartrule\",\"$dststarttime\",\"$dstendrule\",\"$dstendtime\"\n";

}
else
{
# Use the last row if everything fails (Asia/Calcutta, Europe/Moscow)

if($lastindex < 0)
{
$lastindex = $#ttinfo;
}

# ID, STD ABBR, STD NAME, DST ABBR, DST NAME, GMT offset, DST adjustment, Rule1, Time1, Rule2, Time2
# "Europe/Moscow","MSK","MSK","","","+03:00:00","+00:00:00","","","","+00:00:00"

my $isdst = $ttinfo[$lastindex]{isdst};
my $abbrind = $ttinfo[$lastindex]{abbrind};
my $isgmt = $gmtdata[$lastindex];
my $isstd = $stddata[$lastindex];

my $zname = substr($tznames,$abbrind,index($tznames,"\000",$abbrind)-$abbrind);

my $offset = $ttinfo[$lastindex]{gmtoff};
my $gmtoffset = timeoffset($offset);
my $dstadjustment = "+00:00:00";

my $dststartrule = "";
my $dststarttime = "";

my $dstendrule = "";
my $dstendtime = "+00:00:00"; # no idea why the Boost.Date_time does this

# print "$gmtoffset $offset\n";

print "\"$zone\",\"$zname\",\"$zname\",\"\",\"\",\"$gmtoffset\",\"$dstadjustment\",\"$dststartrule\",\"$dststarttime\",\"$dstendrule\",\"$dstendtime\"\n";

}
}


foreach my $filename (@ARGV)
{
tzinfo ($filename);
}
4 changes: 2 additions & 2 deletions include/boost/date_time/local_time/local_date_time.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ namespace local_time {
return local_date_time_base(utc_time_type(this->time_) + td, new_tz);
}

//! Returns name of associated time zone or "Coordinated Universal Time".
//! Returns name of associated time zone or "UTC".
/*! Optional bool parameter will return time zone as an offset
* (ie "+07:00" extended ISO 8601 format). Empty string is returned for
* classes that do not use a time_zone */
Expand All @@ -324,7 +324,7 @@ namespace local_time {
return std::string("Z");
}
else {
return std::string("Coordinated Universal Time");
return std::string("UTC");
}
}
if (is_dst()) {
Expand Down
26 changes: 13 additions & 13 deletions test/local_time/testlocal_time_facet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,19 +85,19 @@ int main(int /* argc */, char const* argv[]){
pt_facet* ptimefacet1 = new pt_facet("%a %b %d %H:%M:%S %Y %Z"); // show that zone is ignored
std::locale loc2(std::locale::classic(), ptimefacet1);


// as of IANA 2025b the full name is the same as the abbreviated name
std::cout << "\nFull time zone names tests" << std::endl;
teststreaming("ptime with %Z flag\n", a_time, std::string("Wed Dec 15 12:00:00 2004") , loc2);

teststreaming("UTC local_date_time", ldt1, std::string("Wed Dec 15 12:00:00 2004 Coordinated Universal Time"), loc1);
teststreaming("Chicago in summer", ldt2, std::string("Sun Aug 15 07:00:00 2004 Central Daylight Time") , loc1);
teststreaming("Denver in winter", ldt3, std::string("Wed Dec 15 05:00:00 2004 Mountain Standard Time"), loc1);
teststreaming("Los Angeles in summer", ldt4, std::string("Sun Aug 15 05:00:00 2004 Pacific Daylight Time"), loc1);
teststreaming("New York in winter", ldt5, std::string("Wed Dec 15 07:00:00 2004 Eastern Standard Time"), loc1);
teststreaming("Phoenix in Summer", ldt6, std::string("Sun Aug 15 05:00:00 2004 Mountain Standard Time"), loc1);
teststreaming("UTC local_date_time", ldt1, std::string("Wed Dec 15 12:00:00 2004 UTC"), loc1);
teststreaming("Chicago in summer", ldt2, std::string("Sun Aug 15 07:00:00 2004 CDT") , loc1);
teststreaming("Denver in winter", ldt3, std::string("Wed Dec 15 05:00:00 2004 MST"), loc1);
teststreaming("Los Angeles in summer", ldt4, std::string("Sun Aug 15 05:00:00 2004 PDT"), loc1);
teststreaming("New York in winter", ldt5, std::string("Wed Dec 15 07:00:00 2004 EST"), loc1);
teststreaming("Phoenix in Summer", ldt6, std::string("Sun Aug 15 05:00:00 2004 MST"), loc1);

teststreaming("UTC local_time_period", ltp1, std::string("[Wed Dec 15 12:00:00 2004 Coordinated Universal Time/Wed Dec 15 22:24:04 2004 Coordinated Universal Time]"), loc1);
teststreaming("LA local_time_period", ltp2, std::string("[Sun Aug 15 05:00:00 2004 Pacific Daylight Time/Sun Aug 15 20:20:40 2004 Pacific Daylight Time]"), loc1);
teststreaming("UTC local_time_period", ltp1, std::string("[Wed Dec 15 12:00:00 2004 UTC/Wed Dec 15 22:24:04 2004 UTC]"), loc1);
teststreaming("LA local_time_period", ltp2, std::string("[Sun Aug 15 05:00:00 2004 PDT/Sun Aug 15 20:20:40 2004 PDT]"), loc1);

//ptimefacet1->format("%c %z"); // show that zone abbrev is ignored
ptimefacet1->format("%a %b %d %H:%M:%S %Y %z"); // show that zone abbrev is ignored
Expand Down Expand Up @@ -170,11 +170,11 @@ int main(int /* argc */, char const* argv[]){
/* Again, wide stream tests are more thoroughly done in the
* time_facet tests. Here we just need to show that they work */
std::cout << "\nFull time zone names tests - wide stream" << std::endl;
teststreaming("UTC local_date_time", ldt1, std::wstring(L"Wed Dec 15 12:00:00 2004 Coordinated Universal Time"), loc3);
teststreaming("Chicago in summer", ldt2, std::wstring(L"Sun Aug 15 07:00:00 2004 Central Daylight Time") , loc3);
teststreaming("UTC local_date_time", ldt1, std::wstring(L"Wed Dec 15 12:00:00 2004 UTC"), loc3);
teststreaming("Chicago in summer", ldt2, std::wstring(L"Sun Aug 15 07:00:00 2004 CDT") , loc3);

teststreaming("UTC local_time_period", ltp1, std::wstring(L"[Wed Dec 15 12:00:00 2004 Coordinated Universal Time/Wed Dec 15 22:24:04 2004 Coordinated Universal Time]"), loc3);
teststreaming("LA local_time_period", ltp2, std::wstring(L"[Sun Aug 15 05:00:00 2004 Pacific Daylight Time/Sun Aug 15 20:20:40 2004 Pacific Daylight Time]"), loc3);
teststreaming("UTC local_time_period", ltp1, std::wstring(L"[Wed Dec 15 12:00:00 2004 UTC/Wed Dec 15 22:24:04 2004 UTC]"), loc3);
teststreaming("LA local_time_period", ltp2, std::wstring(L"[Sun Aug 15 05:00:00 2004 PDT/Sun Aug 15 20:20:40 2004 PDT]"), loc3);

//wtimefacet->format(L"%c %z"); // abbrev
wtimefacet->format(L"%a %b %d %H:%M:%S %Y %z"); // abbrev
Expand Down
6 changes: 3 additions & 3 deletions test/local_time/testtz_database.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ int main(int /* argc */, char const* argv[]){
time_zone_ptr nyc_test = tz_db.time_zone_from_region("America/New_York");
check("nyc Valid pointer", nyc_test != time_zone_ptr() );
check("nyc Abbreviations",nyc_test->std_zone_abbrev() == std::string("EST"));
check("nyc Full Name", nyc_test->std_zone_name() == std::string("Eastern Standard Time"));
check("nyc Full Name", nyc_test->std_zone_name() == std::string("EST"));
check("nyc Abbreviations",nyc_test->dst_zone_abbrev() == std::string("EDT"));
//std::cout << nyc_test->std_zone_name() << std::endl;
check("nyc Full Name", nyc_test->dst_zone_name() == std::string("Eastern Daylight Time"));
check("nyc Full Name", nyc_test->dst_zone_name() == std::string("EDT"));
check("nyc GMT Offset", nyc_test->base_utc_offset() == hours(-5));
check("nyc DST Offset", nyc_test->dst_offset() == hours(1));
//std::cout << nyc_test->dst_local_start_time(2004) << std::endl;
Expand All @@ -67,7 +67,7 @@ int main(int /* argc */, char const* argv[]){
time_zone_ptr phx_test = tz_db.time_zone_from_region("America/Phoenix");
check("az Valid pointer", phx_test != time_zone_ptr() );
check("az Abbreviations",phx_test->std_zone_abbrev() == std::string("MST"));
check("az Full Name", phx_test->std_zone_name() == std::string("Mountain Standard Time"));
check("az Full Name", phx_test->std_zone_name() == std::string("MST"));
check("az Abbreviations", phx_test->dst_zone_abbrev() == std::string(""));
check("az Full Name", phx_test->dst_zone_name() == std::string(""));
check("az GMT Offset", phx_test->base_utc_offset() == hours(-7));
Expand Down
2 changes: 1 addition & 1 deletion xmldoc/local_date_time.xml
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ az.local_time(); // 3am 2004-Nov-5</screen></entry>
<!--
<row>
<entry valign="top" morerows="1"><screen>std::string zone_name()</screen></entry>
<entry>Returns full zone name from associated time zone or "Coordinated Universal Time" if time_zone_ptr is NULL.</entry>
<entry>Returns full zone name from associated time zone or "UTC" if time_zone_ptr is NULL.</entry>
</row>
<row>
<entry>
Expand Down