Move UTC offset for KDMs from the cinema to the point of KDM creation (#2300).
authorCarl Hetherington <cth@carlh.net>
Thu, 1 Dec 2022 21:30:58 +0000 (22:30 +0100)
committerCarl Hetherington <cth@carlh.net>
Thu, 22 Dec 2022 23:11:11 +0000 (00:11 +0100)
19 files changed:
src/lib/cinema.cc
src/lib/cinema.h
src/lib/dkdm_recipient.cc
src/lib/dkdm_recipient.h
src/lib/kdm_cli.cc
src/lib/screen.cc
src/lib/screen.h
src/tools/dcpomatic_kdm.cc
src/wx/cinema_dialog.cc
src/wx/cinema_dialog.h
src/wx/dcpomatic_choice.h
src/wx/dkdm_dialog.cc
src/wx/kdm_dialog.cc
src/wx/kdm_timing_panel.cc
src/wx/kdm_timing_panel.h
src/wx/screens_panel.cc
test/config_test.cc
test/kdm_cli_test.cc
test/kdm_naming_test.cc

index 7e13b50aea775ef4900b637e145913aa742c92a8..3fb5d672b7b0efed5255e37ec894843e7dc30f96 100644 (file)
@@ -43,14 +43,6 @@ Cinema::Cinema (cxml::ConstNodePtr node)
        for (auto i: node->node_children("Email")) {
                emails.push_back (i->content ());
        }
-
-       if (node->optional_number_child<int>("UTCOffset")) {
-               _utc_offset_hour = node->number_child<int>("UTCOffset");
-       } else {
-               _utc_offset_hour = node->optional_number_child<int>("UTCOffsetHour").get_value_or (0);
-       }
-
-       _utc_offset_minute = node->optional_number_child<int>("UTCOffsetMinute").get_value_or (0);
 }
 
 /* This is necessary so that we can use shared_from_this in add_screen (which cannot be done from
@@ -75,9 +67,6 @@ Cinema::as_xml (xmlpp::Element* parent) const
 
        parent->add_child("Notes")->add_child_text (notes);
 
-       parent->add_child("UTCOffsetHour")->add_child_text (raw_convert<string> (_utc_offset_hour));
-       parent->add_child("UTCOffsetMinute")->add_child_text (raw_convert<string> (_utc_offset_minute));
-
        for (auto i: _screens) {
                i->as_xml (parent->add_child ("Screen"));
        }
@@ -96,16 +85,3 @@ Cinema::remove_screen (shared_ptr<Screen> s)
        _screens.remove (s);
 }
 
-void
-Cinema::set_utc_offset_hour (int h)
-{
-       DCPOMATIC_ASSERT (h >= -11 && h <= 12);
-       _utc_offset_hour = h;
-}
-
-void
-Cinema::set_utc_offset_minute (int m)
-{
-       DCPOMATIC_ASSERT (m >= 0 && m <= 59);
-       _utc_offset_minute = m;
-}
index c17454db980f957aff708857ed4d4bcab76f85bd..f8f52dc162160624d8f3b48325886a8cbfa64dd5 100644 (file)
@@ -44,12 +44,10 @@ namespace dcpomatic {
 class Cinema : public std::enable_shared_from_this<Cinema>
 {
 public:
-       Cinema (std::string const & name_, std::list<std::string> const & e, std::string notes_, int utc_offset_hour, int utc_offset_minute)
+       Cinema(std::string const & name_, std::list<std::string> const & e, std::string notes_)
                : name (name_)
                , emails (e)
                , notes (notes_)
-               , _utc_offset_hour (utc_offset_hour)
-               , _utc_offset_minute (utc_offset_minute)
        {}
 
        explicit Cinema (cxml::ConstNodePtr);
@@ -61,33 +59,14 @@ public:
        void add_screen (std::shared_ptr<dcpomatic::Screen>);
        void remove_screen (std::shared_ptr<dcpomatic::Screen>);
 
-       void set_utc_offset_hour (int h);
-       void set_utc_offset_minute (int m);
-
        std::string name;
        std::list<std::string> emails;
        std::string notes;
 
-       int utc_offset_hour () const {
-               return _utc_offset_hour;
-       }
-
-       int utc_offset_minute () const {
-               return _utc_offset_minute;
-       }
-
        std::list<std::shared_ptr<dcpomatic::Screen>> screens () const {
                return _screens;
        }
 
 private:
        std::list<std::shared_ptr<dcpomatic::Screen>> _screens;
-       /** Offset such that the equivalent time in UTC can be determined
-           by subtracting the offset from the local time.
-       */
-       int _utc_offset_hour;
-       /** Additional minutes to add to _utc_offset_hour if _utc_offset_hour is
-           positive, or to subtract if _utc_offset_hour is negative.
-       */
-       int _utc_offset_minute;
 };
index f25934083170d35920c1069d8afdd89d0ba0c8a7..32508eb8c193ac7234b20ce96d01aababee37fea 100644 (file)
@@ -64,23 +64,20 @@ kdm_for_dkdm_recipient (
        shared_ptr<const Film> film,
        boost::filesystem::path cpl,
        shared_ptr<DKDMRecipient> recipient,
-       boost::posix_time::ptime valid_from,
-       boost::posix_time::ptime valid_to
+       dcp::LocalTime valid_from,
+       dcp::LocalTime valid_to
        )
 {
        if (!recipient->recipient) {
                return {};
        }
 
-       dcp::LocalTime const begin(valid_from, dcp::UTCOffset(recipient->utc_offset_hour, recipient->utc_offset_minute));
-       dcp::LocalTime const end  (valid_to,   dcp::UTCOffset(recipient->utc_offset_hour, recipient->utc_offset_minute));
-
        auto const kdm = film->make_kdm (
                        recipient->recipient.get(),
                        vector<string>(),
                        cpl,
-                       begin,
-                       end,
+                       valid_from,
+                       valid_to,
                        dcp::Formulation::MODIFIED_TRANSITIONAL_1,
                        true,
                        0
@@ -88,8 +85,8 @@ kdm_for_dkdm_recipient (
 
        dcp::NameFormat::Map name_values;
        name_values['f'] = kdm.content_title_text();
-       name_values['b'] = begin.date() + " " + begin.time_of_day(true, false);
-       name_values['e'] = end.date() + " " + end.time_of_day(true, false);
+       name_values['b'] = valid_from.date() + " " + valid_from.time_of_day(true, false);
+       name_values['e'] = valid_to.date() + " " + valid_to.time_of_day(true, false);
        name_values['i'] = kdm.cpl_id();
 
        return make_shared<KDMWithMetadata>(name_values, nullptr, recipient->emails, kdm);
index 6e9e4dfb1721bdbb0fdc19dd2954e1f57b53eb25..f8db7d166e4ba46f223cf23cccd78c0173915617 100644 (file)
@@ -60,7 +60,7 @@ kdm_for_dkdm_recipient (
        std::shared_ptr<const Film> film,
        boost::filesystem::path cpl,
        std::shared_ptr<DKDMRecipient> recipient,
-       boost::posix_time::ptime valid_from,
-       boost::posix_time::ptime valid_to
+       dcp::LocalTime valid_from,
+       dcp::LocalTime valid_to
        );
 
index a76155a2c8d8a4bd6ad17bbe9f508e5fb7561c59..c4cf82c56d1ff1608ec3656c05ef69e1578422a7 100644 (file)
@@ -61,8 +61,8 @@ help (std::function<void (string)> out)
        out ("  -o, --output                             output file or directory");
        out ("  -K, --filename-format                    filename format for KDMs");
        out ("  -Z, --container-name-format              filename format for ZIP containers");
-       out ("  -f, --valid-from                         valid from time (in local time zone of the cinema) (e.g. \"2013-09-28 01:41:51\") or \"now\"");
-       out ("  -t, --valid-to                           valid to time (in local time zone of the cinema) (e.g. \"2014-09-28 01:41:51\")");
+       out ("  -f, --valid-from                         valid from time (e.g. \"2013-09-28T01:41:51+04:00\", \"2018-01-01T12:00:30\") or \"now\"");
+       out ("  -t, --valid-to                           valid to time (e.g. \"2014-09-28T01:41:51\")");
        out ("  -d, --valid-duration                     valid duration (e.g. \"1 day\", \"4 hours\", \"2 weeks\")");
        out ("  -F, --formulation                        modified-transitional-1, multiple-modified-transitional-1, dci-any or dci-specific [default modified-transitional-1]");
        out ("  -p, --disable-forensic-marking-picture   disable forensic marking of pictures essences");
@@ -97,17 +97,6 @@ public:
 };
 
 
-static boost::posix_time::ptime
-time_from_string (string t)
-{
-       if (t == "now") {
-               return boost::posix_time::second_clock::local_time ();
-       }
-
-       return boost::posix_time::time_from_string (t);
-}
-
-
 static boost::posix_time::time_duration
 duration_from_string (string d)
 {
@@ -209,8 +198,8 @@ from_film (
        boost::filesystem::path output,
        dcp::NameFormat container_name_format,
        dcp::NameFormat filename_format,
-       boost::posix_time::ptime valid_from,
-       boost::posix_time::ptime valid_to,
+       dcp::LocalTime valid_from,
+       dcp::LocalTime valid_to,
        dcp::Formulation formulation,
        bool disable_forensic_marking_picture,
        optional<int> disable_forensic_marking_audio,
@@ -243,7 +232,7 @@ from_film (
        try {
                list<KDMWithMetadataPtr> kdms;
                for (auto i: screens) {
-                       auto p = kdm_for_screen (film, cpl, i, valid_from, valid_to, formulation, disable_forensic_marking_picture, disable_forensic_marking_audio);
+                       auto p = kdm_for_screen(film, cpl, i, valid_from, valid_to, formulation, disable_forensic_marking_picture, disable_forensic_marking_audio);
                        if (p) {
                                kdms.push_back (p);
                        }
@@ -335,8 +324,8 @@ from_dkdm (
        boost::filesystem::path output,
        dcp::NameFormat container_name_format,
        dcp::NameFormat filename_format,
-       boost::posix_time::ptime valid_from,
-       boost::posix_time::ptime valid_to,
+       dcp::LocalTime valid_from,
+       dcp::LocalTime valid_to,
        dcp::Formulation formulation,
        bool disable_forensic_marking_picture,
        optional<int> disable_forensic_marking_audio,
@@ -354,18 +343,12 @@ from_dkdm (
                                continue;
                        }
 
-                       int const offset_hour = i->cinema ? i->cinema->utc_offset_hour() : 0;
-                       int const offset_minute = i->cinema ? i->cinema->utc_offset_minute() : 0;
-
-                       dcp::LocalTime begin(valid_from, dcp::UTCOffset(offset_hour, offset_minute));
-                       dcp::LocalTime end(valid_to, dcp::UTCOffset(offset_hour, offset_minute));
-
                        auto const kdm = kdm_from_dkdm(
                                                        dkdm,
                                                        i->recipient.get(),
                                                        i->trusted_device_thumbprints(),
-                                                       begin,
-                                                       end,
+                                                       valid_from,
+                                                       valid_to,
                                                        formulation,
                                                        disable_forensic_marking_picture,
                                                        disable_forensic_marking_audio
@@ -375,8 +358,8 @@ from_dkdm (
                        name_values['c'] = i->cinema ? i->cinema->name : "";
                        name_values['s'] = i->name;
                        name_values['f'] = kdm.content_title_text();
-                       name_values['b'] = begin.date() + " " + begin.time_of_day(true, false);
-                       name_values['e'] = end.date() + " " + end.time_of_day(true, false);
+                       name_values['b'] = valid_from.date() + " " + valid_from.time_of_day(true, false);
+                       name_values['e'] = valid_to.date() + " " + valid_to.time_of_day(true, false);
                        name_values['i'] = kdm.cpl_id();
 
                        kdms.push_back (make_shared<KDMWithMetadata>(name_values, i->cinema.get(), i->cinema ? i->cinema->emails : list<string>(), kdm));
@@ -426,8 +409,8 @@ try
        optional<string> screen;
        list<shared_ptr<Screen>> screens;
        optional<dcp::EncryptedKDM> dkdm;
-       optional<boost::posix_time::ptime> valid_from;
-       optional<boost::posix_time::ptime> valid_to;
+       optional<dcp::LocalTime> valid_from;
+       optional<dcp::LocalTime> valid_to;
        bool zip = false;
        bool list_cinemas = false;
        bool list_dkdm_cpls = false;
@@ -488,10 +471,14 @@ try
                        container_name_format = dcp::NameFormat (optarg);
                        break;
                case 'f':
-                       valid_from = time_from_string (optarg);
+                       if (string(optarg) == "now") {
+                               valid_from = dcp::LocalTime();
+                       } else {
+                               valid_from = dcp::LocalTime(optarg);
+                       }
                        break;
                case 't':
-                       valid_to = time_from_string (optarg);
+                       valid_to = dcp::LocalTime(optarg);
                        break;
                case 'd':
                        duration_string = optarg;
@@ -535,7 +522,7 @@ try
                           (for lookup) and by creating a Cinema which the next Screen will be added to.
                        */
                        cinema_name = optarg;
-                       cinema = make_shared<Cinema>(optarg, list<string>(), "", 0, 0);
+                       cinema = make_shared<Cinema>(optarg, list<string>(), "");
                        break;
                case 'S':
                        /* Similarly, this could be the name of a new (temporary) screen or the name of a screen
@@ -608,11 +595,12 @@ try
        }
 
        if (duration_string) {
-               valid_to = valid_from.get() + duration_from_string (*duration_string);
+               valid_to = valid_from.get();
+               valid_to->add(duration_from_string(*duration_string));
        }
 
        if (verbose) {
-               out (String::compose("Making KDMs valid from %1 to %2", boost::posix_time::to_simple_string(valid_from.get()), boost::posix_time::to_simple_string(valid_to.get())));
+               out(String::compose("Making KDMs valid from %1 to %2", valid_from->as_string(), valid_to->as_string()));
        }
 
        string const thing = argv[optind];
index 2c821eceb0225fb2f51a23b4bf1a8cb9a78d404f..801042c242f46d3abc4a0685a54749fdc30ceaa2 100644 (file)
@@ -76,8 +76,8 @@ kdm_for_screen (
        shared_ptr<const Film> film,
        boost::filesystem::path cpl,
        shared_ptr<const dcpomatic::Screen> screen,
-       boost::posix_time::ptime valid_from,
-       boost::posix_time::ptime valid_to,
+       dcp::LocalTime valid_from,
+       dcp::LocalTime valid_to,
        dcp::Formulation formulation,
        bool disable_forensic_marking_picture,
        optional<int> disable_forensic_marking_audio
@@ -88,15 +88,13 @@ kdm_for_screen (
        }
 
        auto cinema = screen->cinema;
-       dcp::LocalTime const begin(valid_from, dcp::UTCOffset(cinema ? cinema->utc_offset_hour() : 0, cinema ? cinema->utc_offset_minute() : 0));
-       dcp::LocalTime const end  (valid_to,   dcp::UTCOffset(cinema ? cinema->utc_offset_hour() : 0, cinema ? cinema->utc_offset_minute() : 0));
 
        auto const kdm = film->make_kdm (
                        screen->recipient.get(),
                        screen->trusted_device_thumbprints(),
                        cpl,
-                       begin,
-                       end,
+                       valid_from,
+                       valid_to,
                        formulation,
                        disable_forensic_marking_picture,
                        disable_forensic_marking_audio
@@ -110,8 +108,8 @@ kdm_for_screen (
        }
        name_values['s'] = screen->name;
        name_values['f'] = kdm.content_title_text();
-       name_values['b'] = begin.date() + " " + begin.time_of_day(true, false);
-       name_values['e'] = end.date() + " " + end.time_of_day(true, false);
+       name_values['b'] = valid_from.date() + " " + valid_from.time_of_day(true, false);
+       name_values['e'] = valid_to.date() + " " + valid_to.time_of_day(true, false);
        name_values['i'] = kdm.cpl_id();
 
        return make_shared<KDMWithMetadata>(name_values, cinema.get(), cinema ? cinema->emails : list<string>(), kdm);
index 84cecb80b4853991cc85b6bfb1c9e95091dceb3c..2fd1b37457c8b7b624384d253a2860d92ce9c87d 100644 (file)
 #define DCPOMATIC_SCREEN_H
 
 
-#include "kdm_with_metadata.h"
 #include "kdm_recipient.h"
+#include "kdm_with_metadata.h"
 #include "trusted_device.h"
 #include <dcp/certificate.h>
+#include <dcp/utc_offset.h>
 #include <libcxml/cxml.h>
 #include <boost/optional.hpp>
 #include <string>
@@ -77,8 +78,8 @@ kdm_for_screen (
        std::shared_ptr<const Film> film,
        boost::filesystem::path cpl,
        std::shared_ptr<const dcpomatic::Screen> screen,
-       boost::posix_time::ptime valid_from,
-       boost::posix_time::ptime valid_to,
+       dcp::LocalTime valid_from,
+       dcp::LocalTime valid_to,
        dcp::Formulation formulation,
        bool disable_forensic_marking_picture,
        boost::optional<int> disable_forensic_marking_audio
index 5a7b4e6de9c927f7c6527492300d0b6098b0b82c..83945456b3832dd62b2d9db573e4bedf9a746398 100644 (file)
@@ -350,13 +350,10 @@ private:
                                                continue;
                                        }
 
-                                       dcp::LocalTime begin(_timing->from(), dcp::UTCOffset(i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()));
-                                       dcp::LocalTime end(_timing->until(), dcp::UTCOffset(i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()));
-
                                        /* Make an empty KDM */
                                        dcp::DecryptedKDM kdm (
-                                               begin,
-                                               end,
+                                               _timing->from(),
+                                               _timing->until(),
                                                decrypted.annotation_text().get_value_or (""),
                                                title,
                                                dcp::LocalTime().as_string()
@@ -376,8 +373,8 @@ private:
                                        name_values['c'] = i->cinema->name;
                                        name_values['s'] = i->name;
                                        name_values['f'] = title;
-                                       name_values['b'] = begin.date() + " " + begin.time_of_day(true, false);
-                                       name_values['e'] = end.date() + " " + end.time_of_day(true, false);
+                                       name_values['b'] = _timing->from().date() + " " + _timing->from().time_of_day(true, false);
+                                       name_values['e'] = _timing->until().date() + " " + _timing->until().time_of_day(true, false);
                                        name_values['i'] = encrypted.cpl_id ();
 
                                        /* Encrypt */
index 8c7194c4a7433084878347568ddcdb1ccb17e322..9b0ed85627d0dafd57b212e3db0d87383f9be782 100644 (file)
@@ -37,7 +37,7 @@ using namespace boost::placeholders;
 #endif
 
 
-CinemaDialog::CinemaDialog (wxWindow* parent, wxString title, string name, list<string> emails, string notes, int utc_offset_hour, int utc_offset_minute)
+CinemaDialog::CinemaDialog (wxWindow* parent, wxString title, string name, list<string> emails, string notes)
        : wxDialog (parent, wxID_ANY, title)
 {
        auto overall_sizer = new wxBoxSizer (wxVERTICAL);
@@ -51,11 +51,6 @@ CinemaDialog::CinemaDialog (wxWindow* parent, wxString title, string name, list<
        sizer->Add (_name, wxGBPosition(r, 1));
        ++r;
 
-       add_label_to_sizer (sizer, this, _("UTC offset (time zone)"), true, wxGBPosition(r, 0));
-       _utc_offset = new wxChoice (this, wxID_ANY);
-       sizer->Add (_utc_offset, wxGBPosition(r, 1));
-       ++r;
-
        add_label_to_sizer (sizer, this, _("Notes"), true, wxGBPosition(r, 0));
        _notes = new wxTextCtrl (this, wxID_ANY, std_to_wx(notes), wxDefaultPosition, wxSize(500, -1));
        sizer->Add (_notes, wxGBPosition(r, 1));
@@ -84,17 +79,6 @@ CinemaDialog::CinemaDialog (wxWindow* parent, wxString title, string name, list<
                overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
        }
 
-       /* Default to UTC */
-       size_t sel = get_offsets (_offsets);
-       for (size_t i = 0; i < _offsets.size(); ++i) {
-               _utc_offset->Append (_offsets[i].name);
-               if (_offsets[i].hour == utc_offset_hour && _offsets[i].minute == utc_offset_minute) {
-                       sel = i;
-               }
-       }
-
-       _utc_offset->SetSelection (sel);
-
        overall_sizer->Layout ();
        overall_sizer->SetSizeHints (this);
 
@@ -131,31 +115,6 @@ CinemaDialog::emails () const
        return e;
 }
 
-
-int
-CinemaDialog::utc_offset_hour () const
-{
-       int const sel = _utc_offset->GetSelection();
-       if (sel < 0 || sel > int(_offsets.size())) {
-               return 0;
-       }
-
-       return _offsets[sel].hour;
-}
-
-
-int
-CinemaDialog::utc_offset_minute () const
-{
-       int const sel = _utc_offset->GetSelection();
-       if (sel < 0 || sel > int(_offsets.size())) {
-               return 0;
-       }
-
-       return _offsets[sel].minute;
-}
-
-
 string
 CinemaDialog::notes () const
 {
index a878e605557cf7a6cf850b82f9d1ecc8728131fe..43f018f5e39912672c74512ca0e9011be4e35e2a 100644 (file)
@@ -38,16 +38,12 @@ public:
                wxString,
                std::string name = "",
                std::list<std::string> emails = std::list<std::string> (),
-               std::string notes = "",
-               int utc_offset_hour = 0,
-               int utc_offset_minute = 0
+               std::string notes = ""
                );
 
        std::string name () const;
        std::string notes () const;
        std::list<std::string> emails () const;
-       int utc_offset_hour () const;
-       int utc_offset_minute () const;
 
 private:
        std::vector<std::string> get_emails () const;
@@ -57,6 +53,4 @@ private:
        wxTextCtrl* _notes;
        EditableList<std::string, EmailDialog>* _email_list;
        std::vector<std::string> _emails;
-       wxChoice* _utc_offset;
-       std::vector<Offset> _offsets;
 };
index 3a5987eadcf5d95ffb9fe7010cc87e92491c1af7..7c9042de98e9066a43e3e4fdedeff0242712bfd3 100644 (file)
@@ -23,6 +23,7 @@
 LIBDCP_DISABLE_WARNINGS
 #include <wx/wx.h>
 LIBDCP_ENABLE_WARNINGS
+#include <boost/bind.hpp>
 #include <boost/optional.hpp>
 
 
@@ -37,6 +38,11 @@ public:
        void set(int index);
        boost::optional<int> get() const;
 
+       template <typename... Args>
+       void bind(Args... args) {
+               Bind(wxEVT_CHOICE, boost::bind(std::forward<Args>(args)...));
+       }
+
 private:
        bool _needs_clearing = true;
 };
index c7503048cea80331645afb15808949ca1a1316a0..279d37a94c3699b525e71092aecd3fbf26fc0dab 100644 (file)
@@ -160,7 +160,7 @@ DKDMDialog::make_clicked ()
        list<KDMWithMetadataPtr> kdms;
        try {
                for (auto i: _recipients->recipients()) {
-                       auto p = kdm_for_dkdm_recipient (film, _cpl->cpl(), i, _timing->from(), _timing->until());
+                       auto p = kdm_for_dkdm_recipient(film, _cpl->cpl(), i, _timing->from(), _timing->until());
                        if (p) {
                                kdms.push_back (p);
                        }
index 268573e6c35e5048699961279b59c87f806ee022..d5824c2b51e476f16e5ff74d77be66187333aa05 100644 (file)
@@ -172,7 +172,7 @@ KDMDialog::make_clicked ()
                }
 
                for (auto i: _screens->screens()) {
-                       auto p = kdm_for_screen (film, _cpl->cpl(), i, _timing->from(), _timing->until(), _output->formulation(), !_output->forensic_mark_video(), for_audio);
+                       auto p = kdm_for_screen(film, _cpl->cpl(), i, _timing->from(), _timing->until(), _output->formulation(), !_output->forensic_mark_video(), for_audio);
                        if (p) {
                                kdms.push_back (p);
                        }
index 0fd00de93bb866f45a065e78a83b82ec842aa390..7759d39496e8dec87e9c7cc4136fb85279605c6a 100644 (file)
 */
 
 
+#include "dcpomatic_choice.h"
 #include "kdm_timing_panel.h"
 #include "static_text.h"
 #include "time_picker.h"
 #include "wx_util.h"
 #include "lib/config.h"
+#include <dcp/utc_offset.h>
 #include <dcp/warnings.h>
 LIBDCP_DISABLE_WARNINGS
 #include <wx/datectrl.h>
@@ -105,6 +107,10 @@ KDMTimingPanel::KDMTimingPanel (wxWindow* parent)
 
        table->Add (_until_time, 0, wxALIGN_CENTER_VERTICAL);
 
+       add_label_to_sizer(table, this, _("UTC offset (time zone)"), true, 1, wxALIGN_CENTRE_VERTICAL);
+       _utc_offset = new Choice(this);
+       table->Add(_utc_offset, 0, wxALIGN_CENTRE_VERTICAL | wxLEFT, DCPOMATIC_SIZER_X_GAP);
+
        overall_sizer->Add (table, 0, wxTOP, DCPOMATIC_SIZER_GAP);
 
        _warning = new StaticText (this, wxT(""));
@@ -115,6 +121,17 @@ KDMTimingPanel::KDMTimingPanel (wxWindow* parent)
        _warning->SetForegroundColour (wxColour (255, 0, 0));
        _warning->SetFont(font);
 
+       /* Default to UTC */
+       size_t sel = 0;
+       for (size_t i = 0; i < _offsets.size(); ++i) {
+               _utc_offset->add(_offsets[i].name);
+               if (_offsets[i].hour == 0 && _offsets[i].minute == 0) {
+                       sel = i;
+               }
+       }
+
+       _utc_offset->set(sel);
+
        /* I said I've been to the year 3000.  Not much has changed but they live underwater.  And your In-in-in-interop DCP
            is pretty fine.
         */
@@ -125,33 +142,38 @@ KDMTimingPanel::KDMTimingPanel (wxWindow* parent)
        _until_date->Bind (wxEVT_DATE_CHANGED, bind (&KDMTimingPanel::changed, this));
        _from_time->Changed.connect (bind (&KDMTimingPanel::changed, this));
        _until_time->Changed.connect (bind (&KDMTimingPanel::changed, this));
+       _utc_offset->bind(&KDMTimingPanel::changed, this);
 
        SetSizer (overall_sizer);
 }
 
 
-boost::posix_time::ptime
+dcp::LocalTime
 KDMTimingPanel::from () const
 {
-       return posix_time (_from_date, _from_time);
+       return local_time(_from_date, _from_time, utc_offset());
 }
 
 
-boost::posix_time::ptime
-KDMTimingPanel::posix_time (wxDatePickerCtrl* date_picker, TimePicker* time_picker)
+dcp::LocalTime
+KDMTimingPanel::local_time(wxDatePickerCtrl* date_picker, TimePicker* time_picker, dcp::UTCOffset offset)
 {
        auto const date = date_picker->GetValue ();
-       return boost::posix_time::ptime (
-               boost::gregorian::date (date.GetYear(), date.GetMonth() + 1, date.GetDay()),
-               boost::posix_time::time_duration (time_picker->hours(), time_picker->minutes(), 0)
+       return dcp::LocalTime(
+               date.GetYear(),
+               date.GetMonth() + 1,
+               date.GetDay(),
+               time_picker->hours(),
+               time_picker->minutes(),
+               offset
                );
 }
 
 
-boost::posix_time::ptime
+dcp::LocalTime
 KDMTimingPanel::until () const
 {
-       return posix_time (_until_date, _until_time);
+       return local_time(_until_date, _until_time, utc_offset());
 }
 
 
@@ -173,3 +195,20 @@ KDMTimingPanel::changed () const
 
        TimingChanged ();
 }
+
+
+dcp::UTCOffset
+KDMTimingPanel::utc_offset() const
+{
+       auto const sel = _utc_offset->get();
+       if (!sel || *sel >= int(_offsets.size())) {
+               return {};
+       }
+
+       auto const& offset = _offsets[*sel];
+       int const minute_scale = offset.hour < 0 ? -1 : 1;
+
+       return { offset.hour, minute_scale * offset.minute };
+}
+
+
index 7221ba722f367eb40d47641af9d72c12fa63ab7b..f847992a7088155c3fd9d21e07185c9cb558efee 100644 (file)
@@ -18,6 +18,9 @@
 
 */
 
+
+#include "wx_util.h"
+#include <dcp/utc_offset.h>
 #include <dcp/warnings.h>
 LIBDCP_DISABLE_WARNINGS
 #include <wx/wx.h>
@@ -25,18 +28,19 @@ LIBDCP_ENABLE_WARNINGS
 #include <boost/date_time/posix_time/posix_time.hpp>
 #include <boost/signals2.hpp>
 
-class wxDatePickerCtrl;
+
+class Choice;
 class TimePicker;
+class wxDatePickerCtrl;
+
 
 class KDMTimingPanel : public wxPanel
 {
 public:
        explicit KDMTimingPanel (wxWindow* parent);
 
-       /** @return KDM from time in local time */
-       boost::posix_time::ptime from () const;
-       /** @return KDM until time in local time */
-       boost::posix_time::ptime until () const;
+       dcp::LocalTime from() const;
+       dcp::LocalTime until() const;
 
        bool valid () const;
 
@@ -44,11 +48,15 @@ public:
 
 private:
        void changed () const;
-       static boost::posix_time::ptime posix_time (wxDatePickerCtrl *, TimePicker *);
+       dcp::UTCOffset utc_offset() const;
+
+       static dcp::LocalTime local_time(wxDatePickerCtrl *, TimePicker *, dcp::UTCOffset offset);
 
        wxDatePickerCtrl* _from_date;
        wxDatePickerCtrl* _until_date;
        TimePicker* _from_time;
        TimePicker* _until_time;
+       Choice* _utc_offset;
        wxStaticText* _warning;
+       std::vector<Offset> _offsets;
 };
index 1d94d1acb9f8027a571403d1811698bdbb73f2ac..94a223fb63010a05f009dc9945d79c6d7f11973e 100644 (file)
@@ -234,7 +234,7 @@ ScreensPanel::add_cinema_clicked ()
        ScopeGuard sg = [dialog]() { dialog->Destroy(); };
 
        if (dialog->ShowModal() == wxID_OK) {
-               auto cinema = make_shared<Cinema>(dialog->name(), dialog->emails(), dialog->notes(), dialog->utc_offset_hour(), dialog->utc_offset_minute());
+               auto cinema = make_shared<Cinema>(dialog->name(), dialog->emails(), dialog->notes());
 
                auto cinemas = sorted_cinemas();
 
@@ -299,7 +299,7 @@ ScreensPanel::edit_cinema_clicked ()
        }
 
        auto dialog = new CinemaDialog(
-               GetParent(), _("Edit cinema"), cinema->name, cinema->emails, cinema->notes, cinema->utc_offset_hour(), cinema->utc_offset_minute()
+               GetParent(), _("Edit cinema"), cinema->name, cinema->emails, cinema->notes
                );
        ScopeGuard sg = [dialog]() { dialog->Destroy(); };
 
@@ -307,8 +307,6 @@ ScreensPanel::edit_cinema_clicked ()
                cinema->name = dialog->name();
                cinema->emails = dialog->emails();
                cinema->notes = dialog->notes();
-               cinema->set_utc_offset_hour(dialog->utc_offset_hour());
-               cinema->set_utc_offset_minute(dialog->utc_offset_minute());
                notify_cinemas_changed();
                auto item = cinema_to_item(cinema);
                DCPOMATIC_ASSERT(item);
index 884f3cb0867b8b5c90c622a8f53255c1fcdbc236..870c5d09e8c66120c9dc01162a1007b1f80264cd 100644 (file)
@@ -246,7 +246,7 @@ BOOST_AUTO_TEST_CASE (config_keep_cinemas_if_making_new_config)
 
        Config::instance()->write();
 
-       Config::instance()->add_cinema(make_shared<Cinema>("My Great Cinema", list<string>(), "", 0, 0));
+       Config::instance()->add_cinema(make_shared<Cinema>("My Great Cinema", list<string>(), ""));
        Config::instance()->write();
 
        boost::filesystem::copy_file (dir / "cinemas.xml", dir / "backup_for_test.xml");
index e300d82a5a04b8b481861ebb8ca47458b22ac791..c420be95feecf2680aad2bc35b8b16f95a20eeba 100644 (file)
@@ -86,13 +86,13 @@ setup_test_config()
        auto config = Config::instance();
        auto const cert = dcp::Certificate(dcp::file_to_string("test/data/cert.pem"));
 
-       auto cinema_a = std::make_shared<Cinema>("Dean's Screens", list<string>(), "", 0, 0);
+       auto cinema_a = std::make_shared<Cinema>("Dean's Screens", list<string>(), "");
        cinema_a->add_screen(std::make_shared<dcpomatic::Screen>("Screen 1", "", cert, boost::none, std::vector<TrustedDevice>()));
        cinema_a->add_screen(std::make_shared<dcpomatic::Screen>("Screen 2", "", cert, boost::none, std::vector<TrustedDevice>()));
        cinema_a->add_screen(std::make_shared<dcpomatic::Screen>("Screen 3", "", cert, boost::none, std::vector<TrustedDevice>()));
        config->add_cinema(cinema_a);
 
-       auto cinema_b = std::make_shared<Cinema>("Floyd's Celluloid", list<string>(), "", 0, 0);
+       auto cinema_b = std::make_shared<Cinema>("Floyd's Celluloid", list<string>(), "");
        cinema_b->add_screen(std::make_shared<dcpomatic::Screen>("Foo", "", cert, boost::none, std::vector<TrustedDevice>()));
        cinema_b->add_screen(std::make_shared<dcpomatic::Screen>("Bar", "", cert, boost::none, std::vector<TrustedDevice>()));
        config->add_cinema(cinema_b);
@@ -173,3 +173,39 @@ BOOST_AUTO_TEST_CASE(kdm_cli_select_screen)
 }
 
 
+BOOST_AUTO_TEST_CASE(kdm_cli_time)
+{
+       ConfigRestorer cr;
+
+       setup_test_config();
+
+       boost::filesystem::path kdm_filename = "build/test/KDM_Test_FTR-1_F-133_XX-XX_MOS_2K_20220109_SMPTE_OV_Deans_Screens_Screen_2.xml";
+
+       boost::system::error_code ec;
+       boost::filesystem::remove(kdm_filename, ec);
+
+       dcp::LocalTime now;
+       now.add_days(2);
+
+       vector<string> args = {
+               "kdm_cli",
+               "--verbose",
+               "--valid-from", now.as_string(),
+               "--valid-duration", "2 weeks",
+               "-c", "Dean's Screens",
+               "-S", "Screen 2",
+               "-o", "build/test",
+               "test/data/dkdm.xml"
+       };
+
+       vector<string> output;
+       auto error = run(args, output);
+       BOOST_CHECK(!error);
+
+       BOOST_REQUIRE_EQUAL(output.size(), 2U);
+       BOOST_CHECK(boost::algorithm::starts_with(output[0], "Making KDMs valid from"));
+       BOOST_CHECK_EQUAL(output[1], "Wrote 1 KDM files to build/test");
+
+       BOOST_CHECK(boost::filesystem::exists(kdm_filename));
+}
+
index dda30f68880f932f48eebf4a4f0aef895902f0c6..73e3d457edb1b0dd2381526bbdf7cd180bf74d26 100644 (file)
@@ -59,16 +59,14 @@ BOOST_AUTO_TEST_CASE (single_kdm_naming_test)
 
        auto crypt_cert = c->decryption_chain()->leaf();
 
-       /* Cinema A: UTC +4:30 */
-       auto cinema_a = make_shared<Cinema>("Cinema A", list<string>(), "", 4, 30);
+       auto cinema_a = make_shared<Cinema>("Cinema A", list<string>(), "");
        cinema_a_screen_1 = std::make_shared<dcpomatic::Screen>("Screen 1", "", crypt_cert, boost::none, vector<TrustedDevice>());
        cinema_a->add_screen (cinema_a_screen_1);
        cinema_a_screen_2 = std::make_shared<dcpomatic::Screen>("Screen 2", "", crypt_cert, boost::none, vector<TrustedDevice>());
        cinema_a->add_screen (cinema_a_screen_2);
        c->add_cinema (cinema_a);
 
-       /* Cinema B: UTC -1:00 */
-       auto cinema_b = make_shared<Cinema>("Cinema B", list<string>(), "", -1, 0);
+       auto cinema_b = make_shared<Cinema>("Cinema B", list<string>(), "");
        cinema_b_screen_x = std::make_shared<dcpomatic::Screen>("Screen X", "", crypt_cert, boost::none, vector<TrustedDevice>());
        cinema_b->add_screen (cinema_b_screen_x);
        cinema_b_screen_y = std::make_shared<dcpomatic::Screen>("Screen Y", "", crypt_cert, boost::none, vector<TrustedDevice>());
@@ -90,21 +88,20 @@ BOOST_AUTO_TEST_CASE (single_kdm_naming_test)
 
        auto sign_cert = c->signer_chain()->leaf();
 
-       dcp::LocalTime from (sign_cert.not_before());
+       dcp::LocalTime from = sign_cert.not_before();
+       from.set_offset({ 4, 30 });
        from.add_months (2);
-       dcp::LocalTime until (sign_cert.not_after());
+       dcp::LocalTime until = sign_cert.not_after();
+       until.set_offset({ 4, 30 });
        until.add_months (-2);
 
-       auto const from_string = from.date() + " " + from.time_of_day(true, false);
-       auto const until_string = until.date() + " " + until.time_of_day(true, false);
-
        auto cpl = cpls.front().cpl_file;
        auto kdm = kdm_for_screen (
                        film,
                        cpls.front().cpl_file,
                        cinema_a_screen_1,
-                       boost::posix_time::time_from_string(from_string),
-                       boost::posix_time::time_from_string(until_string),
+                       from,
+                       until,
                        dcp::Formulation::MODIFIED_TRANSITIONAL_1,
                        false,
                        optional<int>()
@@ -152,9 +149,6 @@ BOOST_AUTO_TEST_CASE (directory_kdm_naming_test, * boost::unit_test::depends_on(
        dcp::LocalTime until (sign_cert.not_after());
        until.add_months (-2);
 
-       string const from_string = from.date() + " " + from.time_of_day(true, false);
-       string const until_string = until.date() + " " + until.time_of_day(true, false);
-
        list<shared_ptr<dcpomatic::Screen>> screens = {
                cinema_a_screen_2, cinema_b_screen_x, cinema_a_screen_1, (cinema_b_screen_z)
        };
@@ -168,8 +162,8 @@ BOOST_AUTO_TEST_CASE (directory_kdm_naming_test, * boost::unit_test::depends_on(
                                film,
                                cpls.front().cpl_file,
                                i,
-                               boost::posix_time::time_from_string(from_string),
-                               boost::posix_time::time_from_string(until_string),
+                               from,
+                               until,
                                dcp::Formulation::MODIFIED_TRANSITIONAL_1,
                                false,
                                optional<int>()