Exercism - Space Age
This post shows you how to get Space Age exercise of Exercism.
Preparation
Before we click on our next exercise, let’s see what concepts of DART we need to consider

So we need to use the following concepts.
Static Constants
Static constants belong to the class itself rather than to instances. They’re perfect for storing shared data that doesn’t change.
class Constants {
static const int earthYearInSeconds = 31557600;
static const double pi = 3.14159;
}
void main() {
print(Constants.earthYearInSeconds); // 31557600
print(Constants.pi); // 3.14159
}
Maps
A Map is a collection of key-value pairs. Each key in a map is unique, and you can use keys to look up their corresponding values.
void main() {
Map<String, double> periods = {
"Mercury": 0.2408467,
"Venus": 0.61519726,
"Earth": 1.0,
};
// Access values
double mercury = periods["Mercury"]!;
print(mercury); // 0.2408467
}
Required Parameters
The required keyword makes named parameters mandatory. This ensures that callers must provide these parameters.
void calculate({required String planet, required int seconds}) {
print('Planet: $planet, Seconds: $seconds');
}
void main() {
calculate(planet: "Earth", seconds: 1000000);
// calculate(seconds: 1000000); // Error: missing required parameter
}
Double Operations
Doubles are used for floating-point numbers. You can perform arithmetic operations and format them to a specific number of decimal places.
void main() {
double a = 10.5;
double b = 3.2;
// Division
double result = a / b;
print(result); // 3.28125
// Format to 2 decimal places
String formatted = result.toStringAsFixed(2);
print(formatted); // "3.28"
// Parse back to double
double parsed = double.parse(formatted);
print(parsed); // 3.28
}
Null Assertion Operator
The null assertion operator ! tells Dart that you’re certain a value is not null. Use it when you know a value exists.
void main() {
Map<String, int> map = {"key": 42};
// Using null assertion
int value = map["key"]!;
print(value); // 42
// Without null assertion (nullable)
int? nullableValue = map["key"];
print(nullableValue); // 42
}
Arithmetic Operations
Basic arithmetic operations include addition (+), subtraction (-), multiplication (*), and division (/).
void main() {
int seconds = 1000000000;
int earthYearInSeconds = 31557600;
// Calculate Earth years
double earthYears = seconds / earthYearInSeconds;
print(earthYears); // 31.68808781402895
// Calculate planet years
double mercuryPeriod = 0.2408467;
double mercuryYears = earthYears / mercuryPeriod;
print(mercuryYears); // 131.56953287725722
}
Introduction
The year is 2525 and you’ve just embarked on a journey to visit all planets in the Solar System (Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus and Neptune). The first stop is Mercury, where customs require you to fill out a form (bureaucracy is apparently not Earth-specific). As you hand over the form to the customs officer, they scrutinize it and frown. “Do you really expect me to believe you’re just 50 years old? You must be closer to 200 years old!”
Amused, you wait for the customs officer to start laughing, but they appear to be dead serious. You realize that you’ve entered your age in Earth years, but the officer expected it in Mercury years! As Mercury’s orbital period around the sun is significantly shorter than Earth, you’re actually a lot older in Mercury years. After some quick calculations, you’re able to provide your age in Mercury Years. The customs officer smiles, satisfied, and waves you through. You make a mental note to pre-calculate your planet-specific age before future customs checks, to avoid such mix-ups.
Instructions
Given an age in seconds, calculate how old someone would be on a planet in our Solar System.
One Earth year equals 365.25 Earth days, or 31,557,600 seconds. If you were told someone was 1,000,000,000 seconds old, their age would be 31.69 Earth-years.
For the other planets, you have to account for their orbital period in Earth Years:
| Planet | Orbital period in Earth Years |
|---|---|
| Mercury | 0.2408467 |
| Venus | 0.61519726 |
| Earth | 1.0 |
| Mars | 1.8808158 |
| Jupiter | 11.862615 |
| Saturn | 29.447498 |
| Uranus | 84.016846 |
| Neptune | 164.79132 |
What is orbital period?
The orbital period is the time a given astronomical object takes to complete one orbit around another object. In this exercise, we’re calculating how many “years” (orbital periods) a person has lived on different planets based on their age in seconds.
Since each planet has a different orbital period, the same amount of time represents different numbers of years on different planets. For example, Mercury orbits the sun much faster than Earth, so someone would be “older” in Mercury years than in Earth years.
— Astronomy
How can we calculate space age?
To calculate age on different planets:
- Convert seconds to Earth years:
seconds / 31557600 - Divide Earth years by the planet’s orbital period to get planet years
- Round to 2 decimal places for the result
For example, with 1,000,000,000 seconds on Mercury:
- Earth years: 1,000,000,000 / 31,557,600 = 31.69
- Mercury years: 31.69 / 0.2408467 = 131.57
⚠️ Old Solution (No Longer Works)
Previously, the solution used optional named parameters and a non-static map. Here’s what the old solution looked like:
class SpaceAge {
Map<String,double> pa = {
"Mercury": 0.2408467,
"Venus": 0.61519726,
"Earth": 1.0,
"Mars": 1.8808158,
"Jupiter": 11.862615,
"Saturn": 29.447498,
"Uranus": 84.016846,
"Neptune": 164.79132
};
double age({String planet, int seconds}) {
return double.parse((seconds / 31557600 / pa[planet]).toStringAsFixed(2));
}
}
Why This Solution Doesn’t Work Anymore
The old solution has several issues:
-
Optional parameters: The parameters
String planetandint secondsare optional (nullable), but the method needs them to work. In null-safe Dart, this can cause runtime errors if parameters are not provided. -
Non-static map: The map
pais an instance variable, meaning each instance ofSpaceAgewould have its own copy. Since the orbital periods never change, this should be a static constant. -
Magic number: The value
31557600is hardcoded in the method. It should be a named constant for better readability and maintainability. -
Null safety: Accessing
pa[planet]can returnnull, but the old code doesn’t handle this case, which can cause runtime errors in null-safe Dart.
The exercise now requires:
- Using
requiredkeyword for named parameters to ensure they’re always provided - Making constants
static constsince they don’t change and don’t need to be instance-specific - Using the null assertion operator
!when accessing map values (since we know the planet exists) - Extracting magic numbers into named constants for better code clarity
Solution
class SpaceAge {
static const _earthYearInSeconds = 31557600;
static const _orbitalPeriods = {
"Mercury": 0.2408467,
"Venus": 0.61519726,
"Earth": 1.0,
"Mars": 1.8808158,
"Jupiter": 11.862615,
"Saturn": 29.447498,
"Uranus": 84.016846,
"Neptune": 164.79132
};
double age({required String planet, required int seconds}) {
final earthYears = seconds / _earthYearInSeconds;
final planetYears = earthYears / _orbitalPeriods[planet]!;
return double.parse(planetYears.toStringAsFixed(2));
}
}
Let’s break down the solution:
-
static const _earthYearInSeconds = 31557600- Defines a constant for the number of seconds in one Earth year:staticmeans it belongs to the class, not instancesconstmeans it’s a compile-time constant_prefix makes it private to the class
-
static const _orbitalPeriods- Defines a constant map of planet names to their orbital periods:- Same as above, static and const for efficiency
- Contains all 8 planets with their orbital periods in Earth years
-
age({required String planet, required int seconds})- Calculates age on a planet:requiredensures both parameters must be provided- Step 1:
final earthYears = seconds / _earthYearInSeconds- Converts seconds to Earth years - Step 2:
final planetYears = earthYears / _orbitalPeriods[planet]!- Divides Earth years by the planet’s orbital period_orbitalPeriods[planet]!uses the null assertion operator since we know the planet exists in the map
- Step 3:
return double.parse(planetYears.toStringAsFixed(2))- Formats to 2 decimal places and parses back to double
The solution is more robust, uses proper null safety, and follows Dart best practices with static constants and required parameters.
You can watch this tutorial on YouTube. So don’t forget to like and subscribe. 😉
Watch on YouTube