21#include "pism/util/Units.hh"
27#include "pism/external/calcalcs/utCalendar2_cal.h"
29#include "pism/external/calcalcs/calcalcs.h"
31#include "pism/util/error_handling.hh"
32#include "pism/util/pism_utilities.hh"
39 Impl(
const std::string &path) {
42 ut_set_error_message_handler(ut_ignore);
44 if (not path.empty()) {
45 tmp = ut_read_xml(path.c_str());
47 tmp = ut_read_xml(NULL);
53 ut_set_error_message_handler(ut_write_to_stderr);
77 const std::string &spec1,
const std::string &spec2) {
86 unit = ut_parse(sys->m_impl->system, spec.c_str(), UT_ASCII);
90 "unit specification '%s' is unknown or invalid",
126 if (
this == &other) {
136 if (
m_impl->unit == NULL) {
144 return ut_are_convertible(
m_impl->unit, other.
m_impl->unit) != 0;
148 return m_impl->unit_string;
151Unit::operator std::string()
const {
152 return m_impl->unit_string;
166 int year, month, day, hour, minute;
170 &hour, &minute, &second,
174 "cannot convert time %f to a date in calendar %s",
177 return {year, month, day, hour, minute, second,
calendar};
190 "cannot convert date and time %d-%d-%d %d:%d:%f to time in calendar %s",
204 Unit u1(sys, spec1), u2(sys, spec2);
206 if (not u1.is_convertible(u2)) {
208 "cannot convert '%s' to '%s'",
209 spec1.c_str(), spec2.c_str());
215 "cannot create a converter from %s to %s",
216 spec1.c_str(), spec2.c_str());
229 "failed to create a converter from '%s' to '%s'",
246 const std::string &spec1,
const std::string &spec2) {
259 return cv_convert_double(
m_impl->converter, input);
263 cv_convert_doubles(
m_impl->converter, data, length, data);
267 const std::string &calendar_) {
282 std::string date, time;
284 auto parts =
split(spec,
' ');
289 "expected a date-time spec consisting of date and time separated by spaces; got '%s'",
294 if (parts.size() == 1) {
296 }
else if (parts.size() == 2) {
301 "expected a date-time spec consisting of date and time separated by spaces; got '%s'",
306 std::vector<int> date_parts = { 0, 0, 0 };
307 std::vector<int> time_parts = { 0, 0, 0 };
313 bool year_is_negative = (date[0] ==
'-');
315 if (year_is_negative and not
set_member(input_calendar, {
"365_day",
"360_day",
"noleap" })) {
318 "negative dates such as '%s' are not allowed with the '%s' calendar.\n"
319 "Please submit a bug report if this is a problem.",
320 date.c_str(), input_calendar.c_str());
323 auto parts =
split(date,
'-');
324 if (parts.size() != 3) {
327 "date string has to contain 3 parts separated by '-' (Y-M-D); got '%s'", spec.c_str());
330 for (
int k = 0;
k < 3; ++
k) {
333 if (
n > std::numeric_limits<int>::max() or
n < std::numeric_limits<int>::min()) {
338 date_parts[
k] =
static_cast<int>(
n);
341 date_parts[0] *= year_is_negative ? -1 : 1;
346 auto parts =
split(time,
':');
347 if (parts.size() < 2 or parts.size() > 3) {
350 "time string has to contain 2 or 3 parts separated by ':' (H:M:S); got '%s'",
354 for (
int k = 0;
k < (
int)parts.size(); ++
k) {
357 if (
n > std::numeric_limits<int>::max() or
n < std::numeric_limits<int>::min()) {
362 time_parts[
k] =
static_cast<int>(
n);
366 e.
add_context(
"parsing a date-time specification %s", spec.c_str());
371 year = date_parts[0];
372 month = date_parts[1];
374 hour = time_parts[0];
385 input_calendar.c_str());
399 if (hour < 0 or hour > 23) {
404 if (minute < 0 or minute > 59) {
409 if (second < 0.0 or second >= 60.0) {
void ccs_free_calendar(calcalcs_cal *cc)
int ccs_date2jday(calcalcs_cal *calendar, int year, int month, int day, int *jday)
calcalcs_cal * ccs_init_calendar(const char *calname)
void add_context(const std::string &message)
Add a message providing some context. This way we can (sort of) get a stack trace even though C++ exc...
static RuntimeError formatted(const ErrorLocation &location, const char format[],...) __attribute__((format(printf
build a RuntimeError with a formatted message
std::shared_ptr< Impl > m_impl
double operator()(double input) const
void convert_doubles(double *data, size_t length) const
System(const std::string &path="")
std::shared_ptr< Impl > m_impl
std::shared_ptr< System > Ptr
double time(const DateTime &d) const
DateTime date(double T, const std::string &calendar) const
Unit & operator=(const Unit &other)
System::Ptr system() const
std::string string() const
Unit(System::Ptr system, const std::string &spec)
std::shared_ptr< Impl > m_impl
bool is_convertible(const Unit &other) const
#define PISM_ERROR_LOCATION
bool are_convertible(const Unit &u1, const Unit &u2)
double convert(System::Ptr system, double input, const std::string &spec1, const std::string &spec2)
Convert a quantity from unit1 to unit2.
std::string string_strip(const std::string &input)
static std::string calendar(const File *input_file, const Config &config, const Logger &log)
long int parse_integer(const std::string &input)
bool set_member(const std::string &string, const std::set< std::string > &set)
std::vector< std::string > split(const std::string &input, char separator)
Transform a separator-separated list (a string) into a vector of strings.
Impl(const Unit &u1, const Unit &u2)
Impl(System::Ptr sys, const std::string &spec1, const std::string &spec2)
DateTime(int year_, int month_, int day_, int hour_, int minute_, double second_, const std::string &calendar_)
Impl(const std::string &path)
Impl(System::Ptr sys, const std::string &spec)
Impl(const Unit::Impl &other)
int utCalendar2_cal(double val, ut_unit *dataunits, int *year, int *month, int *day, int *hour, int *minute, double *second, const char *calendar_name)
int utInvCalendar2_cal(int year, int month, int day, int hour, int minute, double second, ut_unit *user_unit, double *value, const char *calendar_name)