Как вы форматируете день месяца, чтобы сказать "11-й", "21-й" или "23-й" (порядковый индикатор)?
Я знаю, что это даст мне день месяца в виде числа (11
, 21
, 23
):
SimpleDateFormat formatDayOfMonth = new SimpleDateFormat("d");
Но как вы форматируете день месяца, чтобы включить порядковый индикатор, скажем, 11th
, 21st
или же 23rd
?
24 ответа
// https://github.com/google/guava
import static com.google.common.base.Preconditions.*;
String getDayOfMonthSuffix(final int n) {
checkArgument(n >= 1 && n <= 31, "illegal day of month: " + n);
if (n >= 11 && n <= 13) {
return "th";
}
switch (n % 10) {
case 1: return "st";
case 2: return "nd";
case 3: return "rd";
default: return "th";
}
}
Таблица из @kaliatech хороша, но поскольку та же информация повторяется, это открывает шанс для ошибки. Такая ошибка на самом деле существует в таблице для 7tn
, 17tn
, а также 27tn
(эта ошибка может быть исправлена с течением времени из-за изменчивой природы Stackru, поэтому проверьте историю версий в ответе, чтобы увидеть ошибку).
В JDK нет ничего, чтобы сделать это.
static String[] suffixes =
// 0 1 2 3 4 5 6 7 8 9
{ "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th",
// 10 11 12 13 14 15 16 17 18 19
"th", "th", "th", "th", "th", "th", "th", "th", "th", "th",
// 20 21 22 23 24 25 26 27 28 29
"th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th",
// 30 31
"th", "st" };
Date date = new Date();
SimpleDateFormat formatDayOfMonth = new SimpleDateFormat("d");
int day = Integer.parseInt(formatDateOfMonth.format(date));
String dayStr = day + suffixes[day];
Или используя календарь:
Calendar c = Calendar.getInstance();
c.setTime(date);
int day = c.get(Calendar.DAY_OF_MONTH);
String dayStr = day + suffixes[day];
Согласно комментариям @thorbjørn-ravn-andersen, такая таблица может быть полезна при локализации:
static String[] suffixes =
{ "0th", "1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th",
"10th", "11th", "12th", "13th", "14th", "15th", "16th", "17th", "18th", "19th",
"20th", "21st", "22nd", "23rd", "24th", "25th", "26th", "27th", "28th", "29th",
"30th", "31st" };
private String getCurrentDateInSpecificFormat(Calendar currentCalDate) {
String dayNumberSuffix = getDayNumberSuffix(currentCalDate.get(Calendar.DAY_OF_MONTH));
DateFormat dateFormat = new SimpleDateFormat(" d'" + dayNumberSuffix + "' MMMM yyyy");
return dateFormat.format(currentCalDate.getTime());
}
private String getDayNumberSuffix(int day) {
if (day >= 11 && day <= 13) {
return "th";
}
switch (day % 10) {
case 1:
return "st";
case 2:
return "nd";
case 3:
return "rd";
default:
return "th";
}
}
Я хотел бы внести современный ответ. SimpleDateFormat
Класс можно было использовать, когда вопрос был задан 8 лет назад, но вы должны избегать его сейчас, поскольку он не только давно устарел, но и, как известно, хлопотно. использование java.time
вместо.
редактировать
DateTimeFormatterBuilder.appendText(TemporalField, Map<Long, String>)
отлично подходит для этой цели. Используя его, мы создаем форматтер, который делает работу за нас:
Map<Long, String> ordinalNumbers = new HashMap<>(42);
ordinalNumbers.put(1L, "1st");
ordinalNumbers.put(2L, "2nd");
ordinalNumbers.put(3L, "3rd");
ordinalNumbers.put(21L, "21st");
ordinalNumbers.put(22L, "22nd");
ordinalNumbers.put(23L, "23rd");
ordinalNumbers.put(31L, "31st");
for (long d = 1; d <= 31; d++) {
ordinalNumbers.putIfAbsent(d, "" + d + "th");
}
DateTimeFormatter dayOfMonthFormatter = new DateTimeFormatterBuilder()
.appendText(ChronoField.DAY_OF_MONTH, ordinalNumbers)
.appendPattern(" MMMM")
.toFormatter();
LocalDate date = LocalDate.of(2018, Month.AUGUST, 30);
for (int i = 0; i < 6; i++) {
System.out.println(date.format(dayOfMonthFormatter));
date = date.plusDays(1);
}
Выход из этого фрагмента:
30th August 31st August 1st September 2nd September 3rd September 4th September
Старый ответ
Этот код короче, но ИМХО не так уж и элегантно.
// ordinal indicators by numbers (1-based, cell 0 is wasted)
String[] ordinalIndicators = new String[31 + 1];
Arrays.fill(ordinalIndicators, 1, ordinalIndicators.length, "th");
ordinalIndicators[1] = ordinalIndicators[21] = ordinalIndicators[31] = "st";
ordinalIndicators[2] = ordinalIndicators[22] = "nd";
ordinalIndicators[3] = ordinalIndicators[23] = "rd";
DateTimeFormatter dayOfMonthFormatter = DateTimeFormatter.ofPattern("d");
LocalDate today = LocalDate.now(ZoneId.of("America/Menominee")).plusWeeks(1);
System.out.println(today.format(dayOfMonthFormatter)
+ ordinalIndicators[today.getDayOfMonth()]);
Запустив этот фрагмент только сейчас, я получил
двадцать третий
Одна из многих особенностей java.time
является то, что это просто и надежно, чтобы получить день месяца как int
, что, очевидно, необходимо для выбора правильного суффикса из таблицы.
Я рекомендую вам написать модульный тест тоже.
PS Я не пробовал, но я ожидаю, что подобный форматер также может быть использован для разбора строки даты, содержащей порядковые числа, такие как 1st
, 2nd
, так далее.
Ссылка: Oracle, учебник: Дата и время, объясняющие, как использовать java.time
,
String ordinal(int num)
{
String[] suffix = {"th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"};
int m = num % 100;
return String.valueOf(num) + suffix[(m > 3 && m < 21) ? 0 : (m % 10)];
}
Вопрос немного стар. Поскольку этот вопрос очень шумный, публикуем то, что я решил, используя статический метод в качестве утилиты. Просто скопируйте, вставьте и используйте его!
public static String getFormattedDate(Date date){
Calendar cal=Calendar.getInstance();
cal.setTime(date);
//2nd of march 2015
int day=cal.get(Calendar.DATE);
if(!((day>10) && (day<19)))
switch (day % 10) {
case 1:
return new SimpleDateFormat("d'st' 'of' MMMM yyyy").format(date);
case 2:
return new SimpleDateFormat("d'nd' 'of' MMMM yyyy").format(date);
case 3:
return new SimpleDateFormat("d'rd' 'of' MMMM yyyy").format(date);
default:
return new SimpleDateFormat("d'th' 'of' MMMM yyyy").format(date);
}
return new SimpleDateFormat("d'th' 'of' MMMM yyyy").format(date);
}
Для тестирования мочеиспускания
Пример: вызов его из основного метода!
Date date = new Date();
Calendar cal=Calendar.getInstance();
cal.setTime(date);
for(int i=0;i<32;i++){
System.out.println(getFormattedDate(cal.getTime()));
cal.set(Calendar.DATE,(cal.getTime().getDate()+1));
}
Выход:
22nd of February 2018
23rd of February 2018
24th of February 2018
25th of February 2018
26th of February 2018
27th of February 2018
28th of February 2018
1st of March 2018
2nd of March 2018
3rd of March 2018
4th of March 2018
5th of March 2018
6th of March 2018
7th of March 2018
8th of March 2018
9th of March 2018
10th of March 2018
11th of March 2018
12th of March 2018
13th of March 2018
14th of March 2018
15th of March 2018
16th of March 2018
17th of March 2018
18th of March 2018
19th of March 2018
20th of March 2018
21st of March 2018
22nd of March 2018
23rd of March 2018
24th of March 2018
25th of March 2018
Если вы попытаетесь узнать о i18n, решение станет еще сложнее.
Проблема в том, что в других языках суффикс может зависеть не только от самого числа, но и от существительного, которое он считает. Например, на русском языке это будет "2-ой день", но "2-ая неделя" (это означает "2-й день", но "2-я неделя"). Это не применимо, если мы форматируем только дни, но в более общем случае вы должны знать о сложности.
Я думаю, что хорошим решением (у меня не было времени на самом деле реализовать) было бы расширение SimpleDateFormetter для применения LocalForware-MessageFormat перед передачей в родительский класс. Таким образом, вы сможете поддерживать, скажем, для мартовских форматов%M для получения "3-го", %MM для "03-го" и%MMM для "третьего". Со стороны этот класс выглядит как обычный SimpleDateFormatter, но поддерживает больше форматов. Также, если этот шаблон будет по ошибке применен обычным SimpleDateFormetter, результат будет неправильно отформатирован, но все еще читаем.
Многие из приведенных здесь примеров не будут работать для 11, 12, 13. Это более общее и будет работать для всех случаев.
switch (date) {
case 1:
case 21:
case 31:
return "" + date + "st";
case 2:
case 22:
return "" + date + "nd";
case 3:
case 23:
return "" + date + "rd";
default:
return "" + date + "th";
}
Используя новый пакет java.time и новый оператор переключения Java, следующий код позволяет легко разместить порядковый номер в день месяца. Одним из недостатков является то, что это не поддается стандартным форматам, указанным в классе DateFormatter.
Просто создайте день в каком-то формате, но включите
%s%s
чтобы добавить день и порядковый номер позже.
ZonedDateTime ldt = ZonedDateTime.now();
String format = ldt.format(DateTimeFormatter
.ofPattern("EEEE, MMMM '%s%s,' yyyy hh:mm:ss a zzz"));
Теперь передайте день недели и только что отформатированную дату вспомогательному методу, чтобы добавить порядковый день.
int day = ldt.getDayOfMonth();
System.out.println(applyOrdinalDaySuffix(format, day));
Печать
Tuesday, October 6th, 2020 11:38:23 AM EDT
Вот вспомогательный метод.
Используя
Java 14
выражения switch упрощают получение порядкового номера.
public static String applyOrdinalDaySuffix(String format,
int day) {
if (day < 1 || day > 31)
throw new IllegalArgumentException(
String.format("Bad day of month (%s)", day));
String ord = switch (day) {
case 1, 21, 31 -> "st";
case 2, 22 -> "nd";
case 3, 23 -> "rd";
default -> "th";
};
return String.format(format, day, ord);
}
RuleBasedNumberFormat
в библиотеке ICU
Я оценил ссылку на библиотеку проекта ICU от @Pierre-Olivier Dybman (http://www.icu-project.org/apiref/icu4j/com/ibm/icu/text/RuleBasedNumberFormat.html), но все же пришлось понять как его использовать, так что примерRuleBasedNumberFormat
использование ниже.
Он будет форматировать только одно число, а не целую дату, поэтому вам нужно будет создать комбинированную строку, если вы ищете дату в формате: например, понедельник, 3 февраля.
Приведенный ниже код настраивает RuleBasedNumberFormat
как порядковый формат для данной локали, создает java.time ZonedDateTime
, а затем форматирует число с его порядковым номером в строку.
RuleBasedNumberFormat numOrdinalFormat = new RuleBasedNumberFormat(Locale.UK,
RuleBasedNumberFormat.ORDINAL);
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Pacific/Auckland"));
String dayNumAndOrdinal = numOrdinalFormat.format(zdt.toLocalDate().getDayOfMonth());
Пример вывода:
3-й
Или
4-й
и т.п.
Я не могу быть удовлетворен ответами, призывающими к решению только на английском языке, основанном на ручных форматах. Я долго искал подходящее решение и наконец нашел его.
Вы должны использовать RuleBasedNumberFormat. Это работает отлично, и это с уважением к Locale.
Единственная проблема с решением, предоставленным Грегом, состоит в том, что он не учитывает число больше 100 с окончанием "подростковых" чисел. Например, 111 должен быть 111-м, а не 111-м. Это мое решение:
/**
* Return ordinal suffix (e.g. 'st', 'nd', 'rd', or 'th') for a given number
*
* @param value
* a number
* @return Ordinal suffix for the given number
*/
public static String getOrdinalSuffix( int value )
{
int hunRem = value % 100;
int tenRem = value % 10;
if ( hunRem - tenRem == 10 )
{
return "th";
}
switch ( tenRem )
{
case 1:
return "st";
case 2:
return "nd";
case 3:
return "rd";
default:
return "th";
}
}
Существует более простой и надежный способ сделать это. Вам нужно использовать функцию getDateFromDateString(dateString); Он в основном удаляет st/nd/rd/th из строки даты и просто анализирует ее. Вы можете изменить свой SimpleDateFormat на что угодно, и это будет работать.
public static final SimpleDateFormat sdf = new SimpleDateFormat("d");
public static final Pattern p = Pattern.compile("([0-9]+)(st|nd|rd|th)");
private static Date getDateFromDateString(String dateString) throws ParseException {
return sdf.parse(deleteOrdinal(dateString));
}
private static String deleteOrdinal(String dateString) {
Matcher m = p.matcher(dateString);
while (m.find()) {
dateString = dateString.replaceAll(Matcher.quoteReplacement(m.group(0)), m.group(1));
}
return dateString;
}
Вот мой ответ:
public String getOrdinal(int day) {
String ordinal;
switch (day % 20) {
case 1:
ordinal = "st";
break;
case 2:
ordinal = "nd";
break;
case 3:
ordinal = "rd";
break;
default:
ordinal = "th";
}
return ordinal;
}
Просто сделайте по модулю 20, и он будет работать для всех дат. Чтобы получить сегодняшний день, вы можете использовать
LocalDate.now().getDayOfMonth()
. или проведи любой день так
LocalDate.getDayOfMonth()
Попробуйте функцию ниже:
public static String getFormattedDate(Date date)
{
Calendar cal = Calendar.getInstance();
cal.setTime(date);
//2nd of march 2015
int day = cal.get(Calendar.DATE);
if (!((day > 10) && (day < 19)))
switch (day % 10) {
case 1:
return new SimpleDateFormat("d'st' 'of' MMMM yyyy").format(date);
case 2:
return new SimpleDateFormat("d'nd' 'of' MMMM yyyy").format(date);
case 3:
return new SimpleDateFormat("d'rd' 'of' MMMM yyyy").format(date);
default:
return new SimpleDateFormat("d'th' 'of' MMMM yyyy").format(date);
}
return new SimpleDateFormat("d'th' 'of' MMMM yyyy").format(date);
}
Я написал себе вспомогательный метод, чтобы получить шаблоны для этого.
public static String getPattern(int month) {
String first = "MMMM dd";
String last = ", yyyy";
String pos = (month == 1 || month == 21 || month == 31) ? "'st'" : (month == 2 || month == 22) ? "'nd'" : (month == 3 || month == 23) ? "'rd'" : "'th'";
return first + pos + last;
}
и тогда мы можем назвать это как
LocalDate localDate = LocalDate.now();//For reference
int month = localDate.getDayOfMonth();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(getPattern(month));
String date = localDate.format(formatter);
System.out.println(date);
выход
December 12th, 2018
public static String getReadableDate(final int date){
String suffix = "th";
switch (date){
case 1:
case 21:
case 31:
suffix = "st";
break;
case 2:
case 22:
suffix = "nd";
break;
case 3:
case 23:
suffix = "rd";
break;
}
return date + suffix;
}
Для Котлина попробуйте это
fun Int.ordinalAbbrev() =
if (this % 100 / 10 == 1) "th"
else when (this % 10) { 1 -> "st" 2 -> "nd" 3 -> "rd" else -> "th" }
он принимает значение типа int и возвращает "3-й" "1-й" 11-й "" 2-й " . Таким образом, вы также можете использовать его для формата даты.
использование
fun getFormatedDate(date: String): String {
date.let {
try {
val parser = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
val formatter = SimpleDateFormat("dd MMMM", Locale.getDefault())
val dateArray = formatter.format(parser.parse(it)).split(" ").toTypedArray()
val formatedDate = String.format(
"${dateArray[0]}${
dateArray[0].toInt().ordinalAbbrev()
} ${dateArray[1]}"
)
return formatedDate
} catch (e: Exception) {
e.printStackTrace()
}
}
return date
}
Вот подход, который обновляет шаблон DateTimeFormatter с правильным суффиксом, если он находит шаблон d'00'
например, для дня месяца 1 он будет заменен на d'st'
, После того, как шаблон был обновлен, он может быть просто передан в DateTimeFormatter, чтобы сделать все остальное.
private static String[] suffixes = {"th", "st", "nd", "rd"};
private static String updatePatternWithDayOfMonthSuffix(TemporalAccessor temporal, String pattern) {
String newPattern = pattern;
// Check for pattern `d'00'`.
if (pattern.matches(".*[d]'00'.*")) {
int dayOfMonth = temporal.get(ChronoField.DAY_OF_MONTH);
int relevantDigits = dayOfMonth < 30 ? dayOfMonth % 20 : dayOfMonth % 30;
String suffix = suffixes[relevantDigits <= 3 ? relevantDigits : 0];
newPattern = pattern.replaceAll("[d]'00'", "d'" + suffix + "'");
}
return newPattern;
}
Требуется, чтобы исходный шаблон обновлялся непосредственно перед каждым вызовом форматирования, например
public static String format(TemporalAccessor temporal, String pattern) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(updatePatternWithDayOfMonthSuffix(temporal, pattern));
return formatter.format(temporal);
}
Так что это полезно, если шаблон форматирования определен вне Java-кода, например, шаблона, где, как если бы вы могли определить шаблон в Java, то ответ должен быть @OleV.V. может быть более подходящим
Если вам нужно это на Android, вы можете проверить этот ответ
Это интернационализированное решение. И вам не нужно изобретать велосипед;)
В kotlin вы можете использовать как это
fun changeDateFormats(currentFormat: String, dateString: String): String {
var result = ""
try {
val formatterOld = SimpleDateFormat(currentFormat, Locale.getDefault())
formatterOld.timeZone = TimeZone.getTimeZone("UTC")
var date: Date? = null
date = formatterOld.parse(dateString)
val dayFormate = SimpleDateFormat("d", Locale.getDefault())
var day = dayFormate.format(date)
val formatterNew = SimpleDateFormat("hh:mm a, d'" + getDayOfMonthSuffix(day.toInt()) + "' MMM yy", Locale.getDefault())
if (date != null) {
result = formatterNew.format(date)
}
} catch (e: ParseException) {
e.printStackTrace()
return dateString
}
return result
}
private fun getDayOfMonthSuffix(n: Int): String {
if (n in 11..13) {
return "th"
}
when (n % 10) {
1 -> return "st"
2 -> return "nd"
3 -> return "rd"
else -> return "th"
}
}
установить как это
txt_chat_time_me.text = changeDateFormats("SERVER_DATE", "DATE")
Ниже приведен более эффективный ответ на вопрос, чем жесткое программирование стиля.
Чтобы изменить день на порядковый номер, необходимо использовать следующий суффикс.
DD + TH = DDTH result >>>> 4TH
OR to spell the number add SP to the format
DD + SPTH = DDSPTH result >>> FOURTH
Найдите мой законченный ответ в этом вопросе.
Следующий метод может использоваться для получения отформатированной строки даты, которая передана ей. Он отформатирует дату, скажем 1-й,2-й,3-й,4-й.. используя SimpleDateFormat в Java. например:- 1 сентября 2015
public String getFormattedDate(Date date){
Calendar cal=Calendar.getInstance();
cal.setTime(date);
//2nd of march 2015
int day=cal.get(Calendar.DATE);
switch (day % 10) {
case 1:
return new SimpleDateFormat("d'st' 'of' MMMM yyyy").format(date);
case 2:
return new SimpleDateFormat("d'nd' 'of' MMMM yyyy").format(date);
case 3:
return new SimpleDateFormat("d'rd' 'of' MMMM yyyy").format(date);
default:
return new SimpleDateFormat("d'th' 'of' MMMM yyyy").format(date);
}
public String getDaySuffix(int inDay)
{
String s = String.valueOf(inDay);
if (s.endsWith("1"))
{
return "st";
}
else if (s.endsWith("2"))
{
return "nd";
}
else if (s.endsWith("3"))
{
return "rd";
}
else
{
return "th";
}
}