Ошибка SimpleDateFormat при разборе даты yyyyMMdd формат java [duplicate]

Я обнаружил, что Google C ++ Testing Framework содержит хорошую кросс-платформенную реализацию на основе шаблонов AlmostEqual2sComplement, которая работает как по удвоению, так и по плаванию. Учитывая, что он выпущен под лицензией BSD, использование его в вашем собственном коде не должно быть проблемой, если вы сохраняете лицензию. Я извлек приведенный ниже код из http://code.google.com/p/googletest/source/browse/trunk/include/gtest/internal/gtest-internal.h https: / /github.com/google/googletest/blob/master/googletest/include/gtest/internal/gtest-internal.h и добавила лицензию сверху.

Обязательно #define GTEST_OS_WINDOWS к некоторому значению (или изменить код, в котором он используется для чего-то, что соответствует вашей кодовой базе), это лицензия BSD в конце концов).

Пример использования:

double left  = // something
double right = // something
const FloatingPoint lhs(left), rhs(right);

if (lhs.AlmostEquals(rhs)) {
  //they're equal!
}

Вот код:

// Copyright 2005, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee)
//
// The Google C++ Testing Framework (Google Test)


// This template class serves as a compile-time function from size to
// type.  It maps a size in bytes to a primitive type with that
// size. e.g.
//
//   TypeWithSize<4>::UInt
//
// is typedef-ed to be unsigned int (unsigned integer made up of 4
// bytes).
//
// Such functionality should belong to STL, but I cannot find it
// there.
//
// Google Test uses this class in the implementation of floating-point
// comparison.
//
// For now it only handles UInt (unsigned int) as that's all Google Test
// needs.  Other types can be easily added in the future if need
// arises.
template 
class TypeWithSize {
 public:
  // This prevents the user from using TypeWithSize with incorrect
  // values of N.
  typedef void UInt;
};

// The specialization for size 4.
template <>
class TypeWithSize<4> {
 public:
  // unsigned int has size 4 in both gcc and MSVC.
  //
  // As base/basictypes.h doesn't compile on Windows, we cannot use
  // uint32, uint64, and etc here.
  typedef int Int;
  typedef unsigned int UInt;
};

// The specialization for size 8.
template <>
class TypeWithSize<8> {
 public:
#if GTEST_OS_WINDOWS
  typedef __int64 Int;
  typedef unsigned __int64 UInt;
#else
  typedef long long Int;  // NOLINT
  typedef unsigned long long UInt;  // NOLINT
#endif  // GTEST_OS_WINDOWS
};


// This template class represents an IEEE floating-point number
// (either single-precision or double-precision, depending on the
// template parameters).
//
// The purpose of this class is to do more sophisticated number
// comparison.  (Due to round-off error, etc, it's very unlikely that
// two floating-points will be equal exactly.  Hence a naive
// comparison by the == operation often doesn't work.)
//
// Format of IEEE floating-point:
//
//   The most-significant bit being the leftmost, an IEEE
//   floating-point looks like
//
//     sign_bit exponent_bits fraction_bits
//
//   Here, sign_bit is a single bit that designates the sign of the
//   number.
//
//   For float, there are 8 exponent bits and 23 fraction bits.
//
//   For double, there are 11 exponent bits and 52 fraction bits.
//
//   More details can be found at
//   http://en.wikipedia.org/wiki/IEEE_floating-point_standard.
//
// Template parameter:
//
//   RawType: the raw floating-point type (either float or double)
template 
class FloatingPoint {
 public:
  // Defines the unsigned integer type that has the same size as the
  // floating point number.
  typedef typename TypeWithSize::UInt Bits;

  // Constants.

  // # of bits in a number.
  static const size_t kBitCount = 8*sizeof(RawType);

  // # of fraction bits in a number.
  static const size_t kFractionBitCount =
    std::numeric_limits::digits - 1;

  // # of exponent bits in a number.
  static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount;

  // The mask for the sign bit.
  static const Bits kSignBitMask = static_cast(1) << (kBitCount - 1);

  // The mask for the fraction bits.
  static const Bits kFractionBitMask =
    ~static_cast(0) >> (kExponentBitCount + 1);

  // The mask for the exponent bits.
  static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask);

  // How many ULP's (Units in the Last Place) we want to tolerate when
  // comparing two numbers.  The larger the value, the more error we
  // allow.  A 0 value means that two numbers must be exactly the same
  // to be considered equal.
  //
  // The maximum error of a single floating-point operation is 0.5
  // units in the last place.  On Intel CPU's, all floating-point
  // calculations are done with 80-bit precision, while double has 64
  // bits.  Therefore, 4 should be enough for ordinary use.
  //
  // See the following article for more details on ULP:
  // http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm.
  static const size_t kMaxUlps = 4;

  // Constructs a FloatingPoint from a raw floating-point number.
  //
  // On an Intel CPU, passing a non-normalized NAN (Not a Number)
  // around may change its bits, although the new value is guaranteed
  // to be also a NAN.  Therefore, don't expect this constructor to
  // preserve the bits in x when x is a NAN.
  explicit FloatingPoint(const RawType& x) { u_.value_ = x; }

  // Static methods

  // Reinterprets a bit pattern as a floating-point number.
  //
  // This function is needed to test the AlmostEquals() method.
  static RawType ReinterpretBits(const Bits bits) {
    FloatingPoint fp(0);
    fp.u_.bits_ = bits;
    return fp.u_.value_;
  }

  // Returns the floating-point number that represent positive infinity.
  static RawType Infinity() {
    return ReinterpretBits(kExponentBitMask);
  }

  // Non-static methods

  // Returns the bits that represents this number.
  const Bits &bits() const { return u_.bits_; }

  // Returns the exponent bits of this number.
  Bits exponent_bits() const { return kExponentBitMask & u_.bits_; }

  // Returns the fraction bits of this number.
  Bits fraction_bits() const { return kFractionBitMask & u_.bits_; }

  // Returns the sign bit of this number.
  Bits sign_bit() const { return kSignBitMask & u_.bits_; }

  // Returns true iff this is NAN (not a number).
  bool is_nan() const {
    // It's a NAN if the exponent bits are all ones and the fraction
    // bits are not entirely zeros.
    return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0);
  }

  // Returns true iff this number is at most kMaxUlps ULP's away from
  // rhs.  In particular, this function:
  //
  //   - returns false if either number is (or both are) NAN.
  //   - treats really large numbers as almost equal to infinity.
  //   - thinks +0.0 and -0.0 are 0 DLP's apart.
  bool AlmostEquals(const FloatingPoint& rhs) const {
    // The IEEE standard says that any comparison operation involving
    // a NAN must return false.
    if (is_nan() || rhs.is_nan()) return false;

    return DistanceBetweenSignAndMagnitudeNumbers(u_.bits_, rhs.u_.bits_)
        <= kMaxUlps;
  }

 private:
  // The data type used to store the actual floating-point number.
  union FloatingPointUnion {
    RawType value_;  // The raw floating-point number.
    Bits bits_;      // The bits that represent the number.
  };

  // Converts an integer from the sign-and-magnitude representation to
  // the biased representation.  More precisely, let N be 2 to the
  // power of (kBitCount - 1), an integer x is represented by the
  // unsigned number x + N.
  //
  // For instance,
  //
  //   -N + 1 (the most negative number representable using
  //          sign-and-magnitude) is represented by 1;
  //   0      is represented by N; and
  //   N - 1  (the biggest number representable using
  //          sign-and-magnitude) is represented by 2N - 1.
  //
  // Read http://en.wikipedia.org/wiki/Signed_number_representations
  // for more details on signed number representations.
  static Bits SignAndMagnitudeToBiased(const Bits &sam) {
    if (kSignBitMask & sam) {
      // sam represents a negative number.
      return ~sam + 1;
    } else {
      // sam represents a positive number.
      return kSignBitMask | sam;
    }
  }

  // Given two numbers in the sign-and-magnitude representation,
  // returns the distance between them as an unsigned number.
  static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits &sam1,
                                                     const Bits &sam2) {
    const Bits biased1 = SignAndMagnitudeToBiased(sam1);
    const Bits biased2 = SignAndMagnitudeToBiased(sam2);
    return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1);
  }

  FloatingPointUnion u_;
};

EDIT: Это сообщение 4 года. Это, вероятно, все еще актуально, и код хорош, но некоторые люди нашли улучшения. Лучше всего получить последнюю версию AlmostEquals прямо из исходного кода Google Test, а не ту, которую я вставил здесь.

262
задан Stéphane Bonniez 5 December 2008 в 17:23
поделиться

16 ответов

Это просто часть ужасающего беспорядка, который является API дат / времени Java. Перечислить, что с ним не так, займет очень много времени (и я уверен, что не знаю половины проблем). Разумеется, работа с датами и временем сложна, но все равно.

Сделайте себе одолжение и используйте Joda Time вместо, или, возможно, JSR-310 .

EDIT: Что касается причин, почему, как отмечено в других ответах, это может быть связано с старыми API-интерфейсами C или просто с общим чувством начала всего от 0 ... за исключением того, что дни начинаются с 1 , конечно. Я сомневаюсь, что кто-то, кто не был в оригинальной команде по внедрению, действительно мог объяснить причины, - но опять же я настоятельно призывал читателей не беспокоиться о том, почему были приняты плохие решения, чтобы посмотреть на всю гамму гадости в java.util.Calendar и найти что-то лучшее.

Одна точка, которая является в пользу использования индексов на основе 0, состоит в том, что она облегчает такие вещи, как «массивы имен»:

// I "know" there are 12 months
String[] monthNames = new String[12]; // and populate...
String name = monthNames[calendar.get(Calendar.MONTH)];

Конечно, это не удается, как только вы получите календарь с 13 месяцами ... но, по крайней мере, указанный размер - это количество ожидаемых месяцев.

Это isn ' t a хорошая причина, но это причина ...

EDIT: В качестве комментариев типа запросов есть некоторые идеи о том, что я считаю неправильным Дата / Календарь:

  • Удивительные базы (1900 как база года в дате, по общему признанию, для устаревших конструкторов, 0 в качестве базы месяца в обоих)
  • Возможность использования - с использованием неизменяемых типов делает намного проще работать с тем, что действительно эффективно значения
  • Недостаточный набор типов: приятно иметь Date и Calendar как разные вещи, но отсутствует разделение «локальных» и «зональных» значений, а также дата / время и дата против времени
  • API, который приводит к уродливому коду с магическими константами вместо явно названных методов
  • API, о котором очень сложно рассуждать - все дело о том, когда вещи пересчитываются и т. д.
  • Использование конструкторов без параметров по умолчанию для «сейчас», что приводит к жесткому тестированию кода
  • Реализация Date.toString(), которая всегда использует локальный часовой пояс системы (это путают многие Stack Переполнение пользователей до сих пор)
297
ответ дан assylias 22 August 2018 в 01:49
поделиться
  • 1
    ... и что с устаревшими всеми полезными простыми методами Date? Теперь я должен использовать этот ужасный объект Calendar сложными способами, чтобы делать вещи, которые были простыми. – Brian Knoblauch 5 December 2008 в 18:22
  • 2
    @Brian: Я чувствую твою боль. Опять же, Joda Time проще :) (Фактор неизменности делает работу намного приятнее для работы). – Jon Skeet 5 December 2008 в 18:26
  • 3
    Downvoters: причины? – Jon Skeet 13 May 2009 в 22:52
  • 4
    Вы не ответили на вопрос. – Zeemee 2 February 2012 в 15:38
  • 5
    @ user443854: Я перечислил некоторые моменты в редактировании - посмотрите, помогает ли это. – Jon Skeet 9 February 2012 в 16:23

В Java 8 появился новый API Дата / Время JSR 310 , который более разумен. Спектр свинца такой же, как и основной автор JodaTime, и они имеют много похожих концепций и шаблонов.

9
ответ дан Alex Miller 22 August 2018 в 01:49
поделиться
  • 1
    Новый API Date Time теперь является частью Java 8 – mschenk74 25 May 2013 в 19:16

Поскольку математика с месяцами намного проще.

1 месяц после декабря - январь, но, чтобы понять это, вы должны были бы взять номер месяца и сделать математику

12 + 1 = 13 // What month is 13?

Я знаю! Я могу исправить это быстро, используя модуль 12.

(12 + 1) % 12 = 1

Это работает отлично в течение 11 месяцев до ноября ...

(11 + 1) % 12 = 0 // What month is 0?

Вы можете сделать все это работаем снова, вычитая 1 до того, как вы добавите месяц, затем выполните свой модуль и, наконец, добавьте 1 обратно ... aka обходите основную проблему.

((11 - 1 + 1) % 12) + 1 = 12 // Lots of magical numbers!

Теперь давайте подумаем о проблеме с месяцами 0 - 11.

(0 + 1) % 12 = 1 // February
(1 + 1) % 12 = 2 // March
(2 + 1) % 12 = 3 // April
(3 + 1) % 12 = 4 // May
(4 + 1) % 12 = 5 // June
(5 + 1) % 12 = 6 // July
(6 + 1) % 12 = 7 // August
(7 + 1) % 12 = 8 // September
(8 + 1) % 12 = 9 // October
(9 + 1) % 12 = 10 // November
(10 + 1) % 12 = 11 // December
(11 + 1) % 12 = 0 // January

Все месяцы работают одинаково, а работа вокруг не нужна.

32
ответ дан arucker 22 August 2018 в 01:49
поделиться
  • 1
    Это удовлетворительно. По крайней мере, есть какое-то значение для этого безумия! – moljac024 9 July 2014 в 10:34
  • 2
    & quot; Множество магических чисел & quot; - Нет, это просто один, который появляется дважды. – user123444555621 20 August 2014 в 23:53
  • 3
    Возвращение в месяц по-прежнему довольно нехорошо, однако, благодаря неудачному использованию C «остатка», скорее, чем «модуль». Я также не уверен, как часто нужно набирать месяц без корректировки года, а месяцы идут 1-12, не создает проблем с `while (month & gt; 12) {month- = 12; год ++;} – supercat 14 September 2015 в 22:32
  • 4
    Поскольку разумные функции, такие как DateTime.AddMonths, слишком сложны для правильной реализации в lib, мы должны выполнить математику, которую вы описали сами ... Mmmmmkay – nsimeonov 30 March 2016 в 06:19
  • 5
    Кто-то делает математику где-то. Просто потому, что система имеет функцию, определенную для математики в месяцах, не означает, что одна и та же математика не была выполнена. Мой пример не состоял в том, чтобы побуждать людей делать математику в течение нескольких месяцев, это было только для того, чтобы показать, что я объявляю январь как 0. – arucker 11 April 2016 в 18:39

tl; dr

Month.FEBRUARY.getValue()  // February → 2.

2

Подробности

Ответа на этот вопрос Jon Skeet .

Теперь у нас есть современная замена для этих неприятных старых устаревших классов времени: классы java.time .

java.time.Month

Среди этих классов есть Month перечисление . Перечисление содержит один или несколько предопределенных объектов, объекты, которые автоматически создаются при загрузке класса. На Month у нас есть дюжина таких объектов, каждая из которых имеет имя: JANUARY , FEBRUARY, MARCH и т. Д. Каждая из них является константой класса static final public. Вы можете использовать и передавать эти объекты в любом месте вашего кода. Пример: someMethod( Month.AUGUST )

К счастью, они имеют правильную нумерацию, 1-12, где 1 - январь, а 12 - декабрь.

Получить объект Month для определенного месяца ( 1-12).

Month month = Month.of( 2 );  // 2 → February.

. Идя в другом направлении, спросите объект Month для его номера месяца.

int monthNumber = Month.FEBRUARY.getValue();  // February → 2.

Многие другие удобные методы для этого класса, такие как зная количество дней в каждом месяце . Класс может даже генерировать локализованное имя месяца.

Вы можете получить локализованное имя месяца в разных длинах или аббревиатурах.

String output = 
    Month.FEBRUARY.getDisplayName( 
        TextStyle.FULL , 
        Locale.CANADA_FRENCH 
    );
< blockquote>

février

Кроме того, вы должны передавать объекты этого перечисления вокруг своей базы кода, а не просто целые числа. Это обеспечивает безопасность типов, обеспечивает допустимый диапазон значений и делает ваш код более самодокументированным. См. Учебник Oracle , если он не знаком с удивительно мощным средством перечисления в Java.

Вы также можете найти полезные Year и YearMonth .


О java.time

Структура java.time встроена в Java 8 и более поздние версии. Эти классы вытесняют неприятные старые классы времени legacy , такие как java.util.Date , .Calendar и & amp; java.text.SimpleDateFormat .

Проект Joda-Time , теперь в режиме обслуживания , советует перейти на java.time.

Чтобы узнать больше, см. учебное пособие Oracle . И поиск Stack Overflow для многих примеров и объяснений. Спецификация JSR 310 .

Где получить классы java.time?

  • Java SE 8 и SE 9 и более поздние версии. Часть стандартного Java API с интегрированной реализацией. Java 9 добавляет некоторые незначительные функции и исправления.
  • Java SE 6 и SE 7 Большая часть функций java.time возвращается в Java 6 & amp; 7 в ThreeTen-Backport .
  • Android Проект ThreeTenABP адаптирует ThreeTen-Backport (упомянутый выше) специально для Android. См. Как использовать ... .

Проект ThreeTen-Extra расширяет java.time с дополнительными классами. Этот проект является доказательством возможных будущих дополнений к java.time. Здесь вы можете найти полезные классы, такие как Interval , YearWeek , YearQuarter и more .

3
ответ дан Basil Bourque 22 August 2018 в 01:49
поделиться

Потому что программисты одержимы индексами 0. Хорошо, это немного сложнее: имеет смысл, когда вы работаете с логикой более низкого уровня, чтобы использовать индексирование на основе 0. Но по большому счету, я по-прежнему придерживаюсь своего первого предложения.

5
ответ дан Dinah 22 August 2018 в 01:49
поделиться
  • 1
    Это еще одна из тех идиом / привычек, которые идут way обратно на ассемблер или машинный язык, где все делается с точки зрения смещений, а не индексов. Обозначение массива стало сокращением для доступа к смежным блокам, начиная со смещения 0. – Ken Gentle 5 December 2008 в 18:56

Для меня никто не объясняет это лучше, чем mindpro.com :

Gotchas

java.util.GregorianCalendar имеет гораздо меньше ошибок и ошибок, чем класс old java.util.Date, но он все еще не является пикником.

Если бы были программисты, когда впервые предлагалось переход на летнее время, они наложили бы вето на него как на безумного и неразрешимого. При дневном свете существует фундаментальная двусмысленность. Осенью, когда вы устанавливаете часы на один час в 2 часа ночи, есть два разных момента времени, которые называются 1:30 AM по местному времени. Вы можете рассказать им обособленно, только если вы записываете, планируете ли вы дневное или стандартное время с чтением.

К сожалению, нет способа рассказать GregorianCalendar, который вы намеревались. Вы должны прибегать к тому, чтобы сообщать местное время с манекеном UTC TimeZone, чтобы избежать двусмысленности. Программисты обычно закрывают глаза на эту проблему и просто надеются, что в этот час никто ничего не делает.

Ошибка тысячелетия. Ошибки все еще не из классов Calendar. Даже в JDK (Java Development Kit) 1.3 есть ошибка 2001 года. Рассмотрим следующий код:

GregorianCalendar gc = new GregorianCalendar();
gc.setLenient( false );
/* Bug only manifests if lenient set false */
gc.set( 2001, 1, 1, 1, 0, 0 );
int year = gc.get ( Calendar.YEAR );
/* throws exception */

Ошибка исчезла в 7 утра 2001/01/01 для MST.

GregorianCalendar управляется гигантским кучей нетипизированной int magic константы. Этот метод полностью уничтожает любую надежду на проверку ошибок во время компиляции. Например, чтобы получить месяц, который вы используете, GregorianCalendar. get(Calendar.MONTH));

GregorianCalendar имеет исходный GregorianCalendar.get(Calendar.ZONE_OFFSET) и дневной сбережения GregorianCalendar. get( Calendar. DST_OFFSET), но не позволяет получить фактическое смещение часового пояса. Вы должны получить эти два отдельно и добавить их вместе.

GregorianCalendar.set( year, month, day, hour, minute) не устанавливает секунды в 0.

DateFormat и GregorianCalendar не сливаются должным образом. Вы должны указать календарь дважды, косвенно, как Date.

Если пользователь не настроил свой часовой пояс правильно, он по умолчанию будет тихо или PST или GMT.

В GregorianCalendar, Месяцы нумеруются начиная с января = 0, а не 1, как это делают все остальные на планете. Но дни начинаются с 1, как и дни недели с воскресеньем = 1, понедельник = 2, ... Суббота = 7. Тем не менее DateFormat. parse ведет себя традиционным способом с января = 1.

1
ответ дан Edwin Dalorzo 22 August 2018 в 01:49
поделиться

Он точно не определен как нуль как таковой, он определен как Calendar.January. Это проблема использования ints как констант, а не перечислений. Calendar.January == 0.

0
ответ дан Pål GD 22 August 2018 в 01:49
поделиться
  • 1
    Значения одно и то же. API также могут возвращать 0, он идентичен константе. Calendar.JANUARY можно было бы определить как 1 - вот и все. Перечисление было бы хорошим решением, но настоящие перечисления не были добавлены к языку до Java 5, а Date - с самого начала. Это несчастливо, но вы действительно не можете «исправить». такой фундаментальный API, когда используется сторонний код. Лучшее, что можно сделать, это предоставить новый API и отказаться от старого, чтобы побудить людей двигаться дальше. Спасибо, Java 7 ... – Quinn Taylor 11 August 2009 в 04:20

Лично я принял странность API календаря Java как признак того, что мне нужно было отвлечься от григорианского мышления и попытаться более агрессивно программировать в этом отношении. В частности, я снова научился избегать жестко закодированных констант для таких вещей, как месяцы.

Какое из следующего, скорее всего, будет правильным?

if (date.getMonth() == 3) out.print("March");

if (date.getMonth() == Calendar.MARCH) out.print("March");

Это иллюстрирует одну вещь, которая раздражает меня немного о Joda Time - это может побудить программистов думать в терминах жестко закодированных констант. (Только немного, но это не так, как если бы Joda заставлял программистов плохо программировать.)

4
ответ дан Paul Brinkley 22 August 2018 в 01:49
поделиться
  • 1
    Но какая схема с большей вероятностью доставит вам головную боль, когда в вашем коде нет константы - у вас есть значение, которое является результатом вызова веб-службы или чего-то еще. – Jon Skeet 5 December 2008 в 18:28
  • 2
    Разумеется, этот вызов веб-службы также должен использовать эту константу. :-) То же самое относится к любому внешнему абоненту. Как только мы установили, что существует несколько стандартов, необходимо обеспечить соблюдение одного из них. (Надеюсь, я понял ваш комментарий ...) – Paul Brinkley 5 December 2008 в 19:59
  • 3
    Да, мы должны обеспечить соблюдение стандарта, который использует почти все остальное в мире, когда выражают месяцы - стандарт на основе 1. – Jon Skeet 5 December 2008 в 20:20
  • 4
    Ключевое слово здесь «почти». Очевидно, что Jan = 1 и т. Д. Чувствуют себя естественными в системе дат с чрезвычайно широким использованием, но почему позволяют себе делать исключение, чтобы избежать жестко закодированных констант, даже в этом случае? – Paul Brinkley 5 December 2008 в 22:34
  • 5
    Потому что это облегчает жизнь. Это просто так. Я никогда не сталкивался с проблемой "один за другим" в системе с 1 месяцем. Я видел множество таких ошибок с Java API. Игнорировать то, что делают все остальные в мире, просто бессмысленно. – Jon Skeet 6 December 2008 в 08:33

Возможно, потому, что C "struct tm" делает то же самое.

9
ответ дан Paul Tomblin 22 August 2018 в 01:49
поделиться

На это было много ответов, но я дам свое мнение по этому вопросу в любом случае. Причина этого нечетного поведения, как говорилось ранее, исходит от POSIX C time.h, где указаны месяцы, когда они хранятся в int с диапазоном 0-11. Чтобы объяснить, почему, посмотрите на это так: годы и дни считаются числами на разговорном языке, но у месяцев есть свои имена. Поэтому, поскольку январь является первым месяцем, он будет сохранен как смещение 0, первый элемент массива. monthname[JANUARY] будет "January". Первый месяц в году - это элемент массива первого месяца.

Число дней, с другой стороны, поскольку у них нет имен, сохранение их в int как 0-30 будет путать, добавьте много day+1 инструкций для вывода и, конечно же, склонны к большому количеству ошибок.

Как говорится, непоследовательность запутывает, особенно в javascript (который также унаследовал эту «функцию») язык сценариев, где это должно быть абстрагировано далеко от langague.

TL; DR: Поскольку в месяцах имена и дни месяца этого не делают.

22
ответ дан piksel bitworks 22 August 2018 в 01:49
поделиться
  • 1
    "месяца имеют имена и дни нет. & quot; Когда-либо слышали о« пятнице »? ;) Хорошо. Я предполагаю, что вы имели в виду «.. дней месяца нет» - возможно, он заплатит за редактирование вашего (иначе хорошего) ответа. :-) – Andrew Thompson 24 August 2011 в 18:11
  • 2
    Лучше отображается 0/0/0000 как «00-янв-0000». или как «00-XXX-0000»? ИМХО, много кода было бы более чистым, если бы было тринадцать месяцев " но месяц 0 получил фиктивное имя. – supercat 14 September 2015 в 22:29
  • 3
    Это интересное занятие, но 0/0/0000 не является допустимой датой. как бы вы сделали 40/40/0000? – piksel bitworks 15 September 2015 в 19:09

В дополнение к ответу ланды DannySmurf, я добавлю, что это побуждает вас использовать константы, такие как Calendar.JANUARY.

0
ответ дан Powerlord 22 August 2018 в 01:49
поделиться
  • 1
    Это очень хорошо, когда вы явно пишете код за определенный месяц, но это боль, когда у вас есть месяц в «нормальном» состоянии. формы из другого источника. – Jon Skeet 5 December 2008 в 18:11
  • 2
    Это также боль, когда вы пытаетесь напечатать значение месяца в определенном порядке - вы всегда добавляете 1 к нему. – Brian Warshaw 11 February 2011 в 13:39

Языки C на языке копируют C в некоторой степени. Структура tm (определенная в time.h) имеет целочисленное поле tm_mon с диапазоном (commented) от 0-11.

Языки на языке C начинаются с массива с индексом 0. Так что это было удобно для вывода строки в массиве имен месяцев, а tm_mon в качестве индекса.

34
ответ дан stesch 22 August 2018 в 01:49
поделиться

Поскольку все начинается с 0. Это основной факт программирования на Java. Если бы одно было отклоняться от этого, то это привело бы к целому путанице. Давайте не будем спорить о их формировании и кодексах с ними.

-2
ответ дан Syrrus 22 August 2018 в 01:49
поделиться
  • 1
    Нет, большинство вещей в реальном мире начинаются с 1. Offsets начинаются с 0, а месяц в году не является смещением, это один из двенадцати, точно так же, как день месяца является одним из 31 или 30 или 29 или 28. Рассмотрение месяца в качестве смещения является просто капризным, особенно если в то же время мы не рассматриваем день месяца таким же образом. В чем причина этой разницы? – SantiBailors 8 February 2016 в 14:27
  • 2
    в реальном мире начните с 1, В мире Java начните с 0. НО ... Я думаю, это потому, что: - для расчета дня недели он не может быть компенсирован для нескольких вычислений без добавления еще нескольких шагов к это ... - дополнительно показывает полные дни месяца, если это необходимо (без путаницы или необходимости проверять февраль). В течение месяца он заставляет вас выводить в формате даты, который следует использовать в любом случае. Кроме того, поскольку количество месяцев в году является регулярным, а дни в месяц - это не имеет смысла, если вам нужно объявить массивы и использовать смещение, чтобы лучше соответствовать массиву. – Syrrus 7 April 2016 в 21:04

Я бы сказал, лень. Массивы начинаются с 0 (все это знают); месяцы года - это массив, который заставляет меня поверить, что какой-то инженер из Sun просто не потрудился поставить эту маленькую мелочь в код Java.

9
ответ дан TheSmurf 22 August 2018 в 01:49
поделиться
  • 1
    вы можете назвать его эффективностью. – Milhous 5 December 2008 в 17:31
  • 2
    Нет, я бы не стал. Более важно оптимизировать эффективность своих клиентов, чем программистов. Поскольку этот клиент проводит здесь время, они не справились с этим. – TheSmurf 5 December 2008 в 17:45
  • 3
    Это абсолютно не связано с эффективностью - это не так, как если бы месяцы хранились в массиве, и вам понадобится 13, чтобы представлять 12 месяцев. Речь идет не о том, чтобы API не был удобен для пользователя, как должен был быть в первую очередь. Джош Блох тряпится по дате и календарю в «Эффективной Java». Очень немногие API-интерфейсы идеальны, а API-интерфейсы даты и времени на Java имеют неудачную роль в том, что они были запутаны. Это жизнь, но давайте не будем притворяться, что это имеет какое-то отношение к эффективности. – Quinn Taylor 11 August 2009 в 04:06
  • 4
    Почему бы не считать дни от 0 до 30? Это просто непоследовательно и неряшливо. – Juangui Jordán 24 January 2017 в 14:00

Поскольку языковая запись сложнее, чем выглядит, а время обработки в частности намного сложнее, чем думают многие. Для небольшой части проблемы (на самом деле, а не Java) см. Видео на YouTube «Проблема со временем и часовыми поясами - компьютерная печать» на странице https://www.youtube.com/watch?v=-5wpm -gesOY . Не удивляйтесь, если ваша голова упадет от смеха в замешательстве.

0
ответ дан Tihamer 22 August 2018 в 01:49
поделиться
[g2] [f2] [/g2] [g3] Java предоставляет вам еще один способ использования индексов на основе 1 в течение нескольких месяцев. Используйте перечисление [g0] [f3] [/g0]. Один объект предопределен для каждого из двенадцати месяцев. У них есть номера, присвоенные каждому 1-12 на январь-декабрь; позвоните по номеру [g1] [f4] [/g1]. [/g3] [g4] Используйте [f5] (дает вам 7) вместо [f6] (дает вам 6). [/g4] [f1]
3
ответ дан Basil Bourque 5 November 2018 в 00:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: