Palindrome dates: Difference between revisions

From Rosetta Code
Content added Content deleted
m (added a clause.)
(→‎{{header|AppleScript}}: Added subheadings)
Line 44: Line 44:


=={{header|AppleScript}}==
=={{header|AppleScript}}==
===Procedural===

<lang applescript>on palindromeDates(startYear, targetNumber)
<lang applescript>on palindromeDates(startYear, targetNumber)
script o
script o
Line 81: Line 81:
<pre>{"2021-12-02", "2030-03-02", "2040-04-02", "2050-05-02", "2060-06-02", "2070-07-02", "2080-08-02", "2090-09-02", "2101-10-12", "2110-01-12", "2111-11-12", "2120-02-12", "2121-12-12", "2130-03-12", "2140-04-12"}</pre>
<pre>{"2021-12-02", "2030-03-02", "2040-04-02", "2050-05-02", "2060-06-02", "2070-07-02", "2080-08-02", "2090-09-02", "2101-10-12", "2110-01-12", "2111-11-12", "2120-02-12", "2121-12-12", "2130-03-12", "2140-04-12"}</pre>


===Functional===

Or, for a functional composition (rather than a procedure), we can return a count and two samples of all palindromic dates for years in the range [2021 .. 9999], by assembling a function from reusable generics:


<lang applescript>use AppleScript version "2.4"
<lang applescript>use AppleScript version "2.4"
Line 91: Line 90:
-- palinYearsInRange :: Int -> Int -> [String]
-- palinYearsInRange :: Int -> Int -> [String]
on palinYearsInRange(fromYear, toYear)
on palinYearsInRange(fromYear, toYear)

concatMap(palinDay(iso8601Formatter()), ¬
concatMap(palinDay(iso8601Formatter()), ¬
enumFromTo(fromYear, toYear))
enumFromTo(fromYear, toYear))
end palinYearsInRange
end palinYearsInRange


Line 109: Line 108:
set {m, m1, d, d1} to reverse of characters of s
set {m, m1, d, d1} to reverse of characters of s
set mbDate to s & "-" & m & m1 & "-" & d & d1
set mbDate to s & "-" & m & m1 & "-" & d & d1

if missing value is not ¬
if missing value is not ¬
(fmtr's dateFromString:(mbDate & ¬
(fmtr's dateFromString:(mbDate & ¬
Line 124: Line 124:




---------------------------TEST----------------------------
--------------------------- TEST ---------------------------
on run
on run
set xs to palinYearsInRange(2021, 9999)
set xs to palinYearsInRange(2021, 9999)
Line 137: Line 137:




---------------------GENERIC FUNCTIONS---------------------
-------------------- GENERIC FUNCTIONS ---------------------



-- concatMap :: (a -> [b]) -> [a] -> [b]
-- concatMap :: (a -> [b]) -> [a] -> [b]
Line 178: Line 177:
end if
end if
end mReturn
end mReturn



-- iso8601Formatter :: () -> NSISO8601DateFormatter
-- iso8601Formatter :: () -> NSISO8601DateFormatter
Line 188: Line 188:
end tell
end tell
end iso8601Formatter
end iso8601Formatter



-- unlines :: [String] -> String
-- unlines :: [String] -> String

Revision as of 02:29, 11 July 2020

Task
Palindrome dates
You are encouraged to solve this task according to the task description, using any language you may know.

Today   (2020-02-02,   at the time of this writing)   happens to be a palindrome,   without the hyphens,   not only for those countries which express their dates in the   yyyy-mm-dd   format but,   unusually,   also for countries which use the   dd-mm-yyyy   format.


Task

Write a program which calculates and shows the next 15 palindromic dates for those countries which express their dates in the   yyyy-mm-dd   format.

Ada

<lang Ada>with Ada.Text_IO; with Ada.Calendar.Formatting; with Ada.Calendar.Arithmetic;

procedure Palindrome_Dates is

  Desired_Count : constant := 15;
  Start_Date    : constant String := "2020-01-01 00:00:00";
  use Ada.Calendar;
  function Is_Palindrome_Date (Date : Time) return Boolean is
     Image : String renames Formatting.Image (Date);
  begin
     return
       Image (1) = Image (10) and
       Image (2) = Image (9)  and
       Image (3) = Image (7)  and
       Image (4) = Image (6);
  end Is_Palindrome_Date;
  Date  : Ada.Calendar.Time := Formatting.Value (Start_Date);
  Count : Natural := 0;
  use type Ada.Calendar.Arithmetic.Day_Count;

begin

  loop
     if Is_Palindrome_Date (Date) then
        Ada.Text_IO.Put_Line (Formatting.Image (Date) (1 .. 10));
        Count := Count + 1;
     end if;
     exit when Count = Desired_Count;
     Date := Date + 1;
  end loop;

end Palindrome_Dates;</lang>

AppleScript

Procedural

<lang applescript>on palindromeDates(startYear, targetNumber)

   script o
       property output : {}
   end script
   
   set counter to 0
   set y to startYear
   repeat until ((counter = targetNumber) or (y > 9999))
       -- Derive a month number from the last two digits of the current year number. It's valid if it's in the range 1 to 12.
       set m to y mod 10 * 10 + y mod 100 div 10
       if ((m > 0) and (m < 13)) then
           -- Derive a day number from the first two digits of the year number.
           set d to y div 100 mod 10 * 10 + y div 1000
           -- It's valid if it's between 1 and 28. Otherwise, if it's between 29 and 31, check that it fits the month and year.
           -- In fact though, it'll only ever be 2 or 12 in the period containing the 15 palindromic dates after 2020.
           if ((d > 0) and ¬
               ((d < 29) ¬
                   or ((d < 31) and ((m is not 2) or ((d is 29) and (y mod 4 is 0) and ((y mod 100 > 0) or (y mod 400 is 0))))) ¬
                   or ((d is 31) and (m is not in {2, 4, 9, 6, 11})))) then
               -- If the figures represent a valid date, add a yyyy-mm-dd format text to the end of the output list.
               tell ((100000000 + y * 10000 + m * 100 + d) as text) to ¬
                   set end of o's output to text 2 thru 5 & ("-" & text 6 thru 7) & ("-" & text 8 thru 9)
               set counter to counter + 1
           end if
       end if
       set y to y + 1
   end repeat
   
   return o's output

end palindromeDates

palindromeDates(2021, 15)</lang>

Output:
{"2021-12-02", "2030-03-02", "2040-04-02", "2050-05-02", "2060-06-02", "2070-07-02", "2080-08-02", "2090-09-02", "2101-10-12", "2110-01-12", "2111-11-12", "2120-02-12", "2121-12-12", "2130-03-12", "2140-04-12"}

Functional

<lang applescript>use AppleScript version "2.4" use framework "Foundation" use scripting additions


-- palinYearsInRange :: Int -> Int -> [String] on palinYearsInRange(fromYear, toYear)

   concatMap(palinDay(iso8601Formatter()), ¬
       enumFromTo(fromYear, toYear))
   

end palinYearsInRange


-- palinDay :: DateFormatter -> Int -> [String] on palinDay(formatter)

   script
       property fmtr : formatter
       on |λ|(y)
           -- Either an empty list or a list containing a valid
           -- palindromic date for a year in the range [1000 .. 9999]
           if 10000 > y and 999 < y then
               set s to y as string
               set {m, m1, d, d1} to reverse of characters of s
               set mbDate to s & "-" & m & m1 & "-" & d & d1
               if missing value is not ¬
                   (fmtr's dateFromString:(mbDate & ¬
                       "T00:00:00+00:00")) then
                   {mbDate}
               else
                   {}
               end if
           else
               {}
           end if
       end |λ|
   end script

end palinDay



TEST ---------------------------

on run

   set xs to palinYearsInRange(2021, 9999)
   
   unlines({¬
       "Count of palindromic dates [2021..9999]: " & ¬
       ((length of xs) as string), ¬
       "", ¬
       "First 15:", unlines(items 1 thru 15 of xs), "", ¬
       "Last 15:", unlines(items -15 thru -1 of xs)})

end run



GENERIC FUNCTIONS ---------------------

-- concatMap :: (a -> [b]) -> [a] -> [b] on concatMap(f, xs)

   set lng to length of xs
   set acc to {}
   tell mReturn(f)
       repeat with i from 1 to lng
           set acc to acc & (|λ|(item i of xs, i, xs))
       end repeat
   end tell
   return acc

end concatMap


-- enumFromTo :: Int -> Int -> [Int] on enumFromTo(m, n)

   if m ≤ n then
       set lst to {}
       repeat with i from m to n
           set end of lst to i
       end repeat
       lst
   else
       {}
   end if

end enumFromTo


-- mReturn :: First-class m => (a -> b) -> m (a -> b) on mReturn(f)

   -- 2nd class handler function lifted into 1st class script wrapper. 
   if script is class of f then
       f
   else
       script
           property |λ| : f
       end script
   end if

end mReturn


-- iso8601Formatter :: () -> NSISO8601DateFormatter on iso8601Formatter()

   tell current application
       set formatter to its NSISO8601DateFormatter's alloc's init()
       set formatOptions of formatter to ¬
           (its NSISO8601DateFormatWithInternetDateTime as integer)
       return formatter
   end tell

end iso8601Formatter


-- unlines :: [String] -> String on unlines(xs)

   -- A single string formed by the intercalation
   -- of a list of strings with the newline character.
   set {dlm, my text item delimiters} to ¬
       {my text item delimiters, linefeed}
   set str to xs as text
   set my text item delimiters to dlm
   str

end unlines</lang>

Output:
Count of palindromic dates [2021..9999]: 284

First 15:
2021-12-02
2030-03-02
2040-04-02
2050-05-02
2060-06-02
2070-07-02
2080-08-02
2090-09-02
2101-10-12
2110-01-12
2111-11-12
2120-02-12
2121-12-12
2130-03-12
2140-04-12

Last 15:
9170-07-19
9180-08-19
9190-09-19
9201-10-29
9210-01-29
9211-11-29
9220-02-29
9221-12-29
9230-03-29
9240-04-29
9250-05-29
9260-06-29
9270-07-29
9280-08-29
9290-09-29

AWK

<lang AWK>

  1. syntax: GAWK -f PALINDROME_DATES.AWK

BEGIN {

   show = 15
   year_b = 2020
   year_e = 9999
   split("31,28,31,30,31,30,31,31,30,31,30,31",daynum_array,",") # days per month in non leap year
   for (y=year_b; y<=year_e; y++) {
     daynum_array[2] = (y % 400 == 0 || (y % 4 == 0 && y % 100)) ? 29 : 28
     for (m=1; m<=12; m++) {
       for (d=1; d<=daynum_array[m]; d++) {
         ymd = sprintf("%04d%02d%02d",y,m,d)
         if (substr(ymd,1,1) == substr(ymd,8,1)) { # speed up
           if (ymd == reverse(ymd)) {
             arr[++n] = ymd
           }
         }
       }
     }
   }
   printf("%04d0101-%04d1231=%d years, %d palindromes, showing first and last %d\n",year_b,year_e,year_e-year_b+1,n,show)
   printf("YYYYMMDD YYYYMMDD\n")
   for (i=1; i<=show; i++) {
     printf("%s %s\n",arr[i],arr[n-show+i])
   }
   exit(0)

} function reverse(str, i,rts) {

   for (i=length(str); i>=1; i--) {
     rts = rts substr(str,i,1)
   }
   return(rts)

} </lang>

Output:
20200101-99991231=7980 years, 285 palindromes, showing first and last 15
YYYYMMDD YYYYMMDD
20200202 91700719
20211202 91800819
20300302 91900919
20400402 92011029
20500502 92100129
20600602 92111129
20700702 92200229
20800802 92211229
20900902 92300329
21011012 92400429
21100112 92500529
21111112 92600629
21200212 92700729
21211212 92800829
21300312 92900929

BBC BASIC

<lang bbcbasic> INSTALL @lib$ + "DATELIB"

     DIM B% 8
     TestDate%=FN_today
     REPEAT
       $B%=FN_date$(TestDate%, "yyyyMMdd")
       FOR I%=0 TO 3
         IF ?(B% + I%) <> ?(B% + 7 - I%) EXIT FOR
       NEXT
       IF I%=4 PRINT FN_date$(TestDate%, "yyyy-MM-dd")
       TestDate%+=1
     UNTIL VPOS=15
     END</lang>
Output:
2021-12-02
2030-03-02
2040-04-02
2050-05-02
2060-06-02
2070-07-02
2080-08-02
2090-09-02
2101-10-12
2110-01-12
2111-11-12
2120-02-12
2121-12-12
2130-03-12
2140-04-12

C

This only works if time_t is a 64-bit type. <lang c>#include <stdbool.h>

  1. include <stdio.h>
  2. include <string.h>
  3. include <time.h>

bool is_palindrome(const char* str) {

   size_t n = strlen(str);
   for (size_t i = 0; i + 1 < n; ++i, --n) {
       if (str[i] != str[n - 1])
           return false;
   }
   return true;

}

int main() {

   time_t timestamp = time(0);
   const int seconds_per_day = 24*60*60;
   int count = 15;
   char str[32];
   printf("Next %d palindrome dates:\n", count);
   for (; count > 0; timestamp += seconds_per_day) {
       struct tm* ptr = gmtime(&timestamp);
       strftime(str, sizeof(str), "%Y%m%d", ptr);
       if (is_palindrome(str)) {
           strftime(str, sizeof(str), "%F", ptr);
           printf("%s\n", str);
           --count;
       }
   }
   return 0;

}</lang>

Output:
Next 15 palindrome dates:
2021-12-02
2030-03-02
2040-04-02
2050-05-02
2060-06-02
2070-07-02
2080-08-02
2090-09-02
2101-10-12
2110-01-12
2111-11-12
2120-02-12
2121-12-12
2130-03-12
2140-04-12

C#

<lang csharp>using System; using System.Linq; using System.Collections.Generic;

public class Program {

   static void Main()
   {
       foreach (var date in PalindromicDates(2021).Take(15)) WriteLine(date.ToString("yyyy-MM-dd"));
   }
   public static IEnumerable<DateTime> PalindromicDates(int startYear) {
       for (int y = startYear; ; y++) {
           int m = Reverse(y % 100);
           int d = Reverse(y / 100);
           if (IsValidDate(y, m, d, out var date)) yield return date;
       }
       int Reverse(int x) => x % 10 * 10 + x / 10;
       bool IsValidDate(int y, int m, int d, out DateTime date) => DateTime.TryParse($"{y}-{m}-{d}", out date);
   }

}</lang>

Output:
2021-12-02
2030-03-02
2040-04-02
2050-05-02
2060-06-02
2070-07-02
2080-08-02
2090-09-02
2101-10-12
2110-01-12
2111-11-12
2120-02-12
2121-12-12
2130-03-12
2140-04-12

C++

Library: Boost

<lang cpp>#include <iostream>

  1. include <string>
  2. include <boost/date_time/gregorian/gregorian.hpp>

bool is_palindrome(const std::string& str) {

   for (size_t i = 0, j = str.size(); i + 1 < j; ++i, --j) {
       if (str[i] != str[j - 1])
           return false;
   }
   return true;

}

int main() {

   using boost::gregorian::date;
   using boost::gregorian::day_clock;
   using boost::gregorian::date_duration;
   
   date today(day_clock::local_day());
   date_duration day(1);
   int count = 15;
   std::cout << "Next " << count << " palindrome dates:\n";
   for (; count > 0; today += day) {
       if (is_palindrome(to_iso_string(today))) {
           std::cout << to_iso_extended_string(today) << '\n';
           --count;
       }
   }
   return 0;

}</lang>

Output:
Next 15 palindrome dates:
2021-12-02
2030-03-02
2040-04-02
2050-05-02
2060-06-02
2070-07-02
2080-08-02
2090-09-02
2101-10-12
2110-01-12
2111-11-12
2120-02-12
2121-12-12
2130-03-12
2140-04-12

Clojure

<lang Clojure> (defn valid-date? y m d

 (and (<= 1 m 12)
      (<= 1 d 31)))

(defn date-str y m d

 (format "%4d-%02d-%02d" y m d))

(defn yr->date [y]

 (let [[_ m d] (re-find #"(..)(..)" (apply str (reverse (str y))))]
   [y (Long. m) (Long. d)]))

(defn palindrome-dates [start-yr n]

 (->> (iterate inc start-yr)
      (map yr->date)
      (filter valid-date?)
      (map date-str)
      (take n)))

</lang>

Output:
("2021-12-02" "2030-03-02" "2040-04-02" "2050-05-02" "2060-06-02" "2070-07-02" "2080-08-02" "2090-09-02" "2101-10-12" "2110-01-12" "2111-11-12" "2120-02-12" "2121-12-12" "2130-03-12" "2140-04-12")

F#

<lang Fsharp>// palindrome_dates.fsx open System

let is_palindrome_date =

   let date_string (date: DateTime) = date.ToString "yyyyMMdd"
   let is_palindrome s =
       let rev_string = Seq.rev >> Seq.map string >> String.concat ""
       s = rev_string s
   date_string >> is_palindrome

let palindrome_dates =

   let rec loop date =
       seq {
           if is_palindrome_date date
           then
               yield date
               yield! loop (date.AddDays 1.0)
           else
               yield! loop (date.AddDays 1.0)
       }
   loop DateTime.Now

let print_date =

   let iso_string (date: DateTime) = date.ToString "yyyy-MM-dd"
   iso_string >> printfn "%s"

palindrome_dates |> Seq.take 15 |> Seq.iter print_date </lang>

Output:
> dotnet fsi palindrome_dates.fsx 
2021-12-02
2030-03-02
2040-04-02
2050-05-02
2060-06-02
2070-07-02
2080-08-02
2090-09-02
2101-10-12
2110-01-12
2111-11-12
2120-02-12
2121-12-12
2130-03-12
2140-04-12

Factor

Brute force

A simple brute force solution that repeatedly increments a timestamp's day by one and checks whether it's a palindrome:

Works with: Factor version 0.99 2020-01-23

<lang factor>USING: calendar calendar.format io kernel lists lists.lazy sequences sets ;

palindrome-dates ( -- list )
   2020 2 2 <date> [ 1 days time+ ] lfrom-by
   [ timestamp>ymd ] lmap-lazy
   [ "-" without dup reverse = ] lfilter ;

15 palindrome-dates ltake [ print ] leach</lang>

Output:
2020-02-02
2021-12-02
2030-03-02
2040-04-02
2050-05-02
2060-06-02
2070-07-02
2080-08-02
2090-09-02
2101-10-12
2110-01-12
2111-11-12
2120-02-12
2121-12-12
2130-03-12

Faster

A faster version that directly generates palindromic numbers such as 20200202 and keeps those which are valid dates:

Works with: Factor version 0.99 2020-01-23

<lang factor>USING: calendar calendar.format continuations io kernel lists lists.lazy math math.functions math.parser math.ranges sequences ;

create-palindrome ( n odd? -- m )
   dupd [ 10 /i ] when swap [ over 0 > ]
   [ 10 * [ 10 /mod ] [ + ] bi* ] while nip ;
palindromes ( -- list )
   3 lfrom [
       10 swap ^ dup 10 * [a,b)
       [ [ t create-palindrome ] map ]
       [ [ f create-palindrome ] map ] bi
       [ sequence>list ] bi@ lappend
   ] lmap-lazy lconcat [ 20200202 >= ] lfilter ;
palindrome-dates ( -- list )
   palindromes [
       number>string 4 cut* 2 cut [ string>number ] tri@
       [ <date> ] [ 4drop f ] recover
   ] lmap-lazy [ f = not ] lfilter ;

"10,000th palindrome date after 2020-02-02: " write 10,000 palindrome-dates lnth timestamp>ymd print</lang>

Output:
10,000th palindrome date after 2020-02-02: 1250101-05-21

Go

Simple brute force as speed is not an issue here. <lang go>package main

import (

   "fmt"
   "time"

)

func reverse(s string) string {

   chars := []rune(s)
   for i, j := 0, len(chars)-1; i < j; i, j = i+1, j-1 {
       chars[i], chars[j] = chars[j], chars[i]
   }
   return string(chars)

}

func main() {

   const (
       layout  = "20060102"
       layout2 = "2006-01-02"
   )
   fmt.Println("The next 15 palindromic dates in yyyymmdd format after 20200202 are:")
   date := time.Date(2020, 2, 2, 0, 0, 0, 0, time.UTC)
   count := 0
   for count < 15 {
       date = date.AddDate(0, 0, 1)
       s := date.Format(layout)
       r := reverse(s)
       if r == s {
           fmt.Println(date.Format(layout2))
           count++
       }
   }

}</lang>

Output:
2021-12-02
2030-03-02
2040-04-02
2050-05-02
2060-06-02
2070-07-02
2080-08-02
2090-09-02
2101-10-12
2110-01-12
2111-11-12
2120-02-12
2121-12-12
2130-03-12
2140-04-12

Or, a more ambitious version. <lang go>package main

import (

   "fmt"
   "sort"
   "time"

)

func reverse(s string) string {

   chars := []rune(s)
   for i, j := 0, len(chars)-1; i < j; i, j = i+1, j-1 {
       chars[i], chars[j] = chars[j], chars[i]
   }
   return string(chars)

}

func findIndex(sl []string, s string) int {

   return sort.Search(len(sl), func(i int) bool {
       return sl[i] > s
   })

}

func main() {

   const (
       layout  = "20060102"
       layout2 = "2006-01-02"
   )
   palins := []string{}
   for i := 0; i < 10000; i++ {
       y := fmt.Sprintf("%04d", i)
       r := reverse(y)
       if r[:2] > "12" || r[2:] > "31" {
           continue
       }
       d := fmt.Sprintf("%s%s", y, r)
       t, err := time.Parse(layout, d)
       if err == nil {
           palins = append(palins, t.Format(layout2))
       }
   }
   le := len(palins)
   i1 := findIndex(palins, "1001-01-01")
   i2 := findIndex(palins, "2020-02-02")
   fmt.Printf("There are %d palindromic dates after 0000-01-01 of which:\n", le)
   fmt.Printf("          %d are after 1000-01-01\n", le-i1)
   fmt.Printf("          %d are after 2020-02-02\n", le-i2)
   fmt.Println("\nThe first 15 after 2020-02-02 are:")
   for i := 0; i < 15; i++ {
       if i != 0 && i%5 == 0 {
           fmt.Println()
       }
       fmt.Printf("%s   ", palins[i+i2])
   }
   fmt.Println("\n\nThe last 15 before 9999-12-31 are:")
   for i := 15; i >= 1; i-- {
       if i != 15 && i%5 == 0 {
           fmt.Println()
       }
       fmt.Printf("%s   ", palins[le-i])
   }
   fmt.Println()

}</lang>

Output:
There are 366 palindromic dates after 0000-01-01 of which:
          331 are after 1000-01-01
          284 are after 2020-02-02

The first 15 after 2020-02-02 are:
2021-12-02   2030-03-02   2040-04-02   2050-05-02   2060-06-02   
2070-07-02   2080-08-02   2090-09-02   2101-10-12   2110-01-12   
2111-11-12   2120-02-12   2121-12-12   2130-03-12   2140-04-12   

The last 15 before 9999-12-31 are:
9170-07-19   9180-08-19   9190-09-19   9201-10-29   9210-01-29   
9211-11-29   9220-02-29   9221-12-29   9230-03-29   9240-04-29   
9250-05-29   9260-06-29   9270-07-29   9280-08-29   9290-09-29  

Haskell

<lang haskell>import Data.Time.Calendar (Day, fromGregorianValid) import Data.List.Split (chunksOf) import Data.List (unfoldr) import Data.Tuple (swap) import Data.Bool (bool) import Data.Maybe (mapMaybe)

palinDates :: [Day] palinDates = mapMaybe palinDay [2021 .. 9999]

palinDay :: Integer -> Maybe Day palinDay y = fromGregorianValid y m d

 where
   [m, d] = unDigits <$> chunksOf 2 (reversedDecimalDigits (fromInteger y))

reversedDecimalDigits :: Int -> [Int] reversedDecimalDigits =

 unfoldr ((flip bool Nothing . Just . swap . flip quotRem 10) <*> (0 ==))

unDigits :: [Int] -> Int unDigits = foldl ((+) . (10 *)) 0

main :: IO () main = do

 let n = length palinDates
 putStrLn $ "Count of palindromic dates [2021..9999]: " ++ show n
 putStrLn "\nFirst 15:"
 mapM_ print $ take 15 palinDates
 putStrLn "\nLast 15:"
 mapM_ print $ take 15 (drop (n - 15) palinDates)</lang>
Output:
Count of palindromic dates [2021..9999]: 284

First 15:
2021-12-02
2030-03-02
2040-04-02
2050-05-02
2060-06-02
2070-07-02
2080-08-02
2090-09-02
2101-10-12
2110-01-12
2111-11-12
2120-02-12
2121-12-12
2130-03-12
2140-04-12

Last 15:
9170-07-19
9180-08-19
9190-09-19
9201-10-29
9210-01-29
9211-11-29
9220-02-29
9221-12-29
9230-03-29
9240-04-29
9250-05-29
9260-06-29
9270-07-29
9280-08-29
9290-09-29

Java

<lang java> import java.time.LocalDate; import java.time.format.DateTimeFormatter;

public class PalindromeDates {

   public static void main(String[] args) {
       LocalDate date = LocalDate.of(2020, 2, 3);
       DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
       DateTimeFormatter formatterDash = DateTimeFormatter.ofPattern("yyyy-MM-dd");
       System.out.printf("First 15 palindrome dates after 2020-02-02 are:%n");
       for ( int count = 0 ; count < 15 ; date = date.plusDays(1) ) {
           String dateFormatted = date.format(formatter);
           if ( dateFormatted.compareTo(new StringBuilder(dateFormatted).reverse().toString()) == 0 ) {
               count++;
               System.out.printf("date = %s%n", date.format(formatterDash));
           }
       }
   }

} </lang>

Output:
First 15 palindrome dates after 2020-02-02 are:
date = 2021-12-02
date = 2030-03-02
date = 2040-04-02
date = 2050-05-02
date = 2060-06-02
date = 2070-07-02
date = 2080-08-02
date = 2090-09-02
date = 2101-10-12
date = 2110-01-12
date = 2111-11-12
date = 2120-02-12
date = 2121-12-12
date = 2130-03-12
date = 2140-04-12

Julia

Uses the built-in Dates package to check date validity but not for iteration. <lang julia>using Dates

function datepalindromes(nextcount=20)

   println("Date palindromes:")
   count, d = 0, Date(1000, 1, 1)
   for year in 2021:9200
       try
           dig = digits(year)
           month = 10 * dig[1] + dig[2]
           day = 10 * dig[3] + dig[4]
           d = Date(year, month, day)
       catch
           continue
       end
       println(d)
       count += 1
       if count >= nextcount
           break
       end
   end

end

datepalindromes()

</lang>

Output:
Date palindromes:
2021-12-02
2030-03-02
2040-04-02
2050-05-02
2060-06-02
2070-07-02
2080-08-02
2090-09-02
2101-10-12
2110-01-12
2111-11-12
2120-02-12
2121-12-12
2130-03-12
2140-04-12
2150-05-12
2160-06-12
2170-07-12
2180-08-12
2190-09-12

Perl

Date calculation

The more robust solution, using a date/time module. <lang perl>use Time::Piece; my $d = Time::Piece->strptime("2020-02-02", "%Y-%m-%d");

for (my $k = 1 ; $k <= 15 ; $d += Time::Piece::ONE_DAY) {

   my $s = $d->strftime("%Y%m%d");
   if ($s eq reverse($s) and ++$k) {
       print $d->strftime("%Y-%m-%d\n");
   }

}</lang>

Output:
2020-02-02
2021-12-02
2030-03-02
2040-04-02
2050-05-02
2060-06-02
2070-07-02
2080-08-02
2090-09-02
2101-10-12
2110-01-12
2111-11-12
2120-02-12
2121-12-12
2130-03-12

String manipulation

Given the limited look-ahead required by the task, processing date-like strings can also work.

Library: ntheory

<lang perl>use strict; use warnings; use feature 'say'; use ntheory qw/forsetproduct/;

my $start = '2020-02-02' =~ s/-//gr; my($y) = substr($start,0,4);

my(@dates,$cnt); forsetproduct { push @dates, "@_" } [$y..$y+999],['01'..'12'],['01'..'31']; for (@dates) {

   (my $date = $_) =~ s/ //g;
   next unless $date > $start and $date eq reverse $date;
   say s/ /-/gr;
   last if 15 == ++$cnt;

}</lang>

Output:
2021-12-02
2030-03-02
2040-04-02
2050-05-02
2060-06-02
2070-07-02
2080-08-02
2090-09-02
2101-10-12
2110-01-12
2111-11-12
2120-02-12
2121-12-12
2130-03-12
2140-04-12

Phix

Note that parse_date_string() copes with 1/2/4 digit years, but (reasonably enough) throws a wobbly given 5-digit years and beyond. <lang Phix>include builtins\timedate.e sequence res = {} for d=2021 to 9999 do

   string s = sprintf("%4d",d),
          t = reverse(s)
   s &= "-"&t[1..2]&"-"&t[3..4]
   sequence td = parse_date_string(s, {"YYYY-MM-DD"})
   if timedate(td) then res = append(res,s) end if

end for printf(1,"Count of palindromic dates [2021..9999]: %d\n\n",length(res)) printf(1,"first 15:\n%s\n",join_by(res[1..15],3,5)) printf(1,"last 15:\n%s\n",join_by(res[-15..-1],3,5))</lang>

Output:
Count of palindromic dates [2021..9999]: 284

first 15:
2021-12-02   2050-05-02   2080-08-02   2110-01-12   2121-12-12
2030-03-02   2060-06-02   2090-09-02   2111-11-12   2130-03-12
2040-04-02   2070-07-02   2101-10-12   2120-02-12   2140-04-12

last 15:
9170-07-19   9201-10-29   9220-02-29   9240-04-29   9270-07-29
9180-08-19   9210-01-29   9221-12-29   9250-05-29   9280-08-29
9190-09-19   9211-11-29   9230-03-29   9260-06-29   9290-09-29

Python

Functional

Defined in terms of string reversal:

Works with: Python version 3.7

<lang python>Palindrome dates

from datetime import datetime from itertools import chain


  1. palinDay :: Int -> [ISO Date]

def palinDay(y):

   A possibly empty list containing the palindromic
      date for the given year, if such a date exists.
   
   s = str(y)
   r = s[::-1]
   iso = '-'.join([s, r[0:2], r[2:]])
   try:
       datetime.strptime(iso, '%Y-%m-%d')
       return [iso]
   except ValueError:
       return []


  1. --------------------------TEST---------------------------
  2. main :: IO ()

def main():

   Count and samples of palindromic dates [2021..9999]
   
   palinDates = list(chain.from_iterable(
       map(palinDay, range(2021, 10000))
   ))
   for x in [
           'Count of palindromic dates [2021..9999]:',
           len(palinDates),
           '\nFirst 15:',
           '\n'.join(palinDates[0:15]),
           '\nLast 15:',
           '\n'.join(palinDates[-15:])
   ]:
       print(x)


  1. MAIN ---

if __name__ == '__main__':

   main()</lang>
Output:
Count of palindromic dates [2021..9999]:
284

First 15:
2021-12-02
2030-03-02
2040-04-02
2050-05-02
2060-06-02
2070-07-02
2080-08-02
2090-09-02
2101-10-12
2110-01-12
2111-11-12
2120-02-12
2121-12-12
2130-03-12
2140-04-12

Last 15:
9170-07-19
9180-08-19
9190-09-19
9201-10-29
9210-01-29
9211-11-29
9220-02-29
9221-12-29
9230-03-29
9240-04-29
9250-05-29
9260-06-29
9270-07-29
9280-08-29
9290-09-29


Or, defined in terms of integer operations, rather than string reversals:

Works with: Python version 3.7

<lang python>Palindrome dates

from functools import reduce from itertools import chain from datetime import date


  1. palinDay :: Integer -> [ISO Date]

def palinDay(y):

   A possibly empty list containing the palindromic
      date for the given year, if such a date exists.
   
   [m, d] = [undigits(pair) for pair in chunksOf(2)(
       reversedDecimalDigits(y)
   )]
   return [] if (
       1 > m or m > 12 or 31 < d
   ) else validISODate((y, m, d))


  1. --------------------------TEST---------------------------
  2. main :: IO ()

def main():

   Count and samples of palindromic dates [2021..9999]
   
   palinDates = list(chain.from_iterable(
       map(palinDay, range(2021, 10000))
   ))
   for x in [
           'Count of palindromic dates [2021..9999]:',
           len(palinDates),
           '\nFirst 15:',
           '\n'.join(palinDates[0:15]),
           '\nLast 15:',
           '\n'.join(palinDates[-15:])
   ]:
       print(x)


  1. -------------------------GENERIC-------------------------
  1. Just :: a -> Maybe a

def Just(x):

   Constructor for an inhabited Maybe (option type) value.
      Wrapper containing the result of a computation.
   
   return {'type': 'Maybe', 'Nothing': False, 'Just': x}


  1. Nothing :: Maybe a

def Nothing():

   Constructor for an empty Maybe (option type) value.
      Empty wrapper returned where a computation is not possible.
   
   return {'type': 'Maybe', 'Nothing': True}


  1. chunksOf :: Int -> [a] -> a

def chunksOf(n):

   A series of lists of length n, subdividing the
      contents of xs. Where the length of xs is not evenly
      divible, the final list will be shorter than n.
   
   return lambda xs: reduce(
       lambda a, i: a + [xs[i:n + i]],
       range(0, len(xs), n), []
   ) if 0 < n else []


  1. reversedDecimalDigits :: Int -> [Int]

def reversedDecimalDigits(n):

   A list of the decimal digits of n,
      in reversed sequence.
   
   return unfoldr(
       lambda x: Nothing() if (
           0 == x
       ) else Just(divmod(x, 10))
   )(n)


  1. unDigits :: [Int] -> Int

def undigits(xs):

   An integer derived from a list of decimal digits
   
   return reduce(lambda a, x: a * 10 + x, xs, 0)


  1. unfoldr(lambda x: Just((x, x - 1)) if 0 != x else Nothing())(10)
  2. -> [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
  3. unfoldr :: (b -> Maybe (a, b)) -> b -> [a]

def unfoldr(f):

   Dual to reduce or foldr.
      Where catamorphism reduces a list to a summary value,
      the anamorphic unfoldr builds a list from a seed value.
      As long as f returns Just(a, b), a is prepended to the list,
      and the residual b is used as the argument for the next
      application of f.
      When f returns Nothing, the completed list is returned.
   
   def go(v):
       xr = v, v
       xs = []
       while True:
           mb = f(xr[0])
           if mb.get('Nothing'):
               return xs
           else:
               xr = mb.get('Just')
               xs.append(xr[1])
       return xs
   return go


  1. validISODate :: (Int, Int, Int) -> [Date]

def validISODate(ymd):

   A possibly empty list containing the
      ISO8601 string for a date, if that date exists.
   
   try:
       return [date(*ymd).isoformat()]
   except ValueError:
       return []


  1. MAIN ---

if __name__ == '__main__':

   main()</lang>
Output:
Count of palindromic dates [2021..9999]:
284

First 15:
2021-12-02
2030-03-02
2040-04-02
2050-05-02
2060-06-02
2070-07-02
2080-08-02
2090-09-02
2101-10-12
2110-01-12
2111-11-12
2120-02-12
2121-12-12
2130-03-12
2140-04-12

Last 15:
9170-07-19
9180-08-19
9190-09-19
9201-10-29
9210-01-29
9211-11-29
9220-02-29
9221-12-29
9230-03-29
9240-04-29
9250-05-29
9260-06-29
9270-07-29
9280-08-29
9290-09-29

Raku

(formerly Perl 6)

Works with: Rakudo version 2020.01

Pretty basic, but good enough. Could start earlier but 3/2/1 digit years require different handling that isn't necessary for this task. (And would be pretty pointless anyway assuming we need 2 digits for the month and two digits for the day. ISO:8601 anybody?)

<lang perl6>my $start = '1000-01-01';

my @palindate = {

    state $year = $start.substr(0,4);
    ++$year;
    my $m = $year.substr(2, 2).flip;
    my $d = $year.substr(0, 2).flip;
    next if not try Date.new("$year-$m-$d");
    "$year-$m-$d"

} … *;

my $date-today = Date.today; # 2020-02-02

my $k = @palindate.first: { Date.new($_) > $date-today }, :k;

say join "\n", @palindate[$k - 1 .. $k + 14];

say "\nTotal number of four digit year palindrome dates:\n" ~ my $four = @palindate.first( { .substr(5,1) eq '-' }, :k ); say "between {@palindate[0]} and {@palindate[$four - 1]}.";

my $five = @palindate.first: { .substr(6,1) eq '-' }, :k;

say "\nTotal number of five digit year palindrome dates:\n" ~ +@palindate[$four .. $five]</lang>

Output:
2020-02-02
2021-12-02
2030-03-02
2040-04-02
2050-05-02
2060-06-02
2070-07-02
2080-08-02
2090-09-02
2101-10-12
2110-01-12
2111-11-12
2120-02-12
2121-12-12
2130-03-12
2140-04-12

Total number of four digit year palindrome dates:
331
between 1001-10-01 and 9290-09-29.

Total number of five digit year palindrome dates:
3303

REXX

This REXX version works with   Regina REXX.

The   date   BIF   (with the   base   argument)   converts a date to the number of years since the beginning of
the Gregorian calendar,   the date is in the   ISO   format   (International Standards Organization   8601:2004). <lang rexx>/*REXX program finds & displays the next N palindromic dates starting after 2020─02─02*/ /* ───── */ parse arg n from . /*obtain optional argumets from the CL*/ if n== | n=="," then n= 15 /*Not specified? Then use the default.*/ if from== | from=="," then from= '2020-02-02' /* " " " " " " */

  1. = 0 /*the count of palindromic dates so far*/
    do j=date('Base', from, "ISO")+1 until #==n /*find palindromic dates 'til  N  found*/
    aDate= date('ISO', j, "Base")               /*convert a "base" date to ISO format. */
    $= space( translate(aDate, , '-'),  0)      /*elide the dashes  (-)  in this date. */
    if $\==reverse($)  then iterate             /*Not palindromic?  Then skip this date*/
    say 'a palindromic date: '        aDate     /*display a palindromic date ──► term. */
    #= # + 1                                    /*bump the counter of palindromic dates*/
    end   /*j*/                                 /*stick a fork in it,  we're all done. */</lang>
output   when using the default inputs:
a palindromic date:  2021-12-02
a palindromic date:  2030-03-02
a palindromic date:  2040-04-02
a palindromic date:  2050-05-02
a palindromic date:  2060-06-02
a palindromic date:  2070-07-02
a palindromic date:  2080-08-02
a palindromic date:  2090-09-02
a palindromic date:  2101-10-12
a palindromic date:  2110-01-12
a palindromic date:  2111-11-12
a palindromic date:  2120-02-12
a palindromic date:  2121-12-12
a palindromic date:  2130-03-12
a palindromic date:  2140-04-12

Ruby

<lang ruby>require 'date'

palindate = Enumerator.new do |yielder|

 ("2020"..).each do |y|
   m, d = y.reverse.scan(/../) # let the Y10K kids handle 5 digit years
   strings = [y, m, d]
   yielder << strings.join("-") if Date.valid_date?( *strings.map( &:to_i ) )
 end

end

puts palindate.take(15)</lang>

Output:
2020-02-02
2021-12-02
2030-03-02
2040-04-02
2050-05-02
2060-06-02
2070-07-02
2080-08-02
2090-09-02
2101-10-12
2110-01-12
2111-11-12
2120-02-12
2121-12-12
2130-03-12

Sidef

Translation of: Ruby

<lang ruby>var palindates = gather {

   for y in (2020 .. 9999) {
       var (m, d) = Str(y).flip.last(4).split(2)...
       with ([y,m,d].join('-')) {|t|
           take(t) if Date.valid(t, "%Y-%m-%d")
       }
   }

}

say "Count of palindromic dates [2020..9999]: #{palindates.len}"

for a,b in ([

   ["First 15:", palindates.head(15)],
   ["Last 15:",  palindates.tail(15)]

]) {

   say ("\n#{a}\n", b.slices(5).map { .join("   ") }.join("\n"))

}</lang>

Output:
Count of palindromic dates [2020..9999]: 285

First 15:
2020-02-02   2021-12-02   2030-03-02   2040-04-02   2050-05-02
2060-06-02   2070-07-02   2080-08-02   2090-09-02   2101-10-12
2110-01-12   2111-11-12   2120-02-12   2121-12-12   2130-03-12

Last 15:
9170-07-19   9180-08-19   9190-09-19   9201-10-29   9210-01-29
9211-11-29   9220-02-29   9221-12-29   9230-03-29   9240-04-29
9250-05-29   9260-06-29   9270-07-29   9280-08-29   9290-09-29

Wren

Library: Wren-fmt
Library: Wren-date

<lang ecmascript>import "/fmt" for Fmt import "/date" for Date

var isPalDate = Fn.new { |date|

   date = date.format(Date.rawDate)
   return date == date[-1..0]

}

Date.default = Date.isoDate System.print("The next 15 palindromic dates in yyyy-mm-dd format after 2020-02-02 are:") var date = Date.new(2020, 2, 2) var count = 0 while (count < 15) {

   date = date.addDays(1)
   if (isPalDate.call(date)) {
       System.print(date)
       count = count + 1
   }

}</lang>

Output:
The next 15 palindromic dates in yyyy-mm-dd format after 2020-02-02 are:
2021-12-02
2030-03-02
2040-04-02
2050-05-02
2060-06-02
2070-07-02
2080-08-02
2090-09-02
2101-10-12
2110-01-12
2111-11-12
2120-02-12
2121-12-12
2130-03-12
2140-04-12

zkl

<lang zkl>TD,date,n := Time.Date, T(2020,02,02), 15; while(n){

  ds:=TD.toYMDString(date.xplode()) - "-";
  if(ds==ds.reverse()){ n-=1; println(TD.toYMDString(date.xplode())); }
  date=TD.addYMD(date,0,0,1);

}</lang>

Output:
2020-02-02
2021-12-02
2030-03-02
2040-04-02
2050-05-02
2060-06-02
2070-07-02
2080-08-02
2090-09-02
2101-10-12
2110-01-12
2111-11-12
2120-02-12
2121-12-12
2130-03-12