## DEV Community is a community of 890,060 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

# Daily Challenge #296 - Years to Centuries

The first century spans from the year 1 up to and including the year 100, The second - from the year 101 up to and including the year 200, etc. Return the century of the input year.

### Examples

"1999" --> "20th"
"2011" --> "21st"
"2154" --> "22nd"
"2259" --> "23rd"
"1124" --> "12th"
"2000" --> "21st"
"20000" --> "210th"

### Tests

`8120`
`30200`
`1601`
`2020`
`3030`
`1900`
`1776`

Good luck!

This challenge comes from Cpt.ManlyPink on CodeWars. Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Want to propose a challenge idea for a future post? Email yo+challenge@dev.to with your suggestions!

## Discussion (20) Sooraj (PS)
``````const toCentury = (year) => {
const century = Math.ceil(year / 100);
if (century.toString().length > 2) return year + " --> " + century + "th";
switch(century % 10) {
case 1: return year + " --> " + century + "st";
case 2: return year + " --> " + century + "nd";
case 3: return year + " --> " + century + "rd";
default: return year + " --> " + century + "th";
}
}
`````` Matt Ellen • Edited on

"1066 --> 11st" should be 11th
"10266 --> 103th" should be 103rd Comment deleted Sooraj (PS)

But according to the math, 1 - 100 is 1st century. So I thought 1901 - 2000 is 20th century and 2001 - 2100 is 21st century. Is my understanding wrong ? This question has conflicts.

"20000" --> "210th" (should it be 201st?)

In the question:

"The first century spans from the year 1 up to and including the year 100, The second - from the year 101 up to and including the year 200, etc. Return the century of the input year"

but toCentury(2000) -> 21st? Matt Ellen

Good question. I didn't notice that in the test cases. Asliddinbek Azizovich • Edited on

Here is my golang solution

``````
package main

import (
"fmt"
)

func getSuffix(number int) string {
if number%100 >= 11 && number%100 <= 13 {
return "th"
}
switch number%10 {
case 1:
return "st"
case 2:
return "nd"
case 3:
return "rd"
}

return "th"
}

func yearToCentury(year int) string {
century := year / 100

return fmt.Sprintf("%d%s century", century+1, getSuffix(century+1))
}

func main() {
tests := []int{1999, 2011, 2154, 2259, 1124, 2000, 20000}

for _, year := range tests {
fmt.Printf("%d --> %s\n", year, yearToCentury(year))
}
}

`````` Asliddinbek Azizovich • Edited on

I think, it will be 112nd century, because, current, 2020 year is inside of 21st century :) Matt Ellen

It should be 112th not 112nd, because 12 gets a th Valts Liepiņš

``````import Numeric.Natural (Natural)

toCentury :: Natural -> String
toCentury = suffix . ceiling . (/ 100) . (+ 1) . fromIntegral
where
suffix x
| x `mod` 10 == 1 = show x <> "st"
| x `mod` 10 == 2 = show x <> "nd"
| x `mod` 10 == 3 = show x <> "rd"
| otherwise       = show x <> "th"
`````` willsmart • Edited on

Here's a typescript implementation, and a ✨verbose-and-pretty✨ reference to check it against.
It's funny, but I take much more care with these little easy tasks than I used to. There's always more nuance than you expect and the implementations end up sitting at the bottom of your library being used for everything.

(btw, I tend to use `~~` as an alternative to of `Math.floor` because it's more succinct, and most often a little quicker)

``````function suffixForOrdinal(ordinal: number): string {
return (~~(ordinal / 10) % 10 !== 1 && ['th', 'st', 'nd', 'rd'][ordinal % 10]) || 'th';
}

function nameOfCentury(yearOrdinal: string | number): string {
const centuryOrdinal = ~~((+yearOrdinal + 99) / 100);
return `\${centuryOrdinal}\${suffixForOrdinal(centuryOrdinal)}`;
}
``````

I'll check my working by comparing with a naive implementation that's more readable and hopefully reliable out of the gate...

``````// Stringifying the ordinal is an easier way to get individual the parts of the year...
function suffixForOrdinal_slowButSure(ordinal: number): string {
const ordinalString = String(ordinal),
tens = +ordinalString.slice(-2, -1),
ones = +ordinalString.slice(-1);
if (tens == 1) return 'th'; // i.e. 11th, not 11st
if (ones == 1) return 'st'; // 81st
if (ones == 2) return 'nd'; // 82nd
if (ones == 3) return 'rd'; // 83rd
return 'th'; // anything else is 'th'. 45th
}

function nameOfCentury_slowButSure(yearOrdinal: string | number): string {
// When extracting the century we need to be working with the index, not ordinal.
// That's why the weirdness about 2000 -> 20th, 2001 -> 21st
//    (The year 2000 as an ordinal has index 1999)
const yearIndex = +yearOrdinal - 1,
yearIndexString = String(yearIndex),
centuryIndex = +yearIndexString.slice(0, -2) || 0,
centuryOrdinal = centuryIndex + 1;
return `\${centuryOrdinal}\${suffixForOrdinal_slowButSure(centuryOrdinal)}`;
}

// Now do some 'testing' by comparing the implementations
// This ensures that either both are right or both are wrong,
//   I'm pretty sure that the verbose one is as right as any static fixtures I could make.
(function hareVsTortoise(startYearOrdinal = 1, endYearOrdinal = 100000) {
for (let yearOrdinal = startYearOrdinal; yearOrdinal <= endYearOrdinal; yearOrdinal++) {
if (nameOfCentury(yearOrdinal) !== nameOfCentury_slowButSure(yearOrdinal)) {
console.error(
`Conflicting answers for year \${yearOrdinal}:\n      Hare: "\${nameOfCentury(
yearOrdinal
)}"\n  Tortoise: "\${nameOfCentury_slowButSure(yearOrdinal)}"`
);
return;
}
}
console.log(`Checked \${endYearOrdinal + 1 - startYearOrdinal} years, all seems good`);
})();

/*-->
Checked 100000 years, all seems good
``````
``````[1, 99, 100, 101, 200, 300, 400, 1000, 1100, 1200, 1300, 1400, 1900, 2000, 2001, 2020, 10000, 10100].forEach(year => {
console.log(`Year \${year} --> \${nameOfCentury(year)}`);
});

/*-->
Year 1 --> 1st
Year 99 --> 1st
Year 100 --> 1st
Year 101 --> 2nd
Year 200 --> 2nd
Year 300 --> 3rd
Year 400 --> 4th
Year 1000 --> 10th
Year 1100 --> 11th
Year 1200 --> 12th
Year 1300 --> 13th
Year 1400 --> 14th
Year 1900 --> 19th
Year 2000 --> 20th
Year 2001 --> 21st
Year 2020 --> 21st
Year 10000 --> 100th
Year 10100 --> 101st
`````` Py <3

``````def what_century(year):
c, r = divmod(year,100)

if r > 0:   c += 1

#name

if c in [11,12,13]:         return str(c)+'th'
elif str(c)[-1] == '1':     return str(c)+'st'
elif str(c)[-1] == '2':     return str(c) + 'nd'
elif str(c)[-1] == '3':     return str(c) + 'rd'
else:                        return str(c) + 'th'
`````` Matt Ellen
``````function thuffix(n)
{
let ending = 'th';
if(n%100 != 11 && n%100 != 12 && n%100 != 13)
{
if(n%10 == 1)
{
ending = 'st';
}
else if(n%10 == 2)
{
ending = 'nd';
}
else if(n%10 == 3)
{
ending = 'rd';
}
}
return ending;
}

function getCentury(year)
{
let c = Math.floor(year/100);
if(year%100 != 0)
{
c++;
}
return `\${c}\${thuffix(c)}`;
}
`````` Dmitry Mineev

The ruby example

``````def ordinalize(number)
case number % 10
when 1
"st"
when 2
"nd"
when 3
"rd"
else
"th"
end
end

def test_years(years_array)
years_array.each do |year|
correction = 0
if year % 100 == 0
correction = 1
end
result = (year/100.0).ceil + correction
puts "#{year} --> #{result}#{ordinalize(result)}"
end
end

puts "Example scope:"
test_years([1999, 2011, 2154, 2259, 1124, 2000, 20000])
puts "Test scope:"
test_years([8120, 30200, 1601, 2020, 3030, 1900, 1776])
``````

And one-line style with `activesupport` gem installed

`````` [{name: "Example", years: [1999, 2011, 2154, 2259, 1124, 2000, 20000]},
{name: "Test", years: [8120, 30200, 1601, 2020, 3030, 1900, 1776]}].
each{ |scope| puts "#{scope[:name]} scope:"; scope[:years].
each{ |year| year % 100 == 0 ? correction = 1 : correction = 0;
result = (year/100.0).ceil + correction;
puts "#{year} --> #{result.ordinalize}" }}
`````` 3limin4t0r • Edited on

This might be a bit over-engineered, but here is another Ruby solution:

``````def to_century(year)
(year.to_i / 100 + 1).english.ordinal
end

class Integer
def english
English::Integer.new(self)
end
end

module English
class Integer
def initialize(integer)
@value = integer
end

##
# Returns ordinal notation of the current integer ("-2nd", "1st", "312th").
def ordinal
"#{@value}#{ordinal_suffix}"
end

##
# Returns the ordinal suffix of the current integer ("st", "nd", "rd", "th").
def ordinal_suffix
case @value.abs % 100
when 1, 21, 31, 41, 51, 61, 71, 81, 91 then 'st'
when 2, 22, 32, 42, 52, 62, 72, 82, 92 then 'nd'
when 3, 23, 33, 43, 53, 63, 73, 83, 93 then 'rd'
else 'th'
end
end
end
end
``````

Which produces the following output:

``````years = %w[8120 30200 1601 2020 3030 1900 1776]
years.to_h { |year| [year, to_century(year)] }
#=> {"8120"=>"82nd", "30200"=>"303rd", "1601"=>"17th", "2020"=>"21st", "3030"=>"31st", "1900"=>"20th", "1776"=>"18th"}
`````` Josh • Edited on
``````defmodule Year do
@doc """
Returns the stringified, suffixed form of a numeric year.

Use `to_century/1` if you need to do further numeric work on the
century number before (or in lieu of) using it as a string.

## Examples

iex> Year.centurify(8120)
"82nd"
iex> Year.centurify(30200)
"303rd"
iex> Year.centurify(1601)
"17th"
iex> Year.centurify(2020)
"21st"
iex> Year.centurify(3030)
"31st"
iex> Year.centurify(1900)
"20th"
iex> Year.centurify(1776)
"18th"
"""
def centurify(year) do
century = to_century(year)
"#{century}#{suffix(century)}"
end

def to_century(year), do: div(year, 100) + 1

defp suffix(number) when number >= 100,
do: number |> rem(100) |> suffix
defp suffix(number) when number in 11..13, do: "th"
defp suffix(number) do
case rem(number, 10) do
1 -> "st"
2 -> "nd"
3 -> "rd"
_ -> "th"
end
end
end

defmodule YearTest do
import ExUnit.Case
doctest Year
end
`````` peter279k

Here is my simple solution with Python:

``````def what_century(year):
year = str(year)
pre_two_number = int(year + year)

if year == '2000':
return '20th'

if year != '0':
pre_two_number += 1

if  year == '0' and year == '0':
pre_two_number += 0

if  year == '0' and year != '0':
pre_two_number += 1

pre_two_number = str(pre_two_number)

if pre_two_number == '1' and pre_two_number != '1':
pre_two_number += "st"
elif pre_two_number == '2' and pre_two_number != '1':
pre_two_number += "nd"
elif pre_two_number == '3' and pre_two_number != '1':
pre_two_number += "rd"
else:
pre_two_number += "th"

return pre_two_number
``````