diff --git a/src/@dates/dates.m b/src/@dates/dates.m
index 95b1612034583c00ae0ec920b2500b447f5697a6..fe0e1741703eef28f343442661fac5fd937bd16c 100644
--- a/src/@dates/dates.m
+++ b/src/@dates/dates.m
@@ -172,6 +172,27 @@ end % classdef
 
 %@test:3
 %$ % Define some dates
+%$ B1 = '1945H1';
+%$ B2 = '1950H2';
+%$ B3 = '1950h1';
+%$ B4 = '1953h2';
+%$
+%$ % Define expected results.
+%$ e.time = [1945 1; 1950 2; 1950 1; 1953 2];
+%$ e.freq = 2;
+%$
+%$ % Call the tested routine.
+%$ d = dates(B1,B2,B3,B4);
+%$
+%$ % Check the results.
+%$ t(1) = dassert(d.time,e.time);
+%$ t(2) = dassert(d.freq,e.freq);
+%$ t(3) = dassert(d.ndat(), size(e.time, 1));
+%$ T = all(t);
+%@eof:3
+
+%@test:4
+%$ % Define some dates
 %$ B1 = '1945y';
 %$ B2 = '1950Y';
 %$ B3 = '1950a';
@@ -189,9 +210,29 @@ end % classdef
 %$ t(2) = dassert(d.freq, e.freq);
 %$ t(3) = dassert(d.ndat(), size(e.time, 1));
 %$ T = all(t);
-%@eof:3
+%@eof:4
 
-%@test:4
+%@test:5
+%$ % Define a dates object
+%$ B = dates('1950H1'):dates('1960H2');
+%$
+%$
+%$ % Call the tested routine.
+%$ d = B(2);
+%$ if isa(d,'dates')
+%$     t(1) = 1;
+%$ else
+%$     t(1) = 0;
+%$ end
+%$
+%$ if t(1)
+%$     t(2) = dassert(d.freq,B.freq);
+%$     t(3) = dassert(d.time,[1950 2]);
+%$ end
+%$ T = all(t);
+%@eof:5
+
+%@test:6
 %$ % Define a dates object
 %$ B = dates('1950Q1'):dates('1960Q3');
 %$
@@ -209,9 +250,9 @@ end % classdef
 %$     t(3) = dassert(d.time,[1950 2]);
 %$ end
 %$ T = all(t);
-%@eof:4
+%@eof:6
 
-%@test:5
+%@test:7
 %$ % Define a dates object
 %$ B = dates(4,1950,1):dates(4,1960,3);
 %$
@@ -228,9 +269,9 @@ end % classdef
 %$     t(3) = dassert(d.time,[1950 2]);
 %$ end
 %$ T = all(t);
-%@eof:5
+%@eof:7
 
-%@test:6
+%@test:8
 %$ % Define a dates object
 %$ B = dates(4,[1950 1]):dates(4,[1960 3]);
 %$
@@ -247,9 +288,9 @@ end % classdef
 %$     t(3) = dassert(d.time,[1950 2]);
 %$ end
 %$ T = all(t);
-%@eof:6
+%@eof:8
 
-%@test:7
+%@test:9
 %$ try
 %$   B = dates(4,[1950; 1950], [1; 2]);
 %$   t = 1;
@@ -258,9 +299,9 @@ end % classdef
 %$ end
 %$
 %$ T = all(t);
-%@eof:7
+%@eof:9
 
-%@test:8
+%@test:10
 %$ try
 %$   B = dates(4,[1950, 1950], [1, 2]);
 %$   t = 1;
@@ -269,4 +310,4 @@ end % classdef
 %$ end
 %$
 %$ T = all(t);
-%@eof:8
+%@eof:10
diff --git a/src/@dates/subperiod.m b/src/@dates/subperiod.m
index 1f391782684a3b38d133adf05ef8b40318b28435..0722b672291ff83b258315510e982103ea90f974 100644
--- a/src/@dates/subperiod.m
+++ b/src/@dates/subperiod.m
@@ -1,8 +1,8 @@
 function s = subperiod(d)
 
-% Returns the subperiod (quarter, month or week depending on the frequency).
+% Returns the subperiod (half-year, quarter, month or week depending on the frequency).
 
-% Copyright (C) 2016-2017 Dynare Team
+% Copyright (C) 2016-2020 Dynare Team
 %
 % This code is free software: you can redistribute it and/or modify
 % it under the terms of the GNU General Public License as published by
diff --git a/src/@dseries/dseries.m b/src/@dseries/dseries.m
index 3097274ceba84782bf6cfa65b3a4b88a519b9171..afce0d6148fd86d17cb35a16a64ee1673757c73f 100644
--- a/src/@dseries/dseries.m
+++ b/src/@dseries/dseries.m
@@ -124,7 +124,7 @@ methods
                 init = dates(1,1);
             elseif (isdates(b) && isequal(length(b),1))
                 init = b;
-            elseif ischar(b) && isdate(b)% Weekly, Monthly, Quaterly or Annual data (string).
+            elseif ischar(b) && isdate(b)% Weekly, Monthly, Quaterly, Bi-annual or Annual data (string).
                 init = dates(b);
             elseif (isnumeric(b) && isscalar(b) && isint(b)) % Yearly data.
                 init = dates([num2str(b) 'Y']);
diff --git a/src/@dseries/hdiff.m b/src/@dseries/hdiff.m
new file mode 100644
index 0000000000000000000000000000000000000000..d79dde58ecdad4cc57dffb5f0e2c195805b9dcb8
--- /dev/null
+++ b/src/@dseries/hdiff.m
@@ -0,0 +1,49 @@
+function o = hdiff(o) % --*-- Unitary tests --*--
+
+% Computes bi-annual differences.
+%
+% INPUTS
+% - o   [dseries]
+%
+% OUTPUTS
+% - o   [dseries]
+
+% Copyright (C) 2020 Dynare Team
+%
+% This file is part of Dynare.
+%
+% Dynare is free software: you can redistribute it and/or modify
+% it under the terms of the GNU General Public License as published by
+% the Free Software Foundation, either version 3 of the License, or
+% (at your option) any later version.
+%
+% Dynare is distributed in the hope that it will be useful,
+% but WITHOUT ANY WARRANTY; without even the implied warranty of
+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+% GNU General Public License for more details.
+%
+% You should have received a copy of the GNU General Public License
+% along with Dynare.  If not, see <http://www.gnu.org/licenses/>.
+
+o = copy(o);
+o.hdiff_();
+
+%@test:1
+%$ try
+%$     data = transpose(0:1:50);
+%$     ts = dseries(data,'1950H1');
+%$     ds = ts.hdiff();
+%$     t(1) = 1;
+%$ catch
+%$     t(1) = 0;
+%$ end
+%$
+%$ if t(1)
+%$     DATA = NaN(1,ds.vobs);
+%$     DATA = [DATA; ones(ds.nobs-1,ds.vobs)];
+%$     t(2) = dassert(ds.data, DATA, 1e-15);
+%$     t(3) = dassert(ts.data, data, 1e-15);
+%$ end
+%$
+%$ T = all(t);
+%@eof:1
\ No newline at end of file
diff --git a/src/@dseries/hdiff_.m b/src/@dseries/hdiff_.m
new file mode 100644
index 0000000000000000000000000000000000000000..46b20ceb29e43ba842f7e93097ceb1b0da638b1e
--- /dev/null
+++ b/src/@dseries/hdiff_.m
@@ -0,0 +1,141 @@
+function o = hdiff_(o) % --*-- Unitary tests --*--
+
+% Computes bi-annual differences.
+%
+% INPUTS
+% - o   [dseries]
+%
+% OUTPUTS
+% - o   [dseries]
+
+% Copyright (C) 2020 Dynare Team
+%
+% This file is part of Dynare.
+%
+% Dynare is free software: you can redistribute it and/or modify
+% it under the terms of the GNU General Public License as published by
+% the Free Software Foundation, either version 3 of the License, or
+% (at your option) any later version.
+%
+% Dynare is distributed in the hope that it will be useful,
+% but WITHOUT ANY WARRANTY; without even the implied warranty of
+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+% GNU General Public License for more details.
+%
+% You should have received a copy of the GNU General Public License
+% along with Dynare.  If not, see <http://www.gnu.org/licenses/>.
+
+switch frequency(o)
+  case 1
+    error('dseries::hdiff: I cannot compute bi-annual differences from yearly data!')
+  case 2
+    o.data(2:end,:) = o.data(2:end,:)-o.data(1:end-1,:);
+    o.data(1,:) = NaN;
+  case 4
+    o.data(3:end,:) = o.data(3:end,:)-o.data(1:end-2,:);
+    o.data(1:2,:) = NaN;  
+  case 12
+    o.data(7:end,:) = o.data(7:end,:)-o.data(1:end-6,:);
+    o.data(1:6,:) = NaN;
+  case 52
+    error('dseries::hdiff: I do not know yet how to compute bi-annual differences from weekly data!')
+  otherwise
+    error(['dseries::hdiff: object ' inputname(1) ' has unknown frequency']);
+end
+
+for i = 1:vobs(o)
+    if isempty(o.ops{i})
+        o.ops(i) = {['hdiff(' o.name{i} ')']};
+    else
+        o.ops(i) = {['hdiff(' o.ops{i} ')']};
+    end
+end
+
+%@test:1
+%$ try
+%$     data = transpose(1:100);
+%$     ts = dseries(data,'1950Q1',{'H1'},{'H_1'});
+%$     ts.hdiff_;
+%$     t(1) = true;
+%$ catch
+%$     t(1) = false;
+%$ end
+%$
+%$
+%$ if t(1)
+%$     DATA = NaN(2,ts.vobs);
+%$     DATA = [DATA; 2*ones(ts.nobs-2,ts.vobs)];
+%$     t(2) = dassert(ts.data,DATA);
+%$     t(3) = dassert(ts.ops{1},['hdiff(H1)']);
+%$ end
+%$
+%$ T = all(t);
+%@eof:1
+
+%@test:2
+%$ try
+%$     data = transpose(1:100);
+%$     ts = dseries(data,'1950M1',{'H1'},{'H_1'});
+%$     ts.hdiff_;
+%$     t(1) = true;
+%$ catch
+%$     t(1) = false;
+%$ end
+%$
+%$
+%$ if t(1)
+%$     DATA = NaN(6,ts.vobs);
+%$     DATA = [DATA; 6*ones(ts.nobs-6,ts.vobs)];
+%$     t(2) = dassert(ts.data,DATA);
+%$     t(3) = dassert(ts.ops{1},['hdiff(H1)']);
+%$ end
+%$
+%$ T = all(t);
+%@eof:2
+
+%@test:3
+%$ try
+%$     data = transpose(1:100);
+%$     ts = dseries(data,'1950W1',{'H1'},{'H_1'});
+%$     ts.hdiff_;
+%$     t(1) = false;
+%$ catch
+%$     t(1) = true;
+%$ end
+%$
+%$ T = all(t);
+%@eof:3
+
+%@test:4
+%$ try
+%$     data = transpose(1:100);
+%$     ts = dseries(data,'1950Y',{'H1'},{'H_1'});
+%$     ts.hdiff_;
+%$     t(1) = false;
+%$ catch
+%$     t(1) = true;
+%$ end
+%$
+%$ T = all(t);
+%@eof:4
+
+%@test:5
+%$ try
+%$     data = transpose(1:100);
+%$     ts = dseries(data,'1950H1',{'H1'},{'H_1'});
+%$     ts.hdiff_;
+%$     t(1) = true;
+%$ catch
+%$     t(1) = false;
+%$ end
+%$
+%$ if t(1)
+%$     DATA = NaN(1,ts.vobs);
+%$     DATA = [DATA; ones(ts.nobs-1,ts.vobs)];
+%$     t(2) = dassert(ts.data,DATA);
+%$     t(3) = dassert(ts.ops{1},['hdiff(H1)']);
+%$ end
+%$
+%$ T = all(t);
+%@eof:5
+
diff --git a/src/@dseries/hgrowth.m b/src/@dseries/hgrowth.m
new file mode 100644
index 0000000000000000000000000000000000000000..d984445c5e7fd70e98969054f2d9fe24b2a0439d
--- /dev/null
+++ b/src/@dseries/hgrowth.m
@@ -0,0 +1,52 @@
+function o = hgrowth(o) % --*-- Unitary tests --*--
+
+% Computes bi-annual growth rates.
+%
+% INPUTS
+% - o   [dseries]
+%
+% OUTPUTS
+% - o   [dseries]
+
+% Copyright (C) 2020 Dynare Team
+%
+% This file is part of Dynare.
+%
+% Dynare is free software: you can redistribute it and/or modify
+% it under the terms of the GNU General Public License as published by
+% the Free Software Foundation, either version 3 of the License, or
+% (at your option) any later version.
+%
+% Dynare is distributed in the hope that it will be useful,
+% but WITHOUT ANY WARRANTY; without even the implied warranty of
+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+% GNU General Public License for more details.
+%
+% You should have received a copy of the GNU General Public License
+% along with Dynare.  If not, see <http://www.gnu.org/licenses/>.
+
+o = copy(o);
+o.hgrowth_();
+
+%@test:1
+%$ t = zeros(2,1);
+%$
+%$ try
+%$     data = repmat(transpose(1:2),100,1);
+%$     ts = dseries(data,'1950Q1');
+%$     ds = ts.hgrowth;
+%$     t(1) = 1;
+%$ catch
+%$     t = 0;
+%$ end
+%$
+%$
+%$ if length(t)>1
+%$     DATA = NaN(2,ts.vobs);
+%$     DATA = [DATA; zeros(ts.nobs-2,ts.vobs)];
+%$     t(2) = dassert(ds.data,DATA);
+%$     t(3) = dassert(ts.data,data);
+%$ end
+%$
+%$ T = all(t);
+%@eof:1
\ No newline at end of file
diff --git a/src/@dseries/hgrowth_.m b/src/@dseries/hgrowth_.m
new file mode 100644
index 0000000000000000000000000000000000000000..ac30fd80093912b92b59df22d659a9bdb4782cb3
--- /dev/null
+++ b/src/@dseries/hgrowth_.m
@@ -0,0 +1,110 @@
+function o = hgrowth_(o) % --*-- Unitary tests --*--
+
+% Computes bi-annual growth rates.
+%
+% INPUTS
+% - o   [dseries]
+%
+% OUTPUTS
+% - o   [dseries]
+
+% Copyright (C) 2020 Dynare Team
+%
+% This file is part of Dynare.
+%
+% Dynare is free software: you can redistribute it and/or modify
+% it under the terms of the GNU General Public License as published by
+% the Free Software Foundation, either version 3 of the License, or
+% (at your option) any later version.
+%
+% Dynare is distributed in the hope that it will be useful,
+% but WITHOUT ANY WARRANTY; without even the implied warranty of
+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+% GNU General Public License for more details.
+%
+% You should have received a copy of the GNU General Public License
+% along with Dynare.  If not, see <http://www.gnu.org/licenses/>.
+
+switch frequency(o)
+  case 1
+    error('dseries::hgrowth: I cannot compute bi-annual growth rates from yearly data!')
+  case 2
+    o.data(2:end,:) = o.data(2:end,:)./o.data(1:end-1,:) - 1;
+    o.data(1,:) = NaN;
+  case 4
+    o.data(3:end,:) = o.data(3:end,:)./o.data(1:end-2,:) - 1;
+    o.data(1:2,:) = NaN;
+  case 12
+    o.data(7:end,:) = o.data(7:end,:)./o.data(1:end-6,:) - 1;
+    o.data(1:6,:) = NaN;
+  case 52
+    error('dseries::hgrowth: I do not know yet how to compute bi-annual growth rates from weekly data!')
+  otherwise
+    error(['dseries::hgrowth: object ' inputname(1) ' has unknown frequency']);
+end
+
+for i = 1:vobs(o)
+    if isempty(o.ops{i})
+        o.ops(i) = {['hgrowth(' o.name{i} ')']};
+    else
+        o.ops(i) = {['hgrowth(' o.ops{i} ')']};
+    end
+end
+
+%@test:1
+%$ try
+%$     data = (1+.01).^transpose(0:1:50);
+%$     ts = dseries(data,'1950H1');
+%$     ts.hgrowth_;
+%$     t(1) = 1;
+%$ catch
+%$     t(1) = 0;
+%$ end
+%$
+%$ if t(1)
+%$     DATA = NaN(1,ts.vobs);
+%$     DATA = [DATA; .01*ones(ts.nobs-1,ts.vobs)];
+%$     t(2) = dassert(ts.data,DATA,1e-15);
+%$ end
+%$
+%$ T = all(t);
+%@eof:1
+
+%@test:2
+%$ try
+%$     data = repmat(transpose(1:2),100,1);
+%$     ts = dseries(data,'1950Q1');
+%$     ts.hgrowth_;
+%$     t(1) = true;
+%$ catch
+%$     t(1) = false;
+%$ end
+%$
+%$
+%$ if t(1)
+%$     DATA = NaN(2,ts.vobs);
+%$     DATA = [DATA; zeros(ts.nobs-2,ts.vobs)];
+%$     t(2) = dassert(ts.data,DATA);
+%$ end
+%$
+%$ T = all(t);
+%@eof:2
+
+%@test:3
+%$ try
+%$     data = repmat(transpose(1:6),100,1);
+%$     ts = dseries(data,'1950M1');
+%$     ts.hgrowth_();
+%$     t(1) = true;
+%$ catch
+%$     t(1) = false;
+%$ end
+%$
+%$ if t(1)
+%$     DATA = NaN(6,ts.vobs);
+%$     DATA = [DATA; zeros(ts.nobs-6,ts.vobs)];
+%$     t(2) = dassert(ts.data,DATA);
+%$ end
+%$
+%$ T = all(t);
+%@eof:3
diff --git a/src/@dseries/mdiff_.m b/src/@dseries/mdiff_.m
index 73b8a29699caa0d91cedcc4bba645257e28a248d..a4e6b316d23db54e0c60a8912c59090d46023d6a 100644
--- a/src/@dseries/mdiff_.m
+++ b/src/@dseries/mdiff_.m
@@ -8,7 +8,7 @@ function o = mdiff_(o) % --*-- Unitary tests --*--
 % OUTPUTS
 % - o   [dseries]
 
-% Copyright (C) 2017 Dynare Team
+% Copyright (C) 2017-2020 Dynare Team
 %
 % This file is part of Dynare.
 %
@@ -28,6 +28,8 @@ function o = mdiff_(o) % --*-- Unitary tests --*--
 switch frequency(o)
   case 1
     error('dseries::mdiff: I cannot compute monthly differences from yearly data!')
+  case 2
+    error('dseries::mdiff: I cannot compute monthly differences from bi-annual data!')
   case 4
     error('dseries::mdiff: I cannot compute monthly differences from quarterly data!')
   case 12
diff --git a/src/@dseries/mgrowth_.m b/src/@dseries/mgrowth_.m
index 94c8c2e5595ff69861bee121156811304e2e4270..53369cfe135bcc888e2f036f75422b6dd7f5b0ec 100644
--- a/src/@dseries/mgrowth_.m
+++ b/src/@dseries/mgrowth_.m
@@ -8,7 +8,7 @@ function o = mgrowth_(o) % --*-- Unitary tests --*--
 % OUTPUTS
 % - o   [dseries]
 
-% Copyright (C) 2017 Dynare Team
+% Copyright (C) 2017-2020 Dynare Team
 %
 % This file is part of Dynare.
 %
@@ -28,6 +28,8 @@ function o = mgrowth_(o) % --*-- Unitary tests --*--
 switch frequency(o)
   case 1
     error('dseries::mgrowth: I cannot compute monthly growth rates from yearly data!')
+  case 2
+    error('dseries::mgrowth: I cannot compute monthly growth rates from bi-annual data!')
   case 4
     error('dseries::mgrowth: I cannot compute monthly growth rates from quaterly data!')
   case 12
diff --git a/src/@dseries/qdiff_.m b/src/@dseries/qdiff_.m
index a8ba54b74fa690ba714d96e359285f7eda9ca14e..7e02f3a263cc1b1629d5ca8f6f6ca943b05ae723 100644
--- a/src/@dseries/qdiff_.m
+++ b/src/@dseries/qdiff_.m
@@ -8,7 +8,7 @@ function o = qdiff_(o) % --*-- Unitary tests --*--
 % OUTPUTS
 % - o   [dseries]
 
-% Copyright (C) 2012-2017 Dynare Team
+% Copyright (C) 2012-2020 Dynare Team
 %
 % This file is part of Dynare.
 %
@@ -28,6 +28,8 @@ function o = qdiff_(o) % --*-- Unitary tests --*--
 switch frequency(o)
   case 1
     error('dseries::qdiff: I cannot compute quaterly differences from yearly data!')
+  case 2
+    error('dseries::qdiff: I cannot compute quaterly differences from bi-annual data!');
   case 4
     o.data(2:end,:) = o.data(2:end,:)-o.data(1:end-1,:);
     o.data(1,:) = NaN;
diff --git a/src/@dseries/qgrowth_.m b/src/@dseries/qgrowth_.m
index 02fdf6d470f49593e78db9f0f51f9b22f72d189f..658b5c48dea1c156a2d0c9e43de7dcbf3a59bc6a 100644
--- a/src/@dseries/qgrowth_.m
+++ b/src/@dseries/qgrowth_.m
@@ -8,7 +8,7 @@ function o = qgrowth_(o) % --*-- Unitary tests --*--
 % OUTPUTS
 % - o   [dseries]
 
-% Copyright (C) 2012-2017 Dynare Team
+% Copyright (C) 2012-2020 Dynare Team
 %
 % This file is part of Dynare.
 %
@@ -28,6 +28,8 @@ function o = qgrowth_(o) % --*-- Unitary tests --*--
 switch frequency(o)
   case 1
     error('dseries::qgrowth: I cannot compute quaterly growth rates from yearly data!')
+  case 2
+    error('dseries::qgrowth: I cannot compute quaterly growth rates from bi-annual data!')
   case 4
     o.data(2:end,:) = o.data(2:end,:)./o.data(1:end-1,:) - 1;
     o.data(1,:) = NaN;
diff --git a/src/@dseries/subsref.m b/src/@dseries/subsref.m
index ba808dc92a7a0e8c99ad660a0042c7b7f07ca3ab..f39b4a8dc0b510e36e33bbc09c0a0783af0a9239 100644
--- a/src/@dseries/subsref.m
+++ b/src/@dseries/subsref.m
@@ -15,7 +15,7 @@ function r = subsref(o, S) % --*-- Unitary tests --*--
 %                     by applying a public method on `o`, or a dseries object built by extracting
 %                     a variable from `o`, or a dseries object containing a subsample.
 
-% Copyright (C) 2011-2019 Dynare Team
+% Copyright (C) 2011-2020 Dynare Team
 %
 % This file is part of Dynare.
 %
@@ -109,9 +109,11 @@ switch S(1).type
             'log','log_', ...
             'exp','exp_', ...
             'ygrowth','ygrowth_', ...
+            'hgrowth','hgrowth_', ...
             'qgrowth','qgrowth_', ...
             'mgrowth','mgrowth_', ...
             'ydiff','ydiff_', ...
+            'hdiff','hdiff_', ...
             'qdiff','qdiff_', ...
             'diff', 'diff_', ...
             'mdiff','mdiff_', ...
@@ -725,3 +727,20 @@ return
 
  T = all(t);
 %@eof:19
+
+%@test:20
+ try
+     tseries = dseries(dates('2000H1'));
+     ts = tseries(ones(6,1));
+     t(1) = true;
+ catch
+     t(1) = false;
+ end
+
+ if t(1)
+     t(2) = ts.dates(1)==dates('2000H1');
+     t(3) = ts.dates(6)==dates('2002H2');
+ end
+
+ T = all(t);
+%@eof:20
\ No newline at end of file
diff --git a/src/@dseries/ydiff_.m b/src/@dseries/ydiff_.m
index f03d25a6da3ed3ab740deaf1327ef94c63439df1..16e2f6aeb502df8e6d0c7edbf229ba1f46d696f8 100644
--- a/src/@dseries/ydiff_.m
+++ b/src/@dseries/ydiff_.m
@@ -8,7 +8,7 @@ function o = ydiff_(o) % --*-- Unitary tests --*--
 % OUTPUTS
 % - o   [dseries]
 
-% Copyright (C) 2012-2017 Dynare Team
+% Copyright (C) 2012-2020 Dynare Team
 %
 % This file is part of Dynare.
 %
@@ -29,6 +29,9 @@ switch frequency(o)
   case 1
     o.data(2:end,:) = o.data(2:end,:)-o.data(1:end-1,:);
     o.data(1,:) = NaN;
+  case 2
+    o.data(3:end,:) = o.data(3:end,:)-o.data(1:end-2,:);
+    o.data(1:2,:) = NaN;
   case 4
     o.data(5:end,:) = o.data(5:end,:)-o.data(1:end-4,:);
     o.data(1:4,:) = NaN;
@@ -132,3 +135,23 @@ end
 %$
 %$ T = all(t);
 %@eof:4
+
+%@test:5
+%$ try
+%$     data = transpose(1:100);
+%$     ts = dseries(data,'1950H1',{'A1'},{'A_1'});
+%$     ts.ydiff_;
+%$     t(1) = true;
+%$ catch
+%$     t(1) = false;
+%$ end
+%$
+%$ if t(1)
+%$     DATA = NaN(2,ts.vobs);
+%$     DATA = [DATA; 2*ones(ts.nobs-2,ts.vobs)];
+%$     t(2) = dassert(ts.data,DATA);
+%$     t(3) = dassert(ts.ops{1},['ydiff(A1)']);
+%$ end
+%$
+%$ T = all(t);
+%@eof:5
diff --git a/src/@dseries/ygrowth_.m b/src/@dseries/ygrowth_.m
index 67f2f0e2fd40a18c8b2dd4ddeb00effc58657c32..3bc3d77e81e991797e0738956f4085c87eb271cd 100644
--- a/src/@dseries/ygrowth_.m
+++ b/src/@dseries/ygrowth_.m
@@ -8,7 +8,7 @@ function o = ygrowth_(o) % --*-- Unitary tests --*--
 % OUTPUTS
 % - o   [dseries]
 
-% Copyright (C) 2012-2017 Dynare Team
+% Copyright (C) 2012-2020 Dynare Team
 %
 % This file is part of Dynare.
 %
@@ -29,6 +29,9 @@ switch frequency(o)
   case 1
     o.data(2:end,:) = o.data(2:end,:)./o.data(1:end-1,:) - 1;
     o.data(1,:) = NaN;
+  case 2
+    o.data(3:end,:) = o.data(3:end,:)./o.data(1:end-2,:) - 1;
+    o.data(1:2,:) = NaN;  
   case 4
     o.data(5:end,:) = o.data(5:end,:)./o.data(1:end-4,:) - 1;
     o.data(1:4,:) = NaN;
@@ -50,9 +53,28 @@ for i = 1:vobs(o)
     end
 end
 
-
 %@test:1
 %$ try
+%$     data = repmat(transpose(1:2),100,1);
+%$     ts = dseries(data,'1950H1');
+%$     ts.ygrowth_;
+%$     t(1) = true;
+%$ catch
+%$     t(1) = false;
+%$ end
+%$
+%$
+%$ if t(1)
+%$     DATA = NaN(2,ts.vobs);
+%$     DATA = [DATA; zeros(ts.nobs-2,ts.vobs)];
+%$     t(2) = dassert(ts.data,DATA);
+%$ end
+%$
+%$ T = all(t);
+%@eof:1
+
+%@test:2
+%$ try
 %$     data = repmat(transpose(1:4),100,1);
 %$     ts = dseries(data,'1950Q1');
 %$     ts.ygrowth_;
@@ -69,9 +91,9 @@ end
 %$ end
 %$
 %$ T = all(t);
-%@eof:1
+%@eof:2
 
-%@test:2
+%@test:3
 %$ try
 %$     data = repmat(transpose(1:12),100,1);
 %$     ts = dseries(data,'1950M1');
@@ -88,9 +110,9 @@ end
 %$ end
 %$
 %$ T = all(t);
-%@eof:2
+%@eof:3
 
-%@test:3
+%@test:4
 %$ try
 %$     data = repmat(transpose(1:52),100,1);
 %$     ts = dseries(data,'1950W1');
@@ -108,4 +130,4 @@ end
 %$ end
 %$
 %$ T = all(t);
-%@eof:3
+%@eof:4
diff --git a/src/utilities/convert/date2string.m b/src/utilities/convert/date2string.m
index c8d03441a1109533b695ee972aa94866755b5824..49d675fe869dd813e628975e6294e028c2cb6403 100644
--- a/src/utilities/convert/date2string.m
+++ b/src/utilities/convert/date2string.m
@@ -5,12 +5,12 @@ function s = date2string(varargin) % --*-- Unitary tests --*--
 % INPUTS
 %  o varargin{1}     + dates object with one element, if nargin==1.
 %                    + 1*2 vector of integers (first element is the year, second element is the subperiod), if nargin==2.
-%  o varargin{2}     integer scalar equal to 1, 4, 12 or 52 (frequency).
+%  o varargin{2}     integer scalar equal to 1, 2, 4, 12 or 52 (frequency).
 %
 % OUTPUTS
 %  o s               string.
 
-% Copyright (C) 2013-2017 Dynare Team
+% Copyright (C) 2013-2020 Dynare Team
 %
 % This file is part of Dynare.
 %
@@ -37,8 +37,8 @@ if isequal(nargin,1)
 end
 
 if isequal(nargin,2)
-    if ~(isvector(varargin{1}) && isequal(length(varargin{1}),2) && all(isint(varargin{1})) && isscalar(varargin{2} && ismember(varargin{2},[1 4 12 52])))
-        error(['dates::format: First input must be a 1*2 vector of integers and second input must be a scalar integer (1, 4, 12 or 52)!'])
+    if ~(isvector(varargin{1}) && isequal(length(varargin{1}),2) && all(isint(varargin{1})) && isscalar(varargin{2} && ismember(varargin{2},[1 2 4 12 52])))
+        error(['dates::format: First input must be a 1*2 vector of integers and second input must be a scalar integer (1, 2, 4, 12 or 52)!'])
     else
         if varargin{1}(2)>varargin{2} || varargin{1}(2)<1
             error('dates::format: Second element of the first input be between 1 and %s!',num2str(varargin{2}))
@@ -119,3 +119,19 @@ end
 %$
 %$ T = all(t);
 %@eof:5
+
+%@test:6
+%$ try
+%$     str = date2string([1938; 2], 2);
+%$     t(1) = true;
+%$ catch
+%$     t(1) = false;
+%$ end
+%$
+%$ if t(1)
+%$     t(2) = dassert(str, '1938H2');
+%$ end
+%$
+%$ T = all(t);
+%@eof:6
+
diff --git a/src/utilities/convert/freq2string.m b/src/utilities/convert/freq2string.m
index b1b85dd704c85280525345fa2bf5da6b2e585bae..09b750237a821e242e9e63fb1b895ab2f670c1bf 100644
--- a/src/utilities/convert/freq2string.m
+++ b/src/utilities/convert/freq2string.m
@@ -1,12 +1,12 @@
 function s = freq2string(freq) % --*-- Unitary tests --*--
 
 % INPUTS
-%  o freq     scalar integer,  equal to 1, 4, 12 or 52 (resp. annual, quaterly, monthly or weekly)
+%  o freq     scalar integer,  equal to 1, 2, 4, 12 or 52 (resp. annual, bi-annual, quaterly, monthly or weekly)
 %
 % OUTPUTS
-%  o s        character, equal to Y, Q, M or W (resp. annual, quaterly, monthly or weekly)
+%  o s        character, equal to Y, H, Q, M or W (resp. annual, bi-annual, quaterly, monthly or weekly)
 
-% Copyright (C) 2013-2017 Dynare Team
+% Copyright (C) 2013-2020 Dynare Team
 %
 % This file is part of Dynare.
 %
@@ -26,6 +26,8 @@ function s = freq2string(freq) % --*-- Unitary tests --*--
 switch freq
   case 1
     s = 'Y';
+  case 2
+    s = 'H';
   case 4
     s = 'Q';
   case 12
@@ -39,6 +41,7 @@ end
 %@test:1
 %$ try
 %$     strY = freq2string(1);
+%$     strH = freq2string(2);
 %$     strQ = freq2string(4);
 %$     strM = freq2string(12);
 %$     strW = freq2string(52);
@@ -49,9 +52,10 @@ end
 %$
 %$ if t(1)
 %$     t(2) = dassert(strY, 'Y');
-%$     t(3) = dassert(strQ, 'Q');
-%$     t(4) = dassert(strM, 'M');
-%$     t(5) = dassert(strW, 'W');
+%$     t(3) = dassert(strH, 'H');
+%$     t(4) = dassert(strQ, 'Q');
+%$     t(5) = dassert(strM, 'M');
+%$     t(6) = dassert(strW, 'W');
 %$ end
 %$
 %$ T = all(t);
diff --git a/src/utilities/convert/string2date.m b/src/utilities/convert/string2date.m
index 96835ea6df0f708409f2b3029429b82db2a36ac0..1801c42fd60cbb148143810bfe43cf64446008e3 100644
--- a/src/utilities/convert/string2date.m
+++ b/src/utilities/convert/string2date.m
@@ -1,6 +1,6 @@
 function date = string2date(a) % --*-- Unitary tests --*--
 
-% Copyright (C) 2013-2017 Dynare Team
+% Copyright (C) 2013-2020 Dynare Team
 %
 % This file is part of Dynare.
 %
@@ -30,6 +30,13 @@ if isyearly(a)
     return
 end
 
+if isbiannual(a)
+    year = 1:(regexp(a,'[Hh]')-1);
+    date.freq = 2;
+    date.time = write_time_field(a, year);
+    return
+end
+
 if isquarterly(a)
     year = 1:(regexp(a,'[Qq]')-1);
     date.freq = 4;
@@ -69,6 +76,8 @@ b(2) = 1;
 %$ date_4 = '1950a';
 %$ date_5 = '1967y';
 %$ date_6 = '2009A';
+%$ date_7 = '2009H1';
+%$ date_8 = '2009h2';
 %$
 %$ % Define expected results.
 %$ e_date_1 = [1950 2];
@@ -83,6 +92,10 @@ b(2) = 1;
 %$ e_freq_5 = 1;
 %$ e_date_6 = [2009 1];
 %$ e_freq_6 = 1;
+%$ e_date_7 = [2009 1];
+%$ e_freq_7 = 2;
+%$ e_date_8 = [2009 2];
+%$ e_freq_8 = 2;
 %$
 %$ % Call the tested routine.
 %$ d1 = string2date(date_1);
@@ -91,6 +104,8 @@ b(2) = 1;
 %$ d4 = string2date(date_4);
 %$ d5 = string2date(date_5);
 %$ d6 = string2date(date_6);
+%$ d7 = string2date(date_7);
+%$ d8 = string2date(date_8);
 %$
 %$ % Check the results.
 %$ t(1) = dassert(d1.time,e_date_1);
@@ -99,11 +114,15 @@ b(2) = 1;
 %$ t(4) = dassert(d4.time,e_date_4);
 %$ t(5) = dassert(d5.time,e_date_5);
 %$ t(6) = dassert(d6.time,e_date_6);
-%$ t(7) = dassert(d1.freq,e_freq_1);
-%$ t(8) = dassert(d2.freq,e_freq_2);
-%$ t(9) = dassert(d3.freq,e_freq_3);
-%$ t(10) = dassert(d4.freq,e_freq_4);
-%$ t(11)= dassert(d5.freq,e_freq_5);
-%$ t(12)= dassert(d6.freq,e_freq_6);
+%$ t(7) = dassert(d7.time,e_date_7);
+%$ t(8) = dassert(d8.time,e_date_8);
+%$ t(9) = dassert(d1.freq,e_freq_1);
+%$ t(10) = dassert(d2.freq,e_freq_2);
+%$ t(11) = dassert(d3.freq,e_freq_3);
+%$ t(12) = dassert(d4.freq,e_freq_4);
+%$ t(13)= dassert(d5.freq,e_freq_5);
+%$ t(14)= dassert(d6.freq,e_freq_6);
+%$ t(15)= dassert(d7.freq,e_freq_7);
+%$ t(16)= dassert(d8.freq,e_freq_8);
 %$ T = all(t);
 %@eof:1
\ No newline at end of file
diff --git a/src/utilities/convert/string2freq.m b/src/utilities/convert/string2freq.m
index 8a88b927934e2338e8aae661b24c83ce0fa4ff0f..a15e97c6d9209d07091922f7d469cb7d723d6a58 100644
--- a/src/utilities/convert/string2freq.m
+++ b/src/utilities/convert/string2freq.m
@@ -1,12 +1,12 @@
 function freq = string2freq(s) % --*-- Unitary tests --*--
 
 % INPUTS
-%  o s        character, equal to Y, Q, M or W (resp. annual, quaterly, monthly or weekly)
+%  o s        character, equal to Y, H, Q, M or W (resp. annual, bi-annual, quaterly, monthly or weekly)
 %
 % OUTPUTS
-%  o freq     scalar integer,  equal to 1, 4, 12 or 52 (resp. annual, quaterly, monthly or weekly)
+%  o freq     scalar integer,  equal to 1, 2, 4, 12 or 52 (resp. annual, bi-annual, quaterly, monthly or weekly)
 
-% Copyright (C) 2013-2017 Dynare Team
+% Copyright (C) 2013-2020 Dynare Team
 %
 % This file is part of Dynare.
 %
@@ -26,6 +26,8 @@ function freq = string2freq(s) % --*-- Unitary tests --*--
 switch upper(s)
   case {'Y','A'}
     freq = 1;
+  case 'H'
+    freq = 2;
   case 'Q'
     freq = 4;
   case 'M'
@@ -39,6 +41,7 @@ end
 %@test:1
 %$ try
 %$     nY = string2freq('Y');
+%$     nH = string2freq('H');
 %$     nQ = string2freq('Q');
 %$     nM = string2freq('M');
 %$     nW = string2freq('W');
@@ -49,9 +52,10 @@ end
 %$
 %$ if t(1)
 %$     t(2) = dassert(nY, 1);
-%$     t(3) = dassert(nQ, 4);
-%$     t(4) = dassert(nM, 12);
-%$     t(5) = dassert(nW, 52);
+%$     t(3) = dassert(nH, 2);
+%$     t(4) = dassert(nQ, 4);
+%$     t(5) = dassert(nM, 12);
+%$     t(6) = dassert(nW, 52);
 %$ end
 %$
 %$ T = all(t);
diff --git a/src/utilities/is/isbiannual.m b/src/utilities/is/isbiannual.m
new file mode 100644
index 0000000000000000000000000000000000000000..149d24718db689eb6fc056411e087a9b5bd78908
--- /dev/null
+++ b/src/utilities/is/isbiannual.m
@@ -0,0 +1,56 @@
+function b = isbiannual(str)  % --*-- Unitary tests --*--
+
+% Tests if the input can be interpreted as a bi-annual date.
+%
+% INPUTS
+%  o str     string.
+%
+% OUTPUTS
+%  o b       integer scalar, equal to 1 if str can be interpreted as a bi-annual date or 0 otherwise.
+
+% Copyright (C) 2020 Dynare Team
+%
+% This file is part of Dynare.
+%
+% Dynare is free software: you can redistribute it and/or modify
+% it under the terms of the GNU General Public License as published by
+% the Free Software Foundation, either version 3 of the License, or
+% (at your option) any later version.
+%
+% Dynare is distributed in the hope that it will be useful,
+% but WITHOUT ANY WARRANTY; without even the implied warranty of
+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+% GNU General Public License for more details.
+%
+% You should have received a copy of the GNU General Public License
+% along with Dynare.  If not, see <http://www.gnu.org/licenses/>.
+
+if ischar(str)
+    if isempty(regexp(str,'^-?[0-9]+[Hh][1-2]$','once'))
+        b = false;
+    else
+        b = true;
+    end
+else
+    b = false;
+end
+
+%@test:1
+%$
+%$ date_1 = '1950H2';
+%$ date_2 = '1950h2';
+%$ date_3 = '-1950h2';
+%$ date_4 = '1950h3';
+%$ date_5 = '1950 azd ';
+%$ date_6 = '1950Y';
+%$ date_7 = '1950m24';
+%$
+%$ t(1) = dassert(isbiannual(date_1),true);
+%$ t(2) = dassert(isbiannual(date_2),true);
+%$ t(3) = dassert(isbiannual(date_3),true);
+%$ t(4) = dassert(isbiannual(date_4),false);
+%$ t(5) = dassert(isbiannual(date_5),false);
+%$ t(6) = dassert(isbiannual(date_6),false);
+%$ t(7) = dassert(isbiannual(date_7),false);
+%$ T = all(t);
+%@eof:1
\ No newline at end of file
diff --git a/src/utilities/is/isfreq.m b/src/utilities/is/isfreq.m
index d966d6c132f9e95f8921fc333e7be0d35bcf6491..b6562de7c4200447cecc2bcea6732d7c51e8ec57 100644
--- a/src/utilities/is/isfreq.m
+++ b/src/utilities/is/isfreq.m
@@ -8,7 +8,7 @@ function B = isfreq(A) % --*-- Unitary tests --*--
 % OUTPUTS
 %  o B     scalar integer equal to one if A can be interpreted as a frequency, zero otherwise.
 
-% Copyright (C) 2013-2017 Dynare Team
+% Copyright (C) 2013-2020 Dynare Team
 %
 % This file is part of Dynare.
 %
@@ -28,13 +28,13 @@ function B = isfreq(A) % --*-- Unitary tests --*--
 B = false;
 
 if ischar(A)
-    if isequal(length(A),1) && ismember(upper(A),{'Y','A','Q','M','W'})
+    if isequal(length(A),1) && ismember(upper(A),{'Y','A', 'H', 'Q','M','W'})
         B = true;
         return
     end
 end
 
-if isnumeric(A) && isequal(length(A),1) && ismember(A,[1 4 12 52])
+if isnumeric(A) && isequal(length(A),1) && ismember(A,[1 2 4 12 52])
     B = true;
 end
 
@@ -112,3 +112,18 @@ end
 %$
 %$ T = all(t);
 %@eof:5
+
+%@test:6
+%$ try
+%$     boolean = isfreq('H');
+%$     t(1) = true;
+%$ catch
+%$     t(1) = false;
+%$ end
+%$
+%$ if t(1)
+%$     t(2) = dassert(boolean, true);
+%$ end
+%$
+%$ T = all(t);
+%@eof:6
diff --git a/src/utilities/is/isstringdate.m b/src/utilities/is/isstringdate.m
index 3221c94e9a18419b2094b0ddefe442712b49cc92..1e063d896014607dd25f2d0429f3b78b4d0009c9 100644
--- a/src/utilities/is/isstringdate.m
+++ b/src/utilities/is/isstringdate.m
@@ -8,7 +8,7 @@ function b = isstringdate(str)  % --*-- Unitary tests --*--
 % OUTPUTS
 %  o b       integer scalar, equal to 1 if str can be interpreted as a date or 0 otherwise.
 
-% Copyright (C) 2013-2017 Dynare Team
+% Copyright (C) 2013-2020 Dynare Team
 %
 % This file is part of Dynare.
 %
@@ -26,7 +26,7 @@ function b = isstringdate(str)  % --*-- Unitary tests --*--
 % along with Dynare.  If not, see <http://www.gnu.org/licenses/>.
 
 if ischar(str)
-    b = isquarterly(str) || isyearly(str) || ismonthly(str) || isweekly(str);
+    b = isquarterly(str) || isyearly(str) || isbiannual(str) || ismonthly(str) || isweekly(str);
 else
     b = false;
 end
@@ -48,6 +48,8 @@ end
 %$ date_13 = 'M11';
 %$ date_14 = '1W';
 %$ date_15 = 'W1';
+%$ date_16 = '1948H1';
+%$ date_17 = 'h2';
 %$
 %$ t(1) = dassert(isstringdate(date_1),true);
 %$ t(2) = dassert(isstringdate(date_2),true);
@@ -64,6 +66,8 @@ end
 %$ t(13) = dassert(isstringdate(date_13),false);
 %$ t(14) = dassert(isstringdate(date_14),false);
 %$ t(15) = dassert(isstringdate(date_15),false);
+%$ t(16) = dassert(isstringdate(date_16),true);
+%$ t(17) = dassert(isstringdate(date_17),false);
 %$
 %$ T = all(t);
 %@eof:1
\ No newline at end of file
diff --git a/src/utilities/is/issubperiod.m b/src/utilities/is/issubperiod.m
index 60fb951e663c9420394373e70871b1be1b68c74c..bf02fee0a4954c99bf9aa1aeaa4c86a8230c9d5a 100644
--- a/src/utilities/is/issubperiod.m
+++ b/src/utilities/is/issubperiod.m
@@ -1,6 +1,6 @@
 function C = issubperiod(A,B) % --*-- Unitary tests --*--
 
-% Copyright (C) 2013-2015 Dynare Team
+% Copyright (C) 2013-2020 Dynare Team
 %
 % This file is part of Dynare.
 %
@@ -20,7 +20,7 @@ function C = issubperiod(A,B) % --*-- Unitary tests --*--
 if isfreq(B)
     C = all(isint(A)) && all(A>=1) && all(A<=B);
 else
-    error('issubperiod:: Second input argument must be equal to 1, 4, 12 or 52 (frequency)!')
+    error('issubperiod:: Second input argument must be equal to 1, 2, 4, 12 or 52 (frequency)!')
 end
 
 %@test:1
@@ -66,4 +66,19 @@ end
 %$ end
 %$
 %$ T = all(t);
-%@eof:3
\ No newline at end of file
+%@eof:3
+
+%@test:4
+%$ try
+%$    b = issubperiod(3, 2);
+%$    t(1) = true;
+%$ catch
+%$    t(1) = false;
+%$ end
+%$
+%$ if t(1)
+%$     t(2) = dassert(b, false);
+%$ end
+%$
+%$ T = all(t);
+%@eof:4
\ No newline at end of file